security-knowledge-os 0.2.0__tar.gz → 0.4.0__tar.gz

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 (247) hide show
  1. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/PKG-INFO +10 -9
  2. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/PUBLICATION_MANIFEST.md +45 -14
  3. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/README.md +9 -8
  4. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/REVIEW_PACKAGE.md +2 -2
  5. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/__init__.py +1 -1
  6. security_knowledge_os-0.4.0/app/adapters/capgraph.py +219 -0
  7. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/adapters/mcp_config.py +100 -23
  8. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/cli.py +110 -24
  9. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/reviewer/facts.py +7 -2
  10. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/constraints.txt +1 -1
  11. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/docs/acceptance-criteria.md +1 -1
  12. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/docs/knowledge-corpus.md +10 -7
  13. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/docs/pack-schema.md +28 -2
  14. security_knowledge_os-0.4.0/knowledge/public/prompt-security/KU-0015-role-confusion.md +122 -0
  15. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/pyproject.toml +1 -1
  16. security_knowledge_os-0.4.0/rules/prompt/PI-004.yaml +28 -0
  17. security_knowledge_os-0.4.0/safe_tests/ST-RC-001.yaml +43 -0
  18. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/sbom.json +3 -3
  19. security_knowledge_os-0.4.0/tests/unit/test_capgraph.py +380 -0
  20. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_cli.py +2 -2
  21. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_mcp_scan.py +5 -2
  22. security_knowledge_os-0.4.0/tests/unit/test_pi004_role_confusion.py +143 -0
  23. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_rule_loader.py +3 -1
  24. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_safe_test.py +3 -1
  25. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/.env.example +0 -0
  26. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/.github/workflows/ci.yml +0 -0
  27. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/.github/workflows/publish.yml +0 -0
  28. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/.gitignore +0 -0
  29. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/LICENSE +0 -0
  30. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/NOTICE +0 -0
  31. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/REVIEW_CHECKLIST.md +0 -0
  32. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/adapters/__init__.py +0 -0
  33. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/config.py +0 -0
  34. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/eval/__init__.py +0 -0
  35. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/eval/metrics.py +0 -0
  36. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/ingestion/__init__.py +0 -0
  37. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/ingestion/chunker.py +0 -0
  38. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/ingestion/loader.py +0 -0
  39. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/ingestion/parser.py +0 -0
  40. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/ingestion/snapshot.py +0 -0
  41. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/ingestion/validator.py +0 -0
  42. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/llm/__init__.py +0 -0
  43. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/llm/anthropic_client.py +0 -0
  44. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/llm/base.py +0 -0
  45. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/llm/factory.py +0 -0
  46. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/llm/mock.py +0 -0
  47. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/main.py +0 -0
  48. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/models/__init__.py +0 -0
  49. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/models/_credential_shapes.py +0 -0
  50. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/models/answer.py +0 -0
  51. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/models/assessment.py +0 -0
  52. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/models/context.py +0 -0
  53. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/models/knowledge.py +0 -0
  54. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/models/llm_io.py +0 -0
  55. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/models/pack.py +0 -0
  56. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/models/policy_outcome.py +0 -0
  57. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/models/report.py +0 -0
  58. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/models/retrieval.py +0 -0
  59. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/models/reviewer_output.py +0 -0
  60. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/models/risk.py +0 -0
  61. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/models/rule_clause.py +0 -0
  62. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/pack_cli.py +0 -0
  63. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/packs/__init__.py +0 -0
  64. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/packs/archive.py +0 -0
  65. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/packs/build.py +0 -0
  66. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/packs/diff.py +0 -0
  67. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/packs/license.py +0 -0
  68. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/packs/loader.py +0 -0
  69. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/packs/manifest.py +0 -0
  70. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/packs/signing.py +0 -0
  71. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/packs/store.py +0 -0
  72. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/packs/trusted_keys.py +0 -0
  73. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/packs/verify.py +0 -0
  74. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/policy/__init__.py +0 -0
  75. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/policy/classification.py +0 -0
  76. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/policy/human_gate.py +0 -0
  77. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/policy/knowledge_guard.py +0 -0
  78. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/policy/safe_test.py +0 -0
  79. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/retrieval/__init__.py +0 -0
  80. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/retrieval/base.py +0 -0
  81. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/retrieval/bm25.py +0 -0
  82. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/retrieval/hybrid.py +0 -0
  83. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/retrieval/index.py +0 -0
  84. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/reviewer/__init__.py +0 -0
  85. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/reviewer/answers.py +0 -0
  86. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/reviewer/assess.py +0 -0
  87. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/reviewer/attack_surface.py +0 -0
  88. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/reviewer/clause_eval.py +0 -0
  89. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/reviewer/evidence.py +0 -0
  90. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/reviewer/extensions.py +0 -0
  91. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/reviewer/llm_review.py +0 -0
  92. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/reviewer/normalize.py +0 -0
  93. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/reviewer/questions.py +0 -0
  94. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/reviewer/report.py +0 -0
  95. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/reviewer/rollup.py +0 -0
  96. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/reviewer/rule_engine.py +0 -0
  97. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/reviewer/rule_loader.py +0 -0
  98. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/reviewer/vocabulary.py +0 -0
  99. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/safe_errors.py +0 -0
  100. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/storage/__init__.py +0 -0
  101. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/storage/db.py +0 -0
  102. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/storage/integrity.py +0 -0
  103. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/app/storage/repository.py +0 -0
  104. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/docs/architecture.md +0 -0
  105. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/docs/attribution.md +0 -0
  106. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/docs/history-rewrite-map.md +0 -0
  107. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/docs/knowledge-schema.md +0 -0
  108. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/docs/rule-schema.md +0 -0
  109. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/docs/safe-test-schema.md +0 -0
  110. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/docs/safety-boundaries.md +0 -0
  111. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/docs/threat-model.md +0 -0
  112. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/private/README.md +0 -0
  113. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/private/confidential/.gitkeep +0 -0
  114. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/private/internal/.gitkeep +0 -0
  115. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/agent-security/.gitkeep +0 -0
  116. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/agent-security/KU-0004-excessive-agency-least-privilege.md +0 -0
  117. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/agent-security/KU-0005-human-approval-gates.md +0 -0
  118. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/agent-security/KU-0006-outbound-channels-allowlist.md +0 -0
  119. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/credential-security/.gitkeep +0 -0
  120. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/credential-security/KU-0008-secrets-not-reachable-by-model.md +0 -0
  121. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/credential-security/KU-0009-credential-broker-pattern.md +0 -0
  122. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/governance/KU-0012-human-oversight.md +0 -0
  123. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/incidents/.gitkeep +0 -0
  124. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/incidents/KU-0014-llm-payload-identifier-leak.md +0 -0
  125. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/memory-security/.gitkeep +0 -0
  126. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/memory-security/KU-0007-persistent-memory-poisoning.md +0 -0
  127. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/methodology/.gitkeep +0 -0
  128. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/methodology/KU-0011-improper-output-handling.md +0 -0
  129. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/methodology/KU-0013-assume-compromise-ja.md +0 -0
  130. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/prompt-security/.gitkeep +0 -0
  131. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/prompt-security/KU-0001-direct-prompt-injection.md +0 -0
  132. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/prompt-security/KU-0010-system-prompt-leakage.md +0 -0
  133. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/rag-security/.gitkeep +0 -0
  134. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/rag-security/KU-0002-indirect-prompt-injection.md +0 -0
  135. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/knowledge/public/rag-security/KU-0003-untrusted-content-isolation.md +0 -0
  136. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/rules/agent/.gitkeep +0 -0
  137. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/rules/agent/OUT-001.yaml +0 -0
  138. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/rules/agent/TOOL-000.yaml +0 -0
  139. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/rules/agent/TOOL-001.yaml +0 -0
  140. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/rules/credential/.gitkeep +0 -0
  141. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/rules/credential/CRED-001.yaml +0 -0
  142. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/rules/governance/.gitkeep +0 -0
  143. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/rules/governance/GOV-001.yaml +0 -0
  144. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/rules/memory/.gitkeep +0 -0
  145. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/rules/memory/MEM-001.yaml +0 -0
  146. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/rules/prompt/.gitkeep +0 -0
  147. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/rules/rag/.gitkeep +0 -0
  148. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/rules/rag/PI-003.yaml +0 -0
  149. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/safe_tests/ST-CRED-001.yaml +0 -0
  150. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/safe_tests/ST-IPI-001.yaml +0 -0
  151. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/safe_tests/ST-MEM-001.yaml +0 -0
  152. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/safe_tests/ST-TOOL-001.yaml +0 -0
  153. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/scripts/_scheduled_cross_review.sh +0 -0
  154. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/scripts/assess.py +0 -0
  155. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/scripts/build_index.py +0 -0
  156. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/scripts/evaluate.py +0 -0
  157. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/scripts/generate_sbom.py +0 -0
  158. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/scripts/ingest.py +0 -0
  159. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/scripts/issue_license.py +0 -0
  160. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/scripts/preflight.py +0 -0
  161. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/scripts/run_cross_review.sh +0 -0
  162. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/scripts/secret_scan.py +0 -0
  163. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/scripts/validate_knowledge.py +0 -0
  164. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/scripts/validate_rules.py +0 -0
  165. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/scripts/validate_safe_tests.py +0 -0
  166. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/conftest.py +0 -0
  167. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/assessments/safe/S-001-prompt-only.yaml +0 -0
  168. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/assessments/safe/S-002-rag-trusted-no-actions.yaml +0 -0
  169. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/assessments/safe/S-003-readonly-tool-with-approval.yaml +0 -0
  170. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/assessments/safe/S-004-credential-proxy.yaml +0 -0
  171. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/assessments/unknown/U-001-tool-permissions-missing.yaml +0 -0
  172. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/assessments/unknown/U-002-memory-persistence-unspecified.yaml +0 -0
  173. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/assessments/unknown/U-003-outbound-destination-unspecified.yaml +0 -0
  174. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/assessments/unknown/U-004-credential-handling-unspecified.yaml +0 -0
  175. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/assessments/unknown/U-005-tool-permission-unrecognized.yaml +0 -0
  176. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/assessments/vulnerable/V-001-indirect-injection-auto-email.yaml +0 -0
  177. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/assessments/vulnerable/V-002-rag-delete-tool-no-approval.yaml +0 -0
  178. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/assessments/vulnerable/V-003-persistent-memory-untrusted.yaml +0 -0
  179. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/assessments/vulnerable/V-004-env-secret-readable.yaml +0 -0
  180. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/assessments/vulnerable/V-005-unlisted-rag-source-untrusted-memory.yaml +0 -0
  181. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/corpus/private/confidential/KU-1020-confidential-tool-matrix.md +0 -0
  182. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/corpus/private/internal/KU-1010-internal-memory-heuristic.md +0 -0
  183. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/corpus/public/credential-security/KU-1003-env-secret-exposure.md +0 -0
  184. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/corpus/public/prompt-security/KU-1001-instruction-data-boundary.md +0 -0
  185. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/corpus/public/rag-security/KU-1002-indirect-injection-external-document.md +0 -0
  186. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/corpus/secret/KU-1099-secret-must-not-index.md +0 -0
  187. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/corpus_alt/public/methodology/KU-2001-alt-corpus-marker.md +0 -0
  188. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/knowledge/invalid/KU-0010-mislabeled.md +0 -0
  189. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/knowledge/invalid/bad-classification.md +0 -0
  190. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/knowledge/invalid/bad-id.md +0 -0
  191. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/knowledge/invalid/missing-fields.md +0 -0
  192. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/knowledge/invalid/no-front-matter.md +0 -0
  193. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/knowledge/private/internal/KU-0002-internal-note.md +0 -0
  194. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/knowledge/public/methodology/KU-0003-sparse.md +0 -0
  195. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/knowledge/public/prompt-security/KU-0001-instruction-hierarchy.md +0 -0
  196. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/fixtures/knowledge/secret/KU-0009-secret.md +0 -0
  197. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/integration/.gitkeep +0 -0
  198. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_answers.py +0 -0
  199. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_anthropic_client.py +0 -0
  200. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_api.py +0 -0
  201. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_assess.py +0 -0
  202. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_assess_m4.py +0 -0
  203. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_assess_m5.py +0 -0
  204. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_assess_script.py +0 -0
  205. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_assessment_input.py +0 -0
  206. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_build_index_script.py +0 -0
  207. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_classification_gate.py +0 -0
  208. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_classification_leakage.py +0 -0
  209. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_clause_eval.py +0 -0
  210. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_config.py +0 -0
  211. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_cross_review_script.py +0 -0
  212. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_db.py +0 -0
  213. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_eval.py +0 -0
  214. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_evaluate_script.py +0 -0
  215. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_evidence.py +0 -0
  216. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_fail_closed.py +0 -0
  217. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_finding_boundary.py +0 -0
  218. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_generate_sbom.py +0 -0
  219. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_human_gate.py +0 -0
  220. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_ingest_script.py +0 -0
  221. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_ja_retrieval.py +0 -0
  222. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_knowledge_corpus.py +0 -0
  223. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_knowledge_guard.py +0 -0
  224. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_knowledge_root_config.py +0 -0
  225. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_knowledge_schema.py +0 -0
  226. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_llm_adapter.py +0 -0
  227. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_llm_boundary.py +0 -0
  228. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_llm_output_schema.py +0 -0
  229. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_llm_review.py +0 -0
  230. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_loader.py +0 -0
  231. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_no_code_execution.py +0 -0
  232. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_no_secret_echo.py +0 -0
  233. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_normalize.py +0 -0
  234. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_packs.py +0 -0
  235. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_packs_cli.py +0 -0
  236. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_parser.py +0 -0
  237. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_policy_outcome.py +0 -0
  238. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_preflight_manifest.py +0 -0
  239. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_questions.py +0 -0
  240. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_reindex.py +0 -0
  241. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_report.py +0 -0
  242. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_repository.py +0 -0
  243. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_retrieval.py +0 -0
  244. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_rollup.py +0 -0
  245. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_rule_engine.py +0 -0
  246. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_secret_scan.py +0 -0
  247. {security_knowledge_os-0.2.0 → security_knowledge_os-0.4.0}/tests/unit/test_snapshot.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: security-knowledge-os
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Summary: Deterministic security assessment engine + knowledge retrieval + optional LLM assistance
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE
@@ -141,11 +141,11 @@ treated as data, never as a control instruction. Details in
141
141
  | --- | --- | --- |
