afs commented on code in PR #3966:
URL: https://github.com/apache/jena/pull/3966#discussion_r3426397793


##########
THREAT_MODEL.md:
##########
@@ -0,0 +1,212 @@
+<!--
+SPDX-License-Identifier: Apache-2.0
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+# Apache Jena — Threat Model (v1)
+
+## §1 Header
+
+- **Project:** Apache Jena (`apache/jena`), `main`, against which this draft 
was written. A monorepo: the RDF/SPARQL Java framework (`jena-core`, 
`jena-arq`, `jena-base`, RIOT parsers, `jena-tdb1`/`jena-tdb2` stores, SHACL 
(`jena-shacl`), ShEx (`jena-shex`), GeoSPARQL `jena-geosparql`, text index 
`jena-text` **and** the Fuseki HTTP server (`jena-fuseki2`).
+- **Date:** 2026-06-02 (v0); 2026-06 (v1, PMC-reviewed). **Status:** v1 — 
reviewed by the Jena PMC (Rob Vesse + Andy Seaborne) on apache/jena#3966; their 
inline suggestions are folded in and their answers to the §14 questions promote 
the load-bearing claims to *(maintainer)* (see §14). **Author:** ASF Security 
team (drafted via the Scovetta rubric); now PMC-reviewed.
+- **Version binding:** versioned with the project; a report against version 
*N* is triaged against the model as it stood at *N*.
+- **Reporting cross-reference:** §8-property violations → report privately per 
ASF process (`[email protected]` → `[email protected]`); §3/§9 findings 
are closed citing this document.
+- **Provenance legend:** *(documented)* = Jena's own docs/repo; *(maintainer)* 
= confirmed by a Jena PMC member through this process (andy@ has ratified 
destination + the help-with-model request); *(inferred)* = reasoned from 
architecture, not yet confirmed — each has a matching §14 open question.
+- **Confidence:** v1, PMC-reviewed — rvesse + afs answered every §14 question 
and folded their inline edits in; the high-value query-surface claims 
(SERVICE/SSRF, file/URI, JS functions, XXE, JSON-LD remote-context, the 
resource line) are now *(maintainer)*.
+- **What Jena is:** Apache Jena is a Java framework for building Semantic-Web 
/ linked-data applications using RDF and SPARQL. It provides an in-process API 
to RDF data held in memory or in a native store (TDB), the ARQ SPARQL 
query/update engine, RIOT parsers/serialisers for RDF syntaxes (Turtle, 
RDF/XML, JSON-LD, N-Triples, …), and **Fuseki** — a standalone HTTP server 
exposing SPARQL query, SPARQL Update, and the Graph Store Protocol over the 
network. *(documented — README, jena.apache.org; maintainer — andy@ 2026-06-01: 
"an HTTP-based data server (Fuseki) and a Java API to RDF data stored in memory 
and in a custom database")*
+
+## §2 Scope and intended use
+
+- **Two deployment shapes** *(maintainer — andy@)*:
+  - **Fuseki** — a long-running **HTTP server** that answers SPARQL Query and 
SPARQL Update as well as the SPARQL Graph Store Protocol (read and read-write 
forms) over the network. The primary network trust surface.
+  - **The Jena Java API** — `jena-core`/`jena-arq`/TDB embedded **in-process** 
in another application. Trusted caller; the bytes/queries it feeds Jena are 
that application's responsibility.
+- **Caller roles** (Fuseki is a network service — the role splits):
+  - **anonymous SPARQL client** — issues SPARQL queries over HTTP. 
**Default-public for SPARQL query** *(documented — Fuseki security docs: 
"SPARQL endpoints are open to the public but administrative functions are 
limited to localhost")*.
+  - **authenticated user / admin** — gated by Apache Shiro (`shiro.ini`); 
admin functions (`/$/*`) restricted to localhost by default *(documented)*.
+  - **operator/deployer** — configures Shiro, datasets, TDB location, and 
which endpoints are read-only vs updatable. **Trusted.** *(inferred)*
+  - **embedding application** (Java API) — trusted; supplies queries/RDF to 
the library. *(inferred)*
+
+**Component-family table** *(monorepo; in/out of model):*
+
+| Family | Entry point | Touches OS/network | In model? |
+| --- | --- | --- | --- |
+| Fuseki HTTP server | `jena-fuseki2` — SPARQL query / Update / Graph Store 
Protocol, admin `/$/*` | network (listens) | **In — primary boundary** 
*(documented)* |
+| SPARQL engine (ARQ) | `jena-arq` — query/update eval, `SERVICE` federation, 
custom functions | network out (SERVICE), file (file: URLs) | **In — high 
value** *(inferred)* |
+| RDF I/O (RIOT) | `jena-arq`/`jena-core` parsers (RDF/XML, Turtle, JSON-LD, 
…) | parses untrusted RDF | **In — XXE / parser-DoS surface** *(inferred)* |
+| Stores + text index | `jena-tdb1`, `jena-tdb2`; **`jena-text` (Lucene)** | 
filesystem | **In.** On-disk store is operator-trusted and private to the 
owning process *(maintainer)*; the Lucene text index is reachable from SPARQL 
via `text:query` — an in-model query surface *(maintainer — afs flagged 
jena-text)* |
+| IRI / langtag | `jena-iri3986`, `jena-langtag`, `jena-base` | none | **In 
(input parsing)** *(inferred)* |
+| Extensions | `jena-geosparql`, `jena-serviceenhancer` | SERVICE | **In 
(reachable from queries)** *(inferred)* |
+| Validations | `jena-shacl`, `jena-shex` | HTTP GET requests |
+| Client/API helpers | `jena-rdfconnection`, `jena-querybuilder`, 
`jena-rdfpatch`, `jena-commonsrdf`, `jena-ontapi` | none | **In as libraries 
(memory/correctness)** *(inferred)* |
+| CLI tools | `jena-cmds` | filesystem | **In iff fed untrusted input; usually 
operator-run** *(inferred)* |
+| Examples / tests / benchmarks | `jena-examples`, `jena-integration-tests`, 
`jena-benchmarks` | n/a | **Out** *(see §3)* |
+
+## §3 Out of scope (explicit non-goals)
+
+- **`jena-examples`, `jena-integration-tests`, `jena-benchmarks`** — 
illustrative/test, not production. *(inferred)*
+- **Attackers who control the host, the Fuseki config (`shiro.ini`, dataset 
config), the TDB data directory, or the embedding Java application.** 
Operator-trusted. *(inferred)*
+- **The embedding application's own use of the Java API** — if an app feeds 
attacker-controlled SPARQL it built by string-concatenation to ARQ, that 
injection is the app's bug, not Jena's (analogous to SQL injection in a JDBC 
caller). *(inferred)*
+- **Generic DoS / query-complexity exhaustion** beyond a to-be-confirmed line 
— Andy raised resource-volume as a concern; the §8 resource line + §14 frame 
it. *(inferred)*
+- **Confidentiality of RDF data at rest / TLS on the wire** — operator 
deployment (reverse proxy for TLS; filesystem perms for TDB). *(inferred)*
+
+## §4 Trust boundaries and data flow
+
+- **Primary boundary: the Fuseki SPARQL endpoint.** Queries arrive over HTTP 
from (by default) **anonymous** clients. The boundary question is what an 
anonymous/low-privilege SPARQL query can reach: read data it shouldn't, 
**write** (SPARQL Update / GSP) without authorisation, make Fuseki issue 
outbound requests (`SERVICE` → SSRF), read local files (`file:` URLs / FROM), 
execute code (ARQ custom/JavaScript functions if enabled), or exhaust 
resources. *(inferred; public-query default documented)*
+- **Admin boundary:** the `/$/*` admin surface is localhost-only by default 
*(documented)*; exposing it to the network (without configuring 
authentication/authorisation) is an operator misconfiguration.
+- **RDF-parse boundary:** any endpoint that **parses** caller-supplied RDF 
(Update bodies, GSP PUT/POST, content negotiation) runs RIOT on untrusted 
bytes. RDF/XML external-entity (XXE) processing is **off by default** 
*(maintainer — afs)*. **JSON-LD `@context` resolution, however, fetches remote 
files by default** — a remote-read/SSRF surface inherited from the JSON-LD 
dependency (W3C JSON-LD WG mitigation in progress); safer than XXE but real 
*(maintainer — afs)*. Plus the general parser-DoS surface *(inferred)*.
+- **Reachability preconditions:**
+  - A finding in ARQ/RIOT/stores is **in-model** iff reachable from a Fuseki 
request at the relevant role (default: anonymous query; authenticated for 
Update). *(inferred)*
+  - A finding reachable only through the **in-process Java API** with 
caller-supplied trusted input is `OUT-OF-MODEL: trusted-input` (the embedding 
app owns it). *(inferred)*
+  - A finding requiring operator config (`shiro.ini`, exposing admin, enabling 
JS functions) is `OUT-OF-MODEL: trusted-input` / `non-default-build`. 
*(inferred)*
+
+## §5 Assumptions about the environment
+
+- **Runtime:** JVM (Java; "old in places" per andy@). *(maintainer)*
+- **Fuseki auth:** Apache Shiro via `$FUSEKI_BASE/shiro.ini`; changing it 
needs a restart *(documented — Fuseki security docs)*.
+- **Store:** TDB1/TDB2 on the local filesystem, private to the owning 
Fuseki/JVM process, multiple processes accessing a single store location 
prevented by code. *(maintainer)*
+- **Network:** TLS is the deployer's (reverse proxy); Fuseki's bundled example 
setup is plaintext *(documented — "no TLS, passwords in plain text")*.
+- **Negative side-effects inventory** (inferred — wave-1/2 target): Fuseki 
listens on HTTP; ARQ can make **outbound** network requests via `SERVICE` 
(federation), `SERVICE` can be disabled by operator in configuration; ARQ can 
read **`file:`/http: URLs** named in queries (FROM/FROM NAMED/SERVICE); RIOT 
parses untrusted RDF; ARQ may execute **custom/JavaScript functions** if the 
operator enabled them; TDB reads/writes the data directory. *(inferred — these 
are the load-bearing confirmations)*
+
+## §5a Build-time and configuration variants
+
+Security-relevant configuration *(Fuseki auth documented; the rest inferred — 
confirm defaults):*
+
+| Knob | Default | Effect / stance |
+| --- | --- | --- |
+| Fuseki Shiro auth (`shiro.ini`) | SPARQL **query** public; admin `/$/*` 
**localhost-only** | *(documented)* Restricting query access requires Shiro 
`[urls]` ACLs. |
+| Fuseki example user setup | `admin`/`pw`, plaintext, no TLS | *(documented)* 
explicitly "not recommended for production". Any "default admin/pw in prod" 
report → `OUT-OF-MODEL: non-default-build`. |
+| SPARQL **Update** / Graph Store write | per-dataset (read-only vs read-write 
service) — **default to confirm** | *(inferred)* If a dataset ships 
update-enabled + unauthenticated, anonymous write is in-model; if read-only by 
default, anonymous write is not reachable. **Wave-1 question.** |
+| `SERVICE` (federated query) | may be disabled by operator config 
**(documented)** | *(inferred)* SSRF surface
+| ARQ **JavaScript / custom functions** | opt-in feature, requires explicit 
operator config of both Fuseki and JVM | *(inferred)* If enabled, SPARQL can 
execute code, executable JS functions controlled by explicit white list 
*(documented)*, some JS functions, e.g. `eval()`, are explicitly blacklisted 
regardless of whitelist → by-design-if-operator-enabled, like a trusted 
extension.  Java custom functions require explicit operator configuration of 
class path, if added to class path operator responsibility to verify function 
code is safe |
+| RDF/XML & external-entity handling in RIOT | XXE off | *(inferred)* Whether 
external entities / `file:` access are disabled by default in the parsers. |
+| JSON_LD & external context handling in RIOT | On | Accessed by http/https or 
local file. |
+| Query timeout / result limits | query timeout configurable at server or 
per-dataset level *(documented)* | *(inferred)* the resource/DoS lever (Andy's 
concern). |
+
+## §6 Assumptions about inputs
+
+Per-surface trust table *(Fuseki defaults documented; the rest inferred):*
+
+| Surface | Input | Attacker-controllable? | Caller/operator must enforce |
+| --- | --- | --- | --- |
+| Fuseki SPARQL query endpoint | SPARQL query text | **yes (anonymous by 
default)** | Shiro ACLs if data is sensitive; SERVICE/file/JS-function 
restrictions; query timeout |
+| Fuseki SPARQL Update / GSP | update text / RDF body | **yes — must be 
authorised** | read-only-by-default or Shiro-gated write; RDF parse hardening |
+| RDF parse (RIOT) anywhere | RDF/XML, Turtle, JSON-LD, … | **yes** | RDF/XML 
XXE off by default *(maintainer)*; bounded nesting/size |
+| JSON-LD `@context` resolution (RIOT) | `@context` URL | **yes** | 
remote-context fetch is **on by default** (SSRF / remote-read) — restrict on 
untrusted-input endpoints *(maintainer — afs)* |
+| `SERVICE <url>` (federation) | target URL | **yes — enabled by default; SSRF 
is conceded `VALID`** *(maintainer)* | no allow-list yet — disable `SERVICE` or 
add egress controls on untrusted endpoints |
+| `FROM` / `FROM NAMED` URI | dataset URI | **dataset-impl-dependent** 
*(maintainer)* | with TDB2 these are in-dataset graph names, **not fetched**; 
only an arbitrary-URI-configured dataset reads `file:`/remote |
+| Fuseki admin `/$/*` | dataset mgmt, backups | **must not be on the public 
net** | localhost-only (default) / operator network |
+| Java API (`QueryExecution`, `Model.read`) | query / RDF from the app | no — 
the embedding app's trust | app validates its own untrusted inputs |
+
+- **Size/shape/rate:** query-cost / result-size / parser-nesting bounds; §8 
resource line. *(inferred)*
+
+## §7 Adversary model
+
+- **Anonymous SPARQL client (primary)** — can reach Fuseki's public query 
endpoint; goals: read non-public graphs, write via an exposed Update endpoint, 
SSRF via `SERVICE`, local-file read via `file:`/FROM, code execution via JS 
functions (if enabled), resource exhaustion via expensive queries. *(inferred; 
public-query default documented)*
+- **Authenticated low-privilege user** — bounded by Shiro/dataset ACLs; goal: 
exceed them. *(inferred)*
+- **Crafted-RDF attacker** — supplies malicious RDF (RDF/XML XXE, 
deeply-nested/oversized documents) to any parse path. *(inferred)*
+- **Out of scope:** operator/host control; the embedding app supplying its own 
trusted input; anyone who can edit `shiro.ini` or enable JS functions. 
*(inferred)*
+
+## §8 Security properties the project provides
+
+*(All inferred pending PMC confirmation except where Fuseki defaults are 
documented.)*
+
+- **Admin surface is localhost-bound by default.** Fuseki's `/$/*` admin 
functions are not reachable from the network unless the operator exposes them. 
*Violation symptom:* an admin function reachable anonymously over the network 
in the default config. *Severity:* CVE-class. *(documented — Fuseki security 
docs)*
+- **Shiro access control is enforced when configured.** A Shiro `[urls]` ACL 
restricting an endpoint cannot be bypassed by request manipulation. *Violation 
symptom:* a restricted endpoint reached without satisfying its Shiro rule. 
*Severity:* CVE-class. *(inferred)*
+- **SPARQL operations cannot escape the dataset's authorised scope.** An 
anonymous/low-priv query cannot read graphs, write data, reach the filesystem, 
or make Fuseki act as an SSRF proxy beyond what the dataset config permits. 
*Violation symptom:* SERVICE-SSRF, `file:` read, cross-graph read, or 
unauthorised write from an in-scope query. *Severity:* CVE-class. *(inferred — 
the core boundary to ratify; these are the classic Jena CVE classes)*
+- **RDF parsing is safe against untrusted documents.** RIOT parsing of 
attacker RDF does not resolve external entities (XXE), execute code, or 
recurse/allocate unboundedly. *Violation symptom:* XXE, SSRF, or DoS from a 
parsed RDF document. *Severity:* CVE-class. *(inferred)*
+- **Resource bounds.** Expensive SPARQL queries, or large RDF bodies are an 
operator-tuned limit (query timeout). *(maintainer)*
+
+## §9 Security properties the project does *not* provide
+
+- **No protection if the operator exposes the admin surface, ships the example 
`admin`/`pw` setup, or runs without TLS** — deployment hardening (pending §5a 
rulings). *(documented that the example setup is not for production)*
+- **No defense when ARQ JavaScript/custom functions are enabled on an 
untrusted endpoint** — JS functions are opt-in with an explicit allow-list 
(`eval()` etc. blacklisted regardless); custom Java functions require the 
operator to add trusted code to the class path. Reachable code execution is 
**`by-design-operator-enabled`** *(maintainer — rvesse)*. **False friend:** a 
SPARQL endpoint being "read-only" does not by itself prevent SSRF (`SERVICE`) 
unless `SERVICE` is separately restricted.
+- **No SPARQL-injection defense for the embedding application** — an app that 
concatenates untrusted input into a query string owns that bug (use 
parameterised queries / `QueryBuilder`). *(inferred)*
+- **No transport security / authentication unless the operator configures 
Shiro + TLS.** *(documented/inferred)*
+- **No generic-DoS / query-complexity guarantee** beyond operator-set query 
timeouts + reverse-proxy size limits; a trivial query can compute a huge 
cross-product, which is inherent to spec-compliant SPARQL, not a Jena bug 
*(maintainer — rvesse)*.
+- **Well-known classes — note the split:** **SSRF via `SERVICE`** from an 
anonymous query against the default config is a **conceded `VALID` attack 
vector** *(maintainer — rvesse)*, not merely operator-disclaimed (there is no 
allow-list yet; operators must disable `SERVICE` or add egress controls). Left 
to the caller/operator: SPARQL injection (embedding app), 
algorithmic-complexity DoS (operator timeouts), and — only for a dataset 
explicitly configured for arbitrary-URI access — `file:`/remote read. With a 
TDB2 store, `FROM`/`FROM NAMED` access only in-dataset graphs and do **not** 
fetch URIs *(maintainer — rvesse)*. JSON-LD `@context` remote fetch is a 
separate remote-read surface (§4/§6).
+
+## §10 Downstream responsibilities (operator/deployer)
+
+- **Put Fuseki behind auth (Shiro) + TLS** before exposing sensitive data; 
never ship the example `admin`/`pw` setup to production. *(documented)*
+- **Keep the admin `/$/*` surface localhost-only / operator-network.** 
*(documented)*
+- **Make datasets read-only unless write is intended**, and gate SPARQL Update 
/ GSP behind Shiro. *(inferred)*
+- **Restrict or disable `SERVICE` federation and `file:` access** on endpoints 
reachable by untrusted clients (SSRF / local-file). *(inferred)*
+- **Do not enable ARQ JavaScript/custom functions on untrusted endpoints.** 
*(inferred)*
+- **Set query timeouts / result-size limits** appropriate to capacity (the 
volume lever). *(inferred)*
+- **Use parameterised queries** (`QueryBuilder`/parameterised 
`QueryExecution`) in embedding apps; never string-concatenate untrusted input 
into SPARQL. *(inferred)*
+
+## §11 Known misuse patterns
+
+*(Draft one-liners — expand before publishing.)*

Review Comment:
   Presumably remove for final first version
   
   ```suggestion
   ```



##########
THREAT_MODEL.md:
##########
@@ -0,0 +1,212 @@
+<!--
+SPDX-License-Identifier: Apache-2.0
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+# Apache Jena — Threat Model (v1)
+
+## §1 Header
+
+- **Project:** Apache Jena (`apache/jena`), `main`, against which this draft 
was written. A monorepo: the RDF/SPARQL Java framework (`jena-core`, 
`jena-arq`, `jena-base`, RIOT parsers, `jena-tdb1`/`jena-tdb2` stores, SHACL 
(`jena-shacl`), ShEx (`jena-shex`), GeoSPARQL `jena-geosparql`, text index 
`jena-text` **and** the Fuseki HTTP server (`jena-fuseki2`).
+- **Date:** 2026-06-02 (v0); 2026-06 (v1, PMC-reviewed). **Status:** v1 — 
reviewed by the Jena PMC (Rob Vesse + Andy Seaborne) on apache/jena#3966; their 
inline suggestions are folded in and their answers to the §14 questions promote 
the load-bearing claims to *(maintainer)* (see §14). **Author:** ASF Security 
team (drafted via the Scovetta rubric); now PMC-reviewed.
+- **Version binding:** versioned with the project; a report against version 
*N* is triaged against the model as it stood at *N*.
+- **Reporting cross-reference:** §8-property violations → report privately per 
ASF process (`[email protected]` → `[email protected]`); §3/§9 findings 
are closed citing this document.
+- **Provenance legend:** *(documented)* = Jena's own docs/repo; *(maintainer)* 
= confirmed by a Jena PMC member through this process (andy@ has ratified 
destination + the help-with-model request); *(inferred)* = reasoned from 
architecture, not yet confirmed — each has a matching §14 open question.
+- **Confidence:** v1, PMC-reviewed — rvesse + afs answered every §14 question 
and folded their inline edits in; the high-value query-surface claims 
(SERVICE/SSRF, file/URI, JS functions, XXE, JSON-LD remote-context, the 
resource line) are now *(maintainer)*.
+- **What Jena is:** Apache Jena is a Java framework for building Semantic-Web 
/ linked-data applications using RDF and SPARQL. It provides an in-process API 
to RDF data held in memory or in a native store (TDB), the ARQ SPARQL 
query/update engine, RIOT parsers/serialisers for RDF syntaxes (Turtle, 
RDF/XML, JSON-LD, N-Triples, …), and **Fuseki** — a standalone HTTP server 
exposing SPARQL query, SPARQL Update, and the Graph Store Protocol over the 
network. *(documented — README, jena.apache.org; maintainer — andy@ 2026-06-01: 
"an HTTP-based data server (Fuseki) and a Java API to RDF data stored in memory 
and in a custom database")*
+
+## §2 Scope and intended use
+
+- **Two deployment shapes** *(maintainer — andy@)*:
+  - **Fuseki** — a long-running **HTTP server** that answers SPARQL Query and 
SPARQL Update as well as the SPARQL Graph Store Protocol (read and read-write 
forms) over the network. The primary network trust surface.
+  - **The Jena Java API** — `jena-core`/`jena-arq`/TDB embedded **in-process** 
in another application. Trusted caller; the bytes/queries it feeds Jena are 
that application's responsibility.
+- **Caller roles** (Fuseki is a network service — the role splits):
+  - **anonymous SPARQL client** — issues SPARQL queries over HTTP. 
**Default-public for SPARQL query** *(documented — Fuseki security docs: 
"SPARQL endpoints are open to the public but administrative functions are 
limited to localhost")*.
+  - **authenticated user / admin** — gated by Apache Shiro (`shiro.ini`); 
admin functions (`/$/*`) restricted to localhost by default *(documented)*.
+  - **operator/deployer** — configures Shiro, datasets, TDB location, and 
which endpoints are read-only vs updatable. **Trusted.** *(inferred)*
+  - **embedding application** (Java API) — trusted; supplies queries/RDF to 
the library. *(inferred)*
+
+**Component-family table** *(monorepo; in/out of model):*
+
+| Family | Entry point | Touches OS/network | In model? |
+| --- | --- | --- | --- |
+| Fuseki HTTP server | `jena-fuseki2` — SPARQL query / Update / Graph Store 
Protocol, admin `/$/*` | network (listens) | **In — primary boundary** 
*(documented)* |
+| SPARQL engine (ARQ) | `jena-arq` — query/update eval, `SERVICE` federation, 
custom functions | network out (SERVICE), file (file: URLs) | **In — high 
value** *(inferred)* |
+| RDF I/O (RIOT) | `jena-arq`/`jena-core` parsers (RDF/XML, Turtle, JSON-LD, 
…) | parses untrusted RDF | **In — XXE / parser-DoS surface** *(inferred)* |
+| Stores + text index | `jena-tdb1`, `jena-tdb2`; **`jena-text` (Lucene)** | 
filesystem | **In.** On-disk store is operator-trusted and private to the 
owning process *(maintainer)*; the Lucene text index is reachable from SPARQL 
via `text:query` — an in-model query surface *(maintainer — afs flagged 
jena-text)* |
+| IRI / langtag | `jena-iri3986`, `jena-langtag`, `jena-base` | none | **In 
(input parsing)** *(inferred)* |
+| Extensions | `jena-geosparql`, `jena-serviceenhancer` | SERVICE | **In 
(reachable from queries)** *(inferred)* |
+| Validations | `jena-shacl`, `jena-shex` | HTTP GET requests |
+| Client/API helpers | `jena-rdfconnection`, `jena-querybuilder`, 
`jena-rdfpatch`, `jena-commonsrdf`, `jena-ontapi` | none | **In as libraries 
(memory/correctness)** *(inferred)* |
+| CLI tools | `jena-cmds` | filesystem | **In iff fed untrusted input; usually 
operator-run** *(inferred)* |
+| Examples / tests / benchmarks | `jena-examples`, `jena-integration-tests`, 
`jena-benchmarks` | n/a | **Out** *(see §3)* |
+
+## §3 Out of scope (explicit non-goals)
+
+- **`jena-examples`, `jena-integration-tests`, `jena-benchmarks`** — 
illustrative/test, not production. *(inferred)*
+- **Attackers who control the host, the Fuseki config (`shiro.ini`, dataset 
config), the TDB data directory, or the embedding Java application.** 
Operator-trusted. *(inferred)*
+- **The embedding application's own use of the Java API** — if an app feeds 
attacker-controlled SPARQL it built by string-concatenation to ARQ, that 
injection is the app's bug, not Jena's (analogous to SQL injection in a JDBC 
caller). *(inferred)*
+- **Generic DoS / query-complexity exhaustion** beyond a to-be-confirmed line 
— Andy raised resource-volume as a concern; the §8 resource line + §14 frame 
it. *(inferred)*
+- **Confidentiality of RDF data at rest / TLS on the wire** — operator 
deployment (reverse proxy for TLS; filesystem perms for TDB). *(inferred)*
+
+## §4 Trust boundaries and data flow
+
+- **Primary boundary: the Fuseki SPARQL endpoint.** Queries arrive over HTTP 
from (by default) **anonymous** clients. The boundary question is what an 
anonymous/low-privilege SPARQL query can reach: read data it shouldn't, 
**write** (SPARQL Update / GSP) without authorisation, make Fuseki issue 
outbound requests (`SERVICE` → SSRF), read local files (`file:` URLs / FROM), 
execute code (ARQ custom/JavaScript functions if enabled), or exhaust 
resources. *(inferred; public-query default documented)*
+- **Admin boundary:** the `/$/*` admin surface is localhost-only by default 
*(documented)*; exposing it to the network (without configuring 
authentication/authorisation) is an operator misconfiguration.
+- **RDF-parse boundary:** any endpoint that **parses** caller-supplied RDF 
(Update bodies, GSP PUT/POST, content negotiation) runs RIOT on untrusted 
bytes. RDF/XML external-entity (XXE) processing is **off by default** 
*(maintainer — afs)*. **JSON-LD `@context` resolution, however, fetches remote 
files by default** — a remote-read/SSRF surface inherited from the JSON-LD 
dependency (W3C JSON-LD WG mitigation in progress); safer than XXE but real 
*(maintainer — afs)*. Plus the general parser-DoS surface *(inferred)*.
+- **Reachability preconditions:**
+  - A finding in ARQ/RIOT/stores is **in-model** iff reachable from a Fuseki 
request at the relevant role (default: anonymous query; authenticated for 
Update). *(inferred)*
+  - A finding reachable only through the **in-process Java API** with 
caller-supplied trusted input is `OUT-OF-MODEL: trusted-input` (the embedding 
app owns it). *(inferred)*
+  - A finding requiring operator config (`shiro.ini`, exposing admin, enabling 
JS functions) is `OUT-OF-MODEL: trusted-input` / `non-default-build`. 
*(inferred)*
+
+## §5 Assumptions about the environment
+
+- **Runtime:** JVM (Java; "old in places" per andy@). *(maintainer)*
+- **Fuseki auth:** Apache Shiro via `$FUSEKI_BASE/shiro.ini`; changing it 
needs a restart *(documented — Fuseki security docs)*.
+- **Store:** TDB1/TDB2 on the local filesystem, private to the owning 
Fuseki/JVM process, multiple processes accessing a single store location 
prevented by code. *(maintainer)*
+- **Network:** TLS is the deployer's (reverse proxy); Fuseki's bundled example 
setup is plaintext *(documented — "no TLS, passwords in plain text")*.
+- **Negative side-effects inventory** (inferred — wave-1/2 target): Fuseki 
listens on HTTP; ARQ can make **outbound** network requests via `SERVICE` 
(federation), `SERVICE` can be disabled by operator in configuration; ARQ can 
read **`file:`/http: URLs** named in queries (FROM/FROM NAMED/SERVICE); RIOT 
parses untrusted RDF; ARQ may execute **custom/JavaScript functions** if the 
operator enabled them; TDB reads/writes the data directory. *(inferred — these 
are the load-bearing confirmations)*
+
+## §5a Build-time and configuration variants
+
+Security-relevant configuration *(Fuseki auth documented; the rest inferred — 
confirm defaults):*
+
+| Knob | Default | Effect / stance |
+| --- | --- | --- |
+| Fuseki Shiro auth (`shiro.ini`) | SPARQL **query** public; admin `/$/*` 
**localhost-only** | *(documented)* Restricting query access requires Shiro 
`[urls]` ACLs. |
+| Fuseki example user setup | `admin`/`pw`, plaintext, no TLS | *(documented)* 
explicitly "not recommended for production". Any "default admin/pw in prod" 
report → `OUT-OF-MODEL: non-default-build`. |
+| SPARQL **Update** / Graph Store write | per-dataset (read-only vs read-write 
service) — **default to confirm** | *(inferred)* If a dataset ships 
update-enabled + unauthenticated, anonymous write is in-model; if read-only by 
default, anonymous write is not reachable. **Wave-1 question.** |
+| `SERVICE` (federated query) | may be disabled by operator config 
**(documented)** | *(inferred)* SSRF surface
+| ARQ **JavaScript / custom functions** | opt-in feature, requires explicit 
operator config of both Fuseki and JVM | *(inferred)* If enabled, SPARQL can 
execute code, executable JS functions controlled by explicit white list 
*(documented)*, some JS functions, e.g. `eval()`, are explicitly blacklisted 
regardless of whitelist → by-design-if-operator-enabled, like a trusted 
extension.  Java custom functions require explicit operator configuration of 
class path, if added to class path operator responsibility to verify function 
code is safe |
+| RDF/XML & external-entity handling in RIOT | XXE off | *(inferred)* Whether 
external entities / `file:` access are disabled by default in the parsers. |
+| JSON_LD & external context handling in RIOT | On | Accessed by http/https or 
local file. |
+| Query timeout / result limits | query timeout configurable at server or 
per-dataset level *(documented)* | *(inferred)* the resource/DoS lever (Andy's 
concern). |
+
+## §6 Assumptions about inputs
+
+Per-surface trust table *(Fuseki defaults documented; the rest inferred):*
+
+| Surface | Input | Attacker-controllable? | Caller/operator must enforce |
+| --- | --- | --- | --- |
+| Fuseki SPARQL query endpoint | SPARQL query text | **yes (anonymous by 
default)** | Shiro ACLs if data is sensitive; SERVICE/file/JS-function 
restrictions; query timeout |
+| Fuseki SPARQL Update / GSP | update text / RDF body | **yes — must be 
authorised** | read-only-by-default or Shiro-gated write; RDF parse hardening |
+| RDF parse (RIOT) anywhere | RDF/XML, Turtle, JSON-LD, … | **yes** | RDF/XML 
XXE off by default *(maintainer)*; bounded nesting/size |
+| JSON-LD `@context` resolution (RIOT) | `@context` URL | **yes** | 
remote-context fetch is **on by default** (SSRF / remote-read) — restrict on 
untrusted-input endpoints *(maintainer — afs)* |
+| `SERVICE <url>` (federation) | target URL | **yes — enabled by default; SSRF 
is conceded `VALID`** *(maintainer)* | no allow-list yet — disable `SERVICE` or 
add egress controls on untrusted endpoints |
+| `FROM` / `FROM NAMED` URI | dataset URI | **dataset-impl-dependent** 
*(maintainer)* | with TDB2 these are in-dataset graph names, **not fetched**; 
only an arbitrary-URI-configured dataset reads `file:`/remote |
+| Fuseki admin `/$/*` | dataset mgmt, backups | **must not be on the public 
net** | localhost-only (default) / operator network |
+| Java API (`QueryExecution`, `Model.read`) | query / RDF from the app | no — 
the embedding app's trust | app validates its own untrusted inputs |
+
+- **Size/shape/rate:** query-cost / result-size / parser-nesting bounds; §8 
resource line. *(inferred)*
+
+## §7 Adversary model
+
+- **Anonymous SPARQL client (primary)** — can reach Fuseki's public query 
endpoint; goals: read non-public graphs, write via an exposed Update endpoint, 
SSRF via `SERVICE`, local-file read via `file:`/FROM, code execution via JS 
functions (if enabled), resource exhaustion via expensive queries. *(inferred; 
public-query default documented)*
+- **Authenticated low-privilege user** — bounded by Shiro/dataset ACLs; goal: 
exceed them. *(inferred)*
+- **Crafted-RDF attacker** — supplies malicious RDF (RDF/XML XXE, 
deeply-nested/oversized documents) to any parse path. *(inferred)*
+- **Out of scope:** operator/host control; the embedding app supplying its own 
trusted input; anyone who can edit `shiro.ini` or enable JS functions. 
*(inferred)*
+
+## §8 Security properties the project provides
+
+*(All inferred pending PMC confirmation except where Fuseki defaults are 
documented.)*
+
+- **Admin surface is localhost-bound by default.** Fuseki's `/$/*` admin 
functions are not reachable from the network unless the operator exposes them. 
*Violation symptom:* an admin function reachable anonymously over the network 
in the default config. *Severity:* CVE-class. *(documented — Fuseki security 
docs)*
+- **Shiro access control is enforced when configured.** A Shiro `[urls]` ACL 
restricting an endpoint cannot be bypassed by request manipulation. *Violation 
symptom:* a restricted endpoint reached without satisfying its Shiro rule. 
*Severity:* CVE-class. *(inferred)*
+- **SPARQL operations cannot escape the dataset's authorised scope.** An 
anonymous/low-priv query cannot read graphs, write data, reach the filesystem, 
or make Fuseki act as an SSRF proxy beyond what the dataset config permits. 
*Violation symptom:* SERVICE-SSRF, `file:` read, cross-graph read, or 
unauthorised write from an in-scope query. *Severity:* CVE-class. *(inferred — 
the core boundary to ratify; these are the classic Jena CVE classes)*
+- **RDF parsing is safe against untrusted documents.** RIOT parsing of 
attacker RDF does not resolve external entities (XXE), execute code, or 
recurse/allocate unboundedly. *Violation symptom:* XXE, SSRF, or DoS from a 
parsed RDF document. *Severity:* CVE-class. *(inferred)*
+- **Resource bounds.** Expensive SPARQL queries, or large RDF bodies are an 
operator-tuned limit (query timeout). *(maintainer)*
+
+## §9 Security properties the project does *not* provide
+
+- **No protection if the operator exposes the admin surface, ships the example 
`admin`/`pw` setup, or runs without TLS** — deployment hardening (pending §5a 
rulings). *(documented that the example setup is not for production)*
+- **No defense when ARQ JavaScript/custom functions are enabled on an 
untrusted endpoint** — JS functions are opt-in with an explicit allow-list 
(`eval()` etc. blacklisted regardless); custom Java functions require the 
operator to add trusted code to the class path. Reachable code execution is 
**`by-design-operator-enabled`** *(maintainer — rvesse)*. **False friend:** a 
SPARQL endpoint being "read-only" does not by itself prevent SSRF (`SERVICE`) 
unless `SERVICE` is separately restricted.
+- **No SPARQL-injection defense for the embedding application** — an app that 
concatenates untrusted input into a query string owns that bug (use 
parameterised queries / `QueryBuilder`). *(inferred)*
+- **No transport security / authentication unless the operator configures 
Shiro + TLS.** *(documented/inferred)*
+- **No generic-DoS / query-complexity guarantee** beyond operator-set query 
timeouts + reverse-proxy size limits; a trivial query can compute a huge 
cross-product, which is inherent to spec-compliant SPARQL, not a Jena bug 
*(maintainer — rvesse)*.
+- **Well-known classes — note the split:** **SSRF via `SERVICE`** from an 
anonymous query against the default config is a **conceded `VALID` attack 
vector** *(maintainer — rvesse)*, not merely operator-disclaimed (there is no 
allow-list yet; operators must disable `SERVICE` or add egress controls). Left 
to the caller/operator: SPARQL injection (embedding app), 
algorithmic-complexity DoS (operator timeouts), and — only for a dataset 
explicitly configured for arbitrary-URI access — `file:`/remote read. With a 
TDB2 store, `FROM`/`FROM NAMED` access only in-dataset graphs and do **not** 
fetch URIs *(maintainer — rvesse)*. JSON-LD `@context` remote fetch is a 
separate remote-read surface (§4/§6).
+
+## §10 Downstream responsibilities (operator/deployer)
+
+- **Put Fuseki behind auth (Shiro) + TLS** before exposing sensitive data; 
never ship the example `admin`/`pw` setup to production. *(documented)*
+- **Keep the admin `/$/*` surface localhost-only / operator-network.** 
*(documented)*
+- **Make datasets read-only unless write is intended**, and gate SPARQL Update 
/ GSP behind Shiro. *(inferred)*
+- **Restrict or disable `SERVICE` federation and `file:` access** on endpoints 
reachable by untrusted clients (SSRF / local-file). *(inferred)*
+- **Do not enable ARQ JavaScript/custom functions on untrusted endpoints.** 
*(inferred)*
+- **Set query timeouts / result-size limits** appropriate to capacity (the 
volume lever). *(inferred)*
+- **Use parameterised queries** (`QueryBuilder`/parameterised 
`QueryExecution`) in embedding apps; never string-concatenate untrusted input 
into SPARQL. *(inferred)*
+
+## §11 Known misuse patterns
+
+*(Draft one-liners — expand before publishing.)*
+
+- Exposing a public, update-enabled SPARQL endpoint with no auth. *(inferred)*

Review Comment:
   ```suggestion
   - Leaving `SERVICE` reachable from anonymous queries (SSRF / file read). 
*(inferred)*
   ```
   `file:` for queries does not mean a local file. It is just a URL name that 
is not deferenced.
   



##########
THREAT_MODEL.md:
##########
@@ -0,0 +1,212 @@
+<!--
+SPDX-License-Identifier: Apache-2.0
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+# Apache Jena — Threat Model (v1)
+
+## §1 Header
+
+- **Project:** Apache Jena (`apache/jena`), `main`, against which this draft 
was written. A monorepo: the RDF/SPARQL Java framework (`jena-core`, 
`jena-arq`, `jena-base`, RIOT parsers, `jena-tdb1`/`jena-tdb2` stores, SHACL 
(`jena-shacl`), ShEx (`jena-shex`), GeoSPARQL `jena-geosparql`, text index 
`jena-text` **and** the Fuseki HTTP server (`jena-fuseki2`).

Review Comment:
   ```suggestion
   - **Project:** Apache Jena (`apache/jena`), `main`, against which this 
threat model was written. A monorepo: the RDF/SPARQL Java framework 
(`jena-core`, `jena-arq`, `jena-base`, RIOT parsers, `jena-tdb1`/`jena-tdb2` 
stores, SHACL (`jena-shacl`), ShEx (`jena-shex`), GeoSPARQL `jena-geosparql`, 
text index `jena-text` **and** the Fuseki HTTP server (`jena-fuseki2`).
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to