- Je wijst dubbele JSON-keys en afwijkende velden of waarden af.
- Je scheidt geldige vorm, specifieke toestemming en een nieuwe lokale toestand.
Een model stelt voor om kaart-a groen te maken. Het voorstel is keurig als JSON geschreven, maar de gebruiker mag op dat kaartje alleen blauw kiezen. De tekst ziet er geldig uit; de actie hoort toch te worden geweigerd.
Modeluitvoer blijft invoer voor het onderdeel dat haar ontvangt. Onveilige uitvoerverwerking (improper output handling) ontstaat wanneer die uitvoer zonder passende controle een vervolgactie bereikt. Wij gebruiken uitsluitend lokale kleurkaartjes, zonder HTML, SQL, shell of netwerkactie uit te voeren.
1. Een voorstel krijgt een exact formaat
Denk aan een formulier met drie vakjes: operation, target en color. De bewerking is paint, het doel is een geldige kaart-id en de kleur is blauw, groen of oranje. Een ontbrekend of extra vakje past niet bij dit oefencontract.
K is de verzameling keys die het formulier moet hebben. Keys(p) geeft de keys van het gedecodeerde voorstel p. De gelijkheid betekent precies deze drie, niet slechts dat ergens de juiste woorden voorkomen.
text = (
'{"operation":"paint",'
'"target":"kaart-a",'
'"color":"groen"}')
proposal = parse_proposal(text)
print(proposal["target"])
print(proposal["color"])
# kaart-a
# groen
Beperk de tekst vóór het parsen tot 1.024 Python-codepunten. Gebruik json.loads in plaats van eval, en laat daarna de gegeven helper _o_proposal de exacte velden en waarden controleren. Een geldig JSON-getal of een JSON-lijst is nog geen geldig kleurvoorstel.
Let op dubbele keys: de standaarddecoder bewaart anders alleen de laatste waarde. Met object_pairs_hook controleer je de gedecodeerde naam/waardeparen vóór je een dict maakt; een herhaalde naam geeft ValueError. Dit vangt ook twee anders geschreven JSON-keys die na escape-decoding dezelfde naam krijgen.
Wijs daarnaast NaN en Infinity af via parse_constant. Vang ongeldige JSON af als ValueError en ga niet verder met een half gecontroleerd voorstel. Deze kleine parser is een oefening, geen algemene productieparser voor willekeurige documenten.
012. Controleer het doel én diens kleur
Het beleid is een expliciete tabel: elk doel heeft een eigen lijst toegestane kleuren. Voor kaart-a staat alleen blauw in die lijst. Groen is dus een geldige kleur, maar niet toegestaan voor dit doel.
P is de beleidstabel, t het gevraagde doel en c de gevraagde kleur. A betekent dat het paar is toegestaan. Het EN-teken ∧ vereist zowel een bekend beleidsdoel als een kleur in juist diens lijst.
policies = [
dict(target="kaart-a",
colors=["blauw"]),
]
print(policy_allows(proposal, policies))
# False
Een veelgemaakte fout is alle toegestane kleuren uit de hele tabel samenvoegen. Als kaart-b groen toestaat, geeft dat nog geen groenrecht op kaart-a. Een lege lijst betekent ook geen onbeperkte toestemming.
02Een model mag een voorstel doen. Vormcontrole, specifiek beleid en de bestaande toestand bepalen samen of er iets gebeurt.
{"operation": "paint", "target": "kaart-a", "color": "groen"}- Vorm
Geldig JSON-voorstel met precies operation, target en color.
- Beleid voor dit doel/kleurpaar
Dit paar staat niet in het beleid.
kaart-a → groen
- Toestand
Niet toegepast: beleid weigert dit doel/kleurpaar.
Vóór
kaart-a
blauwkaart-b
oranjeNa controle
kaart-a
blauwkaart-b
oranjeEen lege selectie verleent geen toestemming. Een onbekend doel wordt nooit aangemaakt. Dezelfde kleur opnieuw toepassen kan toegestaan zijn, ook als de kaart er gelijk uit blijft zien. Alle voorstellen blijven tekst.
Eigen, vooraf berekende oefengegevens. Je bedient een begrensd model; er worden geen externe systemen, documenten of tools aangesproken.
3. Pas pas daarna de lokale toestand aan
Een voorstel kan een geldig formaat hebben en toch naar een onbekend kaartje verwijzen. Controleer daarom ook de bestaande toestand. Bij geldige invoer melden we eerst unknown-target als het doel ontbreekt, anders policy bij geweigerd beleid, en anders applied.
Maak altijd een nieuwe toestandkopie, ook bij weigering. Zo blijft de oorspronkelijke toestand beschikbaar voor vergelijking. Controleer alle invoer voordat je een reden teruggeeft; een onbekend doel is geen reden om een kapotte beleidstabel stil te accepteren.
state = {"kaart-a": "blauw"}
blocked = apply_proposal(
state, proposal, policies)
print(blocked["reason"])
print(blocked["state"]["kaart-a"])
# policy
# blauw
Voor een bestaande én toegestane kaart wordt alleen de doelkleur toegewezen. In de formule is S de oude toestand, S met een accent de nieuwe, t het doel en c de kleur. Alle andere kaartjes behouden hun waarde.
allowed = [
dict(target="kaart-a",
colors=["groen"]),
]
accepted = apply_proposal(
state, proposal, allowed)
print(accepted["reason"])
print(accepted["state"]["kaart-a"])
print(state["kaart-a"])
# applied
# groen
# blauw
03Ook een toegestane toekenning van dezelfde kleur heet applied: de actie was toegestaan, al zie je geen kleurverschil. Een weigering en een geslaagde actie zonder zichtbaar verschil zijn verschillende uitkomsten.
In echte toepassingen vragen HTML-tekst, databasequeries en systeemcommando’s ieder hun eigen bescherming. Een schema vervangt geen uitvoerencoding, geparameteriseerde query of passende bevoegdheidscontrole. Daar kom je later uitgebreider op terug; dit kaartje laat eerst de afzonderlijke beslissingen zien.
Wat je nu kunt
- Je onderscheidt geldig JSON, het afgesproken schema en toestemming.
- Je bindt toestemming aan een specifiek doel/kleurpaar.
- Je past alleen toegestane lokale acties toe en bewaart de oorspronkelijke toestand.
Hierna begrens je wat een agent met meerdere tools en rechten mag bereiken.
Bronnen
Gecontroleerd op 30 oktober 2026: OWASP LLM10 over onveilige uitvoerverwerking, OWASP Input Validation en de Python-json-documentatie over decoderinstellingen. De kaarten en beleidstabel zijn eigen oefenmateriaal.