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.
This commit is contained in:
parent
5397ba15a1
commit
5cd9b451fb
1 changed files with 198 additions and 0 deletions
198
docs/OKF-INGESTION-BRIEF.md
Normal file
198
docs/OKF-INGESTION-BRIEF.md
Normal file
|
|
@ -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/)
|
||||
Loading…
Add table
Add a link
Reference in a new issue