1
0
Fork 0
llm-ingestion-pipeline-secu.../CLAUDE.md
Kjell Tore Guttormsen 58704834b6 feat(okf): the mapping class gets one expressible form, typed and allowlisted
OKF v0.2 writes its whole trust and provenance layer as mappings, and T2 gave
the mapping class no expressible form. A consumer measured 0 of 53 upstream
concepts through the gate on 0.3.4, 1.0.0 and 1.1.0. That was a contract
collision, not a calibration setting: SPEC.md @ 62432a09 uses flow mappings in
its own 5.1/5.2 examples, and 11 carries a hard MUST for consumers ("MUST treat
a bare `verified` mapping as a one-element list") that presupposes they parse.

Admitted: a flow mapping, as a value or as a block-list item, whose every key is
on a nine-name allowlist and whose every leaf is a plain scalar run through the
unchanged dangerous-value and mapping-construct predicates. The form is safe
because the allowlist inspects every key -- the blanket refusal was the
enforcement, not the point.

Refused, each on its own rule and ground-truthed against PyYAML 6.0.3: a key off
the allowlist, a nested collection, a quoted leaf, a duplicate key, an empty or
unclosed mapping, trailing junk, and {a:b} (which PyYAML reads as the KEY a:b).
A refused mapping still raises rather than degrading into a string, so the 1.1.0
type-confusion defect is not reopened, and the block, dotted and inline-colon
routes still raise.

`resource` is deliberately off the allowlist though SPEC.md 5.1 names it inside
a `sources` entry: it is a pointer rather than a label and the only key T3
exists for, so admitting it would let `executor: { resource: skills/run.md }`
carry an executable-code pointer through in typed clothes -- the door-C route
closed in 1.1.0. It costs nothing today, because the conformant carrier for
sources[].resource is a block sequence of block mappings, which stays refused.

Mapping leaves are scanned like every other frontmatter value (T1). Coverage
matrix 130/130 (new row: the off-allowlist key). No exported surface, detector
behaviour or calibration changed.
2026-08-21 21:04:51 +02:00

3.8 KiB

llm-ingestion-pipeline-security

Kontekst

Gjenbrukbar, minimal defensiv layer for LLM ingestion-pipelines (write-time), til forskjell fra query-time chatbot-guardrails. Pakker det arkitektoniske kontraktet — sanitize → fence → tool-less karantenert transform → per-stadium capability-isolasjon → scan output før commit → fail-secure — som komponerbar, framework-agnostisk kode.

Referanse-implementasjon: claude-code-llm-wiki Stage B (tools/wiki_ingest/). Lexikon-seed: injection-patterns.mjs fra llm-security-pluginen.

Repoet er på v1.1.0 — den eksporterte Python-surfacen er frosset under semver (deteksjonsatferd er det IKKE; kalibrering flytter seg i 1.x). Stdlib-kjernen er bygget og testet (15 moduler + topp-nivå wiring, showcase + korpus), inkl. OKF-adapter og aktivt-innhold- detektor (EchoLeak-klassen) i output-gaten. OKF-frontmatterens mapping-klasse har én uttrykkbar form (G3, 21.08): en flow-mapping (generated: { by: x, at: y }) — som verdi eller som blokkliste- element — der HVER nøkkel står på en ni-navns allowlist og hvert blad er en ren skalar. Formen er trygg fordi allowlisten inspiserer hver nøkkel; det blanke avslaget var håndhevelsen, ikke poenget. resource er bevisst UTE av allowlisten (peker, ikke etikett — den ene nøkkelen T3 finnes for). Blokk-, dotted- og inline-kolon-rutene raiser fortsatt, og en avvist mapping raiser — den degraderer aldri til en streng (1.1.0-defekten). Mode-b import_bundle skanner reserverte strukturfiler (index.md/log.md) i mottatte bundles i stedet for å path-avvise dem; upload-front-end beholder shadow-reject (allow_reserved=False). Output-gatens decode-and-rescan mater dekodet base64-klartekst gjennom BÅDE lexicon og secret-egress (LLM02), så en base64-innpakket credential fanges som decoded:egress:* i stedet for å forsvinne; hex-innpakket er en dokumentert restgap (entropy eksponerer kun base64-klartekst). active:raw-html krever et EKSTERNT mål på URL-attributt-grenen, og <base> er ute av det aktive navnesettet; scanner og mutator har hver sin predikat (is_active_tag / is_defangable_tag). Rå HTML graderes nå også på BÆRER: <a>/<area> er klikk-krevende og rapporteres som active:raw-html-link (MEDIUM), og en tagg hvis hele affordans ER en URL den ikke bærer (</a>, <Frame>, <video />) er inert. Klassifisering skjer i active_tag_class; is_active_tag er en tynn wrapper, og census patcher den FØRSTE (en boolsk patch kan ikke uttrykke en regradering). ZWJ (U+200D) dømmes på KONTEKST, ikke identitet — unntas kun mellom to emoji, på begge flater (sanitize eier predikatet, output importerer det). Start med docs/BRIEF.md for design, README.md for bruk, docs/PLAN.md for byggerekkefølgen.

Konvensjoner

  • Norsk for dialog og planer, engelsk for kode og innhold (repoet er publisert).
  • Ingen GitHub — kun Forgejo (git.fromaitochitta.com).
  • Remote satt: offentlig open/-speil på Forgejo; push hver commit (durabelt autorisert).
  • Minimal-dependency: stdlib-first kjerne; ML/judge-detektorer bak extras.

Communication patterns

Linking to local files

When pointing to local files in responses, always use markdown link syntax with a descriptive name:

  • Use [Human-friendly name](file:///absolute/path) — never bare file:///... URLs or autolinks <file://...>.
  • Always use absolute paths. Never ~/ or relative paths.
  • For multiple files, render as a bullet list of named markdown links.

Why: bare file:// URLs only render the first as clickable across multiple lines. Named markdown links make each entry independently clickable and look cleaner.

Example (the path is a placeholder — the checkout root is the reader's own, and this file is published, so it must not carry one machine's directory layout):