@nebulacomponents/citable 1.19.0 → 1.21.0
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.
- package/CHANGELOG.md +35 -0
- package/README.md +12 -1
- package/dist/universal/.agents/skills/citable/SKILL.md +11 -2
- package/dist/universal/.agents/skills/citable/VERSION +1 -1
- package/dist/universal/.agents/skills/citable/manifest.json +24 -12
- package/dist/universal/.agents/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.agents/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.agents/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.agents/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.agents/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.agents/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.agents/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.agents/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.agents/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.agents/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.agents/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.agents/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.agents/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.agents/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.agents/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.agents/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.agents/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.agents/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.agents/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.claude/agents/citable/manifest.json +2 -2
- package/dist/universal/.claude/skills/citable/SKILL.md +11 -2
- package/dist/universal/.claude/skills/citable/VERSION +1 -1
- package/dist/universal/.claude/skills/citable/manifest.json +24 -12
- package/dist/universal/.claude/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.claude/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.claude/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.claude/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.claude/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.claude/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.claude/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.claude/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.claude/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.claude/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.claude/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.claude/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.claude/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.claude/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.claude/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.claude/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.claude/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.claude/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.claude/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.cursor/skills/citable/SKILL.md +11 -2
- package/dist/universal/.cursor/skills/citable/VERSION +1 -1
- package/dist/universal/.cursor/skills/citable/manifest.json +24 -12
- package/dist/universal/.cursor/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.cursor/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.cursor/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.cursor/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.cursor/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.cursor/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.cursor/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.cursor/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.cursor/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.cursor/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.cursor/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.cursor/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.cursor/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.cursor/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.cursor/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.cursor/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.cursor/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.cursor/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.cursor/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.gemini/skills/citable/SKILL.md +11 -2
- package/dist/universal/.gemini/skills/citable/VERSION +1 -1
- package/dist/universal/.gemini/skills/citable/manifest.json +24 -12
- package/dist/universal/.gemini/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.gemini/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.gemini/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.gemini/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.gemini/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.gemini/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.gemini/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.gemini/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.gemini/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.gemini/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.gemini/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.gemini/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.gemini/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.gemini/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.gemini/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.gemini/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.gemini/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.gemini/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.gemini/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.github/skills/citable/SKILL.md +11 -2
- package/dist/universal/.github/skills/citable/VERSION +1 -1
- package/dist/universal/.github/skills/citable/manifest.json +24 -12
- package/dist/universal/.github/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.github/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.github/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.github/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.github/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.github/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.github/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.github/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.github/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.github/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.github/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.github/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.github/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.github/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.github/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.github/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.github/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.github/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.github/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.kiro/skills/citable/SKILL.md +11 -2
- package/dist/universal/.kiro/skills/citable/VERSION +1 -1
- package/dist/universal/.kiro/skills/citable/manifest.json +24 -12
- package/dist/universal/.kiro/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.kiro/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.kiro/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.kiro/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.kiro/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.kiro/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.kiro/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.kiro/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.kiro/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.kiro/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.kiro/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.kiro/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.kiro/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.kiro/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.kiro/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.kiro/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.kiro/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.kiro/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.kiro/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.opencode/skills/citable/SKILL.md +11 -2
- package/dist/universal/.opencode/skills/citable/VERSION +1 -1
- package/dist/universal/.opencode/skills/citable/manifest.json +24 -12
- package/dist/universal/.opencode/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.opencode/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.opencode/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.opencode/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.opencode/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.opencode/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.opencode/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.opencode/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.opencode/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.opencode/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.opencode/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.opencode/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.opencode/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.opencode/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.opencode/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.opencode/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.opencode/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.opencode/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.opencode/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.pi/agent/skills/citable/SKILL.md +11 -2
- package/dist/universal/.pi/agent/skills/citable/VERSION +1 -1
- package/dist/universal/.pi/agent/skills/citable/manifest.json +24 -12
- package/dist/universal/.pi/agent/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.pi/agent/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.pi/agent/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.pi/agent/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.pi/agent/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.pi/agent/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.pi/agent/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.qoder/skills/citable/SKILL.md +11 -2
- package/dist/universal/.qoder/skills/citable/VERSION +1 -1
- package/dist/universal/.qoder/skills/citable/manifest.json +24 -12
- package/dist/universal/.qoder/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.qoder/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.qoder/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.qoder/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.qoder/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.qoder/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.qoder/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.qoder/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.qoder/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.qoder/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.qoder/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.qoder/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.qoder/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.qoder/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.qoder/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.qoder/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.qoder/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.qoder/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.qoder/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.rovodev/skills/citable/SKILL.md +11 -2
- package/dist/universal/.rovodev/skills/citable/VERSION +1 -1
- package/dist/universal/.rovodev/skills/citable/manifest.json +24 -12
- package/dist/universal/.rovodev/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.rovodev/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.rovodev/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.rovodev/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.rovodev/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.rovodev/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.rovodev/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.rovodev/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.rovodev/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.rovodev/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.rovodev/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.rovodev/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.rovodev/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.rovodev/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.rovodev/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.rovodev/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.rovodev/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.rovodev/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.rovodev/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.trae/skills/citable/SKILL.md +11 -2
- package/dist/universal/.trae/skills/citable/VERSION +1 -1
- package/dist/universal/.trae/skills/citable/manifest.json +24 -12
- package/dist/universal/.trae/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.trae/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.trae/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.trae/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.trae/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.trae/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.trae/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.trae/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.trae/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.trae/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.trae/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.trae/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.trae/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.trae/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.trae/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.trae/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.trae/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.trae/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.trae/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.trae-cn/skills/citable/SKILL.md +11 -2
- package/dist/universal/.trae-cn/skills/citable/VERSION +1 -1
- package/dist/universal/.trae-cn/skills/citable/manifest.json +24 -12
- package/dist/universal/.trae-cn/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.trae-cn/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.trae-cn/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.trae-cn/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.trae-cn/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.trae-cn/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.trae-cn/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/manifest.json +292 -148
- package/package.json +1 -1
- package/schemas/action-plan.schema.json +38 -1
- package/schemas/audit-coverage.schema.json +118 -0
- package/schemas/claim-evidence.schema.json +21 -0
- package/schemas/claim-verification.schema.json +18 -0
- package/schemas/collection-result.schema.json +35 -0
- package/schemas/config.schema.json +2 -0
- package/schemas/cro-report.schema.json +8 -1
- package/schemas/derivation.schema.json +18 -0
- package/schemas/external-comparison.schema.json +16 -0
- package/schemas/external-observation.schema.json +32 -0
- package/schemas/finding.schema.json +105 -2
- package/schemas/prompt-set.schema.json +47 -0
- package/schemas/remediation-verification.schema.json +12 -1
- package/schemas/representation-data.schema.json +21 -0
- package/schemas/research-dataset.schema.json +27 -0
- package/schemas/run.schema.json +15 -2
- package/schemas/search-discovery-data.schema.json +20 -0
- package/schemas/search-report.schema.json +8 -1
- package/schemas/summary.schema.json +41 -0
- package/skill/SKILL.md +11 -2
- package/src/acceptance/reproducibility.js +5 -25
- package/src/analysis/croRoadmap.js +3 -3
- package/src/analysis/strategicRoadmap.js +3 -3
- package/src/artifacts/interchange.js +2 -6
- package/src/cli/index.js +15 -3
- package/src/commands/actionPlan.js +46 -5
- package/src/commands/applyRemediation.js +5 -5
- package/src/commands/audit.js +75 -8
- package/src/commands/ciWorkflow.js +7 -2
- package/src/commands/compareSnapshots.js +155 -47
- package/src/commands/connect.js +24 -4
- package/src/commands/context.js +41 -2
- package/src/commands/croSuite.js +51 -13
- package/src/commands/delivery.js +7 -7
- package/src/commands/governance.js +9 -10
- package/src/commands/implementationKit.js +2 -1
- package/src/commands/init.js +8 -1
- package/src/commands/previewCro.js +1 -1
- package/src/commands/prioritize.js +3 -2
- package/src/commands/reviews.js +31 -5
- package/src/commands/roadmapCmd.js +7 -13
- package/src/commands/sweep.js +15 -2
- package/src/commands/verifyRemediation.js +94 -19
- package/src/connectors/collectionResult.js +80 -0
- package/src/connectors/ga4.js +46 -11
- package/src/connectors/gsc.js +45 -6
- package/src/connectors/index.js +1 -1
- package/src/connectors/webflow.js +140 -10
- package/src/connectors/wordpress.js +92 -8
- package/src/crawler/fetch.js +220 -24
- package/src/crawler/resourceValidity.js +119 -0
- package/src/crawler/sitemap.js +435 -16
- package/src/crawler/sitemapCollector.js +364 -0
- package/src/crawler/urlIdentity.js +97 -0
- package/src/detectors/agent.js +11 -0
- package/src/detectors/ans.js +16 -1
- package/src/detectors/arch.js +6 -0
- package/src/detectors/claim.js +9 -0
- package/src/detectors/crawl.js +7 -0
- package/src/detectors/cro.js +21 -0
- package/src/detectors/cwv.js +4 -0
- package/src/detectors/entity.js +7 -0
- package/src/detectors/evd.js +9 -0
- package/src/detectors/framework.js +28 -2
- package/src/detectors/geoReco.js +16 -0
- package/src/detectors/hreflang.js +3 -0
- package/src/detectors/lifeMeas.js +9 -0
- package/src/detectors/link.js +6 -1
- package/src/detectors/page.js +11 -0
- package/src/detectors/schemaData.js +16 -1
- package/src/detectors/tech.js +34 -3
- package/src/discovery/searchConsole.js +186 -0
- package/src/evidence/coverage.js +448 -0
- package/src/evidence/determination.js +157 -0
- package/src/evidence/downstream.js +110 -0
- package/src/evidence/external.js +251 -0
- package/src/evidence/hashes.js +109 -0
- package/src/evidence/run.js +68 -8
- package/src/extractor/page.js +29 -2
- package/src/extractor/site.js +298 -50
- package/src/reporting/executiveCroReport.js +36 -13
- package/src/reporting/executiveExport.js +45 -16
- package/src/reporting/executiveSearchReport.js +56 -22
- package/src/reporting/report.js +56 -9
- package/src/representation/prompts.js +117 -0
- package/src/representation/representation.js +186 -0
- package/src/shared/epistemicLanguage.js +86 -0
- package/src/shared/evidenceSourceResolver.js +48 -17
- package/src/shared/htmlEscape.js +8 -4
- package/src/shared/runPackageVerifier.js +99 -8
- package/src/shared/verifiedRunLoader.js +129 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for keeping presentation language within the scope of its evidence.
|
|
3
|
+
*
|
|
4
|
+
* These checks intentionally target assurance phrases, rather than individual
|
|
5
|
+
* words such as `complete` or `verified` which are also legitimate schema and
|
|
6
|
+
* lifecycle states. A report may say that a package is checksum-verified or
|
|
7
|
+
* that a collection completed; it may not turn that fact into an unsupported
|
|
8
|
+
* corpus-wide assurance.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const UNSUPPORTED_ASSURANCE_PATTERNS = Object.freeze([
|
|
12
|
+
/\ball\s+(?:conversion\s+pathways|pages?|resources?|checks?|findings?).{0,40}\b(?:verified|clean|passed|complete)\b/i,
|
|
13
|
+
/\b(?:verified[- ]clean|clean\s+and\s+verified|no\s+issues)\b/i,
|
|
14
|
+
/\b100%\s+(?:clean|verified|complete|pass(?:ed)?|coverage|conversion|funnel)/i,
|
|
15
|
+
/\b(?:site[- ]wide|entire\s+(?:site|corpus|population))\s+(?:clean|verified|complete|resolved|no\s+issues)/i,
|
|
16
|
+
/\b(?:all|entire)\s+(?:site|corpus|population)\s+(?:passed|verified|clean|complete)\b/i,
|
|
17
|
+
/(?:^|\n)\s*(?:verified|clean|resolved|optimal|complete|site[- ]wide|entire|none|all|passed)\b/im,
|
|
18
|
+
/<(?:h[1-6]|span|strong|div)[^>]*>\s*(?:verified|clean|resolved|optimal|complete|site[- ]wide|entire|none|all|passed)\b/i,
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
function normaliseContext(context = {}) {
|
|
22
|
+
const coverage = context.coverage_status || context.coverageStatus || 'indeterminate';
|
|
23
|
+
const determination = context.determination_status || context.determinationStatus || 'indeterminate';
|
|
24
|
+
const evaluated = Number.isInteger(context.evaluated) ? context.evaluated : null;
|
|
25
|
+
const eligible = Number.isInteger(context.eligible) ? context.eligible : null;
|
|
26
|
+
const packageVerified = context.package_verified ?? context.packageVerified ?? false;
|
|
27
|
+
const legacy = context.legacy === true || context.integrity_mode === 'legacy_unverified';
|
|
28
|
+
return { coverage, determination, evaluated, eligible, packageVerified, legacy };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function hasVerifiedEvidenceScope(context = {}) {
|
|
32
|
+
const state = normaliseContext(context);
|
|
33
|
+
return state.packageVerified === true
|
|
34
|
+
&& context.integrity_mode === 'sealed'
|
|
35
|
+
&& !state.legacy
|
|
36
|
+
&& state.coverage === 'complete'
|
|
37
|
+
&& state.determination === 'supported'
|
|
38
|
+
&& Number.isInteger(state.evaluated)
|
|
39
|
+
&& Number.isInteger(state.eligible)
|
|
40
|
+
&& state.evaluated > 0
|
|
41
|
+
&& state.eligible > 0
|
|
42
|
+
&& state.evaluated === state.eligible;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Find assurance phrases that are unsafe for a presentation context.
|
|
47
|
+
* Complete, supported, verified packages may use the phrases when the caller
|
|
48
|
+
* supplies an explicit population; incomplete/legacy/empty evidence may not.
|
|
49
|
+
*/
|
|
50
|
+
export function findEpistemicLanguageViolations(text, context = {}) {
|
|
51
|
+
const value = String(text ?? '');
|
|
52
|
+
const state = normaliseContext(context);
|
|
53
|
+
const cannotClaimCorpus = !hasVerifiedEvidenceScope(context);
|
|
54
|
+
if (!cannotClaimCorpus) return [];
|
|
55
|
+
return UNSUPPORTED_ASSURANCE_PATTERNS
|
|
56
|
+
.map((pattern) => {
|
|
57
|
+
const match = value.match(pattern);
|
|
58
|
+
return match ? { phrase: match[0], index: match.index ?? 0, pattern: pattern.source } : null;
|
|
59
|
+
})
|
|
60
|
+
.filter(Boolean);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function assertEpistemicLanguage(text, context = {}) {
|
|
64
|
+
const violations = findEpistemicLanguageViolations(text, context);
|
|
65
|
+
if (violations.length) {
|
|
66
|
+
throw new Error(`unsupported assurance language: ${violations.map((item) => item.phrase).join('; ')}`);
|
|
67
|
+
}
|
|
68
|
+
return text;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** A short, reusable disclosure for reports and machine-readable exports. */
|
|
72
|
+
export function evidenceScopeStatement(context = {}) {
|
|
73
|
+
const state = normaliseContext(context);
|
|
74
|
+
if (state.evaluated === 0 || state.eligible === 0) {
|
|
75
|
+
return 'No determination is available because no eligible resource was evaluated.';
|
|
76
|
+
}
|
|
77
|
+
if (state.coverage !== 'complete' || state.determination !== 'supported' || state.legacy || !state.packageVerified) {
|
|
78
|
+
const population = state.evaluated == null ? 'the evaluated subset' : `${state.evaluated} evaluated resource(s)`;
|
|
79
|
+
const denominator = state.eligible == null ? '' : ` of ${state.eligible} eligible`;
|
|
80
|
+
return `Coverage is ${state.coverage}; this statement applies only to ${population}${denominator}. Corpus-wide absence or cleanliness is not established.`;
|
|
81
|
+
}
|
|
82
|
+
const population = state.evaluated == null ? 'the evaluated population' : `${state.evaluated} evaluated resource(s)`;
|
|
83
|
+
return `This statement applies to ${population} under the supported evidence contract.`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const EPISTEMIC_LANGUAGE_PATTERNS = UNSUPPORTED_ASSURANCE_PATTERNS;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { readJson, sha256
|
|
4
|
-
import {
|
|
3
|
+
import { readJson, sha256 } from './io.js';
|
|
4
|
+
import { loadVerifiedRun } from './verifiedRunLoader.js';
|
|
5
5
|
import { buildContext } from '../commands/context.js';
|
|
6
6
|
import { selectDetectors } from '../detectors/index.js';
|
|
7
7
|
import { runDetectors, indexTargets } from '../detectors/framework.js';
|
|
@@ -161,6 +161,12 @@ export async function resolveEvidenceSource(root, options = {}) {
|
|
|
161
161
|
integrity_hash: sha256(findingsJson),
|
|
162
162
|
generation_mode: generationMode,
|
|
163
163
|
run_metadata: null,
|
|
164
|
+
package_verified: false,
|
|
165
|
+
integrity_mode: 'in_memory_unsealed',
|
|
166
|
+
legacy: false,
|
|
167
|
+
coverage: null,
|
|
168
|
+
coverage_status: 'indeterminate',
|
|
169
|
+
determination_status: 'indeterminate',
|
|
164
170
|
};
|
|
165
171
|
}
|
|
166
172
|
|
|
@@ -177,10 +183,14 @@ export async function resolveEvidenceSource(root, options = {}) {
|
|
|
177
183
|
}
|
|
178
184
|
|
|
179
185
|
try {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
186
|
+
// Historical packages are explicitly opened in legacy mode until all
|
|
187
|
+
// producers persist coverage.json. The loader still verifies the sealed
|
|
188
|
+
// package and marks their coverage indeterminate; it never invents a
|
|
189
|
+
// complete corpus.
|
|
190
|
+
const verification = loadVerifiedRun(runPath, {
|
|
191
|
+
requireCompletedExecution: generationMode === 'CONTRACTUAL',
|
|
192
|
+
allowLegacy: true,
|
|
193
|
+
requireCoverage: false,
|
|
184
194
|
});
|
|
185
195
|
|
|
186
196
|
return {
|
|
@@ -188,16 +198,19 @@ export async function resolveEvidenceSource(root, options = {}) {
|
|
|
188
198
|
source_identifier: runId,
|
|
189
199
|
findings: verification.findings,
|
|
190
200
|
findings_count: verification.findingsCount,
|
|
191
|
-
integrity_hash:
|
|
201
|
+
integrity_hash: verification.package_hash,
|
|
192
202
|
generation_mode: generationMode,
|
|
193
203
|
run_metadata: verification.manifest,
|
|
194
204
|
checksums_verified: verification.checksumsVerified,
|
|
205
|
+
package_verified: verification.verified === true && verification.integrity_mode === 'sealed',
|
|
206
|
+
integrity_mode: verification.integrity_mode,
|
|
207
|
+
legacy: verification.legacy === true,
|
|
208
|
+
coverage: verification.coverage,
|
|
209
|
+
coverage_status: verification.coverage_status,
|
|
210
|
+
determination_status: verification.determination_status,
|
|
195
211
|
};
|
|
196
212
|
} catch (err) {
|
|
197
|
-
|
|
198
|
-
throw new FindingsInvalidError(runId, err.message);
|
|
199
|
-
}
|
|
200
|
-
throw err;
|
|
213
|
+
throw new FindingsInvalidError(runId, err.message);
|
|
201
214
|
}
|
|
202
215
|
}
|
|
203
216
|
|
|
@@ -225,6 +238,12 @@ export async function resolveEvidenceSource(root, options = {}) {
|
|
|
225
238
|
generation_mode: generationMode,
|
|
226
239
|
run_metadata: null,
|
|
227
240
|
context: ctx,
|
|
241
|
+
package_verified: false,
|
|
242
|
+
integrity_mode: 'live_unsealed',
|
|
243
|
+
legacy: false,
|
|
244
|
+
coverage: ctx.coverage || null,
|
|
245
|
+
coverage_status: ctx.coverage?.coverage_status || 'indeterminate',
|
|
246
|
+
determination_status: ctx.coverage?.determination_status || 'indeterminate',
|
|
228
247
|
};
|
|
229
248
|
} catch (err) {
|
|
230
249
|
throw new LiveInspectionFailedError(target, err.message);
|
|
@@ -243,12 +262,11 @@ export async function resolveEvidenceSource(root, options = {}) {
|
|
|
243
262
|
const sorted = sortRunCandidatesChronologically(runsDir, candidateDirs);
|
|
244
263
|
for (const candidateId of sorted) {
|
|
245
264
|
const runPath = path.join(runsDir, candidateId);
|
|
246
|
-
const findPath = path.join(runPath, 'findings.json');
|
|
247
265
|
try {
|
|
248
|
-
const verification =
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
266
|
+
const verification = loadVerifiedRun(runPath, {
|
|
267
|
+
requireCompletedExecution: false,
|
|
268
|
+
allowLegacy: true,
|
|
269
|
+
requireCoverage: false,
|
|
252
270
|
});
|
|
253
271
|
|
|
254
272
|
return {
|
|
@@ -256,9 +274,16 @@ export async function resolveEvidenceSource(root, options = {}) {
|
|
|
256
274
|
source_identifier: candidateId,
|
|
257
275
|
findings: verification.findings,
|
|
258
276
|
findings_count: verification.findingsCount,
|
|
259
|
-
integrity_hash:
|
|
277
|
+
integrity_hash: verification.package_hash,
|
|
260
278
|
generation_mode: generationMode,
|
|
261
279
|
run_metadata: verification.manifest,
|
|
280
|
+
checksums_verified: verification.checksumsVerified,
|
|
281
|
+
package_verified: verification.verified === true && verification.integrity_mode === 'sealed',
|
|
282
|
+
integrity_mode: verification.integrity_mode,
|
|
283
|
+
legacy: verification.legacy === true,
|
|
284
|
+
coverage: verification.coverage,
|
|
285
|
+
coverage_status: verification.coverage_status,
|
|
286
|
+
determination_status: verification.determination_status,
|
|
262
287
|
};
|
|
263
288
|
} catch {
|
|
264
289
|
// Try next candidate
|
|
@@ -278,6 +303,12 @@ export async function resolveEvidenceSource(root, options = {}) {
|
|
|
278
303
|
integrity_hash: null,
|
|
279
304
|
generation_mode: 'NON_CONTRACTUAL_SAMPLE',
|
|
280
305
|
run_metadata: null,
|
|
306
|
+
package_verified: false,
|
|
307
|
+
integrity_mode: 'sample_unverified',
|
|
308
|
+
legacy: false,
|
|
309
|
+
coverage: null,
|
|
310
|
+
coverage_status: 'indeterminate',
|
|
311
|
+
determination_status: 'indeterminate',
|
|
281
312
|
};
|
|
282
313
|
}
|
|
283
314
|
|
package/src/shared/htmlEscape.js
CHANGED
|
@@ -26,7 +26,7 @@ export function escapeHtmlAttr(str) {
|
|
|
26
26
|
return escapeHtml(str);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
const SAFE_PROTOCOLS = new Set(['http:', 'https:', 'mailto:']);
|
|
29
|
+
const SAFE_PROTOCOLS = new Set(['http:', 'https:', 'mailto:', 'ftp:']);
|
|
30
30
|
|
|
31
31
|
export function sanitizeUrl(urlStr, fallback = '#') {
|
|
32
32
|
if (!urlStr || typeof urlStr !== 'string') return fallback;
|
|
@@ -38,11 +38,15 @@ export function sanitizeUrl(urlStr, fallback = '#') {
|
|
|
38
38
|
try {
|
|
39
39
|
const parsed = new URL(trimmed);
|
|
40
40
|
if (SAFE_PROTOCOLS.has(parsed.protocol)) {
|
|
41
|
-
|
|
41
|
+
// Additional substring sanitization: ensure no CRLF injection via encoded newlines
|
|
42
|
+
const normalized = parsed.href.replace(/\r\n|\r/g, '\n');
|
|
43
|
+
return escapeHtmlAttr(normalized);
|
|
42
44
|
}
|
|
43
|
-
return fallback
|
|
45
|
+
// Unsafe protocol: return fully sanitized fallback
|
|
46
|
+
return escapeHtmlAttr(fallback);
|
|
44
47
|
} catch {
|
|
45
|
-
return fallback
|
|
48
|
+
// Invalid URL: return sanitized fallback
|
|
49
|
+
return escapeHtmlAttr(fallback);
|
|
46
50
|
}
|
|
47
51
|
}
|
|
48
52
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { sha256 } from './io.js';
|
|
4
4
|
import { validateAgainst } from './schemaValidator.js';
|
|
5
5
|
|
|
6
6
|
export class RunVerificationError extends Error {
|
|
@@ -12,6 +12,17 @@ export class RunVerificationError extends Error {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
function readStableBytes(file, runDir, relative) {
|
|
16
|
+
const first = fs.readFileSync(file);
|
|
17
|
+
const second = fs.readFileSync(file);
|
|
18
|
+
if (!first.equals(second)) {
|
|
19
|
+
throw new RunVerificationError(`Artifact changed while being verified: ${relative}`, {
|
|
20
|
+
code: 'PACKAGE_MUTATION', runDir, file: relative,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return first;
|
|
24
|
+
}
|
|
25
|
+
|
|
15
26
|
/**
|
|
16
27
|
* Verifies the cryptographic and structural integrity of an audit run evidence package.
|
|
17
28
|
*
|
|
@@ -28,13 +39,24 @@ export function verifyRunPackage(runDir, options = {}) {
|
|
|
28
39
|
requireFindings = true,
|
|
29
40
|
requireCompleted = false,
|
|
30
41
|
requireChecksums = true,
|
|
42
|
+
ignoredFiles = [],
|
|
31
43
|
} = options;
|
|
32
44
|
|
|
33
45
|
if (!runDir || !fs.existsSync(runDir)) {
|
|
34
46
|
throw new RunVerificationError(`Run directory not found: ${runDir}`, { code: 'RUN_NOT_FOUND', runDir });
|
|
35
47
|
}
|
|
48
|
+
let rootStat;
|
|
49
|
+
try { rootStat = fs.lstatSync(runDir); } catch (error) {
|
|
50
|
+
throw new RunVerificationError(`Run directory cannot be inspected: ${error.message}`, { code: 'RUN_NOT_FOUND', runDir });
|
|
51
|
+
}
|
|
52
|
+
if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
|
|
53
|
+
throw new RunVerificationError(`Run path is not a real directory: ${runDir}`, { code: 'RUN_PATH_INVALID', runDir });
|
|
54
|
+
}
|
|
36
55
|
|
|
37
56
|
const manifestPath = path.join(runDir, 'manifest.json');
|
|
57
|
+
if (fs.existsSync(manifestPath) && fs.lstatSync(manifestPath).isSymbolicLink()) {
|
|
58
|
+
throw new RunVerificationError('Run manifest.json is a symbolic link', { code: 'PACKAGE_SYMLINK', runDir, file: 'manifest.json' });
|
|
59
|
+
}
|
|
38
60
|
if (!fs.existsSync(manifestPath)) {
|
|
39
61
|
throw new RunVerificationError(`Run package is missing manifest.json at ${manifestPath}`, {
|
|
40
62
|
code: 'MANIFEST_MISSING',
|
|
@@ -44,7 +66,7 @@ export function verifyRunPackage(runDir, options = {}) {
|
|
|
44
66
|
|
|
45
67
|
let manifest;
|
|
46
68
|
try {
|
|
47
|
-
manifest =
|
|
69
|
+
manifest = JSON.parse(readStableBytes(manifestPath, runDir, 'manifest.json').toString('utf8'));
|
|
48
70
|
} catch (err) {
|
|
49
71
|
throw new RunVerificationError(`Run manifest.json is invalid JSON: ${err.message}`, {
|
|
50
72
|
code: 'MANIFEST_INVALID',
|
|
@@ -53,6 +75,12 @@ export function verifyRunPackage(runDir, options = {}) {
|
|
|
53
75
|
}
|
|
54
76
|
|
|
55
77
|
const schemaValidation = validateAgainst('run.schema.json', manifest);
|
|
78
|
+
if (Object.prototype.hasOwnProperty.call(manifest, 'schema_version')
|
|
79
|
+
&& ![1, 2].includes(manifest.schema_version)) {
|
|
80
|
+
throw new RunVerificationError(`Unsupported run manifest schema_version: ${String(manifest.schema_version)}`, {
|
|
81
|
+
code: 'MANIFEST_SCHEMA_UNSUPPORTED', runDir, schema_version: manifest.schema_version,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
56
84
|
if (!schemaValidation.valid) {
|
|
57
85
|
throw new RunVerificationError(`Run manifest violates run.schema.json: ${schemaValidation.errors.join('; ')}`, {
|
|
58
86
|
code: 'MANIFEST_SCHEMA_INVALID',
|
|
@@ -69,42 +97,94 @@ export function verifyRunPackage(runDir, options = {}) {
|
|
|
69
97
|
});
|
|
70
98
|
}
|
|
71
99
|
|
|
100
|
+
// A sealed run is closed-world: every regular file except the checksum
|
|
101
|
+
// index itself must be listed, and every listed path must be safe. Do not
|
|
102
|
+
// use stat() here because it follows symlinks and could verify bytes outside
|
|
103
|
+
// the package directory.
|
|
104
|
+
const packageFiles = [];
|
|
105
|
+
const walk = (directory) => {
|
|
106
|
+
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
107
|
+
const absolute = path.join(directory, entry.name);
|
|
108
|
+
const relative = path.relative(runDir, absolute).split(path.sep).join('/');
|
|
109
|
+
if (entry.isSymbolicLink()) {
|
|
110
|
+
throw new RunVerificationError(`Run package contains symbolic link: ${relative}`, {
|
|
111
|
+
code: 'PACKAGE_SYMLINK', runDir, file: relative,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
if (entry.isDirectory()) walk(absolute);
|
|
115
|
+
else if (entry.isFile()) packageFiles.push(relative);
|
|
116
|
+
else throw new RunVerificationError(`Run package contains unsupported filesystem entry: ${relative}`, {
|
|
117
|
+
code: 'PACKAGE_ENTRY_INVALID', runDir, file: relative,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
walk(runDir);
|
|
122
|
+
const ignored = new Set(ignoredFiles);
|
|
123
|
+
|
|
72
124
|
// Verify checksums.json
|
|
73
125
|
const checksumsPath = path.join(runDir, 'checksums.json');
|
|
74
126
|
let checksumsVerified = false;
|
|
127
|
+
let artifactHashes = {};
|
|
128
|
+
const artifactBytes = new Map();
|
|
129
|
+
const retainedArtifactNames = new Set(['findings.json', 'coverage.json', 'summary.json']);
|
|
75
130
|
if (fs.existsSync(checksumsPath)) {
|
|
131
|
+
if (fs.lstatSync(checksumsPath).isSymbolicLink()) throw new RunVerificationError('checksums.json is a symbolic link', { code: 'PACKAGE_SYMLINK', runDir, file: 'checksums.json' });
|
|
76
132
|
let checksums;
|
|
77
133
|
try {
|
|
78
|
-
checksums =
|
|
134
|
+
checksums = JSON.parse(readStableBytes(checksumsPath, runDir, 'checksums.json').toString('utf8'));
|
|
79
135
|
} catch (err) {
|
|
80
136
|
throw new RunVerificationError(`checksums.json is invalid JSON: ${err.message}`, {
|
|
81
137
|
code: 'CHECKSUMS_INVALID',
|
|
82
138
|
runDir,
|
|
83
139
|
});
|
|
84
140
|
}
|
|
141
|
+
artifactHashes = checksums;
|
|
85
142
|
|
|
86
143
|
const tamperedFiles = [];
|
|
87
144
|
const missingFiles = [];
|
|
88
145
|
|
|
146
|
+
const safePath = (value) => typeof value === 'string'
|
|
147
|
+
&& value.length > 0
|
|
148
|
+
&& !path.isAbsolute(value)
|
|
149
|
+
&& !value.includes('\\')
|
|
150
|
+
&& !value.split('/').includes('..')
|
|
151
|
+
&& !value.split('/').includes('')
|
|
152
|
+
&& !/^[a-z][a-z0-9+.-]*:/i.test(value);
|
|
89
153
|
for (const [relPath, expectedHash] of Object.entries(checksums)) {
|
|
154
|
+
if (!safePath(relPath) || relPath === 'checksums.json') {
|
|
155
|
+
throw new RunVerificationError(`unsafe checksum path (escapes the run package): ${relPath}`, {
|
|
156
|
+
code: 'CHECKSUM_PATH_INVALID', runDir, file: relPath,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
90
159
|
const artifactPath = path.join(runDir, relPath);
|
|
91
160
|
if (!fs.existsSync(artifactPath)) {
|
|
92
161
|
missingFiles.push(relPath);
|
|
93
162
|
continue;
|
|
94
163
|
}
|
|
95
|
-
const
|
|
164
|
+
const artifactStat = fs.lstatSync(artifactPath);
|
|
165
|
+
if (artifactStat.isSymbolicLink() || !artifactStat.isFile()) {
|
|
166
|
+
throw new RunVerificationError(`checksum artifact is not a regular file: ${relPath}`, { code: 'PACKAGE_SYMLINK', runDir, file: relPath });
|
|
167
|
+
}
|
|
168
|
+
const bytes = readStableBytes(artifactPath, runDir, relPath);
|
|
169
|
+
if (retainedArtifactNames.has(relPath)) artifactBytes.set(relPath, bytes);
|
|
170
|
+
const actualHash = sha256(bytes);
|
|
96
171
|
if (actualHash !== expectedHash) {
|
|
97
172
|
tamperedFiles.push({ file: relPath, expected: expectedHash, actual: actualHash });
|
|
98
173
|
}
|
|
99
174
|
}
|
|
100
175
|
|
|
101
|
-
|
|
176
|
+
const expectedFiles = Object.keys(checksums).sort();
|
|
177
|
+
const actualFiles = packageFiles.filter((file) => file !== 'checksums.json' && !ignored.has(file)).sort();
|
|
178
|
+
const unexpectedFiles = actualFiles.filter((file) => !expectedFiles.includes(file));
|
|
179
|
+
const unlistedFiles = expectedFiles.filter((file) => !actualFiles.includes(file));
|
|
180
|
+
if (tamperedFiles.length > 0 || missingFiles.length > 0 || unexpectedFiles.length > 0 || unlistedFiles.length > 0) {
|
|
102
181
|
throw new RunVerificationError(
|
|
103
|
-
`Cryptographic verification failed for run package: ${tamperedFiles.length} tampered files, ${missingFiles.length} missing files`,
|
|
182
|
+
`Cryptographic verification failed for run package (checksum mismatch; integrity failed; completeness failure; unsealed): ${tamperedFiles.length} tampered files, ${missingFiles.length + unlistedFiles.length} missing files, ${unexpectedFiles.length} unexpected files`,
|
|
104
183
|
{
|
|
105
184
|
code: 'CHECKSUM_MISMATCH',
|
|
106
185
|
tamperedFiles,
|
|
107
|
-
missingFiles,
|
|
186
|
+
missingFiles: [...missingFiles, ...unlistedFiles],
|
|
187
|
+
unexpectedFiles,
|
|
108
188
|
runDir,
|
|
109
189
|
}
|
|
110
190
|
);
|
|
@@ -122,10 +202,19 @@ export function verifyRunPackage(runDir, options = {}) {
|
|
|
122
202
|
let findings = null;
|
|
123
203
|
if (fs.existsSync(findingsPath)) {
|
|
124
204
|
try {
|
|
125
|
-
|
|
205
|
+
if (fs.lstatSync(findingsPath).isSymbolicLink()) throw new Error('findings.json is a symbolic link');
|
|
206
|
+
const findingsBytes = artifactBytes.get('findings.json') || readStableBytes(findingsPath, runDir, 'findings.json');
|
|
207
|
+
artifactBytes.set('findings.json', findingsBytes);
|
|
208
|
+
findings = JSON.parse(findingsBytes.toString('utf8'));
|
|
126
209
|
if (!Array.isArray(findings)) {
|
|
127
210
|
throw new Error('findings.json must contain an array of findings');
|
|
128
211
|
}
|
|
212
|
+
for (const [index, finding] of findings.entries()) {
|
|
213
|
+
const findingValidation = validateAgainst('finding.schema.json', finding);
|
|
214
|
+
if (!findingValidation.valid) {
|
|
215
|
+
throw new Error(`finding[${index}] violates finding.schema.json: ${findingValidation.errors.join('; ')}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
129
218
|
} catch (err) {
|
|
130
219
|
throw new RunVerificationError(`findings.json is malformed: ${err.message}`, {
|
|
131
220
|
code: 'FINDINGS_MALFORMED',
|
|
@@ -149,5 +238,7 @@ export function verifyRunPackage(runDir, options = {}) {
|
|
|
149
238
|
findingsPath: fs.existsSync(findingsPath) ? findingsPath : null,
|
|
150
239
|
findingsCount: Array.isArray(findings) ? findings.length : 0,
|
|
151
240
|
findings,
|
|
241
|
+
artifactHashes,
|
|
242
|
+
artifactBytes,
|
|
152
243
|
};
|
|
153
244
|
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { readJson, sha256File } from './io.js';
|
|
4
|
+
import { verifyRunPackage, RunVerificationError } from './runPackageVerifier.js';
|
|
5
|
+
import { validateAgainst } from './schemaValidator.js';
|
|
6
|
+
|
|
7
|
+
export const VERIFIED_RUN_LOADER_VERSION = 'verified-run-loader-v1';
|
|
8
|
+
|
|
9
|
+
export class VerifiedRunLoadError extends Error {
|
|
10
|
+
constructor(message, details = {}) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = 'VerifiedRunLoadError';
|
|
13
|
+
this.code = details.code || 'VERIFIED_RUN_LOAD_FAILED';
|
|
14
|
+
this.details = details;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function readContractArtifact(runDir, file, schema, { required = true, bytes = null } = {}) {
|
|
19
|
+
const filePath = path.join(runDir, file);
|
|
20
|
+
if (!fs.existsSync(filePath)) {
|
|
21
|
+
if (required) throw new VerifiedRunLoadError(`Verified run is missing ${file}`, { code: 'ARTIFACT_MISSING', file });
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
if (fs.lstatSync(filePath).isSymbolicLink()) {
|
|
25
|
+
throw new VerifiedRunLoadError(`${file} is a symbolic link`, { code: 'PACKAGE_SYMLINK', file });
|
|
26
|
+
}
|
|
27
|
+
let value;
|
|
28
|
+
try {
|
|
29
|
+
value = bytes ? JSON.parse(bytes.toString('utf8')) : readJson(filePath);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
throw new VerifiedRunLoadError(`${file} is invalid JSON: ${error.message}`, { code: 'ARTIFACT_INVALID', file });
|
|
32
|
+
}
|
|
33
|
+
const validation = schema ? validateAgainst(schema, value) : { valid: true, errors: [] };
|
|
34
|
+
if (!validation.valid) {
|
|
35
|
+
throw new VerifiedRunLoadError(`${file} violates ${schema}: ${validation.errors.join('; ')}`, {
|
|
36
|
+
code: 'ARTIFACT_SCHEMA_INVALID', file, errors: validation.errors,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function readFindingsArtifact(runDir, bytes = null) {
|
|
43
|
+
const findings = readContractArtifact(runDir, 'findings.json', null, { bytes });
|
|
44
|
+
if (!Array.isArray(findings)) throw new VerifiedRunLoadError('findings.json must contain an array of findings', { code: 'ARTIFACT_SCHEMA_INVALID', file: 'findings.json' });
|
|
45
|
+
for (const [index, finding] of findings.entries()) {
|
|
46
|
+
const validation = validateAgainst('finding.schema.json', finding);
|
|
47
|
+
if (!validation.valid) throw new VerifiedRunLoadError(`findings.json finding[${index}] violates finding.schema.json: ${validation.errors.join('; ')}`, { code: 'ARTIFACT_SCHEMA_INVALID', file: 'findings.json' });
|
|
48
|
+
}
|
|
49
|
+
return findings;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Load a run only after its closed-world checksum seal and execution contracts
|
|
54
|
+
* have been verified. Consumers must use this API rather than reading
|
|
55
|
+
* findings.json directly: the returned findings are bound to the verified
|
|
56
|
+
* package, manifest, and (when present) coverage artifact.
|
|
57
|
+
*
|
|
58
|
+
* Legacy packages can be opened explicitly for migration. Their coverage is
|
|
59
|
+
* indeterminate and never receives a synthesized complete default.
|
|
60
|
+
*/
|
|
61
|
+
export function loadVerifiedRun(runDir, {
|
|
62
|
+
requireCompletedExecution = true,
|
|
63
|
+
allowLegacy = false,
|
|
64
|
+
requireCoverage = !allowLegacy,
|
|
65
|
+
} = {}) {
|
|
66
|
+
try {
|
|
67
|
+
let verification;
|
|
68
|
+
try {
|
|
69
|
+
verification = verifyRunPackage(runDir, {
|
|
70
|
+
requireFindings: true,
|
|
71
|
+
requireCompleted: requireCompletedExecution,
|
|
72
|
+
requireChecksums: !allowLegacy,
|
|
73
|
+
});
|
|
74
|
+
} catch (error) {
|
|
75
|
+
if (!allowLegacy || !['CHECKSUMS_MISSING', 'MANIFEST_MISSING'].includes(error.code)) throw error;
|
|
76
|
+
const manifestPath = path.join(runDir, 'manifest.json');
|
|
77
|
+
const manifest = fs.existsSync(manifestPath) ? readJson(manifestPath) : { run_id: path.basename(runDir), status: 'incomplete' };
|
|
78
|
+
const findings = readFindingsArtifact(runDir);
|
|
79
|
+
const coverageFile = path.join(runDir, 'coverage.json');
|
|
80
|
+
const coverage = fs.existsSync(coverageFile) ? readContractArtifact(runDir, 'coverage.json', 'audit-coverage.schema.json') : null;
|
|
81
|
+
const summary = readContractArtifact(runDir, 'summary.json', 'summary.schema.json', { required: false });
|
|
82
|
+
return {
|
|
83
|
+
verified: false, verification_version: VERIFIED_RUN_LOADER_VERSION,
|
|
84
|
+
integrity_mode: 'legacy_unverified', package_dir: path.resolve(runDir),
|
|
85
|
+
package_hash: null, manifest, findings, findingsCount: findings.length,
|
|
86
|
+
coverage, summary, legacy: true,
|
|
87
|
+
coverage_status: coverage?.coverage_status || 'indeterminate', determination_status: 'indeterminate',
|
|
88
|
+
checksumsVerified: false,
|
|
89
|
+
artifactHashes: {
|
|
90
|
+
...(fs.existsSync(path.join(runDir, 'manifest.json')) ? { 'manifest.json': sha256File(path.join(runDir, 'manifest.json')) } : {}),
|
|
91
|
+
'findings.json': sha256File(path.join(runDir, 'findings.json')),
|
|
92
|
+
...(fs.existsSync(coverageFile) ? { 'coverage.json': sha256File(coverageFile) } : {}),
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const coveragePath = path.join(runDir, 'coverage.json');
|
|
98
|
+
const hasCoverage = fs.existsSync(coveragePath);
|
|
99
|
+
if (!hasCoverage && !allowLegacy && requireCoverage) {
|
|
100
|
+
throw new VerifiedRunLoadError('Verified run is missing coverage.json; use explicit legacy mode to open historical packages', {
|
|
101
|
+
code: 'COVERAGE_MISSING',
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
const coverage = hasCoverage ? readContractArtifact(runDir, 'coverage.json', 'audit-coverage.schema.json', { bytes: verification.artifactBytes.get('coverage.json') }) : null;
|
|
105
|
+
const summary = readContractArtifact(runDir, 'summary.json', 'summary.schema.json', { required: false, bytes: verification.artifactBytes.get('summary.json') });
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
...verification,
|
|
109
|
+
verified: hasCoverage,
|
|
110
|
+
verification_version: VERIFIED_RUN_LOADER_VERSION,
|
|
111
|
+
integrity_mode: hasCoverage ? 'sealed' : 'legacy_unverified',
|
|
112
|
+
package_dir: path.resolve(runDir),
|
|
113
|
+
package_hash: verification.checksumsVerified ? sha256File(path.join(runDir, 'checksums.json')) : null,
|
|
114
|
+
findings: verification.findings,
|
|
115
|
+
coverage,
|
|
116
|
+
summary,
|
|
117
|
+
legacy: !hasCoverage,
|
|
118
|
+
coverage_status: coverage?.coverage_status || 'indeterminate',
|
|
119
|
+
determination_status: coverage ? verification.manifest.determination_status : 'indeterminate',
|
|
120
|
+
artifactHashes: verification.artifactHashes,
|
|
121
|
+
};
|
|
122
|
+
} catch (error) {
|
|
123
|
+
if (error instanceof VerifiedRunLoadError) throw error;
|
|
124
|
+
if (error instanceof RunVerificationError) {
|
|
125
|
+
throw new VerifiedRunLoadError(error.message, { code: error.code, cause: error });
|
|
126
|
+
}
|
|
127
|
+
throw new VerifiedRunLoadError(error.message, { cause: error });
|
|
128
|
+
}
|
|
129
|
+
}
|