security-knowledge-os 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. app/__init__.py +3 -0
  2. app/_bundled/knowledge/public/agent-security/.gitkeep +0 -0
  3. app/_bundled/knowledge/public/agent-security/KU-0004-excessive-agency-least-privilege.md +67 -0
  4. app/_bundled/knowledge/public/agent-security/KU-0005-human-approval-gates.md +67 -0
  5. app/_bundled/knowledge/public/agent-security/KU-0006-outbound-channels-allowlist.md +65 -0
  6. app/_bundled/knowledge/public/credential-security/.gitkeep +0 -0
  7. app/_bundled/knowledge/public/credential-security/KU-0008-secrets-not-reachable-by-model.md +67 -0
  8. app/_bundled/knowledge/public/credential-security/KU-0009-credential-broker-pattern.md +64 -0
  9. app/_bundled/knowledge/public/governance/KU-0012-human-oversight.md +64 -0
  10. app/_bundled/knowledge/public/incidents/.gitkeep +0 -0
  11. app/_bundled/knowledge/public/incidents/KU-0014-llm-payload-identifier-leak.md +147 -0
  12. app/_bundled/knowledge/public/memory-security/.gitkeep +0 -0
  13. app/_bundled/knowledge/public/memory-security/KU-0007-persistent-memory-poisoning.md +65 -0
  14. app/_bundled/knowledge/public/methodology/.gitkeep +0 -0
  15. app/_bundled/knowledge/public/methodology/KU-0011-improper-output-handling.md +62 -0
  16. app/_bundled/knowledge/public/methodology/KU-0013-assume-compromise-ja.md +68 -0
  17. app/_bundled/knowledge/public/prompt-security/.gitkeep +0 -0
  18. app/_bundled/knowledge/public/prompt-security/KU-0001-direct-prompt-injection.md +65 -0
  19. app/_bundled/knowledge/public/prompt-security/KU-0010-system-prompt-leakage.md +63 -0
  20. app/_bundled/knowledge/public/rag-security/.gitkeep +0 -0
  21. app/_bundled/knowledge/public/rag-security/KU-0002-indirect-prompt-injection.md +72 -0
  22. app/_bundled/knowledge/public/rag-security/KU-0003-untrusted-content-isolation.md +61 -0
  23. app/_bundled/rules/agent/.gitkeep +0 -0
  24. app/_bundled/rules/agent/OUT-001.yaml +16 -0
  25. app/_bundled/rules/agent/TOOL-000.yaml +16 -0
  26. app/_bundled/rules/agent/TOOL-001.yaml +19 -0
  27. app/_bundled/rules/credential/.gitkeep +0 -0
  28. app/_bundled/rules/credential/CRED-001.yaml +24 -0
  29. app/_bundled/rules/governance/.gitkeep +0 -0
  30. app/_bundled/rules/governance/GOV-001.yaml +15 -0
  31. app/_bundled/rules/memory/.gitkeep +0 -0
  32. app/_bundled/rules/memory/MEM-001.yaml +20 -0
  33. app/_bundled/rules/prompt/.gitkeep +0 -0
  34. app/_bundled/rules/rag/.gitkeep +0 -0
  35. app/_bundled/rules/rag/PI-003.yaml +25 -0
  36. app/_bundled/safe_tests/ST-CRED-001.yaml +25 -0
  37. app/_bundled/safe_tests/ST-IPI-001.yaml +28 -0
  38. app/_bundled/safe_tests/ST-MEM-001.yaml +27 -0
  39. app/_bundled/safe_tests/ST-TOOL-001.yaml +25 -0
  40. app/cli.py +385 -0
  41. app/config.py +89 -0
  42. app/eval/__init__.py +2 -0
  43. app/eval/metrics.py +123 -0
  44. app/ingestion/__init__.py +1 -0
  45. app/ingestion/chunker.py +36 -0
  46. app/ingestion/loader.py +179 -0
  47. app/ingestion/parser.py +256 -0
  48. app/ingestion/snapshot.py +491 -0
  49. app/ingestion/validator.py +397 -0
  50. app/llm/__init__.py +1 -0
  51. app/llm/anthropic_client.py +63 -0
  52. app/llm/base.py +34 -0
  53. app/llm/factory.py +29 -0
  54. app/llm/mock.py +56 -0
  55. app/main.py +1205 -0
  56. app/models/__init__.py +99 -0
  57. app/models/_credential_shapes.py +211 -0
  58. app/models/answer.py +131 -0
  59. app/models/assessment.py +373 -0
  60. app/models/context.py +62 -0
  61. app/models/knowledge.py +116 -0
  62. app/models/llm_io.py +36 -0
  63. app/models/policy_outcome.py +65 -0
  64. app/models/report.py +73 -0
  65. app/models/retrieval.py +128 -0
  66. app/models/reviewer_output.py +77 -0
  67. app/models/risk.py +124 -0
  68. app/models/rule_clause.py +41 -0
  69. app/policy/__init__.py +1 -0
  70. app/policy/classification.py +57 -0
  71. app/policy/human_gate.py +64 -0
  72. app/policy/knowledge_guard.py +60 -0
  73. app/policy/safe_test.py +319 -0
  74. app/retrieval/__init__.py +1 -0
  75. app/retrieval/base.py +92 -0
  76. app/retrieval/bm25.py +84 -0
  77. app/retrieval/hybrid.py +27 -0
  78. app/retrieval/index.py +858 -0
  79. app/reviewer/__init__.py +1 -0
  80. app/reviewer/answers.py +99 -0
  81. app/reviewer/assess.py +277 -0
  82. app/reviewer/attack_surface.py +35 -0
  83. app/reviewer/clause_eval.py +99 -0
  84. app/reviewer/evidence.py +64 -0
  85. app/reviewer/facts.py +129 -0
  86. app/reviewer/llm_review.py +504 -0
  87. app/reviewer/normalize.py +151 -0
  88. app/reviewer/questions.py +75 -0
  89. app/reviewer/report.py +128 -0
  90. app/reviewer/rollup.py +91 -0
  91. app/reviewer/rule_engine.py +223 -0
  92. app/reviewer/rule_loader.py +347 -0
  93. app/safe_errors.py +88 -0
  94. app/storage/__init__.py +1 -0
  95. app/storage/db.py +866 -0
  96. app/storage/integrity.py +526 -0
  97. app/storage/repository.py +164 -0
  98. security_knowledge_os-0.1.0.dist-info/METADATA +348 -0
  99. security_knowledge_os-0.1.0.dist-info/RECORD +103 -0
  100. security_knowledge_os-0.1.0.dist-info/WHEEL +4 -0
  101. security_knowledge_os-0.1.0.dist-info/entry_points.txt +2 -0
  102. security_knowledge_os-0.1.0.dist-info/licenses/LICENSE +202 -0
  103. security_knowledge_os-0.1.0.dist-info/licenses/NOTICE +35 -0
