security-knowledge-os 0.1.0__tar.gz → 0.3.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 (243) hide show
  1. security_knowledge_os-0.3.0/.github/workflows/ci.yml +41 -0
  2. security_knowledge_os-0.3.0/.github/workflows/publish.yml +52 -0
  3. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/PKG-INFO +23 -2
  4. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/PUBLICATION_MANIFEST.md +54 -13
  5. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/README.md +21 -1
  6. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/__init__.py +1 -1
  7. security_knowledge_os-0.3.0/app/adapters/__init__.py +1 -0
  8. security_knowledge_os-0.3.0/app/adapters/capgraph.py +219 -0
  9. security_knowledge_os-0.3.0/app/adapters/mcp_config.py +605 -0
  10. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/cli.py +189 -2
  11. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/assessment.py +38 -1
  12. security_knowledge_os-0.3.0/app/models/pack.py +69 -0
  13. security_knowledge_os-0.3.0/app/pack_cli.py +215 -0
  14. security_knowledge_os-0.3.0/app/packs/__init__.py +1 -0
  15. security_knowledge_os-0.3.0/app/packs/archive.py +104 -0
  16. security_knowledge_os-0.3.0/app/packs/build.py +163 -0
  17. security_knowledge_os-0.3.0/app/packs/diff.py +85 -0
  18. security_knowledge_os-0.3.0/app/packs/license.py +96 -0
  19. security_knowledge_os-0.3.0/app/packs/loader.py +99 -0
  20. security_knowledge_os-0.3.0/app/packs/manifest.py +129 -0
  21. security_knowledge_os-0.3.0/app/packs/signing.py +98 -0
  22. security_knowledge_os-0.3.0/app/packs/store.py +650 -0
  23. security_knowledge_os-0.3.0/app/packs/trusted_keys.py +25 -0
  24. security_knowledge_os-0.3.0/app/packs/verify.py +242 -0
  25. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/assess.py +50 -1
  26. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/clause_eval.py +17 -5
  27. security_knowledge_os-0.3.0/app/reviewer/extensions.py +100 -0
  28. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/questions.py +8 -25
  29. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/report.py +27 -2
  30. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/rule_engine.py +8 -0
  31. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/rule_loader.py +40 -11
  32. security_knowledge_os-0.3.0/app/reviewer/vocabulary.py +192 -0
  33. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/constraints.txt +3 -0
  34. security_knowledge_os-0.3.0/docs/pack-schema.md +295 -0
  35. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/pyproject.toml +4 -1
  36. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/sbom.json +61 -1
  37. security_knowledge_os-0.3.0/scripts/issue_license.py +60 -0
  38. security_knowledge_os-0.3.0/tests/unit/test_capgraph.py +380 -0
  39. security_knowledge_os-0.3.0/tests/unit/test_mcp_scan.py +328 -0
  40. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_no_code_execution.py +26 -0
  41. security_knowledge_os-0.3.0/tests/unit/test_packs.py +1075 -0
  42. security_knowledge_os-0.3.0/tests/unit/test_packs_cli.py +137 -0
  43. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/.env.example +0 -0
  44. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/.gitignore +0 -0
  45. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/LICENSE +0 -0
  46. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/NOTICE +0 -0
  47. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/REVIEW_CHECKLIST.md +0 -0
  48. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/REVIEW_PACKAGE.md +0 -0
  49. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/config.py +0 -0
  50. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/eval/__init__.py +0 -0
  51. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/eval/metrics.py +0 -0
  52. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/ingestion/__init__.py +0 -0
  53. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/ingestion/chunker.py +0 -0
  54. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/ingestion/loader.py +0 -0
  55. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/ingestion/parser.py +0 -0
  56. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/ingestion/snapshot.py +0 -0
  57. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/ingestion/validator.py +0 -0
  58. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/llm/__init__.py +0 -0
  59. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/llm/anthropic_client.py +0 -0
  60. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/llm/base.py +0 -0
  61. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/llm/factory.py +0 -0
  62. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/llm/mock.py +0 -0
  63. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/main.py +0 -0
  64. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/__init__.py +0 -0
  65. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/_credential_shapes.py +0 -0
  66. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/answer.py +0 -0
  67. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/context.py +0 -0
  68. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/knowledge.py +0 -0
  69. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/llm_io.py +0 -0
  70. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/policy_outcome.py +0 -0
  71. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/report.py +0 -0
  72. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/retrieval.py +0 -0
  73. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/reviewer_output.py +0 -0
  74. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/risk.py +0 -0
  75. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/rule_clause.py +0 -0
  76. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/policy/__init__.py +0 -0
  77. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/policy/classification.py +0 -0
  78. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/policy/human_gate.py +0 -0
  79. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/policy/knowledge_guard.py +0 -0
  80. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/policy/safe_test.py +0 -0
  81. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/retrieval/__init__.py +0 -0
  82. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/retrieval/base.py +0 -0
  83. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/retrieval/bm25.py +0 -0
  84. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/retrieval/hybrid.py +0 -0
  85. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/retrieval/index.py +0 -0
  86. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/__init__.py +0 -0
  87. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/answers.py +0 -0
  88. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/attack_surface.py +0 -0
  89. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/evidence.py +0 -0
  90. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/facts.py +0 -0
  91. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/llm_review.py +0 -0
  92. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/normalize.py +0 -0
  93. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/rollup.py +0 -0
  94. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/safe_errors.py +0 -0
  95. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/storage/__init__.py +0 -0
  96. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/storage/db.py +0 -0
  97. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/storage/integrity.py +0 -0
  98. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/storage/repository.py +0 -0
  99. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/docs/acceptance-criteria.md +0 -0
  100. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/docs/architecture.md +0 -0
  101. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/docs/attribution.md +0 -0
  102. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/docs/history-rewrite-map.md +0 -0
  103. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/docs/knowledge-corpus.md +0 -0
  104. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/docs/knowledge-schema.md +0 -0
  105. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/docs/rule-schema.md +0 -0
  106. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/docs/safe-test-schema.md +0 -0
  107. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/docs/safety-boundaries.md +0 -0
  108. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/docs/threat-model.md +0 -0
  109. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/private/README.md +0 -0
  110. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/private/confidential/.gitkeep +0 -0
  111. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/private/internal/.gitkeep +0 -0
  112. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/agent-security/.gitkeep +0 -0
  113. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/agent-security/KU-0004-excessive-agency-least-privilege.md +0 -0
  114. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/agent-security/KU-0005-human-approval-gates.md +0 -0
  115. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/agent-security/KU-0006-outbound-channels-allowlist.md +0 -0
  116. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/credential-security/.gitkeep +0 -0
  117. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/credential-security/KU-0008-secrets-not-reachable-by-model.md +0 -0
  118. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/credential-security/KU-0009-credential-broker-pattern.md +0 -0
  119. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/governance/KU-0012-human-oversight.md +0 -0
  120. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/incidents/.gitkeep +0 -0
  121. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/incidents/KU-0014-llm-payload-identifier-leak.md +0 -0
  122. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/memory-security/.gitkeep +0 -0
  123. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/memory-security/KU-0007-persistent-memory-poisoning.md +0 -0
  124. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/methodology/.gitkeep +0 -0
  125. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/methodology/KU-0011-improper-output-handling.md +0 -0
  126. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/methodology/KU-0013-assume-compromise-ja.md +0 -0
  127. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/prompt-security/.gitkeep +0 -0
  128. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/prompt-security/KU-0001-direct-prompt-injection.md +0 -0
  129. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/prompt-security/KU-0010-system-prompt-leakage.md +0 -0
  130. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/rag-security/.gitkeep +0 -0
  131. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/rag-security/KU-0002-indirect-prompt-injection.md +0 -0
  132. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/rag-security/KU-0003-untrusted-content-isolation.md +0 -0
  133. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/agent/.gitkeep +0 -0
  134. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/agent/OUT-001.yaml +0 -0
  135. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/agent/TOOL-000.yaml +0 -0
  136. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/agent/TOOL-001.yaml +0 -0
  137. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/credential/.gitkeep +0 -0
  138. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/credential/CRED-001.yaml +0 -0
  139. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/governance/.gitkeep +0 -0
  140. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/governance/GOV-001.yaml +0 -0
  141. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/memory/.gitkeep +0 -0
  142. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/memory/MEM-001.yaml +0 -0
  143. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/prompt/.gitkeep +0 -0
  144. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/rag/.gitkeep +0 -0
  145. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/rag/PI-003.yaml +0 -0
  146. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/safe_tests/ST-CRED-001.yaml +0 -0
  147. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/safe_tests/ST-IPI-001.yaml +0 -0
  148. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/safe_tests/ST-MEM-001.yaml +0 -0
  149. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/safe_tests/ST-TOOL-001.yaml +0 -0
  150. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/_scheduled_cross_review.sh +0 -0
  151. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/assess.py +0 -0
  152. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/build_index.py +0 -0
  153. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/evaluate.py +0 -0
  154. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/generate_sbom.py +0 -0
  155. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/ingest.py +0 -0
  156. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/preflight.py +0 -0
  157. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/run_cross_review.sh +0 -0
  158. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/secret_scan.py +0 -0
  159. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/validate_knowledge.py +0 -0
  160. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/validate_rules.py +0 -0
  161. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/validate_safe_tests.py +0 -0
  162. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/conftest.py +0 -0
  163. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/safe/S-001-prompt-only.yaml +0 -0
  164. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/safe/S-002-rag-trusted-no-actions.yaml +0 -0
  165. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/safe/S-003-readonly-tool-with-approval.yaml +0 -0
  166. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/safe/S-004-credential-proxy.yaml +0 -0
  167. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/unknown/U-001-tool-permissions-missing.yaml +0 -0
  168. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/unknown/U-002-memory-persistence-unspecified.yaml +0 -0
  169. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/unknown/U-003-outbound-destination-unspecified.yaml +0 -0
  170. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/unknown/U-004-credential-handling-unspecified.yaml +0 -0
  171. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/unknown/U-005-tool-permission-unrecognized.yaml +0 -0
  172. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/vulnerable/V-001-indirect-injection-auto-email.yaml +0 -0
  173. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/vulnerable/V-002-rag-delete-tool-no-approval.yaml +0 -0
  174. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/vulnerable/V-003-persistent-memory-untrusted.yaml +0 -0
  175. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/vulnerable/V-004-env-secret-readable.yaml +0 -0
  176. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/vulnerable/V-005-unlisted-rag-source-untrusted-memory.yaml +0 -0
  177. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/corpus/private/confidential/KU-1020-confidential-tool-matrix.md +0 -0
  178. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/corpus/private/internal/KU-1010-internal-memory-heuristic.md +0 -0
  179. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/corpus/public/credential-security/KU-1003-env-secret-exposure.md +0 -0
  180. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/corpus/public/prompt-security/KU-1001-instruction-data-boundary.md +0 -0
  181. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/corpus/public/rag-security/KU-1002-indirect-injection-external-document.md +0 -0
  182. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/corpus/secret/KU-1099-secret-must-not-index.md +0 -0
  183. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/corpus_alt/public/methodology/KU-2001-alt-corpus-marker.md +0 -0
  184. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/knowledge/invalid/KU-0010-mislabeled.md +0 -0
  185. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/knowledge/invalid/bad-classification.md +0 -0
  186. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/knowledge/invalid/bad-id.md +0 -0
  187. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/knowledge/invalid/missing-fields.md +0 -0
  188. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/knowledge/invalid/no-front-matter.md +0 -0
  189. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/knowledge/private/internal/KU-0002-internal-note.md +0 -0
  190. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/knowledge/public/methodology/KU-0003-sparse.md +0 -0
  191. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/knowledge/public/prompt-security/KU-0001-instruction-hierarchy.md +0 -0
  192. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/knowledge/secret/KU-0009-secret.md +0 -0
  193. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/integration/.gitkeep +0 -0
  194. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_answers.py +0 -0
  195. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_anthropic_client.py +0 -0
  196. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_api.py +0 -0
  197. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_assess.py +0 -0
  198. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_assess_m4.py +0 -0
  199. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_assess_m5.py +0 -0
  200. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_assess_script.py +0 -0
  201. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_assessment_input.py +0 -0
  202. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_build_index_script.py +0 -0
  203. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_classification_gate.py +0 -0
  204. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_classification_leakage.py +0 -0
  205. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_clause_eval.py +0 -0
  206. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_cli.py +0 -0
  207. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_config.py +0 -0
  208. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_cross_review_script.py +0 -0
  209. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_db.py +0 -0
  210. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_eval.py +0 -0
  211. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_evaluate_script.py +0 -0
  212. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_evidence.py +0 -0
  213. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_fail_closed.py +0 -0
  214. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_finding_boundary.py +0 -0
  215. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_generate_sbom.py +0 -0
  216. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_human_gate.py +0 -0
  217. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_ingest_script.py +0 -0
  218. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_ja_retrieval.py +0 -0
  219. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_knowledge_corpus.py +0 -0
  220. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_knowledge_guard.py +0 -0
  221. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_knowledge_root_config.py +0 -0
  222. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_knowledge_schema.py +0 -0
  223. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_llm_adapter.py +0 -0
  224. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_llm_boundary.py +0 -0
  225. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_llm_output_schema.py +0 -0
  226. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_llm_review.py +0 -0
  227. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_loader.py +0 -0
  228. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_no_secret_echo.py +0 -0
  229. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_normalize.py +0 -0
  230. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_parser.py +0 -0
  231. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_policy_outcome.py +0 -0
  232. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_preflight_manifest.py +0 -0
  233. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_questions.py +0 -0
  234. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_reindex.py +0 -0
  235. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_report.py +0 -0
  236. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_repository.py +0 -0
  237. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_retrieval.py +0 -0
  238. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_rollup.py +0 -0
  239. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_rule_engine.py +0 -0
  240. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_rule_loader.py +0 -0
  241. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_safe_test.py +0 -0
  242. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_secret_scan.py +0 -0
  243. {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_snapshot.py +0 -0
@@ -0,0 +1,41 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ # TODO: pin these actions to a full commit SHA for supply-chain safety.
16
+ - uses: actions/checkout@v4
17
+ with:
18
+ persist-credentials: false
19
+ fetch-depth: 0 # full history (the manifest-ancestor check needs it)
20
+ - uses: actions/setup-python@v5
21
+ with:
22
+ python-version: "3.12"
23
+ - name: Install (pinned)
24
+ run: pip install -e ".[api,llm,dev]" -c constraints.txt
25
+ # Mirrors the project's own gate (scripts/preflight.py runs `ruff check`,
26
+ # not `ruff format --check`), so CI enforces exactly what the repo commits
27
+ # to. (`ruff format --check` is not run repo-wide yet - a separate cleanup.)
28
+ - name: Ruff (lint)
29
+ run: ruff check .
30
+ - name: Mypy (strict)
31
+ run: mypy
32
+ # Two tests assert the tracked sbom.json matches the *exact* installed
33
+ # dependency closure of the maintainer's environment (e.g. build-time
34
+ # packages like trove-classifiers); they cannot pass in an arbitrary CI
35
+ # runner and are verified locally via scripts/preflight.py before a
36
+ # release. Everything else - including the manifest-ancestor check - runs.
37
+ - name: Tests
38
+ run: >
39
+ pytest -q
40
+ --deselect tests/unit/test_generate_sbom.py::test_no_unknown_licences_for_a_real_specific_osi_classifier
41
+ --deselect tests/unit/test_preflight_manifest.py::test_real_tracked_sbom_matches_the_current_environment
@@ -0,0 +1,52 @@
1
+ name: Publish to PyPI
2
+
3
+ # Publishes on a version tag (e.g. `v0.1.1`). Uses PyPI Trusted Publishing
4
+ # (OIDC) - NO API token / secret is stored in the repo. One-time setup on
5
+ # PyPI: project -> Publishing -> add a GitHub trusted publisher for
6
+ # repo `kagioneko/security-knowledge-os`, workflow `publish.yml`,
7
+ # environment `pypi`. Until that is configured the publish JOB simply FAILS
8
+ # (it is not skipped) and nothing is uploaded - a failed release, never a
9
+ # silent or partial one.
10
+ on:
11
+ push:
12
+ tags: ["v*"]
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ build:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ # TODO: pin these actions to a full commit SHA for supply-chain safety.
22
+ - uses: actions/checkout@v4
23
+ with:
24
+ persist-credentials: false
25
+ - uses: actions/setup-python@v5
26
+ with:
27
+ python-version: "3.12"
28
+ - name: Verify the tag matches the packaged version
29
+ run: |
30
+ pip install build
31
+ python -m build
32
+ tag="${GITHUB_REF_NAME#v}"
33
+ pkg=$(ls dist/*.whl | sed -E 's/.*-([0-9]+\.[0-9]+\.[0-9]+)-.*/\1/')
34
+ echo "tag=$tag packaged=$pkg"
35
+ test "$tag" = "$pkg"
36
+ - uses: actions/upload-artifact@v4
37
+ with:
38
+ name: dist
39
+ path: dist/
40
+
41
+ publish:
42
+ needs: build
43
+ runs-on: ubuntu-latest
44
+ environment: pypi
45
+ permissions:
46
+ id-token: write # OIDC token for PyPI Trusted Publishing
47
+ steps:
48
+ - uses: actions/download-artifact@v4
49
+ with:
50
+ name: dist
51
+ path: dist/
52
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: security-knowledge-os
3
- Version: 0.1.0
3
+ Version: 0.3.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
7
7
  License-File: NOTICE
8
8
  Requires-Python: >=3.12
9
+ Requires-Dist: cryptography>=50.0
9
10
  Requires-Dist: pydantic>=2.6
10
11
  Requires-Dist: pyyaml>=6.0
11
12
  Provides-Extra: api
@@ -25,6 +26,15 @@ Description-Content-Type: text/markdown
25
26
 
26
27
  # Security Knowledge OS
27
28
 
29
+ [![PyPI](https://img.shields.io/pypi/v/security-knowledge-os)](https://pypi.org/project/security-knowledge-os/)
30
+ [![Python](https://img.shields.io/pypi/pyversions/security-knowledge-os)](https://pypi.org/project/security-knowledge-os/)
31
+ [![License](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE)
32
+
33
+ ```bash
34
+ pip install security-knowledge-os
35
+ skos assess your-agent.yaml # deterministic, offline by default; bundled rules/knowledge
36
+ ```
37
+
28
38
  > **Language note / 言語について**: This repository (code, docs, commit history) is
29
39
  > **English-first by design** — it is written to be consumed by AI coding agents and
30
40
  > cites international standards (OWASP, MITRE ATLAS, NIST) directly. The section
@@ -138,7 +148,18 @@ treated as data, never as a control instruction. Details in
138
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) |
139
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` |
140
150
 
141
- ## Quickstart (M1)
151
+ ## Install
152
+
153
+ Requires **Python 3.12+**. From PyPI (ships the default rules / knowledge /
154
+ safe-test data, so `skos assess` works with no checkout):
155
+
156
+ ```bash
157
+ pip install security-knowledge-os # engine + CLI
158
+ pip install "security-knowledge-os[api]" # + the local FastAPI app
159
+ skos assess your-agent.yaml # your-agent.yaml is an input you write
160
+ ```
161
+
162
+ ## Quickstart (M1, from a source checkout)
142
163
 
143
164
  ```bash
144
165
  python3.12 -m venv .venv
@@ -1,16 +1,16 @@
1
- # Publication Manifest — Security Knowledge OS v0.1.0
1
+ # Publication Manifest — Security Knowledge OS v0.3.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: `4c004ab` (packaging: bundle default data into the wheel + config fallback so pip install works standalone; allowlist guard rejects private/hidden/symlink incl. ancestor symlinks; sdist ships only tracked public files; cross-review PASS round 38, not yet pushed)
6
+ - Commit: `2627b44` (v0.3.0: 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: **218**
13
- - Tests: **792** items (791 pass, 1 skip = JA-R02)
12
+ - Tracked files: **244**
13
+ - Tests: **978** items (977 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`
@@ -19,7 +19,7 @@ review; regenerate with `git ls-files` after any change.
19
19
  environment, ignoring only its timestamp, without preflight itself
20
20
  rewriting that tracked file to check it, round 11 Codex#12)
21
21
  - Cross-AI review: Codex (code audit) + Antigravity (adversarial design
22
- audit), **thirty-eight rounds** (through round-38), 2026-09-11 -- 2026-09-26. Rounds 1-2
22
+ audit), **thirty-nine rounds** (through round-39), 2026-09-11 -- 2026-09-26. Rounds 1-2
23
23
  (2026-09-11): CHANGES-REQUIRED both times, 19 then 13 findings, all
24
24
  fixed — see `REVIEW_CHECKLIST.md` sign-off tables. Rounds 3-6
25
25
  (2026-09-12): CHANGES-REQUIRED each time, independent findings each
@@ -774,6 +774,45 @@ review; regenerate with `git ls-files` after any change.
774
774
  pytest test run by the release gate) fixed in `4c004ab` (comment only). This is
775
775
  the packaging change's cross-review PASS for the PyPI release.
776
776
 
777
+ **Round 39** (2026-09-26, post-publication: reviewed the README PyPI
778
+ badges/install additions and the new test CI workflow before pushing them):
779
+ Codex **PASS-with-nits** - no secrets, no dangerous CI settings (contents:
780
+ read, no pull_request_target, no secret use on untrusted PRs). Nits applied:
781
+ `persist-credentials: false` on checkout (+ a SHA-pin TODO), and README notes
782
+ Python 3.12+ and that the assessed YAML is a user-provided input.
783
+
784
+ **v0.2.0 Update Packs review** (2026-09-28, scoped to the change set
785
+ `master..488e0f6`: `app/packs/`, `app/pack_cli.py`, `app/adapters/mcp_config.py`,
786
+ `app/reviewer/{vocabulary,extensions}.py`, `docs/pack-schema.md`, plus the
787
+ separate data-only pack source `skos-pack-mcp`). Codex, four rounds, each
788
+ CHANGES-REQUIRED and fully or partly fixed: round 1 (`c14bc7f`) 16 findings
789
+ (4 HIGH), round 2 (`30840b5`) 8 new (3 HIGH), round 3 (`6cb990e`) 4 new
790
+ (1 HIGH), round 4 (`771d870`) 2 new (MEDIUM F29, LOW F30) plus a residual HIGH
791
+ (F25: a lost `installed.json` made a weakening update look like a first
792
+ install). F25/F29/F30 fixed in `2e0e99c`; that commit reviewed by
793
+ Antigravity: **PASS-with-nits**, the one documentation nit fixed in
794
+ `488e0f6`. Accepted as known limitations (recorded, not fixed): F08
795
+ (`npm exec` trailing options / `npm_config_registry` still inherit a known
796
+ package profile in `skos scan mcp`), F26 (same-version replacement drops the
797
+ displaced copy before the registry commit), F28 (a credential reference or
798
+ empty value in a URL, or a flag with no value, is reported as a literal
799
+ secret - a false positive).
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
+
777
816
  ## Tracked files by area
778
817
 
779
818
  Per-area counts below were last verified at round 2 (commit `bcd9519`) and
@@ -783,20 +822,20 @@ check enforces (total tracked files, total tests) are kept current above.
783
822
 
784
823
  | area | files | notes |
785
824
  | --- | --- | --- |
786
- | `app/` | 59 (.py) | engine, models, policy, retrieval, reviewer, llm, storage, eval, cli, main |
787
- | `tests/` | 83 | 52 test modules + fixtures |
825
+ | `app/` | 77 (.py) | engine, models, policy, retrieval, reviewer, llm, storage, eval, cli, main, packs (Update Pack manager), adapters (MCP config scan, capability labels) |
826
+ | `tests/` | 87 | 56 test modules + fixtures |
788
827
  | `knowledge/` | 24 | 14 public KUs + `private/` skeleton (README + 2 `.gitkeep`) + category `.gitkeep`s |
789
828
  | `rules/` | 13 | 7 rule YAMLs + category `.gitkeep`s |
790
829
  | `safe_tests/` | 4 | 4 vetted templates |
791
- | `scripts/` | 12 | validators, ingest/build_index, assess, evaluate, sbom, secret_scan, preflight, cross-review runner |
792
- | `docs/` | 9 | see below |
830
+ | `scripts/` | 13 | validators, ingest/build_index, assess, evaluate, sbom, secret_scan, preflight, cross-review runner, issue_license |
831
+ | `docs/` | 11 | see below |
793
832
  | `reviews/` | 2 | `.gitignore` + `README.md` only — cross-review output `.md`/`.log` files are gitignored, never tracked |
794
833
  | root | 11 | `README.md`, `LICENSE`, `NOTICE`, `pyproject.toml`, `.gitignore`, `.env.example`, `constraints.txt`, `sbom.json` |
795
834
  | review docs | 3 | `REVIEW_PACKAGE.md`, `REVIEW_CHECKLIST.md`, `PUBLICATION_MANIFEST.md` |
796
835
 
797
836
  Docs: `architecture`, `threat-model`, `safety-boundaries`, `knowledge-schema`,
798
837
  `rule-schema`, `safe-test-schema`, `knowledge-corpus`, `attribution`,
799
- `acceptance-criteria`.
838
+ `acceptance-criteria`, `history-rewrite-map`, `pack-schema`.
800
839
 
801
840
  ## Explicitly excluded (in `.gitignore`)
802
841
 
@@ -868,7 +907,7 @@ Safe-test templates (4): `ST-IPI-001`, `ST-MEM-001`, `ST-TOOL-001`, `ST-CRED-001
868
907
 
869
908
  ## Dependencies (`sbom.json`)
870
909
 
871
- 63 components — the project's actual dependency closure (Codex#9, round 7:
910
+ 66 components — the project's actual dependency closure (Codex#9, round 7:
872
911
  walked outward from every declared root rather than listing every
873
912
  installed distribution, which also included `pip` and unrelated
874
913
  environment packages; Codex#9, round 8: a package reachable only
@@ -880,7 +919,9 @@ one that is itself a *direct* pyproject.toml entry; Codex#6, round 14:
880
919
  requirement, round 9's own fix, had discarded which extras a PARENT
881
920
  requirement actually activated on its dependency, hiding this one).
882
921
  Core install =
883
- `pydantic` + `pyyaml`; the rest are `[api]`/`[llm]` extras or dev/test-only.
922
+ `pydantic` + `pyyaml` + (v0.2.0, Update Pack signatures) `cryptography`
923
+ (Apache-2.0 OR BSD-3-Clause) with its dependencies `cffi` (MIT-0) and
924
+ `pycparser` (BSD-3-Clause); the rest are `[api]`/`[llm]` extras or dev/test-only.
884
925
  Zero `UNKNOWN` licences. Licences present: MIT, BSD (2/3-Clause), Apache-2.0,
885
926
  PSF-2.0 — permissive — plus **`pathspec` and `certifi`, both under MPL-2.0**
886
927
  (Codex finding #10, round 2, 2026-09-11, corrected round 3, 2026-09-12: an
@@ -897,7 +938,7 @@ which use Python's native `truststore` instead of `certifi`); `pathspec`
897
938
  is a transitive dependency of `mypy` (**not** `ruff`, which declares no
898
939
  dependencies of its own per installed-environment metadata) — both
899
940
  dev/test-only or extras, never imported by, bundled with, or distributed
900
- as part of `app/`'s core install. None of the 63 components conflict with
941
+ as part of `app/`'s core install. None of the 66 components conflict with
901
942
  Apache-2.0 distribution of this project. Full list in `sbom.json`.
902
943
 
903
944
  ## §24 evaluation (internal fixtures — not real-world performance)
@@ -1,5 +1,14 @@
1
1
  # Security Knowledge OS
2
2
 
3
+ [![PyPI](https://img.shields.io/pypi/v/security-knowledge-os)](https://pypi.org/project/security-knowledge-os/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/security-knowledge-os)](https://pypi.org/project/security-knowledge-os/)
5
+ [![License](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE)
6
+
7
+ ```bash
8
+ pip install security-knowledge-os
9
+ skos assess your-agent.yaml # deterministic, offline by default; bundled rules/knowledge
10
+ ```
11
+
3
12
  > **Language note / 言語について**: This repository (code, docs, commit history) is
4
13
  > **English-first by design** — it is written to be consumed by AI coding agents and
5
14
  > cites international standards (OWASP, MITRE ATLAS, NIST) directly. The section
@@ -113,7 +122,18 @@ treated as data, never as a control instruction. Details in
113
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) |
114
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` |
115
124
 
116
- ## Quickstart (M1)
125
+ ## Install
126
+
127
+ Requires **Python 3.12+**. From PyPI (ships the default rules / knowledge /
128
+ safe-test data, so `skos assess` works with no checkout):
129
+
130
+ ```bash
131
+ pip install security-knowledge-os # engine + CLI
132
+ pip install "security-knowledge-os[api]" # + the local FastAPI app
133
+ skos assess your-agent.yaml # your-agent.yaml is an input you write
134
+ ```
135
+
136
+ ## Quickstart (M1, from a source checkout)
117
137
 
118
138
  ```bash
119
139
  python3.12 -m venv .venv
@@ -1,3 +1,3 @@
1
1
  """Security Knowledge OS - deterministic security assessment engine."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.3.0"
@@ -0,0 +1 @@
1
+ """Adapters: derive assessment inputs from real-world configuration."""
@@ -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