142
142
  | **M1 — Skeleton + Schema + Validator** | done | directory tree, `app/models/*`, `app/ingestion/validator.py`, `scripts/validate_knowledge.py`, `docs/knowledge-schema.md` |
143
143
  | **M2 — Knowledge Loader + FTS5 Retrieval** | done | `app/ingestion/{loader,chunker}.py`, `app/storage/{db,repository}.py`, `app/retrieval/{base,bm25,hybrid,index}.py`, `scripts/{ingest,build_index}.py` |
144
- | **M3 — Deterministic Rule Engine** | done | `app/models/rule_clause.py`, `app/reviewer/{facts,clause_eval,rule_loader,rule_engine,normalize,attack_surface,evidence,rollup,assess}.py`, `rules/**` (7 rules), `scripts/validate_rules.py`, `docs/rule-schema.md` |
144
+ | **M3 — Deterministic Rule Engine** | done | `app/models/rule_clause.py`, `app/reviewer/{facts,clause_eval,rule_loader,rule_engine,normalize,attack_surface,evidence,rollup,assess}.py`, `rules/**` (8 rules), `scripts/validate_rules.py`, `docs/rule-schema.md` |
145
145
  | **M4 — LLM Adapter + Reviewer** | done | `app/llm/{base,mock,anthropic_client,factory}.py`, `app/models/{reviewer_output,llm_io}.py`, `app/reviewer/{llm_review,questions}.py`, `assess()` full, `scripts/assess.py` |
