From 5cd9b451fbecb65ebb17fbb9864b6b73304df9ea Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Mon, 6 Jul 2026 07:05:45 +0200 Subject: [PATCH] docs(okf): capture OKF-ingestion security brief as v0.2 future work Google's Open Knowledge Format (OKF v0.1) is the LLM-wiki pattern this library guards. Capture a security brief for OKF ingestion as tracked future work -- NOT shipped scope. The v0.1 core is format-agnostic text; OKF surfaces beyond the body (YAML frontmatter, resource URLs, cross-link graph, file path / reserved names, format-level authenticity) are uncovered. - Coverage claims verified against the code at 5397ba1 (brief section 9): no YAML parse in src, neutralize defangs but has no reject-gate, no path/graph logic, disposition machinery exists. - OKF confirmed real at the format level (Google Cloud, 2026-06-12; spec GoogleCloudPlatform/knowledge-catalog). - Design move: an OKF adapter ON TOP of the format-agnostic core; the core stays text->findings. - Section 8 lists 8 v0.2 tasks (T1-T8); T8 (surface two OKF residual risks in README honest-limits) can land independently. --- docs/OKF-INGESTION-BRIEF.md | 198 ++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 docs/OKF-INGESTION-BRIEF.md diff --git a/docs/OKF-INGESTION-BRIEF.md b/docs/OKF-INGESTION-BRIEF.md new file mode 100644 index 0000000..4c67695 --- /dev/null +++ b/docs/OKF-INGESTION-BRIEF.md @@ -0,0 +1,198 @@ +# Security brief: OKF ingestion with `llm-ingestion-guard` + +**Status:** Draft v0.1 — captured 2026-07-06. +**Applies to:** write-time ingestion pipelines for LLM wikis based on Google's +Open Knowledge Format (OKF v0.1). +**Inputs:** internal and external data, ingested automatically or manually. + +> This document is a **future-work brief**, not shipped scope. The `v0.1` core +> (`docs/PLAN.md`, all modules built + hardened at commit `5397ba1`) is +> **format-agnostic text** (`prepare_input(text)` / `screen_output(text, policy)`). +> None of the OKF-specific controls below are implemented yet; §8 is the v0.2 +> task list. The coverage claims in §4 were verified against the code on +> 2026-07-06 — see the verification log in §9. + +--- + +## 1. Summary + +OKF is, in practice, exactly the "LLM-wiki" pattern `llm-ingestion-guard` was +built to protect: untrusted content is enriched/persisted into a markdown corpus +that a *downstream* agent later reads as **trusted context**. The guard therefore +belongs at the persist gate, and the existing contract (carrier-strip → fence → +tool-less transform → capability isolation → output scan → fail-secure) covers the +markdown *body* directly. + +OKF, however, introduces five attack surfaces **beyond** the body that must be +covered explicitly: YAML frontmatter, `resource` URLs, the cross-link graph, file +path / reserved names, and the absence of format-level authenticity. The largest +residual risk is **semantic poisoning**, which is out of scope by design (§7). + +## 2. Scope + +**In scope:** everything written into or merged into an OKF bundle that the +pipeline controls — both its own enrichment output and received external bundles. + +**Out of scope:** query-time / runtime guardrails, the vector/embedding layer, and +semantic truth-assessment of concepts (see §7). These match the existing +out-of-scope boundary in `README.md` and `docs/PLAN.md`. + +## 3. Why OKF is the right place to enforce + +- OKF formalizes the LLM-wiki pattern into a portable markdown format; concepts + are meant to be read directly by agents as curated, authoritative context. +- The format has **no schema registry, no central authority, and no + signing/authenticity**. A received bundle's claimed origin is not verifiable at + the format level. (Verified at the format level — OKF v0.1 is a directory of + markdown files with YAML frontmatter, minimal by design; §9.) +- Consequence: **your ingestion pipeline *is* the trust boundary.** Provenance and + disposition must be stamped by you at ingestion, not assumed from the format. + +## 4. Attack surfaces and required controls + +Status column verified against the code at commit `5397ba1` on 2026-07-06 (§9). + +| Surface | Vector | Required control | Status today | +|---|---|---|---| +| **Markdown body** | Prompt injection, hidden carriers (zero-width, BIDI, Unicode-tag, HTML comments, `data:` URIs) | Carrier-strip → fence → tool-less transform → output scan → fail-secure | ✅ Covered (core contract) | +| **YAML frontmatter** | Injection in `title`/`description`/`tags` + arbitrary unknown keys; `description` propagates into `index.md` (read **first** under progressive disclosure); YAML anchor/alias DoS + dangerous type coercion | Same sanitize/scan on frontmatter *values*; parse YAML with a safe loader | ⚠️ **Partial** — values are scanned **iff** the caller passes the whole document (frontmatter included) as text; the core **never parses YAML**, so the safe-loader is a genuinely new gate at an OKF-adapter boundary (§9) | +| **`resource` URL** | `data:`/`javascript:`/`file:`/SSRF target that a consumer or visualizer fetches | Scheme allowlist (`https` only), validate before commit | ❌ **New control** — `neutralize` defangs schemes for human audit but there is **no reject-gate** and no `resource`-field concept (§9) | +| **Cross-link graph** | "Dead links are valid" → *dormant* injection: plant a link to a non-existent concept-ID now, write the poisoned target later | Constrain link targets to relative in-bundle paths + scheme check; re-scan on write of a link target | ❌ **New control** (graph level) | +| **File path / reserved names** | Concept-ID = file path minus `.md`; path traversal (`../`) and shadowing of reserved `index.md`/`log.md` | Sanitize/normalize paths; reject `..` and reserved filenames as concept names | ❌ **New control** — no path validation in the core (§9) | +| **`log.md` / provenance** | No authenticity at the format level | Stamp disposition + trust tier per concept | ↔️ **Machinery exists** — `Trust` × `Provenance` × `Disposition` types are built; emission-to-`log.md` + an origin/channel stamp is new wiring on top (§9) | + +## 5. Trust-tier model + +Trust follows the data's **origin**, not the insertion channel. A manual paste of +external material is still external. This maps onto the existing `Trust` enum +(`TRUSTED` / `UNTRUSTED`) plus a channel dimension the pipeline records. + +| Origin | Channel | Tier | Treatment | +|---|---|---|---| +| External | Automatic | Lowest | Full contract + fail-secure | +| External | Manual | Low | Full contract (channel grants *no* discount) | +| Internal | Automatic | Medium | Full contract; may relax the lexicon threshold with logging | +| Internal | Manual (trusted author) | Highest | Keep carrier-strip + scan; **note residual risk §7** | + +**Nuance:** "manual internal" is not safe in itself. In-place edits by a trusted +author are outside the guard's scope — the channel does not authenticate the +content's origin. This is the same insider-edit limitation already in +`README.md` honest-limitations. + +## 6. Two ingestion modes + +**(a) Own enrichment output** — your agent writes concepts. The guard runs on your +own output before commit. One transform per concept. This is the existing +`prepare_input` → tool-less transform → `screen_output` bookend flow, applied +per concept. + +**(b) Received external bundle** — you merge a whole third-party OKF bundle (the +format is explicitly built for cross-organization exchange). The guard must +**iterate over each concept** and validate body + frontmatter + links + `resource` +before merge — not treat the bundle as one unit. This is a **new iterator layer** +(§8 T7); the core has no bundle concept. + +## 7. Residual risk (explicitly out of scope) + +Cross-referenced to `README.md` honest-limitations. Items 1, 3, 5 are **already +documented there**; items 2 and 4 are OKF-specific and **not yet surfaced** (→ §8 T8). + +1. **Semantic / factual poisoning** — a plausible-but-wrong concept (wrong + join-path, wrong metric definition, wrong runbook step) passes entropy + lexicon + cleanly and is treated as ground truth. **Highest impact for a wiki.** Needs + human review or source verification, not token analysis. *(Already in README; + the `grounding` module ships the `SourceGroundingCheck` seam for it.)* +2. **Dormant broken-link injection** — see §4; a per-concept write-time scan does + not see it over time. Needs graph / re-scan logic. *(Not yet in README.)* +3. **Trusted-author in-place edits** — normal OKF workflow (git-native editing) is + not gated by the guard. *(Already in README as the insider-edit limitation.)* +4. **Own security content as a false positive** — concepts that *document* + prompt-injection payloads will trip the carrier-strip / fail-secure path. Needs + a deliberate escaped path for "this concept is *about* attacks." *(Not yet in + README; also tracked as the corpus false-positive tension.)* +5. **Multimodal stego, embedding layer, query-time** — refer to the existing + threat-model docs. *(Already in README / out-of-scope boundary.)* + +## 8. Recommendations → v0.2 task list + +The unifying design move: add an **OKF adapter** *on top of* the format-agnostic +core. The core stays `text -> findings` (design principle 3); the adapter knows OKF +structure (frontmatter, paths, links, `resource`, bundles) and feeds scannable text +regions into the existing `sanitize` / `scan_output` / `disposition` machinery. No +YAML/format awareness leaks into the core. + +| # | Task | Builds on | New? | +|---|---|---|---| +| **T1** | Whole-concept scan surface — route frontmatter *values* + `resource` strings through the same `sanitize`/`scan_output` path as the body | `sanitize`, `output` | adapter layer | +| **T2** | YAML parse-safety gate — safe loader (block anchor/alias DoS + dangerous type coercion) at the adapter boundary | — | **new** | +| **T3** | `resource`-URL allowlist gate — hard-reject non-`https` (and `data:`/`javascript:`/`file:`) *before commit* | `neutralize` scheme detection | **new** (reject, not defang) | +| **T4** | Path / reserved-name validation — reject `..` traversal + `index.md`/`log.md` shadowing; normalize concept-ID → path | — | **new** | +| **T5** | Cross-link graph re-scan — constrain targets to relative in-bundle paths + scheme check; re-scan on write of a link target (dormant-injection, §7.2) | — | **new** (graph state) | +| **T6** | Provenance stamping — origin + channel → tier + disposition per concept, emitted to `log.md` | `Trust`/`Provenance`/`Disposition` | new emission on existing types | +| **T7** | Bundle-import iterator (mode b) — iterate concepts; validate body + frontmatter + links + `resource` per concept | §6 bookends | **new** | +| **T8** | Docs — surface §7.2 (dormant broken-link) + §7.4 (own security content) in README honest-limitations; §7.1/§7.3/§7.5 already there | `README.md` | doc | + +**Sequencing note:** T1–T4 + T6 are per-concept and composable with the existing +bookends; T7 is the mode-(b) wrapper around them; T5 is the hardest (needs graph +state across writes) and the one the residual risk §7.2 explicitly warns a +per-concept scan cannot cover. T8 is cheap and can land independently. + +**Naming caveat:** the illustrative stamp vocabulary in an earlier draft +(`SANITIZED`/`FENCED`/`QUARANTINED`/`FAIL_SECURE`) is **not** the actual +`Disposition` enum (which is `WARN` / `QUARANTINE_REVIEW` / `FAIL_SECURE`). A +provenance stamp (T6) would compose from `Trust` × `Provenance` × `Disposition`, +not add new disposition values. + +## 9. Verification log (2026-07-06, commit `5397ba1`) + +Per the operator's verification duty — what was checked against ground truth, and +what remains the operator's premise. + +**Verified against the code:** + +- **The guard is format-agnostic text.** Public API is `prepare_input(text)` / + `screen_output(text, policy)` (`src/llm_ingestion_guard/__init__.py`). A grep of + `src/` finds **no YAML import and no frontmatter parsing** anywhere. Implication + for the §4 "YAML frontmatter" row: frontmatter *values* are scanned **only if the + caller includes them in the text** passed to the scanner; the safe-loader is a + genuinely new gate, because the core never parses YAML. +- **`resource` URL — no reject-gate today.** `neutralize.py` recognizes dangerous + schemes (`javascript|data|vbscript|file|blob` defanged; `http(s)`/`ftp` → `hxxp`) + but this is *neutralization for human audit*, not an allowlist that **rejects** + non-`https` before commit, and there is no `resource`-field concept. §4 "New + control" confirmed. +- **Cross-link graph, file path / reserved names — absent.** No graph logic, no + path normalization / traversal check / reserved-name (`index.md`/`log.md`) check + in `src/`. §4 "New control" confirmed for both. +- **Provenance machinery exists.** `disposition.py` defines `Disposition` + (`WARN`/`QUARANTINE_REVIEW`/`FAIL_SECURE`), `Trust` (`TRUSTED`/`UNTRUSTED`), + `Provenance` (`PROSE`/`CODE_FENCE`/`LOCALIZED`) + presets. Per-concept stamping + to `log.md` is new emission on top; the types are ready. §4 "Machinery exists" + confirmed. +- **README already covers residual risks 1, 3, 5** (semantic poisoning + grounding + seam; insider in-place edits; multimodal/query-time). Residual risks 2 and 4 are + **not** yet in README → T8. + +**Verified externally:** + +- **OKF is real, format level.** Google Cloud's Open Knowledge Format v0.1, + announced 2026-06-12: a directory of markdown files with YAML frontmatter, + minimal by design (only a `type` field required), vendor-neutral, + producer/consumer-independent — which confirms the LLM-wiki framing, the + cross-org exchange mode (b), and the "no central authority / no signing" + premise in §1/§3. Spec: `GoogleCloudPlatform/knowledge-catalog/okf/SPEC.md`. + +**Operator premise — not independently verified this session:** + +- OKF *concept-level* specifics used above (concept-ID = file path minus `.md`; + reserved `index.md`/`log.md`; a `resource` field; `description` propagating into + `index.md` under progressive disclosure) are taken from the operator's brief + citing the OKF spec. They are consistent with the format-level facts but were not + each checked against `SPEC.md`. A focused pass over the OKF spec should confirm + the reserved-filename and `resource`-field details before T3/T4/T5 are built. + +## Sources + +- [Google Cloud — How the Open Knowledge Format can improve data sharing](https://cloud.google.com/blog/products/data-analytics/how-the-open-knowledge-format-can-improve-data-sharing/) +- [GoogleCloudPlatform/knowledge-catalog — okf/SPEC.md](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md) +- [MarkTechPost — Google Cloud Introduces Open Knowledge Format (OKF)](https://www.marktechpost.com/2026/06/16/google-cloud-introduces-open-knowledge-format-okf-a-vendor-neutral-markdown-spec-for-giving-ai-agents-curated-context/)