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.
123 lines
8.3 KiB
Markdown
123 lines
8.3 KiB
Markdown
# 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)
|