146
- | **M5 — Safe Test + Human Gate** | done | `app/models/policy_outcome.py`, `app/policy/{human_gate,knowledge_guard,safe_test}.py`, `app/storage/integrity.py`, `safe_tests/**` (4 templates), `scripts/validate_safe_tests.py` |
146
+ | **M5 — Safe Test + Human Gate** | done | `app/models/policy_outcome.py`, `app/policy/{human_gate,knowledge_guard,safe_test}.py`, `app/storage/integrity.py`, `safe_tests/**` (5 templates), `scripts/validate_safe_tests.py` |
147
147
  | **M6 — Orchestrator + CLI + API** | done | `app/models/report.py`, `app/reviewer/report.py`, `app/cli.py` (`skos`), `app/main.py` (FastAPI), `app/retrieval/index.py::reindex_atomic` |
148
- | **M7 — Fixtures + Evaluation + starter KUs** | done | `knowledge/public/**` (14 KUs), `tests/fixtures/assessments/{safe,vulnerable,unknown}/`, `app/eval/metrics.py`, `scripts/evaluate.py`, JA retrieval (trigram) |
148
+ | **M7 — Fixtures + Evaluation + starter KUs** | done | `knowledge/public/**` (15 KUs), `tests/fixtures/assessments/{safe,vulnerable,unknown}/`, `app/eval/metrics.py`, `scripts/evaluate.py`, JA retrieval (trigram) |
149
149
  | **M8 — Docs + hardening** | done | Security disclaimer (AC-15–18), KU `provenance` schema, `docs/{attribution,threat-model,architecture}.md`, `sbom.json`, `scripts/{secret_scan,generate_sbom,preflight}.py` |
150
150
 
