Add draft threat model + SECURITY.md/AGENTS.md discoverability#769
Conversation
Generated-by: Claude Code
There was a problem hiding this comment.
Pull request overview
Adds security-documentation entry points to make Unomi’s security posture and triage guidance easier to discover (for humans and automated scanners), centered around a draft threat model intended for PMC review and refinement.
Changes:
- Added a draft
THREAT_MODEL.mdoutlining scope, boundaries, properties, and triage dispositions for security findings. - Added
SECURITY.mddescribing ASF-aligned private vulnerability reporting and linking to the threat model. - Added
AGENTS.mdto provide a machine-discoverable pointer chainAGENTS.md → SECURITY.md → THREAT_MODEL.md.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| THREAT_MODEL.md | Introduces a v0 threat model draft, including scope, assumptions, and triage dispositions for findings. |
| SECURITY.md | Adds a security policy with ASF reporting guidance and a link to the threat model. |
| AGENTS.md | Adds an agent-facing index pointing automated tools to SECURITY.md and the linked threat model. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
| | JSON-Schema event validation | schema validation of incoming events | — | **In — the input-validation defense** *(documented: manual `jsonSchema`)* | | ||
| | Admin REST + GraphQL APIs | `rest`, `graphql` | network (authenticated) | **In** *(documented: modules)* | | ||
| | Persistence | `persistence-elasticsearch` / `persistence-opensearch` | network → ES/OS backend | **In (Unomi's use of it); the backend's own security is operator's** *(inferred)* | | ||
| | Plugins / extensions / connectors | `plugins`, `extensions`, `connectors` | varies | **In core ones; third-party/`samples` out** *(inferred)* | |
| | REST / GraphQL admin | conditions, rules, segments, queries | **yes, within the authenticated credential's authority** | authn + authz; restrict who may author expressions | | ||
| | Condition / rule definitions | MVEL/OGNL expressions | **public: must be no; admin: yes-but-trusted** | keep expression authoring on the trusted side | | ||
| | Persistence queries | derived from the above | indirectly | backend hardening; query/scope isolation | | ||
| | Plugins / connectors config | operator-supplied | no — operator-trusted | vet third-party plugins | |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
sergehuber
left a comment
There was a problem hiding this comment.
Thanks for this contribution — the provenance-tagging approach (documented / inferred / maintainer) is excellent and the triage disposition table in §13 will be very useful for the PMC. A few findings below, ranging from substantive content gaps to minor advisory items.
Important: multi-tenancy trust boundary missing (THREAT_MODEL.md §2/§4/§8)
The UNOMI-139 multi-tenant platform core landed in master before this PR was opened. TenantService exists in the core API (api/src/main/java/org/apache/unomi/api/tenants/TenantService.java), Profile now carries tenantId, and SecurityService exposes getTenantEncryptionKey(tenantId). Tenant isolation is therefore a first-class concern in the current codebase.
The threat model covers "Profile/scope access control" in §8 but is silent on tenant isolation: whether an authenticated API client for tenant A can access or corrupt tenant B's profiles, segments, or rules. This is a new trust boundary not present in previous releases. It warrants at minimum an open question in §14 Wave 3, and potentially a §8 property claim if the PMC asserts that tenant isolation is enforced by Unomi itself.
Suggested addition to §14 Wave 3:
"What is the tenant isolation model? Can an API client with credentials for tenant A read or modify tenant B's profiles, segments, or rules — and is this boundary enforced by Unomi or delegated to the integrator?"
Important: /eventcollector endpoint missing from public boundary (THREAT_MODEL.md §2/§4/§6)
The model consistently refers to the public ingestion surface as the "/context.json-style" endpoint, but there is a second equally public servlet: EventsCollectorServlet (wab/src/main/java/org/apache/unomi/web/EventsCollectorServlet.java), mounted at /eventcollector, which handles batch event submission from browsers/clients without authentication. Both surfaces accept untrusted event JSON and feed the same processing pipeline.
Naming only /context.json in §2 (component table), §4 (trust boundary), §6 (inputs table), and §7 (adversary model) means a security scanner that finds /eventcollector and consults this model would land in MODEL-GAP rather than being correctly routed to VALID or KNOWN-NON-FINDING.
Suggested fix throughout: "Public context ingestion: /context.json and /eventcollector (EventsCollectorServlet)"
Advisory: SECURITY.md — private list routing
The ASF security process routes security@apache.org reports internally to the project's private list (private@unomi.apache.org). Mentioning this routing destination (as several other ASF projects do in their SECURITY.md) would help reporters and automated tools understand where the report lands without having to know ASF's internal triage flow. Not a policy error — just a useful precision.
Advisory: THREAT_MODEL.md §5a — naming the protected-events mechanism
The §5a description of the "protected events / third-party-server key concept" is accurate — ThirdPartyServer in services/src/main/java/org/apache/unomi/services/impl/events/ThirdPartyServer.java confirms the mechanism (server id, key, IP allow-list, allowed event types). The §14 Wave 2 question 5 already asks for PMC confirmation, which is the right ask. One small suggestion: naming the key field in the question (e.g. "the third-party server key config in configuration.adoc") would help the PMC immediately know which properties to look up.
| limitations under the License. | ||
| --> | ||
|
|
||
| # Agent Guide for unomi |
There was a problem hiding this comment.
Agree with the Copilot finding: the header should read # Agent Guide for Apache Unomi to match the canonical project name used throughout all three files and in the GitHub repo metadata. Automated security scanners that pattern-match on project names will look for "Apache Unomi", not "unomi".
|
Thanks @sergehuber — exactly the kind of review that makes the model accurate; all four are going in. Tenant isolation (UNOMI-139) — agreed, important. You're right that multi-tenancy is now a first-class boundary (
SECURITY.md — adding the I'll push the revision with all four folded in. Thanks again. |
…nant isolation, ThirdPartyServer config, private-list routing - Name the second public ingestion endpoint /eventcollector (EventsCollectorServlet) alongside /context.json in the component table and adversary client (so a scanner landing on it routes correctly instead of MODEL-GAP). - Add a Wave-3 open question on tenant isolation (UNOMI-139: TenantService, Profile.tenantId, SecurityService.getTenantEncryptionKey) - a new trust boundary. - Name the ThirdPartyServer key config (server-id / key / IP allow-list / allowed-event-types in configuration.adoc) in the Wave-2 protected-events question. - SECURITY.md: note ASF routes Unomi reports to private@unomi.apache.org. Generated-by: Claude Opus 4.8 (1M context)
What this is
A draft threat model for Apache Unomi, proposed by the ASF Security team for the Unomi PMC to review, correct, or reject. It is a starting point for discussion, not a finished document — drafted by the Security team's threat-model tooling from Unomi's public docs, repository, and published CVE advisories, following the ASF Security threat-model rubric.
This PR:
THREAT_MODEL.md— the draft model;SECURITY.md— a short security policy that links the threat model;AGENTS.mdwith a## Securitysection, so the chainAGENTS.md → SECURITY.md → THREAT_MODEL.mdis mechanically discoverable by automated security scanners.How to read it
Every claim is provenance-tagged: (documented) (from Unomi's docs/repo/CVE advisories), (inferred) (reasoned from architecture/history, not yet confirmed), (maintainer) (confirmed by the PMC). This v0 is ~16 documented / ~30 inferred. The §14 Open questions section collects every inferred claim into waves for the PMC to confirm or correct — that is where review time is best spent. The model is built around Unomi's documented threat history (the OGNL/MVEL expression-evaluation CVEs) and treats the public context endpoint as the primary boundary, with JSON-Schema event validation and the post-CVE expression restrictions as the defenses. The highest-impact open questions:
VALIDwhen public input reaches it (wave 3).Nothing here is a requirement — the model is for the PMC to own. Comment inline, edit the branch, or reply on the email thread.