llm-ingestion-pipeline-secu.../docs/OKF-INGESTION-BRIEF.md
Kjell Tore Guttormsen 5cd9b451fb 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.
2026-07-06 07:07:09 +02:00

198 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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:** T1T4 + 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/)