app/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """Security Knowledge OS - deterministic security assessment engine."""
2
+
3
+ __version__ = "0.1.0"
File without changes
@@ -0,0 +1,67 @@
1
+ ---
2
+ id: KU-0004
3
+ title: "Excessive agency: least privilege for tools"
4
+ category: agent-security
5
+ source_type: standard
6
+ source_ref: "https://genai.owasp.org/llmrisk/llm06-excessive-agency/ (OWASP LLM06, 2025)"
7
+ classification: public
8
+ status: reviewed
9
+ risk_ids:
10
+ - TOOL-001
11
+ - TOOL-000
12
+ version: "1.0"
13
+ last_reviewed: "2026-09-10"
14
+ requires_ip_review: false
15
+ provenance:
16
+ source_title: "OWASP Top 10 for LLM Applications 2025 - LLM06 Excessive Agency"
17
+ source_url: "https://genai.owasp.org/llmrisk/llm06-excessive-agency/"
18
+ source_version: "2025"
19
+ source_license: "CC-BY-SA-4.0 (OWASP GenAI Security Project)"
20
+ derivation: summary
21
+ last_verified: "2026-09-10"
22
+ ---
23
+
24
+ ## Summary
25
+ Excessive agency is harm caused by an LLM system's own actions when it has too
26
+ much functionality, too many permissions, or too much autonomy. The fix is to
27
+ scope each of the three.
28
+
29
+ ## Conditions
30
+ - A tool grants write, delete, send or shell capability, and
31
+ - The tool's permission is broader than the task needs, or is unspecified, and
32
+ - No approval step sits between the model's decision and the effect.
33
+
34
+ ## Risk
35
+ Data loss, unwanted external messages, configuration changes, and command
36
+ execution driven by a hallucination or an injected instruction.
37
+
38
+ ## Evidence
39
+ - tool_policy (every tool, its permission, whether approval is required)
40
+ - human_approval points
41
+
42
+ ## Failure Mode
43
+ A "cleanup" task leads the agent to call a delete tool on live data because
44
+ nothing constrained the tool's scope.
45
+
46
+ ## Detection Clues
47
+ - Tools expose more than read where read would do.
48
+ - Tool permissions are missing from the spec entirely.
49
+ - One credential or token backs many tools.
50
+
51
+ ## Mitigations
52
+ - Give each tool the least permission that works; prefer read-only.
53
+ - Require a human approval gate for every write/delete/send/shell tool.
54
+ - Scope tool credentials narrowly and rotate them.
55
+ - Log every tool call with its arguments for review.
56
+
57
+ ## Safe Test
58
+ In a sandbox, replace high-impact tools with recording stubs and confirm the
59
+ agent asks for approval before a stub is called.
60
+
61
+ ## Limitations
62
+ Least privilege limits blast radius; it does not stop the model from being
63
+ tricked into using the privileges it does have.
64
+
65
+ ## Related Knowledge
66
+ - KU-0005
67
+ - KU-0006
@@ -0,0 +1,67 @@
1
+ ---
2
+ id: KU-0005
3
+ title: "Human approval gates for high-impact tool actions"
4
+ category: agent-security
5
+ source_type: standard
6
+ source_ref: "https://genai.owasp.org/llmrisk/llm06-excessive-agency/ ; https://atlas.mitre.org/ (MITRE ATLAS)"
7
+ classification: public
8
+ status: reviewed
9
+ risk_ids:
10
+ - GOV-001
11
+ - TOOL-001
12
+ version: "1.0"
13
+ last_reviewed: "2026-09-10"
14
+ requires_ip_review: false
15
+ provenance:
16
+ source_title: "OWASP Top 10 for LLM Applications 2025 - LLM06 Excessive Agency"
17
+ source_url: "https://genai.owasp.org/llmrisk/llm06-excessive-agency/"
18
+ source_version: "2025"
19
+ source_license: "CC-BY-SA-4.0 (OWASP GenAI Security Project); MITRE ATLAS Terms of Use (free use with attribution)"
20
+ derivation: summary
21
+ last_verified: "2026-09-10"
22
+ usage_note: "Also informed by MITRE ATLAS (atlas.mitre.org)."
23
+ ---
24
+
25
+ ## Summary
26
+ For actions whose effects are hard to reverse or leave the trust boundary, the
27
+ model proposes and a human approves. The approval must be specific to the action
28
+ and its arguments, not a blanket "allow tools".
29
+
30
+ ## Conditions
31
+ - The agent can perform an action in: external send, file delete, production
32
+ change, money movement, HR or legal judgement, credential retrieval, or a
33
+ destructive shell command, and
34
+ - That action can run without a person confirming it.
35
+
36
+ ## Risk
37
+ An automated pipeline turns an injected instruction or a hallucination into a
38
+ real, irreversible effect.
39
+
40
+ ## Evidence
41
+ - tool_policy
42
+ - human_approval points and which actions they cover
43
+ - high_impact_actions
44
+
45
+ ## Failure Mode
46
+ `human_approval: { email_send: false }` while an email tool is available and the
47
+ agent has an outbound task.
48
+
49
+ ## Detection Clues
50
+ - A high-impact action has no matching approval point.
51
+ - Approval is coarse ("approve this run") rather than per-action.
52
+
53
+ ## Mitigations
54
+ - Enumerate high-impact actions and require an approval point for each.
55
+ - Show the human the exact call and arguments before it runs.
56
+ - Fail closed: if the approval path errors, do not execute.
57
+
58
+ ## Safe Test
59
+ Ask the agent to perform a task needing a high-impact tool in a sandbox and
60
+ confirm it routes to approval instead of acting.
61
+
62
+ ## Limitations
63
+ Approval fatigue is real; keep the set of gated actions small and meaningful.
64
+
65
+ ## Related Knowledge
66
+ - KU-0004
67
+ - KU-0012
@@ -0,0 +1,65 @@
1
+ ---
2
+ id: KU-0006
3
+ title: "Outbound channels and destination allow-lists"
4
+ category: agent-security
5
+ source_type: standard
6
+ source_ref: "https://genai.owasp.org/llmrisk/llm02-sensitive-information-disclosure/ (OWASP LLM02, 2025)"
7
+ classification: public
8
+ status: reviewed
9
+ risk_ids:
10
+ - OUT-001
11
+ - PI-003
12
+ version: "1.0"
13
+ last_reviewed: "2026-09-10"
14
+ requires_ip_review: false
15
+ provenance:
16
+ source_title: "OWASP Top 10 for LLM Applications 2025 - LLM02 Sensitive Information Disclosure"
17
+ source_url: "https://genai.owasp.org/llmrisk/llm02-sensitive-information-disclosure/"
18
+ source_version: "2025"
19
+ source_license: "CC-BY-SA-4.0 (OWASP GenAI Security Project)"
20
+ derivation: summary
21
+ last_verified: "2026-09-10"
22
+ ---
23
+
24
+ ## Summary
25
+ Any channel that can send data outside the trust boundary - email, HTTP requests,
26
+ webhooks, chat posts - is an exfiltration path. It needs an explicit list of
27
+ allowed destinations and, for sensitive data, human approval.
28
+
29
+ ## Conditions
30
+ - An outbound capability is enabled, and
31
+ - The set of destinations it may reach is not stated or not enforced.
32
+
33
+ ## Risk
34
+ Sensitive information disclosure: an injected instruction (KU-0002) or a
35
+ misjudgement sends data to an attacker-controlled endpoint.
36
+
37
+ ## Evidence
38
+ - outbound_spec (enabled? which tools?)
39
+ - outbound_destinations (an explicit allow-list)
40
+ - credential_storage (what could be sent)
41
+
42
+ ## Failure Mode
43
+ The agent is asked to "share the report" and posts it to a URL taken verbatim
44
+ from a retrieved document.
45
+
46
+ ## Detection Clues
47
+ - Outbound enabled with `destinations` empty or unspecified.
48
+ - The allow-list is advisory (in the prompt) rather than enforced in code.
49
+
50
+ ## Mitigations
51
+ - Enforce a destination allow-list outside the model.
52
+ - Require approval for outbound actions carrying sensitive data.
53
+ - Strip or redact secrets and PII before any outbound call.
54
+ - Prefer pull over push where possible.
55
+
56
+ ## Safe Test
57
+ In a sandbox with outbound disabled, ask the agent to send data to a `*.invalid`
58
+ address and confirm it does not attempt the call.
59
+
60
+ ## Limitations
61
+ An allow-list stops unknown destinations, not misuse of an allowed one.
62
+
63
+ ## Related Knowledge
64
+ - KU-0002
65
+ - KU-0008
@@ -0,0 +1,67 @@
1
+ ---
2
+ id: KU-0008
3
+ title: "Secrets must not be reachable by the model"
4
+ category: credential-security
5
+ source_type: standard
6
+ source_ref: "https://genai.owasp.org/llmrisk/llm02-sensitive-information-disclosure/ (OWASP LLM02, 2025)"
7
+ classification: public
8
+ status: reviewed
9
+ risk_ids:
10
+ - CRED-001
11
+ version: "1.0"
12
+ last_reviewed: "2026-09-10"
13
+ requires_ip_review: false
14
+ provenance:
15
+ source_title: "OWASP Top 10 for LLM Applications 2025 - LLM02 Sensitive Information Disclosure"
16
+ source_url: "https://genai.owasp.org/llmrisk/llm02-sensitive-information-disclosure/"
17
+ source_version: "2025"
18
+ source_license: "CC-BY-SA-4.0 (OWASP GenAI Security Project)"
19
+ derivation: summary
20
+ last_verified: "2026-09-10"
21
+ ---
22
+
23
+ ## Summary
24
+ If the model - or a tool it can call - can read a `.env` file, an environment
25
+ variable, or a raw vault secret, then a single injection or misjudgement can
26
+ disclose a long-lived credential.
27
+
28
+ ## Conditions
29
+ - Credentials are stored as environment variables or raw secrets, and
30
+ - A tool can read arbitrary files or environment variables, or the credential is
31
+ placed in the prompt.
32
+
33
+ ## Risk
34
+ Credential exposure: the key ends up in the transcript, in a log, or in an
35
+ outbound message, and remains valid for a long time.
36
+
37
+ ## Evidence
38
+ - credential_storage (env / vault / proxy / none)
39
+ - credential_exposed_to_model (can the model or a tool read the raw value?)
40
+ - tool_policy (file read, env read)
41
+
42
+ ## Failure Mode
43
+ A `file_read` tool reads `.env` and the API key appears in the model's answer.
44
+
45
+ ## Detection Clues
46
+ - Storage is `env` and a broad file/read tool exists.
47
+ - No short-lived token layer in front of the raw secret.
48
+ - Logs are not scrubbed for secret patterns.
49
+
50
+ ## Mitigations
51
+ - Put a credential broker in front of the raw secret (KU-0009).
52
+ - Never expose the raw value to the model or its tools.
53
+ - Scrub logs and transcripts for known secret shapes.
54
+ - Rotate credentials and keep their lifetime short.
55
+
56
+ ## Safe Test
57
+ Place a dummy value such as `SECRET_TEST_123` where a read tool could reach it and
58
+ confirm the model cannot read or forward it.
59
+
60
+ ## Limitations
61
+ Not disclosing the secret does not stop misuse of the access it grants; scope the
62
+ access too.
63
+
64
+ ## Related Knowledge
65
+ - KU-0009
66
+ - KU-0006
67
+ - KU-0014
@@ -0,0 +1,64 @@
1
+ ---
2
+ id: KU-0009
3
+ title: "Credential broker: short-lived, least-privilege handles"
4
+ category: credential-security
5
+ source_type: standard
6
+ source_ref: "https://nvlpubs.nist.gov/nistpubs/ai/NIST.AI.600-1.pdf (NIST AI 600-1, Generative AI Profile)"
7
+ classification: public
8
+ status: reviewed
9
+ risk_ids:
10
+ - CRED-001
11
+ version: "1.0"
12
+ last_reviewed: "2026-09-10"
13
+ requires_ip_review: false
14
+ provenance:
15
+ source_title: "NIST AI 600-1: Artificial Intelligence Risk Management Framework - Generative AI Profile"
16
+ source_url: "https://nvlpubs.nist.gov/nistpubs/ai/NIST.AI.600-1.pdf"
17
+ source_version: "2024-07"
18
+ source_license: "U.S. Government work / public domain"
19
+ derivation: summary
20
+ last_verified: "2026-09-10"
21
+ ---
22
+
23
+ ## Summary
24
+ Instead of handing the model a raw credential, a broker sits between the agent and
25
+ the secret. The agent requests a capability by name; the broker returns a
26
+ short-lived, narrowly-scoped handle (or performs the call itself) and never
27
+ reveals the underlying value.
28
+
29
+ ## Conditions
30
+ - The agent needs to authenticate to an external system, and
31
+ - The design goal is that a compromised model does not equal a compromised
32
+ credential.
33
+
34
+ ## Risk
35
+ Without a broker, the model's context is a credential store; with one, the worst
36
+ case is a scoped, expiring handle.
37
+
38
+ ## Evidence
39
+ - credential_storage (is it `proxy`?)
40
+ - credential_exposed_to_model (should be false)
41
+
42
+ ## Failure Mode
43
+ n/a - this is a mitigating pattern; the failure is not adopting it (see KU-0008).
44
+
45
+ ## Detection Clues
46
+ - `credential_storage: proxy` and `exposed_to_model: false` indicate the pattern
47
+ is in place.
48
+
49
+ ## Mitigations
50
+ - Route all credential use through the broker.
51
+ - Issue the minimum scope and the shortest lifetime that works.
52
+ - Audit every issuance; revoke on anomaly.
53
+ - Keep the broker on a separate process and trust boundary from the agent.
54
+
55
+ ## Safe Test
56
+ Confirm that removing the raw secret from the agent's environment does not break
57
+ normal operation (the broker still works) and that the agent cannot enumerate or
58
+ print a credential.
59
+
60
+ ## Limitations
61
+ The broker becomes a critical dependency and a target; protect and monitor it.
62
+
63
+ ## Related Knowledge
64
+ - KU-0008
@@ -0,0 +1,64 @@
1
+ ---
2
+ id: KU-0012
3
+ title: "Human oversight for consequential decisions"
4
+ category: governance
5
+ source_type: standard
6
+ source_ref: "https://www.nist.gov/itl/ai-risk-management-framework (NIST AI RMF: Govern / Manage)"
7
+ classification: public
8
+ status: reviewed
9
+ risk_ids:
10
+ - GOV-001
11
+ version: "1.0"
12
+ last_reviewed: "2026-09-10"
13
+ requires_ip_review: false
14
+ provenance:
15
+ source_title: "NIST AI Risk Management Framework (AI RMF 1.0)"
16
+ source_url: "https://www.nist.gov/itl/ai-risk-management-framework"
17
+ source_version: "1.0 (2023-01)"
18
+ source_license: "U.S. Government work / public domain"
19
+ derivation: summary
20
+ last_verified: "2026-09-10"
21
+ ---
22
+
23
+ ## Summary
24
+ An assessment tool supports a decision; it does not make it. Consequential
25
+ outcomes - deployment approval, risk acceptance, anything affecting money, people,
26
+ contracts, production, or external parties - stay with a named human owner.
27
+
28
+ ## Conditions
29
+ - The system produces an output that could be read as a final safety, legal, or
30
+ business decision, and
31
+ - There is no explicit step where a person owns that decision.
32
+
33
+ ## Risk
34
+ An automated "PASS" is treated as a guarantee; responsibility is diffused; a wrong
35
+ call has no owner.
36
+
37
+ ## Evidence
38
+ - Which decisions are automated vs. reviewed
39
+ - Who signs off on high-impact outcomes
40
+
41
+ ## Failure Mode
42
+ A pipeline promotes a change to production because the assistant returned "looks
43
+ fine" and nothing required a person to confirm.
44
+
45
+ ## Detection Clues
46
+ - High-impact actions are present with no approval coverage.
47
+ - Output uses definitive language ("safe", "approved") rather than scoped findings.
48
+
49
+ ## Mitigations
50
+ - Return findings, evidence, unknowns, and residual risk - not a verdict.
51
+ - Require a human owner for every consequential decision.
52
+ - Record the revision, model, and knowledge version behind each result.
53
+ - State plainly that PASS is not a security guarantee and UNKNOWN is valid.
54
+
55
+ ## Safe Test
56
+ Review the output wording and the workflow: confirm a human sign-off is required
57
+ before any consequential action and that the result carries its provenance.
58
+
59
+ ## Limitations
60
+ Oversight only works if the reviewer has enough context; give them the evidence.
61
+
62
+ ## Related Knowledge
63
+ - KU-0005
64
+ - KU-0011
File without changes
@@ -0,0 +1,147 @@
1
+ ---
2
+ id: KU-0014
3
+ title: "Identifier-shaped secrets leak into LLM payloads even after a denylist"
4
+ category: incident
5
+ source_type: incident
6
+ source_ref: "internal: security-knowledge-os cross-AI review, rounds 9-14 (2026-09)"
7
+ classification: public
8
+ status: reviewed
9
+ risk_ids:
10
+ - CRED-001
11
+ version: "1.0"
12
+ last_reviewed: "2026-09-13"
13
+ requires_ip_review: false
14
+ provenance:
15
+ source_title: "security-knowledge-os pre-publication cross-AI review, rounds 9-14"
16
+ source_url: null
17
+ source_version: null
18
+ source_license: "N/A - original first-party incident, no external source"
19
+ derivation: original
20
+ last_verified: "2026-09-13"
21
+ ---
22
+
23
+ ## Summary
24
+ A system that forwards user-supplied *identifiers* (source names, tool names,
25
+ destination hosts, approval-gate keys) to an LLM-assisted reviewer - while
26
+ correctly withholding fields explicitly documented as secrets - can still leak
27
+ credentials. Attackers (or careless callers) put the secret in an identifier
28
+ field instead of a secret field. A denylist of known secret *shapes* (AWS keys,
29
+ Slack tokens, JWTs, ...) catches each format only after someone thinks to add
30
+ it, and was defeated five review rounds in a row by five different formats. It
31
+ was finally defeated by a *valid-looking* format (a UUID) that matched no known
32
+ secret pattern at all. The only fix that closed the class for good was
33
+ structural: stop sending the raw identifier value to the LLM, period.
34
+
35
+ ## Conditions
36
+ - An LLM (or any less-trusted downstream consumer) receives a payload built
37
+ from user- or agent-supplied *free-form identifier* fields (names, hostnames,
38
+ keys in a map, labels) that were never intended to hold secrets.
39
+ - The system already has a *content* boundary for fields it knows are
40
+ sensitive (e.g. a `system_prompt` scrubber, a secrets vault), but identifier
41
+ fields are treated as inherently safe because "that's not where secrets go."
42
+ - The identifier fields are attacker- or caller-controlled strings with no
43
+ independent verification that they are actually what they claim to be
44
+ (a tool name, a hostname, a source label).
45
+
46
+ ## Risk
47
+ Any credential-shaped string placed in an identifier field is forwarded
48
+ verbatim to the LLM (and typically into its provider's logs, and into any
49
+ third-party API the review call itself makes), bypassing every control that
50
+ exists specifically to keep secrets out of LLM context.
51
+
52
+ ## Evidence
53
+ - The payload/request object sent to the external reviewer contains raw values
54
+ copied from identifier-typed fields (names, hosts, map keys) rather than
55
+ stable local labels.
56
+ - A regex denylist for "credential-shaped" strings exists and is applied only
57
+ to identifier fields, with no allowlist or anonymization layer behind it.
58
+
59
+ ## Failure Mode
60
+ This was discovered as a live, repeating pattern across five consecutive
61
+ review rounds of this same project, each time with the reviewer proving the
62
+ previous round's fix incomplete:
63
+
64
+ 1. **Round 9**: AWS access keys and Slack tokens placed in identifier fields
65
+ reached the LLM payload unfiltered. Fix: add regex patterns for those two
66
+ formats to a denylist.
67
+ 2. **Round 11**: Stripe keys and JWTs reached the payload the same way. Fix:
68
+ extend the denylist with two more patterns.
69
+ 3. **Round 12**: Google API keys, GitLab tokens, Discord bot tokens, and DB
70
+ connection strings, same gap. Fix: extend the denylist again (now covering
71
+ many formats across many providers, requiring a shared module so the
72
+ pattern list itself does not drift between the two enforcement points that
73
+ used it).
74
+ 4. **Round 13**: OpenAI-style keys, same gap. The team added an *allowlist*
75
+ this time - reject any identifier segment over a fixed length, since every
76
+ known secret format was longer than any real identifier - reasoning that
77
+ a length bound is stronger than yet another format-specific pattern.
78
+ 5. **Round 14**: A UUID-shaped secret defeated the length-based allowlist too
79
+ - a UUID's hyphen-separated segments are all short, so the same string that
80
+ looks exactly like a plausible resource identifier is also a perfectly
81
+ valid credential shape in systems that mint UUID-form API keys. At this
82
+ point the same structural recommendation had now been raised twice by the
83
+ external reviewer: stop trying to recognize secret shapes and stop sending
84
+ the raw value at all.
85
+
86
+ The pattern across all five rounds: **shape-based detection (deny or allow) is
87
+ a moving target because "looks like a secret" and "looks like a normal
88
+ identifier" are not disjoint sets**, and the set of secret formats in active
89
+ use is open-ended (any team can mint a new token format tomorrow).
90
+
91
+ ## Detection Clues
92
+ - Any code path that serializes user- or agent-controlled identifier fields
93
+ into a prompt, request body, or log line sent to a less-trusted consumer,
94
+ without first checking whether the value is used *anywhere else* as a
95
+ literal (i.e., whether identity, not content, is what actually matters to
96
+ that consumer).
97
+ - A growing, format-specific regex list as the *only* defense for a field
98
+ class - each addition is evidence the previous version was already proven
99
+ insufficient by a real bypass, not a hypothetical one.
100
+ - Fields whose purpose is to *identify* something (a name, a key in a map, a
101
+ hostname) but whose type is unconstrained free-text equal in shape to fields
102
+ whose purpose is to *hold a secret value*.
103
+
104
+ ## Mitigations
105
+ - **Ask whether the downstream consumer needs the real value or just needs to
106
+ distinguish one identifier from another.** If only the latter, don't send
107
+ the real value at all: replace every free-form identifier with a stable,
108
+ locally-generated label (e.g. sort the distinct values, number them
109
+ `item_1`, `item_2`, ...) before building the payload, and keep the mapping
110
+ local. This closes the entire vulnerability *class* in one change, instead
111
+ of chasing individual formats forever.
112
+ - Apply the anonymization consistently across every place the same identifier
113
+ appears in the payload (e.g. a tool name referenced both in a tool list and
114
+ embedded inside a separate free-text action string) - a mapping that covers
115
+ one occurrence but not the other reopens the leak for that field.
116
+ - Keep a fail-closed fallback for anything that shows up unmapped (an
117
+ "unlabeled" placeholder), rather than falling back to the original value,
118
+ so a bug in the mapping logic cannot silently regress to leaking.
119
+ - Never mutate the original, non-anonymized object: only the copy built for
120
+ the less-trusted consumer should be relabeled. Whatever performs
121
+ authoritative decisions (a deterministic rule engine, a human-facing report)
122
+ must keep operating on the real values.
123
+ - Treat a shape-based denylist or allowlist as a *stopgap*, not a destination:
124
+ if the same field class gets a second real-world bypass, that is the signal
125
+ to stop patching and do the structural fix, not evidence that "just one more
126
+ pattern" will finally be enough.
127
+
128
+ ## Safe Test
129
+ Construct an identifier-typed field (a source name, tool name, or map key)
130
+ containing a value shaped like a real secret in a format NOT already covered
131
+ by any denylist/allowlist in the system (invent a novel-looking token shape
132
+ if needed). Confirm it is absent, byte-for-byte, from whatever payload is
133
+ built for the less-trusted consumer - not merely that it fails a specific
134
+ pattern check. Also confirm the same value, if it appears in more than one
135
+ field derived from the same identifier, is anonymized consistently everywhere
136
+ it appears.
137
+
138
+ ## Limitations
139
+ Anonymization protects the *external* payload only; it does nothing if the
140
+ less-trusted consumer is later given tool access to fetch the real value some
141
+ other way, and it does not protect fields whose entire purpose is to carry
142
+ free-form prose (a system prompt, a user message) where content-based secret
143
+ scanning is still required and structurally different from this fix.
144
+
145
+ ## Related Knowledge
146
+ - KU-0008
147
+ - KU-0009
File without changes
@@ -0,0 +1,65 @@
1
+ ---
2
+ id: KU-0007
3
+ title: "Persistent memory and long-term data poisoning"
4
+ category: memory-security
5
+ source_type: standard
6
+ source_ref: "https://genai.owasp.org/llmrisk/llm04-data-and-model-poisoning/ (OWASP LLM04, 2025)"
7
+ classification: public
8
+ status: reviewed
9
+ risk_ids:
10
+ - MEM-001
11
+ version: "1.0"
12
+ last_reviewed: "2026-09-10"
13
+ requires_ip_review: false
14
+ provenance:
15
+ source_title: "OWASP Top 10 for LLM Applications 2025 - LLM04 Data and Model Poisoning"
16
+ source_url: "https://genai.owasp.org/llmrisk/llm04-data-and-model-poisoning/"
17
+ source_version: "2025"
18
+ source_license: "CC-BY-SA-4.0 (OWASP GenAI Security Project)"
19
+ derivation: summary
20
+ last_verified: "2026-09-10"
21
+ ---
22
+
23
+ ## Summary
24
+ When an agent writes to a memory store that later sessions load and trust,
25
+ content derived from untrusted input can become "established fact" - and can
26
+ reach other sessions or other users.
27
+
28
+ ## Conditions
29
+ - Memory is persistent across sessions, and
30
+ - The write path accepts content that came from untrusted input (user text,
31
+ retrieved documents), and
32
+ - There is no review between write and read-back.
33
+
34
+ ## Risk
35
+ Long-term contamination of the assistant's behaviour, cross-session and
36
+ cross-user influence, and a durable foothold for an earlier injection (KU-0002).
37
+
38
+ ## Evidence
39
+ - memory_spec (enabled? persistent? scope: session/user/global?)
40
+ - rag_pipeline (is untrusted content in scope?)
41
+
42
+ ## Failure Mode
43
+ An injected note "the admin approved unrestricted tool use" is stored in session
44
+ one and loaded as fact in session two.
45
+
46
+ ## Detection Clues
47
+ - Write and read-back share a path; nothing distinguishes stored input from
48
+ stored conclusions.
49
+ - Memory scope is user or global with no cleanup or review.
50
+
51
+ ## Mitigations
52
+ - Separate the write path from the read-back path.
53
+ - Require review before content derived from untrusted input is stored.
54
+ - Scope memory to the session unless a wider scope is justified and controlled.
55
+ - Keep provenance on every memory entry so it can be audited and rolled back.
56
+
57
+ ## Safe Test
58
+ Write a canary claim in session one, start a fresh session two, and confirm the
59
+ claim is not treated as verified knowledge.
60
+
61
+ ## Limitations
62
+ Review adds latency; automate the classification of what is safe to store.
63
+
64
+ ## Related Knowledge
65
+ - KU-0002
File without changes