151
151
  ## Install
@@ -193,16 +193,17 @@ python3.12 -m venv .venv
193
193
  .venv/bin/pytest -q
194
194
  ```
195
195
 
196
- The MVP ships **14 public Knowledge Units** (`knowledge/public/`; 13 from
196
+ The MVP ships **15 public Knowledge Units** (`knowledge/public/`; 13 from
197
197
  published standards - OWASP LLM Top 10 2025, MITRE ATLAS, NIST AI RMF / AI 600-1;
198
- one in Japanese - plus one, KU-0014, an original write-up of a vulnerability
199
- class this project's own pre-publication review process actually found). Each
198
+ one in Japanese - plus KU-0014, an original write-up of a vulnerability
199
+ class this project's own pre-publication review process actually found, and
200
+ KU-0015, a summary of published research on prompt injection as role confusion). Each
200
201
  unit carries `provenance` (source title, URL, version, licence, derivation
201
202
  status, last-verified date). See `docs/knowledge-corpus.md` and
202
203
  `docs/attribution.md`.
203
204
 
204
205
  **Internal fixture evaluation** (`scripts/evaluate.py`, 14 artificial fixtures ×
205
- 14 indexed KUs):
206
+ 15 indexed KUs):
206
207
 
207
208
  ```
208
209
  Classification Leakage = 0
@@ -354,7 +355,7 @@ See `.env.example`.
354
355
  - `docs/knowledge-schema.md` — Knowledge Unit schema (front matter + `provenance` + body)
355
356
  - `docs/rule-schema.md` — data-only rule schema and operators
356
357
  - `docs/safe-test-schema.md` — safe-test schema, validator rules, Human Gate, read-only guard
357
- - `docs/knowledge-corpus.md` — the 14 shipped Knowledge Units and their sources
358
+ - `docs/knowledge-corpus.md` — the 15 shipped Knowledge Units and their sources
358
359
  - `docs/attribution.md` — third-party sources, licences, derivation status
359
360
  - `docs/acceptance-criteria.md` — AC-01..20 status and §24 evaluation metrics
360
361
 
@@ -1,16 +1,16 @@
1
- # Publication Manifest — Security Knowledge OS v0.2.0
1
+ # Publication Manifest — Security Knowledge OS v0.4.0
2
2
 
3
3
  Snapshot of what the first public push contains. Generated for the pre-publication
4
4
  review; regenerate with `git ls-files` after any change.
5
5
 
6
- - Commit: `316e8c6` (v0.2.0: Update Packs + `skos scan mcp`; PyPI 0.1.0 was `a7f463d`)
6
+ - Commit: `4ac7982` (v0.4.0: role-confusion KU-0015 / rule PI-004 / safe test ST-RC-001 + urllib3 2.8.0; v0.3.0 `2627b44` added whole-agent capability labels for the `capgraph` pack; 0.2.0 was `316e8c6`, 0.1.0 `a7f463d`)
7
7
  - History: rewritten once on 2026-09-20, before first publication, to normalise author/committer identity to the
8
8
  GitHub no-reply address (one commit message also scrubbed of a local account name). Every commit's tree is
9
9
  identical before and after, so the reviewed content is the published content; commit hashes cited in the
10
10
  review records below are the **pre-rewrite** ones - see `docs/history-rewrite-map.md` for old -> new.
11
11
  - Licence: **Apache-2.0** (`LICENSE`, `NOTICE`, `pyproject.toml`)
12
- - Tracked files: **242**
13
- - Tests: **943** items (942 pass, 1 skip = JA-R02)
12
+ - Tracked files: **248**
13
+ - Tests: **992** items (991 pass, 1 skip = JA-R02)
14
14
  - `scripts/preflight.py`: **PASS** (now also verifies the documented
15
15
  quickstart `pip install -e` command installs every declared extra,
16
16
  pins `constraints.txt`, AND passes `--build-constraint constraints.txt`
@@ -798,6 +798,32 @@ review; regenerate with `git ls-files` after any change.
798
798
  empty value in a URL, or a flag with no value, is reported as a literal
799
799
  secret - a false positive).
800
800
 
801
+ **v0.3.0 capability-label review** (2026-09-29, scoped to `master..2627b44`:
802
+ `app/adapters/capgraph.py`, the `skos scan mcp --client` / `_agent.yaml` /
803
+ `capability-labels.json` changes in `app/adapters/mcp_config.py` and
804
+ `app/cli.py`, plus the separate data-only `capgraph` pack source). Codex,
805
+ three rounds, each CHANGES-REQUIRED and fixed: round 1 (`5b6a115`) 10
806
+ findings (4 HIGH: package impersonation via launcher path or environment,
807
+ server identity bound to file names, browser and memory profiles claiming
808
+ absent capabilities); round 2 (`75f146f`) residuals plus 2 new (HIGH: a pack
809
+ failing verification treated as not installed); round 3 (`2cc494b`) one
810
+ residual HIGH (`pnpm exec`/`yarn exec` run a local command) and one LOW
811
+ (npx server arguments). Fixed in `970a1b1`, reviewed by Antigravity:
812
+ **PASS-with-nits**; the test nit was added in `2627b44`. The medium-rule
813
+ suppression Codex flagged as G10 is documented in the pack README (engine
814
+ policy), not changed.
815
+
816
+ **v0.4.0 role-confusion review** (2026-10-07 -- 2026-10-08, scoped to
817
+ `3fdd7f8..62fea58`: KU-0015, rule PI-004 and fact `has_unapproved_shell_tool`,
818
+ safe test ST-RC-001, urllib3 2.7.0 -> 2.8.0 for PYSEC-2026-4175/4176/4177).
819
+ Codex round 1 (`43ca56f`) **CHANGES-REQUIRED**: 1 must-fix (KU-0015 claimed a
820
+ model-side fix was required although the source paper evaluates no defence)
821
+ and 5 nice-to-have (PI-004 checked all high-impact tools, RAG-only scope not
822
+ stated, loose test assertions, ST-RC-001 conflated a gate block with no role
823
+ confusion, stale counts); all fixed in `416229d`. Codex round 2 (`bd61f34`)
824
+ **PASS-with-nits**, all round-1 findings confirmed resolved; the one test nit
825
+ (unknown check with manual_review stays UNKNOWN) added in `62fea58`.
826
+
801
827
  ## Tracked files by area
