@skillsmith/core 0.8.2 → 0.10.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 +17 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/api/client.d.ts.map +1 -1
- package/dist/src/api/client.js +6 -0
- package/dist/src/api/client.js.map +1 -1
- package/dist/src/api/client.test.d.ts +9 -0
- package/dist/src/api/client.test.d.ts.map +1 -0
- package/dist/src/api/client.test.js +81 -0
- package/dist/src/api/client.test.js.map +1 -0
- package/dist/src/config/device-identity.d.ts +113 -0
- package/dist/src/config/device-identity.d.ts.map +1 -0
- package/dist/src/config/device-identity.js +171 -0
- package/dist/src/config/device-identity.js.map +1 -0
- package/dist/src/config/device-identity.test.d.ts +10 -0
- package/dist/src/config/device-identity.test.d.ts.map +1 -0
- package/dist/src/config/device-identity.test.js +213 -0
- package/dist/src/config/device-identity.test.js.map +1 -0
- package/dist/src/config/index.d.ts +9 -0
- package/dist/src/config/index.d.ts.map +1 -1
- package/dist/src/config/index.js.map +1 -1
- package/dist/src/exports/services.d.ts +3 -1
- package/dist/src/exports/services.d.ts.map +1 -1
- package/dist/src/exports/services.js +11 -1
- package/dist/src/exports/services.js.map +1 -1
- package/dist/src/index.d.ts +4 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/install/agent-config-merge.json-array.d.ts +29 -0
- package/dist/src/install/agent-config-merge.json-array.d.ts.map +1 -0
- package/dist/src/install/agent-config-merge.json-array.js +100 -0
- package/dist/src/install/agent-config-merge.json-array.js.map +1 -0
- package/dist/src/install/agent-config-merge.json.d.ts +37 -0
- package/dist/src/install/agent-config-merge.json.d.ts.map +1 -0
- package/dist/src/install/agent-config-merge.json.js +134 -0
- package/dist/src/install/agent-config-merge.json.js.map +1 -0
- package/dist/src/install/agent-config-merge.toml-block.d.ts +48 -0
- package/dist/src/install/agent-config-merge.toml-block.d.ts.map +1 -0
- package/dist/src/install/agent-config-merge.toml-block.js +107 -0
- package/dist/src/install/agent-config-merge.toml-block.js.map +1 -0
- package/dist/src/install/agent-config-merge.types.d.ts +87 -0
- package/dist/src/install/agent-config-merge.types.d.ts.map +1 -0
- package/dist/src/install/agent-config-merge.types.js +83 -0
- package/dist/src/install/agent-config-merge.types.js.map +1 -0
- package/dist/src/install/agent-config-merge.yaml.d.ts +46 -0
- package/dist/src/install/agent-config-merge.yaml.d.ts.map +1 -0
- package/dist/src/install/agent-config-merge.yaml.js +133 -0
- package/dist/src/install/agent-config-merge.yaml.js.map +1 -0
- package/dist/src/install/agent-config-merge.yaml.test.d.ts +2 -0
- package/dist/src/install/agent-config-merge.yaml.test.d.ts.map +1 -0
- package/dist/src/install/agent-config-merge.yaml.test.js +210 -0
- package/dist/src/install/agent-config-merge.yaml.test.js.map +1 -0
- package/dist/src/install/agent-harness-targets.d.ts +98 -0
- package/dist/src/install/agent-harness-targets.d.ts.map +1 -0
- package/dist/src/install/agent-harness-targets.js +154 -0
- package/dist/src/install/agent-harness-targets.js.map +1 -0
- package/dist/src/install/agent-home-relocate.d.ts +29 -0
- package/dist/src/install/agent-home-relocate.d.ts.map +1 -0
- package/dist/src/install/agent-home-relocate.js +38 -0
- package/dist/src/install/agent-home-relocate.js.map +1 -0
- package/dist/src/install/agent-manifest-path-guard.d.ts +55 -0
- package/dist/src/install/agent-manifest-path-guard.d.ts.map +1 -0
- package/dist/src/install/agent-manifest-path-guard.js +109 -0
- package/dist/src/install/agent-manifest-path-guard.js.map +1 -0
- package/dist/src/install/agent-manifest-path-guard.test.d.ts +2 -0
- package/dist/src/install/agent-manifest-path-guard.test.d.ts.map +1 -0
- package/dist/src/install/agent-manifest-path-guard.test.js +72 -0
- package/dist/src/install/agent-manifest-path-guard.test.js.map +1 -0
- package/dist/src/install/agent-manifest.d.ts +58 -0
- package/dist/src/install/agent-manifest.d.ts.map +1 -0
- package/dist/src/install/agent-manifest.js +103 -0
- package/dist/src/install/agent-manifest.js.map +1 -0
- package/dist/src/install/agent-pack-installer.d.ts +39 -0
- package/dist/src/install/agent-pack-installer.d.ts.map +1 -0
- package/dist/src/install/agent-pack-installer.entry.d.ts +55 -0
- package/dist/src/install/agent-pack-installer.entry.d.ts.map +1 -0
- package/dist/src/install/agent-pack-installer.entry.js +90 -0
- package/dist/src/install/agent-pack-installer.entry.js.map +1 -0
- package/dist/src/install/agent-pack-installer.fs-helpers.d.ts +33 -0
- package/dist/src/install/agent-pack-installer.fs-helpers.d.ts.map +1 -0
- package/dist/src/install/agent-pack-installer.fs-helpers.js +59 -0
- package/dist/src/install/agent-pack-installer.fs-helpers.js.map +1 -0
- package/dist/src/install/agent-pack-installer.harness.d.ts +66 -0
- package/dist/src/install/agent-pack-installer.harness.d.ts.map +1 -0
- package/dist/src/install/agent-pack-installer.harness.js +310 -0
- package/dist/src/install/agent-pack-installer.harness.js.map +1 -0
- package/dist/src/install/agent-pack-installer.js +221 -0
- package/dist/src/install/agent-pack-installer.js.map +1 -0
- package/dist/src/install/agent-pack-installer.preserve.test.d.ts +11 -0
- package/dist/src/install/agent-pack-installer.preserve.test.d.ts.map +1 -0
- package/dist/src/install/agent-pack-installer.preserve.test.js +72 -0
- package/dist/src/install/agent-pack-installer.preserve.test.js.map +1 -0
- package/dist/src/install/agent-pack-installer.test.d.ts +2 -0
- package/dist/src/install/agent-pack-installer.test.d.ts.map +1 -0
- package/dist/src/install/agent-pack-installer.test.js +350 -0
- package/dist/src/install/agent-pack-installer.test.js.map +1 -0
- package/dist/src/install/agent-pack-installer.types.d.ts +51 -0
- package/dist/src/install/agent-pack-installer.types.d.ts.map +1 -0
- package/dist/src/install/agent-pack-installer.types.js +16 -0
- package/dist/src/install/agent-pack-installer.types.js.map +1 -0
- package/dist/src/install/agent-pack-uninstaller.d.ts +43 -0
- package/dist/src/install/agent-pack-uninstaller.d.ts.map +1 -0
- package/dist/src/install/agent-pack-uninstaller.js +113 -0
- package/dist/src/install/agent-pack-uninstaller.js.map +1 -0
- package/dist/src/install/agent-pack-uninstaller.test.d.ts +2 -0
- package/dist/src/install/agent-pack-uninstaller.test.d.ts.map +1 -0
- package/dist/src/install/agent-pack-uninstaller.test.js +212 -0
- package/dist/src/install/agent-pack-uninstaller.test.js.map +1 -0
- package/dist/src/install/index.d.ts +8 -1
- package/dist/src/install/index.d.ts.map +1 -1
- package/dist/src/install/index.js +8 -1
- package/dist/src/install/index.js.map +1 -1
- package/dist/src/install/paths.d.ts +17 -1
- package/dist/src/install/paths.d.ts.map +1 -1
- package/dist/src/install/paths.js +26 -0
- package/dist/src/install/paths.js.map +1 -1
- package/dist/src/install/paths.test.d.ts +2 -0
- package/dist/src/install/paths.test.d.ts.map +1 -0
- package/dist/src/install/paths.test.js +76 -0
- package/dist/src/install/paths.test.js.map +1 -0
- package/dist/src/journal/hash.d.ts +13 -0
- package/dist/src/journal/hash.d.ts.map +1 -0
- package/dist/src/journal/hash.js +16 -0
- package/dist/src/journal/hash.js.map +1 -0
- package/dist/src/journal/index.d.ts +15 -0
- package/dist/src/journal/index.d.ts.map +1 -0
- package/dist/src/journal/index.js +15 -0
- package/dist/src/journal/index.js.map +1 -0
- package/dist/src/journal/journal.test.d.ts +16 -0
- package/dist/src/journal/journal.test.d.ts.map +1 -0
- package/dist/src/journal/journal.test.js +171 -0
- package/dist/src/journal/journal.test.js.map +1 -0
- package/dist/src/journal/path.d.ts +44 -0
- package/dist/src/journal/path.d.ts.map +1 -0
- package/dist/src/journal/path.js +61 -0
- package/dist/src/journal/path.js.map +1 -0
- package/dist/src/journal/reader.d.ts +38 -0
- package/dist/src/journal/reader.d.ts.map +1 -0
- package/dist/src/journal/reader.js +93 -0
- package/dist/src/journal/reader.js.map +1 -0
- package/dist/src/journal/types.d.ts +87 -0
- package/dist/src/journal/types.d.ts.map +1 -0
- package/dist/src/journal/types.js +21 -0
- package/dist/src/journal/types.js.map +1 -0
- package/dist/src/journal/writer.d.ts +52 -0
- package/dist/src/journal/writer.d.ts.map +1 -0
- package/dist/src/journal/writer.js +125 -0
- package/dist/src/journal/writer.js.map +1 -0
- package/dist/src/paywall-triggers/index.d.ts +13 -0
- package/dist/src/paywall-triggers/index.d.ts.map +1 -0
- package/dist/src/paywall-triggers/index.js +13 -0
- package/dist/src/paywall-triggers/index.js.map +1 -0
- package/dist/src/paywall-triggers/path.d.ts +47 -0
- package/dist/src/paywall-triggers/path.d.ts.map +1 -0
- package/dist/src/paywall-triggers/path.js +73 -0
- package/dist/src/paywall-triggers/path.js.map +1 -0
- package/dist/src/paywall-triggers/store.d.ts +75 -0
- package/dist/src/paywall-triggers/store.d.ts.map +1 -0
- package/dist/src/paywall-triggers/store.js +122 -0
- package/dist/src/paywall-triggers/store.js.map +1 -0
- package/dist/src/paywall-triggers/store.test.d.ts +2 -0
- package/dist/src/paywall-triggers/store.test.d.ts.map +1 -0
- package/dist/src/paywall-triggers/store.test.js +121 -0
- package/dist/src/paywall-triggers/store.test.js.map +1 -0
- package/dist/src/paywall-triggers/types.d.ts +24 -0
- package/dist/src/paywall-triggers/types.d.ts.map +1 -0
- package/dist/src/paywall-triggers/types.js +13 -0
- package/dist/src/paywall-triggers/types.js.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.compound.d.ts +13 -0
- package/dist/src/security/scanner/SecurityScanner.compound.d.ts.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.compound.js +127 -0
- package/dist/src/security/scanner/SecurityScanner.compound.js.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.js +10 -1
- package/dist/src/security/scanner/SecurityScanner.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.pii.d.ts +23 -0
- package/dist/src/security/scanner/SecurityScanner.pii.d.ts.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.pii.js +152 -0
- package/dist/src/security/scanner/SecurityScanner.pii.js.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.scanners.d.ts +5 -2
- package/dist/src/security/scanner/SecurityScanner.scanners.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.scanners.js +63 -70
- package/dist/src/security/scanner/SecurityScanner.scanners.js.map +1 -1
- package/dist/src/security/scanner/patterns.d.ts +2 -0
- package/dist/src/security/scanner/patterns.d.ts.map +1 -1
- package/dist/src/security/scanner/patterns.js +79 -11
- package/dist/src/security/scanner/patterns.js.map +1 -1
- package/dist/src/security/scanner/types.d.ts +2 -0
- package/dist/src/security/scanner/types.d.ts.map +1 -1
- package/dist/src/services/agent-pack/agent-pack.test.d.ts +18 -0
- package/dist/src/services/agent-pack/agent-pack.test.d.ts.map +1 -0
- package/dist/src/services/agent-pack/agent-pack.test.js +344 -0
- package/dist/src/services/agent-pack/agent-pack.test.js.map +1 -0
- package/dist/src/services/agent-pack/hooks.d.ts +29 -0
- package/dist/src/services/agent-pack/hooks.d.ts.map +1 -0
- package/dist/src/services/agent-pack/hooks.js +139 -0
- package/dist/src/services/agent-pack/hooks.js.map +1 -0
- package/dist/src/services/agent-pack/index.d.ts +26 -0
- package/dist/src/services/agent-pack/index.d.ts.map +1 -0
- package/dist/src/services/agent-pack/index.js +109 -0
- package/dist/src/services/agent-pack/index.js.map +1 -0
- package/dist/src/services/agent-pack/prompt-source.d.ts +47 -0
- package/dist/src/services/agent-pack/prompt-source.d.ts.map +1 -0
- package/dist/src/services/agent-pack/prompt-source.js +182 -0
- package/dist/src/services/agent-pack/prompt-source.js.map +1 -0
- package/dist/src/services/agent-pack/shims.d.ts +40 -0
- package/dist/src/services/agent-pack/shims.d.ts.map +1 -0
- package/dist/src/services/agent-pack/shims.js +96 -0
- package/dist/src/services/agent-pack/shims.js.map +1 -0
- package/dist/src/services/agent-pack/skill-md.d.ts +35 -0
- package/dist/src/services/agent-pack/skill-md.d.ts.map +1 -0
- package/dist/src/services/agent-pack/skill-md.js +89 -0
- package/dist/src/services/agent-pack/skill-md.js.map +1 -0
- package/dist/src/services/agent-pack/types.d.ts +118 -0
- package/dist/src/services/agent-pack/types.d.ts.map +1 -0
- package/dist/src/services/agent-pack/types.js +61 -0
- package/dist/src/services/agent-pack/types.js.map +1 -0
- package/dist/src/services/agent-tool-profile.d.ts +59 -0
- package/dist/src/services/agent-tool-profile.d.ts.map +1 -0
- package/dist/src/services/agent-tool-profile.js +76 -0
- package/dist/src/services/agent-tool-profile.js.map +1 -0
- package/dist/src/services/agent-tool-profile.test.d.ts +2 -0
- package/dist/src/services/agent-tool-profile.test.d.ts.map +1 -0
- package/dist/src/services/agent-tool-profile.test.js +28 -0
- package/dist/src/services/agent-tool-profile.test.js.map +1 -0
- package/dist/src/services/bundled-sibling-scan.d.ts +111 -0
- package/dist/src/services/bundled-sibling-scan.d.ts.map +1 -0
- package/dist/src/services/bundled-sibling-scan.js +170 -0
- package/dist/src/services/bundled-sibling-scan.js.map +1 -0
- package/dist/src/services/skill-installation.io.d.ts +16 -5
- package/dist/src/services/skill-installation.io.d.ts.map +1 -1
- package/dist/src/services/skill-installation.io.js +44 -20
- package/dist/src/services/skill-installation.io.js.map +1 -1
- package/dist/src/services/skill-installation.policy.d.ts +96 -0
- package/dist/src/services/skill-installation.policy.d.ts.map +1 -0
- package/dist/src/services/skill-installation.policy.js +144 -0
- package/dist/src/services/skill-installation.policy.js.map +1 -0
- package/dist/src/services/skill-installation.service.d.ts.map +1 -1
- package/dist/src/services/skill-installation.service.js +9 -3
- package/dist/src/services/skill-installation.service.js.map +1 -1
- package/dist/src/sources/index.d.ts +1 -0
- package/dist/src/sources/index.d.ts.map +1 -1
- package/dist/src/sources/index.js +4 -0
- package/dist/src/sources/index.js.map +1 -1
- package/dist/src/sync/index.d.ts +6 -0
- package/dist/src/sync/index.d.ts.map +1 -1
- package/dist/src/sync/index.js +8 -0
- package/dist/src/sync/index.js.map +1 -1
- package/dist/src/sync/inventory-builder.d.ts +21 -0
- package/dist/src/sync/inventory-builder.d.ts.map +1 -0
- package/dist/src/sync/inventory-builder.js +26 -0
- package/dist/src/sync/inventory-builder.js.map +1 -0
- package/dist/src/sync/inventory-client.d.ts +80 -0
- package/dist/src/sync/inventory-client.d.ts.map +1 -0
- package/dist/src/sync/inventory-client.js +204 -0
- package/dist/src/sync/inventory-client.js.map +1 -0
- package/dist/src/sync/inventory-client.test.d.ts +14 -0
- package/dist/src/sync/inventory-client.test.d.ts.map +1 -0
- package/dist/src/sync/inventory-client.test.js +212 -0
- package/dist/src/sync/inventory-client.test.js.map +1 -0
- package/dist/src/sync/inventory-collector.d.ts +35 -0
- package/dist/src/sync/inventory-collector.d.ts.map +1 -0
- package/dist/src/sync/inventory-collector.js +176 -0
- package/dist/src/sync/inventory-collector.js.map +1 -0
- package/dist/src/sync/inventory-collector.test.d.ts +14 -0
- package/dist/src/sync/inventory-collector.test.d.ts.map +1 -0
- package/dist/src/sync/inventory-collector.test.js +156 -0
- package/dist/src/sync/inventory-collector.test.js.map +1 -0
- package/dist/src/sync/inventory-device.d.ts +36 -0
- package/dist/src/sync/inventory-device.d.ts.map +1 -0
- package/dist/src/sync/inventory-device.js +58 -0
- package/dist/src/sync/inventory-device.js.map +1 -0
- package/dist/src/sync/inventory-push.d.ts +45 -0
- package/dist/src/sync/inventory-push.d.ts.map +1 -0
- package/dist/src/sync/inventory-push.js +62 -0
- package/dist/src/sync/inventory-push.js.map +1 -0
- package/dist/src/sync/inventory-push.test.d.ts +16 -0
- package/dist/src/sync/inventory-push.test.d.ts.map +1 -0
- package/dist/src/sync/inventory-push.test.js +104 -0
- package/dist/src/sync/inventory-push.test.js.map +1 -0
- package/dist/src/sync/inventory-types.d.ts +110 -0
- package/dist/src/sync/inventory-types.d.ts.map +1 -0
- package/dist/src/sync/inventory-types.js +45 -0
- package/dist/src/sync/inventory-types.js.map +1 -0
- package/dist/src/telemetry/agent-marker.d.ts +137 -0
- package/dist/src/telemetry/agent-marker.d.ts.map +1 -0
- package/dist/src/telemetry/agent-marker.js +242 -0
- package/dist/src/telemetry/agent-marker.js.map +1 -0
- package/dist/src/telemetry/agent-marker.test.d.ts +13 -0
- package/dist/src/telemetry/agent-marker.test.d.ts.map +1 -0
- package/dist/src/telemetry/agent-marker.test.js +202 -0
- package/dist/src/telemetry/agent-marker.test.js.map +1 -0
- package/dist/src/telemetry/index.d.ts +2 -1
- package/dist/src/telemetry/index.d.ts.map +1 -1
- package/dist/src/telemetry/index.js +6 -1
- package/dist/src/telemetry/index.js.map +1 -1
- package/dist/src/telemetry/posthog.d.ts +10 -0
- package/dist/src/telemetry/posthog.d.ts.map +1 -1
- package/dist/src/telemetry/posthog.js +5 -1
- package/dist/src/telemetry/posthog.js.map +1 -1
- package/dist/src/telemetry/wrap.d.ts +28 -7
- package/dist/src/telemetry/wrap.d.ts.map +1 -1
- package/dist/src/telemetry/wrap.gate.test.d.ts +22 -0
- package/dist/src/telemetry/wrap.gate.test.d.ts.map +1 -0
- package/dist/src/telemetry/wrap.gate.test.js +197 -0
- package/dist/src/telemetry/wrap.gate.test.js.map +1 -0
- package/dist/src/telemetry/wrap.js +111 -26
- package/dist/src/telemetry/wrap.js.map +1 -1
- package/dist/src/telemetry/wrap.marker.test.d.ts +16 -0
- package/dist/src/telemetry/wrap.marker.test.d.ts.map +1 -0
- package/dist/src/telemetry/wrap.marker.test.js +185 -0
- package/dist/src/telemetry/wrap.marker.test.js.map +1 -0
- package/dist/src/telemetry/wrap.test.js +4 -0
- package/dist/src/telemetry/wrap.test.js.map +1 -1
- package/dist/tests/SecurityScanner.exec.test.js +60 -0
- package/dist/tests/SecurityScanner.exec.test.js.map +1 -1
- package/dist/tests/security/ContinuousSecurity.test.js +10 -4
- package/dist/tests/security/ContinuousSecurity.test.js.map +1 -1
- package/dist/tests/security/SecurityScanner.chmod-evasion.test.d.ts +2 -0
- package/dist/tests/security/SecurityScanner.chmod-evasion.test.d.ts.map +1 -0
- package/dist/tests/security/SecurityScanner.chmod-evasion.test.js +126 -0
- package/dist/tests/security/SecurityScanner.chmod-evasion.test.js.map +1 -0
- package/dist/tests/security/chmod-compound.test.d.ts +2 -0
- package/dist/tests/security/chmod-compound.test.d.ts.map +1 -0
- package/dist/tests/security/chmod-compound.test.js +188 -0
- package/dist/tests/security/chmod-compound.test.js.map +1 -0
- package/dist/tests/security/pii-detection.test.js +131 -0
- package/dist/tests/security/pii-detection.test.js.map +1 -1
- package/dist/tests/security/scanner-regression-guard.test.d.ts +23 -7
- package/dist/tests/security/scanner-regression-guard.test.d.ts.map +1 -1
- package/dist/tests/security/scanner-regression-guard.test.js +27 -11
- package/dist/tests/security/scanner-regression-guard.test.js.map +1 -1
- package/dist/tests/security/sensitive-path-fp.test.d.ts +2 -0
- package/dist/tests/security/sensitive-path-fp.test.d.ts.map +1 -0
- package/dist/tests/security/sensitive-path-fp.test.js +142 -0
- package/dist/tests/security/sensitive-path-fp.test.js.map +1 -0
- package/dist/tests/services/bundled-sibling-scan.test.d.ts +2 -0
- package/dist/tests/services/bundled-sibling-scan.test.d.ts.map +1 -0
- package/dist/tests/services/bundled-sibling-scan.test.js +139 -0
- package/dist/tests/services/bundled-sibling-scan.test.js.map +1 -0
- package/dist/tests/unit/services/skill-installation.io.test.js +166 -0
- package/dist/tests/unit/services/skill-installation.io.test.js.map +1 -1
- package/dist/tests/unit/services/skill-installation.policy.test.d.ts +8 -0
- package/dist/tests/unit/services/skill-installation.policy.test.d.ts.map +1 -0
- package/dist/tests/unit/services/skill-installation.policy.test.js +151 -0
- package/dist/tests/unit/services/skill-installation.policy.test.js.map +1 -0
- package/package.json +23 -3
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Bundled-sibling security scan for the local rescan path (SMI-5422 Phase 2).
|
|
3
|
+
* @module @skillsmith/core/services/bundled-sibling-scan
|
|
4
|
+
*
|
|
5
|
+
* Walks an installed skill's bundle directory and scans its sibling bundled
|
|
6
|
+
* files (`.mcp.json`, `.claude/settings*.json`, `package.json` lifecycle hooks,
|
|
7
|
+
* `config.json`, `scripts/*.sh` + top-level `*.sh`) so `skill_rescan` can
|
|
8
|
+
* quarantine a skill whose MALICIOUS sibling — not its `SKILL.md` — carries the
|
|
9
|
+
* threat (CVE-2025-59536 hook execution, `curl|bash` postinstall, a
|
|
10
|
+
* remote-fetch-execute install script).
|
|
11
|
+
*
|
|
12
|
+
* This is the local-FS rescan analogue of the Phase-1 install/validate helpers —
|
|
13
|
+
* NOT a duplicate. The three callers intentionally differ:
|
|
14
|
+
* - install: `skill-installation.io.ts` `fetchAndScanOptionalFiles` (fetch-by-path, no glob)
|
|
15
|
+
* - validate: mcp-server `validate-bundled-scan.ts` `scanBundledSiblings` (returns ValidationError[])
|
|
16
|
+
* - rescan (this): directory walk + symlink-safe reads + structured result.
|
|
17
|
+
*
|
|
18
|
+
* DELIBERATE FP-SAFE DIVERGENCE (Phase 2 review B1, verified empirically):
|
|
19
|
+
* install/validate reject via `isRejectableScan` (= `!report.passed` OR a
|
|
20
|
+
* `code_execution`/`obfuscated_directive` finding). The `!report.passed` clause
|
|
21
|
+
* fires on ANY high/critical finding, and sibling files are non-markdown so they
|
|
22
|
+
* get NO documentation-context downgrade — so routine, benign script idioms fire
|
|
23
|
+
* at full severity and would quarantine a working skill:
|
|
24
|
+
* `chmod 755 ./bin/cli` => privilege_escalation:critical => !passed
|
|
25
|
+
* `cp .env.example .env` => sensitive_path:high => !passed
|
|
26
|
+
* `source .env` / `export X=$1` / `cat ~/.ssh/...` => sensitive_path:high
|
|
27
|
+
* For an ALREADY-INSTALLED skill that false positive hides a working skill from
|
|
28
|
+
* local search. So this module drives the quarantine decision SOLELY from
|
|
29
|
+
* `code_execution`/`obfuscated_directive` presence (still catches `curl|bash`).
|
|
30
|
+
* The root cause — privilege_escalation/sensitive_path over-firing in
|
|
31
|
+
* non-markdown execution contexts (which is the SAME latent FP on the install
|
|
32
|
+
* path) — is tracked in SMI-5424; once those patterns are narrowed at the
|
|
33
|
+
* source, all three callers can safely share the broader criterion.
|
|
34
|
+
*
|
|
35
|
+
* INHERITED DETECTION GAPS (FN, tracked SMI-5424): bare-interpreter hook
|
|
36
|
+
* payloads (`node evil.js`, `python evil.py`, `bun`/`deno`), `&&`/`;`-chained
|
|
37
|
+
* fetch-then-exec, `npx`, and JSON `\uXXXX`-escaped commands inside raw-scanned
|
|
38
|
+
* structured files are NOT detected. `.js`/`.py`/`.mjs`/`.ts` payload files and
|
|
39
|
+
* nested/non-`scripts/` `.sh` are a Phase-3 follow-up (recursive bundle walk).
|
|
40
|
+
* COUNT-CAP DECOY-PADDING (FN): the fixed bundled files are cap-exempt, but the
|
|
41
|
+
* `.sh` glob is capped — an author can name the malicious script to sort last
|
|
42
|
+
* and pad `scripts/` with cap-many benign decoys so it lands in `droppedForCount`
|
|
43
|
+
* and is never scanned. This is surfaced (never a silent drop) but not blocked;
|
|
44
|
+
* a cumulative-resource bound is a Phase-3 follow-up.
|
|
45
|
+
*
|
|
46
|
+
* config.json IS scanned here (only doc-class siblings are skipped), whereas the
|
|
47
|
+
* Phase-1 validate helper (validate-bundled-scan.ts) also skips `config` — rescan
|
|
48
|
+
* is the deliberately stricter superset because it is a quarantine path.
|
|
49
|
+
*/
|
|
50
|
+
import { join } from 'path';
|
|
51
|
+
import { safeFs, resolveSafeRealpath } from '../sources/LocalFilesystemAdapter.helpers.js';
|
|
52
|
+
import { BUNDLED_SCAN_FILES, classifyBundledFile, extractPackageJsonLifecycleScripts, } from './skill-installation.policy.js';
|
|
53
|
+
/** Max `.sh` glob files scanned per skill (fixed bundled files are exempt). */
|
|
54
|
+
export const MAX_SIBLING_SH_FILES = 50;
|
|
55
|
+
/** Per-file byte ceiling; larger siblings are skipped (recorded, never silent). */
|
|
56
|
+
export const MAX_SIBLING_FILE_BYTES = 512 * 1024;
|
|
57
|
+
/** A finding is a quarantine driver only if it is a direct execution threat. */
|
|
58
|
+
function isExecutionThreat(finding) {
|
|
59
|
+
return finding.type === 'code_execution' || finding.type === 'obfuscated_directive';
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Collect `*.sh` candidates: top-level then `scripts/`, regular files only
|
|
63
|
+
* (symlinked scripts are not followed by the glob — Phase 3). Sorted so the cap
|
|
64
|
+
* and `droppedForCount` are reproducible rather than dependent on readdir order.
|
|
65
|
+
* NOTE: sorting does NOT defeat decoy-padding — an author can name the malicious
|
|
66
|
+
* file to sort last; that residual FN is documented in the module header and the
|
|
67
|
+
* dropped names are always surfaced in `droppedForCount` (no silent drop).
|
|
68
|
+
*/
|
|
69
|
+
async function collectShFiles(skillDir) {
|
|
70
|
+
const out = [];
|
|
71
|
+
const top = await safeFs.readdir(skillDir);
|
|
72
|
+
if (top.ok) {
|
|
73
|
+
for (const e of top.value) {
|
|
74
|
+
if (e.isFile() && e.name.endsWith('.sh'))
|
|
75
|
+
out.push(e.name);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const scripts = await safeFs.readdir(join(skillDir, 'scripts'));
|
|
79
|
+
if (scripts.ok) {
|
|
80
|
+
for (const e of scripts.value) {
|
|
81
|
+
if (e.isFile() && e.name.endsWith('.sh'))
|
|
82
|
+
out.push(join('scripts', e.name));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return out.sort();
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Scan a skill bundle's sibling files and return the structured result.
|
|
89
|
+
*
|
|
90
|
+
* Fixed {@link BUNDLED_SCAN_FILES} are always scanned (exempt from the count
|
|
91
|
+
* cap, so a decoy-padding attack on `scripts/` cannot push the primary hook /
|
|
92
|
+
* postinstall surface out of the scan window). The `.sh` glob is capped and
|
|
93
|
+
* any overflow is reported in `droppedForCount`.
|
|
94
|
+
*
|
|
95
|
+
* Doc-class siblings (`README.md`, `examples.md`) are intentionally NOT scanned:
|
|
96
|
+
* prose routinely quotes attack strings, so they can never drive a quarantine
|
|
97
|
+
* (Phase-1 H6 control). Every read is symlink-safe via `resolveSafeRealpath`
|
|
98
|
+
* (containment to `skillDir`, SMI-4287) and reads the resolved realpath.
|
|
99
|
+
*
|
|
100
|
+
* @param skillDir absolute path to the installed skill's bundle directory
|
|
101
|
+
* @param scanner a constructed SecurityScanner (injected to keep this module DB-free)
|
|
102
|
+
* @param opts optional caps
|
|
103
|
+
*/
|
|
104
|
+
export async function scanLocalBundleSiblings(skillDir, scanner, opts = {}) {
|
|
105
|
+
const maxShFiles = opts.maxShFiles ?? MAX_SIBLING_SH_FILES;
|
|
106
|
+
const maxBytes = opts.maxBytesPerFile ?? MAX_SIBLING_FILE_BYTES;
|
|
107
|
+
const result = {
|
|
108
|
+
findings: [],
|
|
109
|
+
rejectable: false,
|
|
110
|
+
rejectableFindings: [],
|
|
111
|
+
rejectableFiles: [],
|
|
112
|
+
scannedFiles: [],
|
|
113
|
+
maxSiblingRiskScore: 0,
|
|
114
|
+
droppedForCount: [],
|
|
115
|
+
skippedOversize: [],
|
|
116
|
+
skippedSymlinkEscape: [],
|
|
117
|
+
};
|
|
118
|
+
const shFiles = await collectShFiles(skillDir);
|
|
119
|
+
result.droppedForCount = shFiles.slice(maxShFiles);
|
|
120
|
+
// Fixed files first (cap-exempt), then the capped, sorted .sh glob.
|
|
121
|
+
const candidates = [...BUNDLED_SCAN_FILES, ...shFiles.slice(0, maxShFiles)];
|
|
122
|
+
for (const rel of candidates) {
|
|
123
|
+
const fileClass = classifyBundledFile(rel);
|
|
124
|
+
if (fileClass === 'doc')
|
|
125
|
+
continue; // prose quotes attack strings (H6) — never scanned
|
|
126
|
+
const abs = join(skillDir, rel);
|
|
127
|
+
const resolved = await resolveSafeRealpath(abs, skillDir, {});
|
|
128
|
+
if (!resolved.ok) {
|
|
129
|
+
// not-found = sibling absent (silent skip). symlink-escape = SMI-4287 guard.
|
|
130
|
+
// loop/permission/io = unreadable; skip (the file contributes no signal).
|
|
131
|
+
if (resolved.error.code === 'symlink-escape')
|
|
132
|
+
result.skippedSymlinkEscape.push(rel);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const realPath = resolved.value;
|
|
136
|
+
const st = await safeFs.stat(realPath);
|
|
137
|
+
if (!st.ok)
|
|
138
|
+
continue;
|
|
139
|
+
if (st.value.size > maxBytes) {
|
|
140
|
+
result.skippedOversize.push(rel);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const read = await safeFs.readFile(realPath);
|
|
144
|
+
if (!read.ok)
|
|
145
|
+
continue;
|
|
146
|
+
let textToScan = read.value;
|
|
147
|
+
if (fileClass === 'package-json') {
|
|
148
|
+
const lifecycle = extractPackageJsonLifecycleScripts(read.value);
|
|
149
|
+
if (lifecycle.length === 0)
|
|
150
|
+
continue; // no install-time hooks — nothing risky
|
|
151
|
+
textToScan = lifecycle;
|
|
152
|
+
}
|
|
153
|
+
const report = scanner.scan(`${skillDir}/${rel}`, textToScan);
|
|
154
|
+
result.scannedFiles.push(rel);
|
|
155
|
+
// Fresh objects (no mutation of the report's findings array) tagged with the file.
|
|
156
|
+
const tagged = report.findings.map((f) => ({ ...f, location: rel }));
|
|
157
|
+
result.findings.push(...tagged);
|
|
158
|
+
const drivers = tagged.filter(isExecutionThreat);
|
|
159
|
+
if (drivers.length > 0) {
|
|
160
|
+
result.rejectable = true;
|
|
161
|
+
result.rejectableFindings.push(...drivers);
|
|
162
|
+
result.rejectableFiles.push(rel);
|
|
163
|
+
// Only a REJECTING sibling contributes to the surfaced score, so a benign
|
|
164
|
+
// high-scoring sibling cannot mis-attribute the quarantine's riskScore.
|
|
165
|
+
result.maxSiblingRiskScore = Math.max(result.maxSiblingRiskScore, report.riskScore);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return result;
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=bundled-sibling-scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundled-sibling-scan.js","sourceRoot":"","sources":["../../../src/services/bundled-sibling-scan.ts"],"names":[],"mappings":"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"}
|
|
@@ -33,11 +33,22 @@ export interface OptionalInstallFilesResult {
|
|
|
33
33
|
}>;
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
-
* SMI-5359 Gap-1: fetch + scan the optional install files
|
|
37
|
-
* The caller runs this BEFORE writeInstallFiles, rejects
|
|
38
|
-
* and only then writes `filesToWrite` (so a malicious
|
|
39
|
-
* leave a partially-installed skill on disk).
|
|
40
|
-
*
|
|
36
|
+
* SMI-5359 Gap-1 / SMI-5422 Phase 1: fetch + scan the optional install files
|
|
37
|
+
* WITHOUT writing them. The caller runs this BEFORE writeInstallFiles, rejects
|
|
38
|
+
* on any `failedScans`, and only then writes `filesToWrite` (so a malicious
|
|
39
|
+
* optional file can never leave a partially-installed skill on disk).
|
|
40
|
+
*
|
|
41
|
+
* Per-file policy (see skill-installation.policy.ts for the authoritative spec):
|
|
42
|
+
* doc – scan failure is a silent skip (FP control H6).
|
|
43
|
+
* config – hard-reject on scan failure (pre-existing behaviour).
|
|
44
|
+
* structured – hard-reject on scan failure, all trust tiers.
|
|
45
|
+
* package-json – KEY-LEVEL: only lifecycle-hook script values are scanned.
|
|
46
|
+
* A package.json with no lifecycle hooks is never rejected.
|
|
47
|
+
*
|
|
48
|
+
* A fetch/404 error is always a silent skip (NOT a scan failure).
|
|
49
|
+
*
|
|
50
|
+
* Phase 3 follow-up: directory-glob scanning (e.g. scripts/*.sh) is out of
|
|
51
|
+
* scope here — fetchFromGitHub fetches by exact path only.
|
|
41
52
|
*/
|
|
42
53
|
export declare function fetchAndScanOptionalFiles(owner: string, repo: string, basePath: string, branch: string, skillId: string, scannerOptions: ScannerOptions | null): Promise<OptionalInstallFilesResult>;
|
|
43
54
|
//# sourceMappingURL=skill-installation.io.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill-installation.io.d.ts","sourceRoot":"","sources":["../../../src/services/skill-installation.io.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"skill-installation.io.d.ts","sourceRoot":"","sources":["../../../src/services/skill-installation.io.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAStE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAQ1E;AAED,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAAe,GACtB,OAAO,CAAC,MAAM,CAAC,CAuBjB;AAED,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,OAAO,CAAC,CAkBlB;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,MAAM,EACzB,aAAa,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,EAC3D,eAAe,EAAE,MAAM,GAAG,SAAS,GAClC,OAAO,CAAC,kBAAkB,CAAC,CA0E7B;AAED,MAAM,WAAW,0BAA0B;IACzC,uEAAuE;IACvE,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB;;;OAGG;IACH,WAAW,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE,CAAC,CAAA;IACxD,4EAA4E;IAC5E,YAAY,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC3D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,cAAc,GAAG,IAAI,GACpC,OAAO,CAAC,0BAA0B,CAAC,CAgDrC"}
|
|
@@ -9,6 +9,7 @@ import * as os from 'os';
|
|
|
9
9
|
import { safeWriteFile } from '../utils/safe-fs.js';
|
|
10
10
|
import { SecurityScanner } from '../security/index.js';
|
|
11
11
|
import { validateOptionalConfig } from './skill-installation.validate.js';
|
|
12
|
+
import { BUNDLED_SCAN_FILES, classifyBundledFile, extractPackageJsonLifecycleScripts, isRejectableScan, } from './skill-installation.policy.js';
|
|
12
13
|
export function assertNotEncrypted(content, filePath) {
|
|
13
14
|
if (content.startsWith('\x00GITCRYPT')) {
|
|
14
15
|
throw new Error('File "' +
|
|
@@ -126,41 +127,64 @@ export async function writeInstallFiles(installPath, skillsDir, skillName, final
|
|
|
126
127
|
return { writtenFiles, subagentPath };
|
|
127
128
|
}
|
|
128
129
|
/**
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
130
|
+
* SMI-5359 Gap-1 / SMI-5422 Phase 1: fetch + scan the optional install files
|
|
131
|
+
* WITHOUT writing them. The caller runs this BEFORE writeInstallFiles, rejects
|
|
132
|
+
* on any `failedScans`, and only then writes `filesToWrite` (so a malicious
|
|
133
|
+
* optional file can never leave a partially-installed skill on disk).
|
|
134
|
+
*
|
|
135
|
+
* Per-file policy (see skill-installation.policy.ts for the authoritative spec):
|
|
136
|
+
* doc – scan failure is a silent skip (FP control H6).
|
|
137
|
+
* config – hard-reject on scan failure (pre-existing behaviour).
|
|
138
|
+
* structured – hard-reject on scan failure, all trust tiers.
|
|
139
|
+
* package-json – KEY-LEVEL: only lifecycle-hook script values are scanned.
|
|
140
|
+
* A package.json with no lifecycle hooks is never rejected.
|
|
141
|
+
*
|
|
142
|
+
* A fetch/404 error is always a silent skip (NOT a scan failure).
|
|
143
|
+
*
|
|
144
|
+
* Phase 3 follow-up: directory-glob scanning (e.g. scripts/*.sh) is out of
|
|
145
|
+
* scope here — fetchFromGitHub fetches by exact path only.
|
|
140
146
|
*/
|
|
141
147
|
export async function fetchAndScanOptionalFiles(owner, repo, basePath, branch, skillId, scannerOptions) {
|
|
142
148
|
const optionalFileScanner = scannerOptions ? new SecurityScanner(scannerOptions) : null;
|
|
143
|
-
const optionalFiles = ['README.md', 'examples.md', 'config.json'];
|
|
144
149
|
const configWarnings = [];
|
|
145
150
|
const failedScans = [];
|
|
146
151
|
const filesToWrite = [];
|
|
147
|
-
for (const file of
|
|
152
|
+
for (const file of BUNDLED_SCAN_FILES) {
|
|
148
153
|
let content;
|
|
149
154
|
try {
|
|
150
155
|
content = await fetchFromGitHub(owner, repo, basePath + file, branch);
|
|
151
156
|
}
|
|
152
157
|
catch {
|
|
153
|
-
// Optional file absent / fetch failed —
|
|
158
|
+
// Optional file absent / fetch failed — silent skip (NOT a scan failure).
|
|
154
159
|
continue;
|
|
155
160
|
}
|
|
156
161
|
if (optionalFileScanner) {
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
162
|
+
const fileClass = classifyBundledFile(file);
|
|
163
|
+
// Determine the text to scan. For package-json, only lifecycle hook values;
|
|
164
|
+
// for everything else, the full file content.
|
|
165
|
+
let textToScan;
|
|
166
|
+
if (fileClass === 'package-json') {
|
|
167
|
+
const lifecycle = extractPackageJsonLifecycleScripts(content);
|
|
168
|
+
// Empty string means no install-time hooks — nothing risky to scan.
|
|
169
|
+
textToScan = lifecycle.length > 0 ? lifecycle : null;
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
textToScan = content;
|
|
173
|
+
}
|
|
174
|
+
if (textToScan !== null) {
|
|
175
|
+
const fileScan = optionalFileScanner.scan(skillId + '/' + file, textToScan);
|
|
176
|
+
// Hard-reject classes also reject on a lone code_execution / obfuscated_
|
|
177
|
+
// directive finding (medium severity), which `passed` alone would miss —
|
|
178
|
+
// remote-fetch-execute / concealed directives have no place in a hook
|
|
179
|
+
// or config file (SMI-5422 Phase 1; isRejectableScan is FP-safe).
|
|
180
|
+
if (isRejectableScan(fileScan)) {
|
|
181
|
+
// H6 FP control: prose docs quote attack strings — never hard-reject.
|
|
182
|
+
if (fileClass === 'doc')
|
|
183
|
+
continue;
|
|
184
|
+
// config, structured, and package-json all hard-reject on failure.
|
|
185
|
+
failedScans.push({ file, report: fileScan });
|
|
161
186
|
continue;
|
|
162
|
-
|
|
163
|
-
continue;
|
|
187
|
+
}
|
|
164
188
|
}
|
|
165
189
|
}
|
|
166
190
|
if (file === 'config.json') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill-installation.io.js","sourceRoot":"","sources":["../../../src/services/skill-installation.io.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,aAAa,CAAA;AACjC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAC5B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAA;
|
|
1
|
+
{"version":3,"file":"skill-installation.io.js","sourceRoot":"","sources":["../../../src/services/skill-installation.io.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Scan policy for bundled optional files in the skill install path.
|
|
3
|
+
* @module @skillsmith/core/services/skill-installation.policy
|
|
4
|
+
* @see SMI-5422 Phase 1: widen the bundled-file scan corpus
|
|
5
|
+
*
|
|
6
|
+
* Kept in its own module so io.ts and mcp-server/validate.ts can share the
|
|
7
|
+
* policy without coupling to the full install service. The three exports —
|
|
8
|
+
* BUNDLED_SCAN_FILES, classifyBundledFile, extractPackageJsonLifecycleScripts —
|
|
9
|
+
* are the contract; everything else is implementation detail.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Per-file scan policy classes for bundled optional install files.
|
|
13
|
+
*
|
|
14
|
+
* doc - Routine prose docs that routinely quote attack strings as
|
|
15
|
+
* examples (FP control H6). A scan failure is a SILENT SKIP —
|
|
16
|
+
* never a hard reject.
|
|
17
|
+
* config - Pre-existing skill config (config.json). Hard-reject on scan
|
|
18
|
+
* failure (pre-existing behaviour, no change in Phase 1).
|
|
19
|
+
* structured - Execution-environment files (.mcp.json, .claude/settings*).
|
|
20
|
+
* Hard-reject on scan failure, uniform across ALL trust tiers.
|
|
21
|
+
* package-json - Only lifecycle-hook script VALUES are scanned, not the whole
|
|
22
|
+
* file. Hard-reject if that extracted text fails. A package.json
|
|
23
|
+
* with only test/lint scripts or dep ranges is never rejected.
|
|
24
|
+
*/
|
|
25
|
+
export type BundledFileClass = 'doc' | 'config' | 'structured' | 'package-json';
|
|
26
|
+
/**
|
|
27
|
+
* Widened fixed-name file list for the install corpus (SMI-5422 Phase 1).
|
|
28
|
+
*
|
|
29
|
+
* The install path fetches each file by exact path via fetchFromGitHub — there
|
|
30
|
+
* is NO directory globbing. Files like scripts/*.sh are OUT of scope for Phase 1
|
|
31
|
+
* and are noted as a Phase 3 follow-up (directory-glob scanning).
|
|
32
|
+
*/
|
|
33
|
+
export declare const BUNDLED_SCAN_FILES: readonly ["README.md", "examples.md", "config.json", ".claude/settings.json", ".claude/settings.local.json", ".mcp.json", "package.json"];
|
|
34
|
+
/** Union type of every file name in the bundled scan corpus. */
|
|
35
|
+
export type BundledScanFile = (typeof BUNDLED_SCAN_FILES)[number];
|
|
36
|
+
/**
|
|
37
|
+
* Return the scan-policy class for a bundled optional file.
|
|
38
|
+
*
|
|
39
|
+
* Unknown names fall back to 'structured' (hard-reject) so that a file added
|
|
40
|
+
* to BUNDLED_SCAN_FILES without a corresponding FILE_CLASS_MAP entry defaults
|
|
41
|
+
* to the conservative posture rather than silently skipped.
|
|
42
|
+
*/
|
|
43
|
+
export declare function classifyBundledFile(filename: string): BundledFileClass;
|
|
44
|
+
/**
|
|
45
|
+
* Extract lifecycle-hook script values from raw package.json content.
|
|
46
|
+
*
|
|
47
|
+
* Returns a newline-joined string of all present lifecycle hook values, or ''
|
|
48
|
+
* when the file is malformed JSON or contains no lifecycle keys at all.
|
|
49
|
+
*
|
|
50
|
+
* An empty return means "no install-time execution risk" — the caller should
|
|
51
|
+
* write the file without scanning it. A package.json with only `scripts.test`,
|
|
52
|
+
* `scripts.lint`, dependency version ranges, or metadata fields will always
|
|
53
|
+
* produce '' here and is NEVER rejected.
|
|
54
|
+
*
|
|
55
|
+
* On JSON parse error: returns '' (silent skip). It is not the scanner's job
|
|
56
|
+
* to reject a malformed package.json — the npm toolchain handles that. A
|
|
57
|
+
* corrupt or non-JSON file simply contributes no risky text to scan.
|
|
58
|
+
*/
|
|
59
|
+
export declare function extractPackageJsonLifecycleScripts(content: string): string;
|
|
60
|
+
/**
|
|
61
|
+
* SMI-5422 Phase 1: should a hard-reject-class file (config/structured/package-
|
|
62
|
+
* json) reject given its scan report?
|
|
63
|
+
*
|
|
64
|
+
* Rejects when the scan fails the standard gate (a high/critical finding OR
|
|
65
|
+
* score ≥ threshold) OR contains ANY `code_execution` / `obfuscated_directive`
|
|
66
|
+
* finding regardless of severity. Those two top-tier categories mean "remote
|
|
67
|
+
* fetch-and-execute" / "concealed directive". A lone such match scores only
|
|
68
|
+
* MEDIUM under the scanner's deliberate SKILL.md prose-FP-avoidance model — but
|
|
69
|
+
* inside a config / hook / lifecycle file there is no documentation-context
|
|
70
|
+
* excuse, so it must reject (this is the CVE-2025-59536 hook-execution threat).
|
|
71
|
+
*
|
|
72
|
+
* FP-safe: legit build commands (tsc, vitest, node-gyp rebuild, `rm -rf` of a
|
|
73
|
+
* cache dir) and the canonical `.mcp.json` shape (`{command:"node",args:[…]}`)
|
|
74
|
+
* do NOT match `code_execution` — it requires curl|wget piped to a shell with a
|
|
75
|
+
* real URL/domain target — so normal lifecycle scripts and server specs pass.
|
|
76
|
+
*
|
|
77
|
+
* KNOWN DETECTION GAPS (inherited from the scanner patterns, tracked in SMI-5424
|
|
78
|
+
* — these are scanner-pattern + edge-twin changes, out of scope for Phase 1's
|
|
79
|
+
* install plumbing): `&&`/`;`-chained or redirect-then-exec download+execute
|
|
80
|
+
* (`curl URL -o /tmp/s && bash /tmp/s`), `npx --yes <pkg>`, `fish`/`bun`/`deno`
|
|
81
|
+
* interpreter sinks, and JSON `\uXXXX`-escaped commands inside raw-scanned
|
|
82
|
+
* STRUCTURED files (.mcp.json / .claude/settings.json are scanned as raw text;
|
|
83
|
+
* package.json lifecycle values are JSON-parsed first, so they are escape-safe).
|
|
84
|
+
* Phase 1 catches what the scanner patterns detect across MORE files; it does
|
|
85
|
+
* not change the patterns.
|
|
86
|
+
*
|
|
87
|
+
* Typed structurally (not against ScanReport) to keep this policy module free of
|
|
88
|
+
* the scanner type graph.
|
|
89
|
+
*/
|
|
90
|
+
export declare function isRejectableScan(report: {
|
|
91
|
+
passed: boolean;
|
|
92
|
+
findings: ReadonlyArray<{
|
|
93
|
+
type: string;
|
|
94
|
+
}>;
|
|
95
|
+
}): boolean;
|
|
96
|
+
//# sourceMappingURL=skill-installation.policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-installation.policy.d.ts","sourceRoot":"","sources":["../../../src/services/skill-installation.policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,QAAQ,GAAG,YAAY,GAAG,cAAc,CAAA;AAE/E;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,2IAQrB,CAAA;AAEV,gEAAgE;AAChE,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAA;AAoBjE;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAEtE;AAqBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kCAAkC,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoB1E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,MAAM,EAAE,OAAO,CAAA;IACf,QAAQ,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC1C,GAAG,OAAO,CAKV"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Scan policy for bundled optional files in the skill install path.
|
|
3
|
+
* @module @skillsmith/core/services/skill-installation.policy
|
|
4
|
+
* @see SMI-5422 Phase 1: widen the bundled-file scan corpus
|
|
5
|
+
*
|
|
6
|
+
* Kept in its own module so io.ts and mcp-server/validate.ts can share the
|
|
7
|
+
* policy without coupling to the full install service. The three exports —
|
|
8
|
+
* BUNDLED_SCAN_FILES, classifyBundledFile, extractPackageJsonLifecycleScripts —
|
|
9
|
+
* are the contract; everything else is implementation detail.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Widened fixed-name file list for the install corpus (SMI-5422 Phase 1).
|
|
13
|
+
*
|
|
14
|
+
* The install path fetches each file by exact path via fetchFromGitHub — there
|
|
15
|
+
* is NO directory globbing. Files like scripts/*.sh are OUT of scope for Phase 1
|
|
16
|
+
* and are noted as a Phase 3 follow-up (directory-glob scanning).
|
|
17
|
+
*/
|
|
18
|
+
export const BUNDLED_SCAN_FILES = [
|
|
19
|
+
'README.md',
|
|
20
|
+
'examples.md',
|
|
21
|
+
'config.json',
|
|
22
|
+
'.claude/settings.json',
|
|
23
|
+
'.claude/settings.local.json',
|
|
24
|
+
'.mcp.json',
|
|
25
|
+
'package.json',
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* Filename-to-class map for O(1) lookup.
|
|
29
|
+
*
|
|
30
|
+
* Design note: any name in BUNDLED_SCAN_FILES that lacks an entry here
|
|
31
|
+
* will fall through to the 'structured' default in classifyBundledFile —
|
|
32
|
+
* intentionally conservative so a future addition is hard-reject by default
|
|
33
|
+
* rather than silently skipped.
|
|
34
|
+
*/
|
|
35
|
+
const FILE_CLASS_MAP = {
|
|
36
|
+
'README.md': 'doc',
|
|
37
|
+
'examples.md': 'doc',
|
|
38
|
+
'config.json': 'config',
|
|
39
|
+
'.claude/settings.json': 'structured',
|
|
40
|
+
'.claude/settings.local.json': 'structured',
|
|
41
|
+
'.mcp.json': 'structured',
|
|
42
|
+
'package.json': 'package-json',
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Return the scan-policy class for a bundled optional file.
|
|
46
|
+
*
|
|
47
|
+
* Unknown names fall back to 'structured' (hard-reject) so that a file added
|
|
48
|
+
* to BUNDLED_SCAN_FILES without a corresponding FILE_CLASS_MAP entry defaults
|
|
49
|
+
* to the conservative posture rather than silently skipped.
|
|
50
|
+
*/
|
|
51
|
+
export function classifyBundledFile(filename) {
|
|
52
|
+
return FILE_CLASS_MAP[filename] ?? 'structured';
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* npm lifecycle hook keys that run automatically during `npm install`.
|
|
56
|
+
*
|
|
57
|
+
* We scan ONLY the VALUES of these keys — not the whole package.json —
|
|
58
|
+
* to avoid false positives from dependency names, semver ranges, and HTTP
|
|
59
|
+
* URLs in other script fields (test, lint, format, etc.).
|
|
60
|
+
*/
|
|
61
|
+
const PKG_LIFECYCLE_KEYS = [
|
|
62
|
+
'preinstall',
|
|
63
|
+
'install',
|
|
64
|
+
'postinstall',
|
|
65
|
+
'prepare',
|
|
66
|
+
'prepublish',
|
|
67
|
+
'prepublishOnly',
|
|
68
|
+
'prepack',
|
|
69
|
+
'postpack',
|
|
70
|
+
'postpublish',
|
|
71
|
+
];
|
|
72
|
+
/**
|
|
73
|
+
* Extract lifecycle-hook script values from raw package.json content.
|
|
74
|
+
*
|
|
75
|
+
* Returns a newline-joined string of all present lifecycle hook values, or ''
|
|
76
|
+
* when the file is malformed JSON or contains no lifecycle keys at all.
|
|
77
|
+
*
|
|
78
|
+
* An empty return means "no install-time execution risk" — the caller should
|
|
79
|
+
* write the file without scanning it. A package.json with only `scripts.test`,
|
|
80
|
+
* `scripts.lint`, dependency version ranges, or metadata fields will always
|
|
81
|
+
* produce '' here and is NEVER rejected.
|
|
82
|
+
*
|
|
83
|
+
* On JSON parse error: returns '' (silent skip). It is not the scanner's job
|
|
84
|
+
* to reject a malformed package.json — the npm toolchain handles that. A
|
|
85
|
+
* corrupt or non-JSON file simply contributes no risky text to scan.
|
|
86
|
+
*/
|
|
87
|
+
export function extractPackageJsonLifecycleScripts(content) {
|
|
88
|
+
let parsed;
|
|
89
|
+
try {
|
|
90
|
+
parsed = JSON.parse(content);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return '';
|
|
94
|
+
}
|
|
95
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
96
|
+
return '';
|
|
97
|
+
const scripts = parsed.scripts;
|
|
98
|
+
if (typeof scripts !== 'object' || scripts === null)
|
|
99
|
+
return '';
|
|
100
|
+
const values = [];
|
|
101
|
+
for (const key of PKG_LIFECYCLE_KEYS) {
|
|
102
|
+
const val = scripts[key];
|
|
103
|
+
if (typeof val === 'string' && val.length > 0) {
|
|
104
|
+
values.push(val);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return values.join('\n');
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* SMI-5422 Phase 1: should a hard-reject-class file (config/structured/package-
|
|
111
|
+
* json) reject given its scan report?
|
|
112
|
+
*
|
|
113
|
+
* Rejects when the scan fails the standard gate (a high/critical finding OR
|
|
114
|
+
* score ≥ threshold) OR contains ANY `code_execution` / `obfuscated_directive`
|
|
115
|
+
* finding regardless of severity. Those two top-tier categories mean "remote
|
|
116
|
+
* fetch-and-execute" / "concealed directive". A lone such match scores only
|
|
117
|
+
* MEDIUM under the scanner's deliberate SKILL.md prose-FP-avoidance model — but
|
|
118
|
+
* inside a config / hook / lifecycle file there is no documentation-context
|
|
119
|
+
* excuse, so it must reject (this is the CVE-2025-59536 hook-execution threat).
|
|
120
|
+
*
|
|
121
|
+
* FP-safe: legit build commands (tsc, vitest, node-gyp rebuild, `rm -rf` of a
|
|
122
|
+
* cache dir) and the canonical `.mcp.json` shape (`{command:"node",args:[…]}`)
|
|
123
|
+
* do NOT match `code_execution` — it requires curl|wget piped to a shell with a
|
|
124
|
+
* real URL/domain target — so normal lifecycle scripts and server specs pass.
|
|
125
|
+
*
|
|
126
|
+
* KNOWN DETECTION GAPS (inherited from the scanner patterns, tracked in SMI-5424
|
|
127
|
+
* — these are scanner-pattern + edge-twin changes, out of scope for Phase 1's
|
|
128
|
+
* install plumbing): `&&`/`;`-chained or redirect-then-exec download+execute
|
|
129
|
+
* (`curl URL -o /tmp/s && bash /tmp/s`), `npx --yes <pkg>`, `fish`/`bun`/`deno`
|
|
130
|
+
* interpreter sinks, and JSON `\uXXXX`-escaped commands inside raw-scanned
|
|
131
|
+
* STRUCTURED files (.mcp.json / .claude/settings.json are scanned as raw text;
|
|
132
|
+
* package.json lifecycle values are JSON-parsed first, so they are escape-safe).
|
|
133
|
+
* Phase 1 catches what the scanner patterns detect across MORE files; it does
|
|
134
|
+
* not change the patterns.
|
|
135
|
+
*
|
|
136
|
+
* Typed structurally (not against ScanReport) to keep this policy module free of
|
|
137
|
+
* the scanner type graph.
|
|
138
|
+
*/
|
|
139
|
+
export function isRejectableScan(report) {
|
|
140
|
+
if (!report.passed)
|
|
141
|
+
return true;
|
|
142
|
+
return report.findings.some((f) => f.type === 'code_execution' || f.type === 'obfuscated_directive');
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=skill-installation.policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-installation.policy.js","sourceRoot":"","sources":["../../../src/services/skill-installation.policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAkBH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,WAAW;IACX,aAAa;IACb,aAAa;IACb,uBAAuB;IACvB,6BAA6B;IAC7B,WAAW;IACX,cAAc;CACN,CAAA;AAKV;;;;;;;GAOG;AACH,MAAM,cAAc,GAAqC;IACvD,WAAW,EAAE,KAAK;IAClB,aAAa,EAAE,KAAK;IACpB,aAAa,EAAE,QAAQ;IACvB,uBAAuB,EAAE,YAAY;IACrC,6BAA6B,EAAE,YAAY;IAC3C,WAAW,EAAE,YAAY;IACzB,cAAc,EAAE,cAAc;CAC/B,CAAA;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,OAAO,cAAc,CAAC,QAAQ,CAAC,IAAI,YAAY,CAAA;AACjD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,kBAAkB,GAAG;IACzB,YAAY;IACZ,SAAS;IACT,aAAa;IACb,SAAS;IACT,YAAY;IACZ,gBAAgB;IAChB,SAAS;IACT,UAAU;IACV,aAAa;CACL,CAAA;AAEV;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,kCAAkC,CAAC,OAAe;IAChE,IAAI,MAAe,CAAA;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,EAAE,CAAA;IAE5D,MAAM,OAAO,GAAI,MAAkC,CAAC,OAAO,CAAA;IAC3D,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,EAAE,CAAA;IAE9D,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,KAAK,MAAM,GAAG,IAAI,kBAAkB,EAAE,CAAC;QACrC,MAAM,GAAG,GAAI,OAAmC,CAAC,GAAG,CAAC,CAAA;QACrD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9C,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAClB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAC1B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAGhC;IACC,IAAI,CAAC,MAAM,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IAC/B,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,CACzB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,gBAAgB,IAAI,CAAC,CAAC,IAAI,KAAK,sBAAsB,CACxE,CAAA;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill-installation.service.d.ts","sourceRoot":"","sources":["../../../src/services/skill-installation.service.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAA;AACzE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,8CAA8C,CAAA;AAC7F,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+CAA+C,CAAA;AAC/F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAA;AAC3D,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACvB,MAAM,+BAA+B,CAAA;AA0BtC,MAAM,WAAW,8BAA8B;IAC7C,EAAE,EAAE,QAAQ,CAAA;IACZ,SAAS,EAAE,eAAe,CAAA;IAC1B,mBAAmB,EAAE,yBAAyB,CAAA;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,gBAAgB,CAAA;IAC7B,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC,wBAAwB,CAAC,EAAE,MAAM,EAAE,CAAA;IACnC,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,gBAAgB,GAAG,IAAI,CAAA;IAC/D,eAAe,CAAC,EAAE,0BAA0B,CAAA;IAC5C,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;CACtC;AACD,qBAAa,wBAAwB;IACnC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAU;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiB;IAC3C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA2B;IAC/D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAC1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkB;IAC7C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAgB;IAChD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAmB;IACtD,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAU;IACnD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAA8C;IAChF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAA4B;IAC7D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAmB;gBAC1C,MAAM,EAAE,8BAA8B;IAc5C,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"skill-installation.service.d.ts","sourceRoot":"","sources":["../../../src/services/skill-installation.service.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAA;AACzE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,8CAA8C,CAAA;AAC7F,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+CAA+C,CAAA;AAC/F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAA;AAC3D,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACvB,MAAM,+BAA+B,CAAA;AA0BtC,MAAM,WAAW,8BAA8B;IAC7C,EAAE,EAAE,QAAQ,CAAA;IACZ,SAAS,EAAE,eAAe,CAAA;IAC1B,mBAAmB,EAAE,yBAAyB,CAAA;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,gBAAgB,CAAA;IAC7B,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC,wBAAwB,CAAC,EAAE,MAAM,EAAE,CAAA;IACnC,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,gBAAgB,GAAG,IAAI,CAAA;IAC/D,eAAe,CAAC,EAAE,0BAA0B,CAAA;IAC5C,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;CACtC;AACD,qBAAa,wBAAwB;IACnC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAU;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiB;IAC3C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA2B;IAC/D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAC1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkB;IAC7C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAgB;IAChD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAmB;IACtD,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAU;IACnD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAA8C;IAChF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAA4B;IAC7D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAmB;gBAC1C,MAAM,EAAE,8BAA8B;IAc5C,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,aAAa,CAAC;IAsW9E,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC;CAU7F"}
|
|
@@ -235,6 +235,9 @@ export class SkillInstallationService {
|
|
|
235
235
|
const first = optionalFiles.failedScans[0];
|
|
236
236
|
const crit = first.report.findings.filter((f) => f.severity === 'critical' || f.severity === 'high');
|
|
237
237
|
const others = optionalFiles.failedScans.slice(1).map((s) => s.file);
|
|
238
|
+
// SMI-5422: surface the matched finding type so the author knows what to fix.
|
|
239
|
+
const topFinding = crit[0] ?? first.report.findings[0];
|
|
240
|
+
const matchedLabel = topFinding ? (topFinding.category ?? topFinding.type) : 'unknown';
|
|
238
241
|
return buildInstallFailure('SCAN_REJECTED', {
|
|
239
242
|
skillId,
|
|
240
243
|
installPath,
|
|
@@ -246,11 +249,14 @@ export class SkillInstallationService {
|
|
|
246
249
|
crit.length +
|
|
247
250
|
' critical/high finding(s) (risk score ' +
|
|
248
251
|
first.report.riskScore +
|
|
249
|
-
'
|
|
252
|
+
', matched: ' +
|
|
253
|
+
matchedLabel +
|
|
254
|
+
'). See https://skillsmith.app/docs/security/scanner',
|
|
250
255
|
tips: [
|
|
251
|
-
'Rejected
|
|
256
|
+
'Rejected file: ' + first.file + ' (matched: ' + matchedLabel + ')',
|
|
252
257
|
'Risk score: ' + first.report.riskScore,
|
|
253
|
-
|
|
258
|
+
'Security scanner docs: https://skillsmith.app/docs/security/scanner',
|
|
259
|
+
...(others.length > 0 ? ['Other rejected files: ' + others.join(', ')] : []),
|
|
254
260
|
],
|
|
255
261
|
});
|
|
256
262
|
}
|