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.
- security_knowledge_os-0.3.0/.github/workflows/ci.yml +41 -0
- security_knowledge_os-0.3.0/.github/workflows/publish.yml +52 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/PKG-INFO +23 -2
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/PUBLICATION_MANIFEST.md +54 -13
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/README.md +21 -1
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/__init__.py +1 -1
- security_knowledge_os-0.3.0/app/adapters/__init__.py +1 -0
- security_knowledge_os-0.3.0/app/adapters/capgraph.py +219 -0
- security_knowledge_os-0.3.0/app/adapters/mcp_config.py +605 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/cli.py +189 -2
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/assessment.py +38 -1
- security_knowledge_os-0.3.0/app/models/pack.py +69 -0
- security_knowledge_os-0.3.0/app/pack_cli.py +215 -0
- security_knowledge_os-0.3.0/app/packs/__init__.py +1 -0
- security_knowledge_os-0.3.0/app/packs/archive.py +104 -0
- security_knowledge_os-0.3.0/app/packs/build.py +163 -0
- security_knowledge_os-0.3.0/app/packs/diff.py +85 -0
- security_knowledge_os-0.3.0/app/packs/license.py +96 -0
- security_knowledge_os-0.3.0/app/packs/loader.py +99 -0
- security_knowledge_os-0.3.0/app/packs/manifest.py +129 -0
- security_knowledge_os-0.3.0/app/packs/signing.py +98 -0
- security_knowledge_os-0.3.0/app/packs/store.py +650 -0
- security_knowledge_os-0.3.0/app/packs/trusted_keys.py +25 -0
- security_knowledge_os-0.3.0/app/packs/verify.py +242 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/assess.py +50 -1
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/clause_eval.py +17 -5
- security_knowledge_os-0.3.0/app/reviewer/extensions.py +100 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/questions.py +8 -25
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/report.py +27 -2
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/rule_engine.py +8 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/rule_loader.py +40 -11
- security_knowledge_os-0.3.0/app/reviewer/vocabulary.py +192 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/constraints.txt +3 -0
- security_knowledge_os-0.3.0/docs/pack-schema.md +295 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/pyproject.toml +4 -1
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/sbom.json +61 -1
- security_knowledge_os-0.3.0/scripts/issue_license.py +60 -0
- security_knowledge_os-0.3.0/tests/unit/test_capgraph.py +380 -0
- security_knowledge_os-0.3.0/tests/unit/test_mcp_scan.py +328 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_no_code_execution.py +26 -0
- security_knowledge_os-0.3.0/tests/unit/test_packs.py +1075 -0
- security_knowledge_os-0.3.0/tests/unit/test_packs_cli.py +137 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/.env.example +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/.gitignore +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/LICENSE +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/NOTICE +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/REVIEW_CHECKLIST.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/REVIEW_PACKAGE.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/config.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/eval/__init__.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/eval/metrics.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/ingestion/__init__.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/ingestion/chunker.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/ingestion/loader.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/ingestion/parser.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/ingestion/snapshot.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/ingestion/validator.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/llm/__init__.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/llm/anthropic_client.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/llm/base.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/llm/factory.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/llm/mock.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/main.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/__init__.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/_credential_shapes.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/answer.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/context.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/knowledge.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/llm_io.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/policy_outcome.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/report.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/retrieval.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/reviewer_output.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/risk.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/models/rule_clause.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/policy/__init__.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/policy/classification.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/policy/human_gate.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/policy/knowledge_guard.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/policy/safe_test.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/retrieval/__init__.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/retrieval/base.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/retrieval/bm25.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/retrieval/hybrid.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/retrieval/index.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/__init__.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/answers.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/attack_surface.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/evidence.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/facts.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/llm_review.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/normalize.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/reviewer/rollup.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/safe_errors.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/storage/__init__.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/storage/db.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/storage/integrity.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/app/storage/repository.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/docs/acceptance-criteria.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/docs/architecture.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/docs/attribution.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/docs/history-rewrite-map.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/docs/knowledge-corpus.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/docs/knowledge-schema.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/docs/rule-schema.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/docs/safe-test-schema.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/docs/safety-boundaries.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/docs/threat-model.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/private/README.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/private/confidential/.gitkeep +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/private/internal/.gitkeep +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/agent-security/.gitkeep +0 -0
- {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
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/agent-security/KU-0005-human-approval-gates.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/agent-security/KU-0006-outbound-channels-allowlist.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/credential-security/.gitkeep +0 -0
- {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
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/credential-security/KU-0009-credential-broker-pattern.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/governance/KU-0012-human-oversight.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/incidents/.gitkeep +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/incidents/KU-0014-llm-payload-identifier-leak.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/memory-security/.gitkeep +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/memory-security/KU-0007-persistent-memory-poisoning.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/methodology/.gitkeep +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/methodology/KU-0011-improper-output-handling.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/methodology/KU-0013-assume-compromise-ja.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/prompt-security/.gitkeep +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/prompt-security/KU-0001-direct-prompt-injection.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/prompt-security/KU-0010-system-prompt-leakage.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/rag-security/.gitkeep +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/rag-security/KU-0002-indirect-prompt-injection.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/knowledge/public/rag-security/KU-0003-untrusted-content-isolation.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/agent/.gitkeep +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/agent/OUT-001.yaml +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/agent/TOOL-000.yaml +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/agent/TOOL-001.yaml +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/credential/.gitkeep +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/credential/CRED-001.yaml +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/governance/.gitkeep +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/governance/GOV-001.yaml +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/memory/.gitkeep +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/memory/MEM-001.yaml +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/prompt/.gitkeep +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/rag/.gitkeep +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/rules/rag/PI-003.yaml +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/safe_tests/ST-CRED-001.yaml +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/safe_tests/ST-IPI-001.yaml +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/safe_tests/ST-MEM-001.yaml +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/safe_tests/ST-TOOL-001.yaml +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/_scheduled_cross_review.sh +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/assess.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/build_index.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/evaluate.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/generate_sbom.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/ingest.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/preflight.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/run_cross_review.sh +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/secret_scan.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/validate_knowledge.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/validate_rules.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/scripts/validate_safe_tests.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/conftest.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/safe/S-001-prompt-only.yaml +0 -0
- {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
- {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
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/safe/S-004-credential-proxy.yaml +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/unknown/U-001-tool-permissions-missing.yaml +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/unknown/U-002-memory-persistence-unspecified.yaml +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/unknown/U-003-outbound-destination-unspecified.yaml +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/unknown/U-004-credential-handling-unspecified.yaml +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/unknown/U-005-tool-permission-unrecognized.yaml +0 -0
- {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
- {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
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/vulnerable/V-003-persistent-memory-untrusted.yaml +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/assessments/vulnerable/V-004-env-secret-readable.yaml +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/knowledge/invalid/KU-0010-mislabeled.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/knowledge/invalid/bad-classification.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/knowledge/invalid/bad-id.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/knowledge/invalid/missing-fields.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/knowledge/invalid/no-front-matter.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/knowledge/private/internal/KU-0002-internal-note.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/knowledge/public/methodology/KU-0003-sparse.md +0 -0
- {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
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/fixtures/knowledge/secret/KU-0009-secret.md +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/integration/.gitkeep +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_answers.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_anthropic_client.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_api.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_assess.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_assess_m4.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_assess_m5.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_assess_script.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_assessment_input.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_build_index_script.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_classification_gate.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_classification_leakage.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_clause_eval.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_cli.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_config.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_cross_review_script.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_db.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_eval.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_evaluate_script.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_evidence.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_fail_closed.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_finding_boundary.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_generate_sbom.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_human_gate.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_ingest_script.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_ja_retrieval.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_knowledge_corpus.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_knowledge_guard.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_knowledge_root_config.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_knowledge_schema.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_llm_adapter.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_llm_boundary.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_llm_output_schema.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_llm_review.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_loader.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_no_secret_echo.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_normalize.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_parser.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_policy_outcome.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_preflight_manifest.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_questions.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_reindex.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_report.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_repository.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_retrieval.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_rollup.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_rule_engine.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_rule_loader.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_safe_test.py +0 -0
- {security_knowledge_os-0.1.0 → security_knowledge_os-0.3.0}/tests/unit/test_secret_scan.py +0 -0
- {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.
|
|
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
|
+
[](https://pypi.org/project/security-knowledge-os/)
|
|
30
|
+
[](https://pypi.org/project/security-knowledge-os/)
|
|
31
|
+
[](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
|
-
##
|
|
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
|
+
# 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: `
|
|
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: **
|
|
13
|
-
- Tests: **
|
|
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-
|
|
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/` |
|
|
787
|
-
| `tests/` |
|
|
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/` |
|
|
792
|
-
| `docs/` |
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
+
[](https://pypi.org/project/security-knowledge-os/)
|
|
4
|
+
[](https://pypi.org/project/security-knowledge-os/)
|
|
5
|
+
[](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
|
-
##
|
|
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
|
|
@@ -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
|