802
828
 
803
829
  Per-area counts below were last verified at round 2 (commit `bcd9519`) and
@@ -807,11 +833,11 @@ check enforces (total tracked files, total tests) are kept current above.
807
833
 
808
834
  | area | files | notes |
809
835
  | --- | --- | --- |
810
- | `app/` | 76 (.py) | engine, models, policy, retrieval, reviewer, llm, storage, eval, cli, main, packs (Update Pack manager), adapters (MCP config scan) |
811
- | `tests/` | 86 | 55 test modules + fixtures |
812
- | `knowledge/` | 24 | 14 public KUs + `private/` skeleton (README + 2 `.gitkeep`) + category `.gitkeep`s |
813
- | `rules/` | 13 | 7 rule YAMLs + category `.gitkeep`s |
814
- | `safe_tests/` | 4 | 4 vetted templates |
836
+ | `app/` | 77 (.py) | engine, models, policy, retrieval, reviewer, llm, storage, eval, cli, main, packs (Update Pack manager), adapters (MCP config scan, capability labels) |
837
+ | `tests/` | 88 | 57 test modules + fixtures |
838
+ | `knowledge/` | 25 | 15 public KUs + `private/` skeleton (README + 2 `.gitkeep`) + category `.gitkeep`s |
839
+ | `rules/` | 14 | 8 rule YAMLs + category `.gitkeep`s |
840
+ | `safe_tests/` | 5 | 5 vetted templates |
815
841
  | `scripts/` | 13 | validators, ingest/build_index, assess, evaluate, sbom, secret_scan, preflight, cross-review runner, issue_license |
816
842
  | `docs/` | 11 | see below |
817
843
  | `reviews/` | 2 | `.gitignore` + `README.md` only — cross-review output `.md`/`.log` files are gitignored, never tracked |
@@ -833,13 +859,15 @@ Docs: `architecture`, `threat-model`, `safety-boundaries`, `knowledge-schema`,
833
859
  Verified: no `secret/`, no `.env`, no private-KU content tracked
834
860
  (`scripts/preflight.py`).
835
861
 
836
- ## Public Knowledge Units (14)
862
+ ## Public Knowledge Units (15)
837
863
 
838
864
  All `classification: public`, `status: reviewed`. 13 are `derivation: summary`
839
865
  (original prose, no verbatim third-party text, citing a published standard).
840
866
  KU-0014 is `derivation: original` / `source_type: incident` — a first-party
841
867
  write-up of a vulnerability class found by this project's own pre-publication
842
- cross-AI review (rounds 9-14), not sourced from any external standard. Full
868
+ cross-AI review (rounds 9-14), not sourced from any external standard.
869
+ KU-0015 is `derivation: summary` / `source_type: manual` — own-prose summary of a
870
+ published research paper (arXiv:2603.12277), no verbatim text. Full
843
871
  list + per-unit sources in `docs/knowledge-corpus.md`; licence analysis in
844
872
  `docs/attribution.md`.
845
873
 
@@ -859,12 +887,14 @@ list + per-unit sources in `docs/knowledge-corpus.md`; licence analysis in
859
887
  | KU-0012 | governance | NIST AI RMF 1.0 |
860
888
  | KU-0013 | methodology (Japanese) | MITRE ATLAS + OWASP LLM06 |
