1
0
Fork 0
llm-ingestion-pipeline-secu.../CLAUDE.md
Kjell Tore Guttormsen f34a424447
docs(plan): record the final measurement on 1.8.0; the repo is public again
Rows 1-3 hold on 1.8.0: 16/16 catalogues current, 0 stale catalogue numbers
(frozen denominator 95 at 6702d7a unchanged), 17 attacks caught and 2
documented gaps with none red. The project rules now say the remote is the
public open/ repository, and .claude/ is ignored so local session state cannot
be added by accident.
2026-10-31 21:57:43 +01:00

123 lines
8.3 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.

# 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.8.0** (release-commiten bumper alle levende versjonsflater:
`pyproject.toml`, `__init__.py`, README-badge + status + install-pin, `llms.txt`,
ADOPTION-BRIEF, BRIEF, CHANGELOG-overskrift). NB: v1.4.0-taggen bærer `1.3.0` i
dokumentflatene — dens release-commit daterte bare CHANGELOG-en — og tags
flyttes aldri. **Flow-sekvens-elementer (1.4.1):** `#` og `:` avvises kun der
YAML leser dem (`#` først i elementet eller etter mellomrom; `:` først, sist
eller foran mellomrom), ellers er de skalar-tegn — okf sine `references:` med
`#anker` og `scheme://` parser (0/5467 feiler på forbrukerens bundle, var 2038). 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 **fire** uttrykkbare bærere (G3 21.08, G30 02.09): flow-mapping som verdi
og som blokkliste-element, flow-sekvens av flow-mappinger, og blokk-sekvens av
blokk-mappinger (SPEC §5.1s egen form). HVER nøkkel i alle fire står på
allowlisten 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 allowlistet KUN inne i en `sources`-oppføring** — foreldre-
nøkkelen avgjør, så `executor`/`attester` sin `resource` (§10, dør C) avvises
gjennom hver eneste bærer. 1.2.0s begrunnelse for å utelate den (parseren
manglet foreldre-kontekst) var målt feil: konteksten var der, den var bare
aldri tredd gjennom. Topp-nivå blokk-mapping, dotted- og inline-kolon-rutene
raiser fortsatt, en blokkliste kan ikke blande skalarer og mappinger, og en
avvist mapping raiser — den degraderer aldri til en streng (1.1.0-defekten).
**Flow-sekvens av rene skalarer (`tags: [a, b]`) PARSER fra 1.4.0** (P1,
operatørbeslutning 08.09) — SPEC §4.1s eget skjelett. Et element er en ren
skalar kun hvis det er ikke-tomt og uten `{ } [ ] : , " ' #`, og så gjelder den
uendrete indikator-regelen; sitert element, kolon/komma i elementet, sekvens i
sekvens, tomt element, anker og alias raiser fortsatt, og en flow-sekvens kan
ikke blande skalarer og mappinger. **Målt med nevner: 0/53 → 6/53** på pinnet
OKF-korpus (`3fcbb9f`). Den bindende skranken er IKKE tags/description, men
`generated` som topp-nivå blokk-mapping (44/53) — den bruker opp dybde-1 og er
en sikkerhetsbeslutning. P2 (blokksekvens uten innrykk) og P3 (foldet plain
scalar) er MÅLT til 0/53 hver og bevisst IKKE bygget. `sources[].resource`
URL-valideres ALDRI (T3 ser kun topp-nivå `resource`) — §5.1 tillater
bundle-relative stier og scope-beskrivelser, så en https-gate ville over-blokkert
konforme bundles; konsumenten må selv kalle `validate_resource_url`. 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:2026), 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).
**Usynlige tegn (1.4.2):** hele nevneren i `docs/threat-2026/codepoints.json`
(DICP ∪ Cf ∪ Co, Unicode 18.0, 141 674 tegn) fanges, eller unntas på KONTEKST:
én VS15/16 etter emoji, én ideografisk velger etter CJK, ZWNJ/ZWJ ved arabisk
skrift eller indisk skrift, LRM/RLM/ALM i dokument med RTL, arabisk talltegn foran
siffer, ZWJ mellom to emoji. En rekke er alltid bærer, og privat bruk fanges helt
(operatøren 28.10). Ett predikat (`sanitize._invisible_carriers`) på begge flater.
Leksikonraden for orddeling dømmer leddet sitt med samme predikat, mens regex-en
er byte-lik med commons.
**OWASP-numre (1.5.0):** hvert nummer bærer utgavens årstall (`LLM10:2026`), og
produsentene navngir risikoen, aldri nummeret. Det er én tabell (`_owasp.py`).
Leksikonet får årstallet når det lastes. En klasse som ingen utgave beskriver, får
INGEN nummer (`Case.owasp is None`), for eksempel fail-secure, parserflate og
URL- og sti-validering. `tests/test_catalog_numbers.py` holder tellingen på 0.
**Skjult markup (1.6.0):** `html-obfuscation:tiny-font` (under 4px) og
`html-obfuscation:invisible-colour` (samme farge som bakgrunnen i SAMME
`style`-attributt, eller gjennomsiktig) bor i `output.py`, ikke i leksikonet, som er
byte-likt med commons. En verdi med ledende `0` overlates commons-raden `zero-font`.
Hvit tekst alene er bevisst ikke et signal. `output:html-comment-present` (MEDIUM)
teller bare kommentarer utenfor kode. Katalogregisteret har 16 oppføringer med
`cited` == `latest` (`tests/test_catalog_register.py`).
**Rekkefølge-bæring (1.7.0):** `active:markdown-image-sequence` (MEDIUM) melder minst
4 vanlige eksterne bilder (inline eller referanse) på samme URL-mal der bare korte
tokener (2 tegn eller færre) eller sifre varierer. Sifre som teller oppover i
dokumentet er en teller og bærer ingenting. Flaggtabeller er en akseptert tvilling.
Bildene graderes fortsatt LOW hver for seg; funnet står ved siden av.
**Instruksjoner i ren prosa (1.8.0):** `indirect:prose-instruction` (MEDIUM) melder et
avsnitt som kombinerer minst 2 av 4 klasser: adresse til modellen, utsettelse («from
now on», «if the user later says»), minneskriving og hemmelighold. Én klasse alene er
vanlig prosa. Regelen bor i `output.py`, ikke i leksikonet. Et forfilter med nøkkelord
hopper over tekst der ingen ikke-adresse-klasse kan treffe. A12 (oppgave uten markør)
er det sjuende dokumenterte hullet. ReDoS-sveipet går nå inn i dict-er (168 mønstre).
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: OFFENTLIG `open/llm-ingestion-pipeline-security` (flyttet tilbake etter
sluttmålingen 31.10, `docs/PLAN.md` § T.4). Alt som pushes er publisert: ingen private
stier, koordineringsmetadata eller økt-lenker i filer eller commit-meldinger. Push hver
commit; push-vakten krever operatørens token.
- 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):
- [Brief](file:///absolute/path/to/llm-ingestion-pipeline-security/docs/BRIEF.md)