861
889
  | KU-0014 | incident | original (this project's own cross-AI review, rounds 9-14) |
890
+ | KU-0015 | prompt-security | arXiv:2603.12277 (ICML 2026), summarised not reproduced |
862
891
 
863
892
  Source licences: OWASP CC-BY-SA-4.0 · MITRE ATLAS Terms of Use · NIST public
864
- domain · KU-0014 original (no external licence applies). No content in the
893
+ domain · KU-0014 original (no external licence applies) · KU-0015 summarises an
894
+ arXiv paper (arXiv non-exclusive licence; no text reproduced). No content in the
865
895
  repo is under terms incompatible with Apache-2.0 redistribution.
866
896
 
867
- ## Rules (7)
897
+ ## Rules (8)
868
898
 
869
899
  `rules/` — data-only YAML, operators `{eq, ne, in, contains, is_unknown}`, facts
870
900
  from `app/reviewer/facts.py::FACT_SPEC` (whitelist).
@@ -872,6 +902,7 @@ from `app/reviewer/facts.py::FACT_SPEC` (whitelist).
872
902
  | id | severity | category | detects |
873
903
  | --- | --- | --- | --- |
874
904
  | PI-003 | high | rag-security | indirect injection + outbound path |
905
+ | PI-004 | high | prompt-security | declared untrusted retrieval + shell tool (role confusion) |
875
906
  | MEM-001 | high | memory-security | persistent memory + untrusted ingestion |
876
907
  | TOOL-001 | high | agent-security | high-impact tool without approval |
877
908
  | TOOL-000 | medium | agent-security | tool permissions unspecified -> UNKNOWN |
@@ -879,7 +910,7 @@ from `app/reviewer/facts.py::FACT_SPEC` (whitelist).
879
910
  | GOV-001 | medium | governance | high-impact actions without approval coverage |
880
911
  | OUT-001 | medium | agent-security | outbound enabled without a destination allow-list |
881
912
 
882
- Safe-test templates (4): `ST-IPI-001`, `ST-MEM-001`, `ST-TOOL-001`, `ST-CRED-001`.
913
+ Safe-test templates (5): `ST-IPI-001`, `ST-MEM-001`, `ST-TOOL-001`, `ST-CRED-001`, `ST-RC-001`.
883
914
 
884
915
  ## Fixtures
885
916
 
@@ -115,11 +115,11 @@ treated as data, never as a control instruction. Details in
115
115
  | --- | --- | --- |
116
116
  | **M1 — Skeleton + Schema + Validator** | done | directory tree, `app/models/*`, `app/ingestion/validator.py`, `scripts/validate_knowledge.py`, `docs/knowledge-schema.md` |
117
117
  | **M2 — Knowledge Loader + FTS5 Retrieval** | done | `app/ingestion/{loader,chunker}.py`, `app/storage/{db,repository}.py`, `app/retrieval/{base,bm25,hybrid,index}.py`, `scripts/{ingest,build_index}.py` |
118
- | **M3 — Deterministic Rule Engine** | done | `app/models/rule_clause.py`, `app/reviewer/{facts,clause_eval,rule_loader,rule_engine,normalize,attack_surface,evidence,rollup,assess}.py`, `rules/**` (7 rules), `scripts/validate_rules.py`, `docs/rule-schema.md` |
118
+ | **M3 — Deterministic Rule Engine** | done | `app/models/rule_clause.py`, `app/reviewer/{facts,clause_eval,rule_loader,rule_engine,normalize,attack_surface,evidence,rollup,assess}.py`, `rules/**` (8 rules), `scripts/validate_rules.py`, `docs/rule-schema.md` |
119
119
  | **M4 — LLM Adapter + Reviewer** | done | `app/llm/{base,mock,anthropic_client,factory}.py`, `app/models/{reviewer_output,llm_io}.py`, `app/reviewer/{llm_review,questions}.py`, `assess()` full, `scripts/assess.py` |
120
- | **M5 — Safe Test + Human Gate** | done | `app/models/policy_outcome.py`, `app/policy/{human_gate,knowledge_guard,safe_test}.py`, `app/storage/integrity.py`, `safe_tests/**` (4 templates), `scripts/validate_safe_tests.py` |
120
+ | **M5 — Safe Test + Human Gate** | done | `app/models/policy_outcome.py`, `app/policy/{human_gate,knowledge_guard,safe_test}.py`, `app/storage/integrity.py`, `safe_tests/**` (5 templates), `scripts/validate_safe_tests.py` |
121
121
  | **M6 — Orchestrator + CLI + API** | done | `app/models/report.py`, `app/reviewer/report.py`, `app/cli.py` (`skos`), `app/main.py` (FastAPI), `app/retrieval/index.py::reindex_atomic` |
122
- | **M7 — Fixtures + Evaluation + starter KUs** | done | `knowledge/public/**` (14 KUs), `tests/fixtures/assessments/{safe,vulnerable,unknown}/`, `app/eval/metrics.py`, `scripts/evaluate.py`, JA retrieval (trigram) |
122
+ | **M7 — Fixtures + Evaluation + starter KUs** | done | `knowledge/public/**` (15 KUs), `tests/fixtures/assessments/{safe,vulnerable,unknown}/`, `app/eval/metrics.py`, `scripts/evaluate.py`, JA retrieval (trigram) |
123
123
  | **M8 — Docs + hardening** | done | Security disclaimer (AC-15–18), KU `provenance` schema, `docs/{attribution,threat-model,architecture}.md`, `sbom.json`, `scripts/{secret_scan,generate_sbom,preflight}.py` |
124
124
 
125
125
  ## Install
@@ -167,16 +167,17 @@ python3.12 -m venv .venv
167
167
  .venv/bin/pytest -q
168
168
  ```
169
169
 
170
- The MVP ships **14 public Knowledge Units** (`knowledge/public/`; 13 from
170
+ The MVP ships **15 public Knowledge Units** (`knowledge/public/`; 13 from
171
171
  published standards - OWASP LLM Top 10 2025, MITRE ATLAS, NIST AI RMF / AI 600-1;
172
- one in Japanese - plus one, KU-0014, an original write-up of a vulnerability
173
- class this project's own pre-publication review process actually found). Each
172
+ one in Japanese - plus KU-0014, an original write-up of a vulnerability
173
+ class this project's own pre-publication review process actually found, and
174
+ KU-0015, a summary of published research on prompt injection as role confusion). Each
174
175
  unit carries `provenance` (source title, URL, version, licence, derivation
175
176
  status, last-verified date). See `docs/knowledge-corpus.md` and
176
177
  `docs/attribution.md`.
177
178
 
178
179
  **Internal fixture evaluation** (`scripts/evaluate.py`, 14 artificial fixtures ×
179
- 14 indexed KUs):
180
+ 15 indexed KUs):
180
181
 
181
182
  ```
182
183
  Classification Leakage = 0
@@ -328,7 +329,7 @@ See `.env.example`.
328
329
  - `docs/knowledge-schema.md` — Knowledge Unit schema (front matter + `provenance` + body)
329
330
  - `docs/rule-schema.md` — data-only rule schema and operators
330
331
  - `docs/safe-test-schema.md` — safe-test schema, validator rules, Human Gate, read-only guard
331
- - `docs/knowledge-corpus.md` — the 14 shipped Knowledge Units and their sources
332
+ - `docs/knowledge-corpus.md` — the 15 shipped Knowledge Units and their sources
332
333
  - `docs/attribution.md` — third-party sources, licences, derivation status
333
334
  - `docs/acceptance-criteria.md` — AC-01..20 status and §24 evaluation metrics
334
335
 
@@ -133,7 +133,7 @@ secret/ OUTSIDE the repository. Never indexed, never sent to an LLM.
133
133
  - Retrieval filters by `is_retrievable(classification, mode, allow_confidential)`
134
134
  in SQL **and** re-checks each returned chunk (defence in depth); a leak raises
135
135
  `PolicyBlocked`.
136
- - All 14 shipped Knowledge Units are `classification: public`.
136
+ - All 15 shipped Knowledge Units are `classification: public`.
137
137
  - `PolicyOutcome` enum: `ALLOWED` / `HUMAN_APPROVAL_REQUIRED` / `POLICY_BLOCKED` /
138
138
  `READ_ONLY_VIOLATION` (typed, never a bare string).
139
139
 
@@ -149,7 +149,7 @@ secret/ OUTSIDE the repository. Never indexed, never sent to an LLM.
149
149
  | JA-R02 (cross-language retrieval) | out of MVP scope; test skipped |
150
150
  | Update Pack / Pack Manager | not in this codebase (M9-M10, separate track); the ZIP-import attack surface it introduces is future work |
151
151
  | snapshot consistency vs. a COORDINATING writer | whole-tree re-verification catches ordinary concurrent updates; a writer that swaps directory generations during the verification pass itself is accepted (trust boundary: the tree root must not be writable by untrusted parties; a writer with write access can replace a rule directly). See `docs/threat-model.md` (Codex#1, round 23) |
152
- | trigram tokenizer | substring matching; can over-retrieve on a large corpus (fine for 14 KUs) |
152
+ | trigram tokenizer | substring matching; can over-retrieve on a large corpus (fine for 15 KUs) |
153
153
 
154
154
  ## 10. How to run
155
155
 
@@ -1,3 +1,3 @@
1
1
  """Security Knowledge OS - deterministic security assessment engine."""
2
2
 
3
- __version__ = "0.2.0"
3
+ __version__ = "0.4.0"
@@ -0,0 +1,219 @@
1
+ """Capability labels for one agent, derived from its MCP client config.
2
+
3
+ Each server gets six labels - ``untrusted_input``, ``sensitive_read``,
4
+ ``egress``, ``exec``, ``write``, ``persistence`` - valued ``True`` /
5
+ ``False`` / ``None`` (unknown). A label is ``True`` only when the config
6
+ (package identity, launch options) establishes it, and ``False`` only when
7
+ the absence is established the same way; a server the scanner does not know
8
+ keeps every label ``None``. Tool descriptions are never used: they can lie.
9
+
10
+ The agent is the union of its servers and the client's own built-in tools,
11
+ combined with a three-valued OR (any ``True`` -> ``True``; all ``False`` ->
12
+ ``False``; otherwise ``None``). When the client is not declared, nothing is
13
+ known about its built-in tools, so an aggregate ``False`` becomes ``None``.
14
+
15
+ The result feeds the ``capgraph`` Update Pack (``extensions.capgraph``) and
16
+ is also written as ``capability-labels.json`` for runtime enforcement (for
17
+ example a gateway that decides trust from the same labels). Only server
18
+ names from the config appear in either output (project paths are replaced by
19
+ opaque ids), never configuration values.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import json
25
+ from dataclasses import dataclass, field
26
+
27
+ from app.adapters.mcp_config import ServerScan
28
+
29
+ LABELS = ("untrusted_input", "sensitive_read", "egress", "exec", "write", "persistence")
30
+ # Written to extensions.capgraph; every key becomes a ``capgraph_<key>`` fact.
31
+ FACT_KEYS = (*LABELS, "flow_gated", "egress_gated")
32
+ SCHEMA = "capgraph/v0"
33
+ AGENT_FILE_STEM = "_agent" # reserved: no server may be written under this name
34
+ CLIENTS = ("claude-code", "none")
35
+
36
+ Label = bool | None
37
+
38
+
39
+ @dataclass(frozen=True)
40
+ class _Caps:
41
+ """What a known package can do beyond the MCP scanner's own facts."""
42
+
43
+ sensitive_read: Label = None
44
+ persistence: Label = None
45
+ write_scope: str | None = None # "fs" = the scanner's fs_scope
46
+ # the scanner's "no shell" does not mean "no code": a browser runs
47
+ # JavaScript on the pages it drives
48
+ exec_unknown: bool = False
49
+
50
+
51
+ # By exact (ecosystem, package) identity, like the scanner's own profiles.
52
+ _KNOWN: dict[tuple[str, str], _Caps] = {
53
+ # scope-dependent: see _labels()
54
+ ("npm", "@modelcontextprotocol/server-filesystem"): _Caps(None, True, "fs"),
55
+ # private repositories/messages behind the token; pushes can change CI
56
+ ("npm", "@modelcontextprotocol/server-github"): _Caps(True, True, "remote"),
57
+ ("npm", "@modelcontextprotocol/server-gitlab"): _Caps(True, True, "remote"),
58
+ ("npm", "@modelcontextprotocol/server-slack"): _Caps(True, None, "remote"),
59
+ # a browser reaches intranet pages and the host's services, submits forms
60
+ # and can store content that is loaded again later (Codex capgraph G03)
61
+ ("npm", "@modelcontextprotocol/server-puppeteer"): _Caps(None, None, "remote", True),
62
+ ("npm", "@playwright/mcp"): _Caps(None, None, "remote", True),
63
+ ("npm", "@modelcontextprotocol/server-brave-search"): _Caps(False, False, "none"),
64
+ # what it stores is loaded into later sessions; what it holds - and where
65
+ # the file is - is not visible (Codex capgraph G04)
66
+ ("npm", "@modelcontextprotocol/server-memory"): _Caps(None, True, None),
67
+ ("npm", "@wonderwhy-er/desktop-commander"): _Caps(True, True, "root"),
68
+ # fetch can reach localhost and intranet URLs
69
+ ("pypi", "mcp-server-fetch"): _Caps(None, False, "none"),
70
+ ("pypi", "mcp-server-git"): _Caps(None, None, "project"),
71
+ ("pypi", "mcp-server-time"): _Caps(False, False, "none"),
72
+ }
73
+
74
+ # Built-in tools of an agent client, by --client name. "none" declares a
75
+ # client without built-in tools (e.g. a custom agent that only uses MCP).
76
+ _CLIENTS: dict[str, dict[str, Label]] = {
77
+ # Bash, Read/Edit/Write, WebFetch/WebSearch
78
+ "claude-code": {
79
+ "untrusted_input": True, "sensitive_read": True, "egress": True,
80
+ "exec": True, "write": True, "persistence": True,
81
+ },
82
+ }
83
+
84
+
85
+ @dataclass
86
+ class ServerLabels:
87
+ name: str # the scan's file stem (used in notes and file names)
88
+ server: str # exact server name in the config
89
+ scope: str # "global" or "project-<id>"
90
+ labels: dict[str, Label]
91
+ write_scope: str | None
92
+
93
+
94
+ @dataclass
95
+ class AgentLabels:
96
+ client: str | None
97
+ servers: list[ServerLabels]
98
+ labels: dict[str, Label] = field(default_factory=dict)
99
+ contributors: dict[str, list[str]] = field(default_factory=dict)
100
+
101
+ def facts(self) -> dict[str, Label]:
102
+ """The ``extensions.capgraph`` block. The gates (``flow_gated``,
103
+ ``egress_gated``) are not visible in a config: they are left for the
104
+ operator and asked when a combination they guard applies."""
105
+ return {**self.labels, "flow_gated": None, "egress_gated": None}
106
+
107
+ def notes(self) -> list[str]:
108
+ out = []
109
+ if self.client is None:
110
+ out.append(
111
+ "client not declared (--client): its built-in tools are unknown, so no label "
112
+ "is claimed absent"
113
+ )
114
+ for label in LABELS:
115
+ if self.contributors.get(label):
116
+ out.append(f"{label} <- {', '.join(self.contributors[label])}")
117
+ return out
118
+
119
+
120
+ def _labels(scan: ServerScan) -> ServerLabels:
121
+ f = scan.facts
122
+ labels: dict[str, Label] = {
123
+ "untrusted_input": f.get("ingests_untrusted_content"),
124
+ "sensitive_read": None,
125
+ "egress": f.get("can_send"),
126
+ "exec": f.get("shell_exec"),
127
+ "write": f.get("can_write"),
128
+ "persistence": None,
129
+ }
130
+ write_scope: str | None = None
131
+ caps = None
132
+ if scan.package is not None and scan.ecosystem is not None:
133
+ caps = _KNOWN.get((scan.ecosystem, scan.package))
134
+ if caps is not None:
135
+ labels["sensitive_read"] = caps.sensitive_read
136
+ labels["persistence"] = caps.persistence
137
+ write_scope = caps.write_scope
138
+ if caps.exec_unknown:
139
+ labels["exec"] = None
140
+ if caps.write_scope == "fs":
141
+ scope = f.get("fs_scope")
142
+ write_scope = scope if scope in ("project", "home", "root") else None
143
+ # the whole home directory or / holds credentials and private files;
144
+ # a project directory may (.env) but that is not visible here
145
+ labels["sensitive_read"] = True if scope in ("home", "root") else None
146
+ if labels["exec"] is True and f.get("sandboxed") is not True:
147
+ # an unsandboxed shell reads and writes anything the user can
148
+ labels["sensitive_read"] = labels["write"] = labels["persistence"] = True
149
+ write_scope = write_scope or "root"
150
+ if labels["write"] is False and labels["exec"] is False and labels["persistence"] is None:
151
+ labels["persistence"] = False
152
+ return ServerLabels(scan.name, scan.server, scan.scope, labels, write_scope)
153
+
154
+
155
+ def _or(values: list[Label]) -> Label:
156
+ if any(v is True for v in values):
157
+ return True
158
+ if values and all(v is False for v in values):
159
+ return False
160
+ return None
161
+
162
+
163
+ def agent_labels(scans: list[ServerScan], client: str | None) -> AgentLabels:
164
+ if client is not None and client not in CLIENTS:
165
+ raise ValueError(f"unknown client {client!r} (known: {', '.join(CLIENTS)})")
166
+ servers = [_labels(s) for s in scans]
167
+ builtin = _CLIENTS.get(client) if client else None
168
+ agent = AgentLabels(client, servers)
169
+ for label in LABELS:
170
+ values = [s.labels[label] for s in servers]
171
+ sources = [s.name for s in servers if s.labels[label] is True]
172
+ if builtin is not None:
173
+ values.append(builtin[label])
174
+ if builtin[label] is True:
175
+ sources.insert(0, f"client:{client}")
176
+ value = _or(values)
177
+ if value is False and client is None:
178
+ value = None
179
+ agent.labels[label] = value
180
+ agent.contributors[label] = sources
181
+ return agent
182
+
183
+
184
+ def labels_json(agent: AgentLabels, source_name: str) -> str:
185
+ doc = {
186
+ "schema": SCHEMA,
187
+ "source": source_name,
188
+ "client": agent.client,
189
+ "agent": agent.labels,
190
+ "contributors": agent.contributors,
191
+ # identify a server by (scope, exact name) - the file stem is only a
192
+ # file name and may be renamed to stay unique (Codex capgraph G02)
193
+ "servers": [
194
+ {
195
+ "server": s.server, "scope": s.scope, "file": s.name,
196
+ "labels": s.labels, "write_scope": s.write_scope,
197
+ }
198
+ for s in agent.servers
199
+ ],
200
+ }
201
+ return json.dumps(doc, indent=2, ensure_ascii=False) + "\n"
202
+
203
+
204
+ def agent_yaml(agent: AgentLabels, source_name: str) -> str:
205
+ import yaml
206
+
207
+ body = yaml.safe_dump(
208
+ {"name": f"agent:{source_name}", "extensions": {"capgraph": agent.facts()}},
209
+ sort_keys=False,
210
+ allow_unicode=True,
211
+ )
212
+ header = [
213
+ f"# generated by `skos scan mcp` from {source_name} (the whole agent: every server"
214
+ + (f" + client {agent.client}" if agent.client else "") + ")",
215
+ "# null = not established by the config. Answer what you can, then `skos assess` this"
216
+ " file.",
217
+ ]
218
+ header += [f"# note: {n}" for n in agent.notes()]
219
+ return "\n".join(header) + "\n" + body