@skillsmith/core 0.12.2 → 0.12.4
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 +173 -0
- package/README.md +2 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/config/owned-lock.acquire.d.ts +8 -0
- package/dist/src/config/owned-lock.acquire.d.ts.map +1 -1
- package/dist/src/config/owned-lock.acquire.js +19 -4
- package/dist/src/config/owned-lock.acquire.js.map +1 -1
- package/dist/src/config/owned-lock.claim.d.ts +4 -2
- package/dist/src/config/owned-lock.claim.d.ts.map +1 -1
- package/dist/src/config/owned-lock.claim.js +42 -8
- package/dist/src/config/owned-lock.claim.js.map +1 -1
- package/dist/src/config/owned-lock.d.ts.map +1 -1
- package/dist/src/config/owned-lock.js +1 -0
- package/dist/src/config/owned-lock.js.map +1 -1
- package/dist/src/config/owned-lock.test.js +126 -2
- package/dist/src/config/owned-lock.test.js.map +1 -1
- package/dist/src/config/owned-lock.types.d.ts +9 -0
- package/dist/src/config/owned-lock.types.d.ts.map +1 -1
- package/dist/src/db/createDatabase.d.ts.map +1 -1
- package/dist/src/db/createDatabase.js +0 -2
- package/dist/src/db/createDatabase.js.map +1 -1
- package/dist/src/exports/services.install.d.ts +1 -0
- package/dist/src/exports/services.install.d.ts.map +1 -1
- package/dist/src/exports/services.install.js +5 -0
- package/dist/src/exports/services.install.js.map +1 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/install/fan-out.cleanup.d.ts +37 -0
- package/dist/src/install/fan-out.cleanup.d.ts.map +1 -0
- package/dist/src/install/fan-out.cleanup.js +75 -0
- package/dist/src/install/fan-out.cleanup.js.map +1 -0
- package/dist/src/install/fan-out.d.ts +41 -31
- package/dist/src/install/fan-out.d.ts.map +1 -1
- package/dist/src/install/fan-out.js +213 -112
- package/dist/src/install/fan-out.js.map +1 -1
- package/dist/src/install/fan-out.leftovers.d.ts +60 -0
- package/dist/src/install/fan-out.leftovers.d.ts.map +1 -0
- package/dist/src/install/fan-out.leftovers.js +251 -0
- package/dist/src/install/fan-out.leftovers.js.map +1 -0
- package/dist/src/install/fan-out.locks.d.ts +12 -0
- package/dist/src/install/fan-out.locks.d.ts.map +1 -0
- package/dist/src/install/fan-out.locks.js +114 -0
- package/dist/src/install/fan-out.locks.js.map +1 -0
- package/dist/src/install/fan-out.manifest.d.ts +64 -0
- package/dist/src/install/fan-out.manifest.d.ts.map +1 -0
- package/dist/src/install/fan-out.manifest.js +130 -0
- package/dist/src/install/fan-out.manifest.js.map +1 -0
- package/dist/src/install/fan-out.overwrite.d.ts +51 -0
- package/dist/src/install/fan-out.overwrite.d.ts.map +1 -0
- package/dist/src/install/fan-out.overwrite.js +343 -0
- package/dist/src/install/fan-out.overwrite.js.map +1 -0
- package/dist/src/install/remove-if-same.d.ts +58 -0
- package/dist/src/install/remove-if-same.d.ts.map +1 -0
- package/dist/src/install/remove-if-same.js +222 -0
- package/dist/src/install/remove-if-same.js.map +1 -0
- package/dist/src/provenance/backfill.d.ts.map +1 -1
- package/dist/src/provenance/backfill.js +15 -2
- package/dist/src/provenance/backfill.js.map +1 -1
- package/dist/src/scripts/__tests__/scan-imported-skills.test.js +9 -1
- package/dist/src/scripts/__tests__/scan-imported-skills.test.js.map +1 -1
- package/dist/src/scripts/scan-imported-skills.d.ts +7 -1
- package/dist/src/scripts/scan-imported-skills.d.ts.map +1 -1
- package/dist/src/scripts/scan-imported-skills.js +15 -2
- package/dist/src/scripts/scan-imported-skills.js.map +1 -1
- package/dist/src/scripts/skill-scanner/allowlist.d.ts +10 -0
- package/dist/src/scripts/skill-scanner/allowlist.d.ts.map +1 -1
- package/dist/src/scripts/skill-scanner/allowlist.js +10 -0
- package/dist/src/scripts/skill-scanner/allowlist.js.map +1 -1
- package/dist/src/scripts/skill-scanner/index.d.ts +8 -0
- package/dist/src/scripts/skill-scanner/index.d.ts.map +1 -1
- package/dist/src/scripts/skill-scanner/index.js +13 -5
- package/dist/src/scripts/skill-scanner/index.js.map +1 -1
- package/dist/src/scripts/skill-scanner/trust-scorer.d.ts.map +1 -1
- package/dist/src/scripts/skill-scanner/trust-scorer.js +6 -0
- package/dist/src/scripts/skill-scanner/trust-scorer.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.action-context.d.ts +15 -0
- package/dist/src/security/scanner/SecurityScanner.action-context.d.ts.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.action-context.js +209 -0
- package/dist/src/security/scanner/SecurityScanner.action-context.js.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.d.ts +25 -0
- package/dist/src/security/scanner/SecurityScanner.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.helpers.d.ts +40 -1
- package/dist/src/security/scanner/SecurityScanner.helpers.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.helpers.js +88 -51
- package/dist/src/security/scanner/SecurityScanner.helpers.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.js +39 -6
- package/dist/src/security/scanner/SecurityScanner.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.pii.d.ts +30 -0
- package/dist/src/security/scanner/SecurityScanner.pii.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.pii.js +8 -1
- package/dist/src/security/scanner/SecurityScanner.pii.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.prose-lexicon.d.ts +60 -0
- package/dist/src/security/scanner/SecurityScanner.prose-lexicon.d.ts.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.prose-lexicon.js +228 -0
- package/dist/src/security/scanner/SecurityScanner.prose-lexicon.js.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.scanners.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.scanners.js +42 -5
- package/dist/src/security/scanner/SecurityScanner.scanners.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.value-gate.d.ts +69 -0
- package/dist/src/security/scanner/SecurityScanner.value-gate.d.ts.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.value-gate.js +271 -0
- package/dist/src/security/scanner/SecurityScanner.value-gate.js.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.weak-passwords.d.ts +55 -0
- package/dist/src/security/scanner/SecurityScanner.weak-passwords.d.ts.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.weak-passwords.js +308 -0
- package/dist/src/security/scanner/SecurityScanner.weak-passwords.js.map +1 -0
- package/dist/src/security/scanner/patterns.d.ts +61 -6
- package/dist/src/security/scanner/patterns.d.ts.map +1 -1
- package/dist/src/security/scanner/patterns.js +70 -61
- package/dist/src/security/scanner/patterns.js.map +1 -1
- package/dist/src/security/scanner/patterns.sensitive-path.d.ts +73 -0
- package/dist/src/security/scanner/patterns.sensitive-path.d.ts.map +1 -0
- package/dist/src/security/scanner/patterns.sensitive-path.js +192 -0
- package/dist/src/security/scanner/patterns.sensitive-path.js.map +1 -0
- package/dist/src/security/scanner/regex-utils.d.ts +31 -17
- package/dist/src/security/scanner/regex-utils.d.ts.map +1 -1
- package/dist/src/security/scanner/regex-utils.js +31 -17
- package/dist/src/security/scanner/regex-utils.js.map +1 -1
- package/dist/src/security/scanner/types.d.ts +9 -0
- package/dist/src/security/scanner/types.d.ts.map +1 -1
- package/dist/src/services/agent-pack/prompt-source.d.ts.map +1 -1
- package/dist/src/services/agent-pack/prompt-source.js +3 -2
- package/dist/src/services/agent-pack/prompt-source.js.map +1 -1
- package/dist/src/services/skill-installation.content.d.ts +7 -2
- package/dist/src/services/skill-installation.content.d.ts.map +1 -1
- package/dist/src/services/skill-installation.content.js +75 -28
- package/dist/src/services/skill-installation.content.js.map +1 -1
- package/dist/src/services/skill-installation.helpers.d.ts.map +1 -1
- package/dist/src/services/skill-installation.helpers.js +8 -0
- package/dist/src/services/skill-installation.helpers.js.map +1 -1
- package/dist/src/services/skill-installation.io.d.ts +1 -29
- package/dist/src/services/skill-installation.io.d.ts.map +1 -1
- package/dist/src/services/skill-installation.io.dirs.d.ts +15 -0
- package/dist/src/services/skill-installation.io.dirs.d.ts.map +1 -0
- package/dist/src/services/skill-installation.io.dirs.js +84 -0
- package/dist/src/services/skill-installation.io.dirs.js.map +1 -0
- package/dist/src/services/skill-installation.io.js +6 -176
- package/dist/src/services/skill-installation.io.js.map +1 -1
- package/dist/src/services/skill-installation.io.rollback.d.ts +138 -0
- package/dist/src/services/skill-installation.io.rollback.d.ts.map +1 -0
- package/dist/src/services/skill-installation.io.rollback.js +218 -0
- package/dist/src/services/skill-installation.io.rollback.js.map +1 -0
- package/dist/src/services/skill-installation.io.write.d.ts +69 -0
- package/dist/src/services/skill-installation.io.write.d.ts.map +1 -0
- package/dist/src/services/skill-installation.io.write.js +417 -0
- package/dist/src/services/skill-installation.io.write.js.map +1 -0
- package/dist/src/services/skill-installation.service.d.ts.map +1 -1
- package/dist/src/services/skill-installation.service.finalize.d.ts +62 -0
- package/dist/src/services/skill-installation.service.finalize.d.ts.map +1 -0
- package/dist/src/services/skill-installation.service.finalize.js +136 -0
- package/dist/src/services/skill-installation.service.finalize.js.map +1 -0
- package/dist/src/services/skill-installation.service.js +66 -80
- package/dist/src/services/skill-installation.service.js.map +1 -1
- package/dist/src/services/skill-installation.target-guard.d.ts +82 -0
- package/dist/src/services/skill-installation.target-guard.d.ts.map +1 -0
- package/dist/src/services/skill-installation.target-guard.js +410 -0
- package/dist/src/services/skill-installation.target-guard.js.map +1 -0
- package/dist/src/services/skill-installation.types.d.ts +13 -1
- package/dist/src/services/skill-installation.types.d.ts.map +1 -1
- package/dist/src/services/skill-installation.types.js.map +1 -1
- package/dist/src/services/skill-installation.uninstall.d.ts +3 -1
- package/dist/src/services/skill-installation.uninstall.d.ts.map +1 -1
- package/dist/src/services/skill-installation.uninstall.helpers.d.ts +59 -0
- package/dist/src/services/skill-installation.uninstall.helpers.d.ts.map +1 -0
- package/dist/src/services/skill-installation.uninstall.helpers.js +124 -0
- package/dist/src/services/skill-installation.uninstall.helpers.js.map +1 -0
- package/dist/src/services/skill-installation.uninstall.js +148 -25
- package/dist/src/services/skill-installation.uninstall.js.map +1 -1
- package/dist/src/utils/index.d.ts +1 -0
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/index.js +2 -0
- package/dist/src/utils/index.js.map +1 -1
- package/dist/src/utils/safe-fs.d.ts +69 -0
- package/dist/src/utils/safe-fs.d.ts.map +1 -1
- package/dist/src/utils/safe-fs.js +113 -2
- package/dist/src/utils/safe-fs.js.map +1 -1
- package/dist/src/utils/skill-name.d.ts +19 -0
- package/dist/src/utils/skill-name.d.ts.map +1 -0
- package/dist/src/utils/skill-name.js +26 -0
- package/dist/src/utils/skill-name.js.map +1 -0
- package/dist/src/utils/skill-name.test.d.ts +2 -0
- package/dist/src/utils/skill-name.test.d.ts.map +1 -0
- package/dist/src/utils/skill-name.test.js +34 -0
- package/dist/src/utils/skill-name.test.js.map +1 -0
- package/dist/tests/install/fan-out.test.js +1951 -13
- package/dist/tests/install/fan-out.test.js.map +1 -1
- package/dist/tests/install/remove-if-same.test.d.ts +2 -0
- package/dist/tests/install/remove-if-same.test.d.ts.map +1 -0
- package/dist/tests/install/remove-if-same.test.js +298 -0
- package/dist/tests/install/remove-if-same.test.js.map +1 -0
- package/dist/tests/integration/fan-out-lock-contention.test.d.ts +11 -0
- package/dist/tests/integration/fan-out-lock-contention.test.d.ts.map +1 -0
- package/dist/tests/integration/fan-out-lock-contention.test.js +53 -0
- package/dist/tests/integration/fan-out-lock-contention.test.js.map +1 -0
- package/dist/tests/provenance/backfill.test.js +43 -0
- package/dist/tests/provenance/backfill.test.js.map +1 -1
- package/dist/tests/security/ContinuousSecurity.performance.test.js +12 -7
- package/dist/tests/security/ContinuousSecurity.performance.test.js.map +1 -1
- package/dist/tests/security/ContinuousSecurity.test.js +14 -2
- package/dist/tests/security/ContinuousSecurity.test.js.map +1 -1
- package/dist/tests/security/ai-defence-multiline-promotion.test.d.ts +10 -12
- package/dist/tests/security/ai-defence-multiline-promotion.test.d.ts.map +1 -1
- package/dist/tests/security/ai-defence-multiline-promotion.test.js +26 -33
- package/dist/tests/security/ai-defence-multiline-promotion.test.js.map +1 -1
- package/dist/tests/security/nested-instruction-long-attribute.test.js +20 -10
- package/dist/tests/security/nested-instruction-long-attribute.test.js.map +1 -1
- package/dist/tests/security/scanner-evidence-tiers.test.js +15 -0
- package/dist/tests/security/scanner-evidence-tiers.test.js.map +1 -1
- package/dist/tests/security/scanner-multiline-cap.test.d.ts +21 -0
- package/dist/tests/security/scanner-multiline-cap.test.d.ts.map +1 -0
- package/dist/tests/security/scanner-multiline-cap.test.js +152 -0
- package/dist/tests/security/scanner-multiline-cap.test.js.map +1 -0
- package/dist/tests/security/scanner-regression-guard.test.js +66 -1
- package/dist/tests/security/scanner-regression-guard.test.js.map +1 -1
- package/dist/tests/security/scanner-witness-sw1.test.d.ts +32 -0
- package/dist/tests/security/scanner-witness-sw1.test.d.ts.map +1 -0
- package/dist/tests/security/scanner-witness-sw1.test.js +255 -0
- package/dist/tests/security/scanner-witness-sw1.test.js.map +1 -0
- package/dist/tests/security/sensitive-path-adversarial-review.test.d.ts +2 -0
- package/dist/tests/security/sensitive-path-adversarial-review.test.d.ts.map +1 -0
- package/dist/tests/security/sensitive-path-adversarial-review.test.js +313 -0
- package/dist/tests/security/sensitive-path-adversarial-review.test.js.map +1 -0
- package/dist/tests/security/sensitive-path-boolean-flag.test.d.ts +2 -0
- package/dist/tests/security/sensitive-path-boolean-flag.test.d.ts.map +1 -0
- package/dist/tests/security/sensitive-path-boolean-flag.test.js +151 -0
- package/dist/tests/security/sensitive-path-boolean-flag.test.js.map +1 -0
- package/dist/tests/security/sensitive-path-fp.test.js +314 -0
- package/dist/tests/security/sensitive-path-fp.test.js.map +1 -1
- package/dist/tests/security/sensitive-path-prefixed-secrets.test.d.ts +2 -0
- package/dist/tests/security/sensitive-path-prefixed-secrets.test.d.ts.map +1 -0
- package/dist/tests/security/sensitive-path-prefixed-secrets.test.js +176 -0
- package/dist/tests/security/sensitive-path-prefixed-secrets.test.js.map +1 -0
- package/dist/tests/security/smi5879-corroboration.core.test.js +44 -0
- package/dist/tests/security/smi5879-corroboration.core.test.js.map +1 -1
- package/dist/tests/security/weak-password-lexicon.test.d.ts +18 -0
- package/dist/tests/security/weak-password-lexicon.test.d.ts.map +1 -0
- package/dist/tests/security/weak-password-lexicon.test.js +132 -0
- package/dist/tests/security/weak-password-lexicon.test.js.map +1 -0
- package/dist/tests/skill-scanner/allowlist.test.js +58 -4
- package/dist/tests/skill-scanner/allowlist.test.js.map +1 -1
- package/dist/tests/skill-scanner/categorizer.test.d.ts +2 -0
- package/dist/tests/skill-scanner/categorizer.test.d.ts.map +1 -0
- package/dist/tests/skill-scanner/categorizer.test.js +71 -0
- package/dist/tests/skill-scanner/categorizer.test.js.map +1 -0
- package/dist/tests/skill-scanner/cli-guard.test.d.ts +23 -0
- package/dist/tests/skill-scanner/cli-guard.test.d.ts.map +1 -0
- package/dist/tests/skill-scanner/cli-guard.test.js +71 -0
- package/dist/tests/skill-scanner/cli-guard.test.js.map +1 -0
- package/dist/tests/unit/services/skill-installation.content.test.js +59 -0
- package/dist/tests/unit/services/skill-installation.content.test.js.map +1 -1
- package/dist/tests/unit/services/skill-installation.io.client-agent-path.test.js +35 -14
- package/dist/tests/unit/services/skill-installation.io.client-agent-path.test.js.map +1 -1
- package/dist/tests/unit/services/skill-installation.io.rollback.test.d.ts +2 -0
- package/dist/tests/unit/services/skill-installation.io.rollback.test.d.ts.map +1 -0
- package/dist/tests/unit/services/skill-installation.io.rollback.test.js +984 -0
- package/dist/tests/unit/services/skill-installation.io.rollback.test.js.map +1 -0
- package/dist/tests/unit/services/skill-installation.io.rollback.unit.test.d.ts +2 -0
- package/dist/tests/unit/services/skill-installation.io.rollback.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/services/skill-installation.io.rollback.unit.test.js +148 -0
- package/dist/tests/unit/services/skill-installation.io.rollback.unit.test.js.map +1 -0
- package/dist/tests/unit/services/skill-installation.service.error-codes.test.js +19 -0
- package/dist/tests/unit/services/skill-installation.service.error-codes.test.js.map +1 -1
- package/dist/tests/unit/services/skill-installation.service.finalize.test.d.ts +2 -0
- package/dist/tests/unit/services/skill-installation.service.finalize.test.d.ts.map +1 -0
- package/dist/tests/unit/services/skill-installation.service.finalize.test.js +147 -0
- package/dist/tests/unit/services/skill-installation.service.finalize.test.js.map +1 -0
- package/dist/tests/unit/services/skill-installation.service.sensitive-path-fp.test.d.ts +29 -0
- package/dist/tests/unit/services/skill-installation.service.sensitive-path-fp.test.d.ts.map +1 -0
- package/dist/tests/unit/services/skill-installation.service.sensitive-path-fp.test.js +200 -0
- package/dist/tests/unit/services/skill-installation.service.sensitive-path-fp.test.js.map +1 -0
- package/dist/tests/unit/services/skill-installation.service.test.js +43 -0
- package/dist/tests/unit/services/skill-installation.service.test.js.map +1 -1
- package/dist/tests/unit/services/skill-installation.target-guard.test.d.ts +2 -0
- package/dist/tests/unit/services/skill-installation.target-guard.test.d.ts.map +1 -0
- package/dist/tests/unit/services/skill-installation.target-guard.test.js +590 -0
- package/dist/tests/unit/services/skill-installation.target-guard.test.js.map +1 -0
- package/dist/tests/unit/services/skill-installation.uninstall.guard.test.d.ts +2 -0
- package/dist/tests/unit/services/skill-installation.uninstall.guard.test.d.ts.map +1 -0
- package/dist/tests/unit/services/skill-installation.uninstall.guard.test.js +473 -0
- package/dist/tests/unit/services/skill-installation.uninstall.guard.test.js.map +1 -0
- package/dist/tests/utils/safe-fs.test.js +129 -2
- package/dist/tests/utils/safe-fs.test.js.map +1 -1
- package/package.json +6 -1
|
@@ -54,6 +54,7 @@ import { classifyBundledFile, extractPackageJsonLifecycleScripts, isRejectableSc
|
|
|
54
54
|
import { validateSkillMd } from './skill-installation.validate.js';
|
|
55
55
|
import { buildInstallFailure } from './skill-installation.errors.js';
|
|
56
56
|
import { writeInstallFiles } from './skill-installation.io.js';
|
|
57
|
+
import { checkInstallTarget } from './skill-installation.target-guard.js';
|
|
57
58
|
import { applyOptimization, generateTips, hashContent, manifestKeyFor, sanitizeInstallError, } from './skill-installation.helpers.js';
|
|
58
59
|
/** SMI-5905: content-based installs always scan at this trust tier — no skipScan opt-out. */
|
|
59
60
|
const CONTENT_INSTALL_TRUST_TIER = 'community';
|
|
@@ -78,10 +79,18 @@ function unsafeContentKeyReason(key) {
|
|
|
78
79
|
// absolute paths are covered by path.posix.isAbsolute().
|
|
79
80
|
if (path.posix.isAbsolute(key) || path.win32.isAbsolute(key))
|
|
80
81
|
return 'is an absolute path';
|
|
81
|
-
|
|
82
|
+
// SMI-6529 M9: split on BOTH separators (the backslash check above already
|
|
83
|
+
// rejects any key containing one, but this stays separator-agnostic rather
|
|
84
|
+
// than relying on that ordering) and drop empty ("a//b.md") and "."
|
|
85
|
+
// segments BEFORE inspecting them. The previous version only checked
|
|
86
|
+
// `segments[0]`, missing a ".git" segment at any OTHER position
|
|
87
|
+
// ("sub/.git/config") and missing it entirely behind a leading "."
|
|
88
|
+
// segment ("./.git/config"). Matching case-insensitively closes the
|
|
89
|
+
// "./.Git/HEAD" / ".GIT/config" class a case-sensitive `===` check missed.
|
|
90
|
+
const segments = key.split(/[/\\]/).filter((seg) => seg !== '' && seg !== '.');
|
|
82
91
|
if (segments.some((seg) => seg === '..'))
|
|
83
92
|
return 'contains a ".." path segment';
|
|
84
|
-
if (segments
|
|
93
|
+
if (segments.some((seg) => seg.toLowerCase() === '.git'))
|
|
85
94
|
return 'targets the .git directory';
|
|
86
95
|
return null;
|
|
87
96
|
}
|
|
@@ -94,8 +103,13 @@ function unsafeContentKeyReason(key) {
|
|
|
94
103
|
* - any key that, after `path.resolve()` against `installPath`, resolves
|
|
95
104
|
* outside `installPath` (a lexical backstop behind the checks above,
|
|
96
105
|
* matching `writeInstallFiles()`'s own lexical escape check);
|
|
97
|
-
* - two keys that `path.normalize()` to the same on-disk path
|
|
98
|
-
*
|
|
106
|
+
* - two keys that `path.normalize()` to the same on-disk path CASE
|
|
107
|
+
* -INSENSITIVELY (SMI-6529 M4/M9) — a collision on any filesystem, but
|
|
108
|
+
* specifically catches a case-insensitive alias (e.g. "SKILL.md" and
|
|
109
|
+
* "skill.md" — literally the same inode on default APFS) that a
|
|
110
|
+
* case-sensitive string comparison would miss, in addition to the
|
|
111
|
+
* exact-string collision it already caught (e.g. "SKILL.md" and
|
|
112
|
+
* "./SKILL.md").
|
|
99
113
|
*/
|
|
100
114
|
export function validateContentKeys(content, installPath) {
|
|
101
115
|
const resolvedInstall = path.resolve(installPath);
|
|
@@ -112,16 +126,21 @@ export function validateContentKeys(content, installPath) {
|
|
|
112
126
|
error: `Rejected content key "${key}": resolves outside the install directory.`,
|
|
113
127
|
};
|
|
114
128
|
}
|
|
129
|
+
// SMI-6529 M4/M9: lowercase the normalized path before comparing so a
|
|
130
|
+
// case-insensitive-filesystem alias ("SKILL.md" vs "skill.md") is caught
|
|
131
|
+
// alongside an exact-string duplicate ("SKILL.md" vs "./SKILL.md") —
|
|
132
|
+
// one check, not two.
|
|
115
133
|
const normalized = path.normalize(key);
|
|
116
|
-
const
|
|
134
|
+
const normalizedKey = normalized.toLowerCase();
|
|
135
|
+
const priorKey = seenByNormalizedPath.get(normalizedKey);
|
|
117
136
|
if (priorKey !== undefined) {
|
|
118
137
|
return {
|
|
119
138
|
valid: false,
|
|
120
|
-
error: `Rejected content keys "${priorKey}" and "${key}": both
|
|
121
|
-
'same on-disk path.',
|
|
139
|
+
error: `Rejected content keys "${priorKey}" and "${key}": both resolve to the ` +
|
|
140
|
+
'same on-disk path (case-insensitively).',
|
|
122
141
|
};
|
|
123
142
|
}
|
|
124
|
-
seenByNormalizedPath.set(
|
|
143
|
+
seenByNormalizedPath.set(normalizedKey, key);
|
|
125
144
|
}
|
|
126
145
|
return { valid: true };
|
|
127
146
|
}
|
|
@@ -237,12 +256,23 @@ export async function installFromContent(params) {
|
|
|
237
256
|
onProgress('manifest', 'Checking manifest');
|
|
238
257
|
const manifestData = await manifest.load();
|
|
239
258
|
const manifestKey = manifestKeyFor(skillName, client);
|
|
240
|
-
|
|
241
|
-
|
|
259
|
+
// SMI-6529 Wave A0: same pre-write install-target safety guard install()
|
|
260
|
+
// runs — refuses an untracked pre-existing directory or a git working
|
|
261
|
+
// tree even under force=true. Replaces the bare manifest-membership
|
|
262
|
+
// ALREADY_INSTALLED check this used to be.
|
|
263
|
+
const targetCheck = await checkInstallTarget({
|
|
264
|
+
installPath,
|
|
265
|
+
skillsDir,
|
|
266
|
+
manifestEntry: manifestData.installedSkills[manifestKey],
|
|
267
|
+
force,
|
|
268
|
+
});
|
|
269
|
+
if (!targetCheck.ok) {
|
|
270
|
+
return buildInstallFailure(targetCheck.code, {
|
|
242
271
|
skillId,
|
|
243
272
|
installPath,
|
|
244
273
|
trustTier,
|
|
245
|
-
error:
|
|
274
|
+
error: targetCheck.error,
|
|
275
|
+
...(targetCheck.tips !== undefined && { tips: targetCheck.tips }),
|
|
246
276
|
});
|
|
247
277
|
}
|
|
248
278
|
onProgress('scan', 'Running security scan');
|
|
@@ -283,26 +313,43 @@ export async function installFromContent(params) {
|
|
|
283
313
|
optimizationInfo.subagentPath = writeResult.subagentPath;
|
|
284
314
|
}
|
|
285
315
|
onProgress('manifest', 'Updating manifest');
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
316
|
+
try {
|
|
317
|
+
await manifest.updateSafely((currentManifest) => ({
|
|
318
|
+
...currentManifest,
|
|
319
|
+
installedSkills: {
|
|
320
|
+
...currentManifest.installedSkills,
|
|
321
|
+
[manifestKey]: {
|
|
322
|
+
id: skillId,
|
|
323
|
+
name: skillName,
|
|
324
|
+
version,
|
|
325
|
+
// SMI-5905: provenance for content-sourced installs (never a github: URL).
|
|
326
|
+
source: 'private-registry:' + skillId,
|
|
327
|
+
installPath,
|
|
328
|
+
installedAt: new Date().toISOString(),
|
|
329
|
+
lastUpdated: new Date().toISOString(),
|
|
330
|
+
originalContentHash: hashContent(finalSkillContent),
|
|
331
|
+
client,
|
|
332
|
+
},
|
|
301
333
|
},
|
|
302
|
-
}
|
|
303
|
-
}
|
|
334
|
+
}));
|
|
335
|
+
}
|
|
336
|
+
catch (manifestError) {
|
|
337
|
+
// SMI-6529 M7: the write itself already succeeded — a manifest-update
|
|
338
|
+
// failure here must undo it via the same rollback logic
|
|
339
|
+
// writeInstallFiles runs on its own internal failure, or the skill is
|
|
340
|
+
// left on disk with no manifest entry at all. `rollback()` throws an
|
|
341
|
+
// InstallRestoreError instead if the restore itself also fails, which
|
|
342
|
+
// supersedes `manifestError` below.
|
|
343
|
+
await writeResult.rollback(manifestError);
|
|
344
|
+
throw manifestError;
|
|
345
|
+
}
|
|
304
346
|
onProgress('done', 'Installation complete');
|
|
305
347
|
const tips = generateTips(skillName, optimizationInfo, client, skillsDir);
|
|
348
|
+
// SMI-6529 M10: a companion agent file was left untouched because this
|
|
349
|
+
// was a fresh install and something already occupied the target path.
|
|
350
|
+
if (writeResult.companionSkipped) {
|
|
351
|
+
tips.push('A companion agent file already existed at the target location and was not overwritten (fresh install). Remove it manually if you want Skillsmith to regenerate it.');
|
|
352
|
+
}
|
|
306
353
|
return {
|
|
307
354
|
success: true,
|
|
308
355
|
skillId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill-installation.content.js","sourceRoot":"","sources":["../../../src/services/skill-installation.content.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAEH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAS5B,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAA;AAE1E,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,OAAO,EACL,mBAAmB,EACnB,kCAAkC,EAClC,gBAAgB,GACjB,MAAM,gCAAgC,CAAA;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAA;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAA;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAC9D,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,cAAc,EACd,oBAAoB,GACrB,MAAM,iCAAiC,CAAA;AAExC,6FAA6F;AAC7F,MAAM,0BAA0B,GAAc,WAAW,CAAA;AAEzD,mEAAmE;AACnE,MAAM,QAAQ,GAAG,MAAM,CAAA;AAEvB;;;;;;GAMG;AACH,SAAS,sBAAsB,CAAC,GAAW;IACzC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,GAAG,KAAK,GAAG;QAAE,OAAO,6BAA6B,CAAA;IACzE,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,qBAAqB,CAAA;IACxD,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,sBAAsB,CAAA;IACrD,sEAAsE;IACtE,2EAA2E;IAC3E,yDAAyD;IACzD,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,qBAAqB,CAAA;IAC1F,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"skill-installation.content.js","sourceRoot":"","sources":["../../../src/services/skill-installation.content.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill-installation.helpers.d.ts","sourceRoot":"","sources":["../../../src/services/skill-installation.helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,8CAA8C,CAAA;AAI7F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAA;AAE3D,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+CAA+C,CAAA;AAC/F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAErF,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAC5D,OAAO,EAA2C,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"skill-installation.helpers.d.ts","sourceRoot":"","sources":["../../../src/services/skill-installation.helpers.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,6 +15,7 @@ import { TransformationService } from '../services/TransformationService.js';
|
|
|
15
15
|
import { computeQualityScore } from '../scoring/quality-score.js';
|
|
16
16
|
export { fetchFromGitHub } from './skill-installation.io.js';
|
|
17
17
|
import { CANONICAL_CLIENT, CLIENT_DISPLAY_LABELS } from '../install/paths.js';
|
|
18
|
+
import { InstallRestoreError } from './skill-installation.io.rollback.js';
|
|
18
19
|
export function hashContent(content) {
|
|
19
20
|
return createHash('sha256').update(content).digest('hex');
|
|
20
21
|
}
|
|
@@ -234,6 +235,13 @@ const KNOWN_ERROR_PREFIXES = [
|
|
|
234
235
|
'uses directory-package mode',
|
|
235
236
|
];
|
|
236
237
|
export function sanitizeInstallError(error) {
|
|
238
|
+
// SMI-6529 H2: a rollback-restore failure is exactly the case where the
|
|
239
|
+
// generic fallback below is actively harmful — it names which pre-existing
|
|
240
|
+
// files could NOT be restored, which the user needs to recover manually.
|
|
241
|
+
// Bypasses the allowlist entirely (never gated on message content).
|
|
242
|
+
if (error instanceof InstallRestoreError) {
|
|
243
|
+
return error.message;
|
|
244
|
+
}
|
|
237
245
|
if (error instanceof Error) {
|
|
238
246
|
if (KNOWN_ERROR_PREFIXES.some((p) => error.message.includes(p))) {
|
|
239
247
|
return error.message;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill-installation.helpers.js","sourceRoot":"","sources":["../../../src/services/skill-installation.helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AAC7C,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAEnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAA;AAC3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AAKnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAA;AAE5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAKjE,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAiB,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"skill-installation.helpers.js","sourceRoot":"","sources":["../../../src/services/skill-installation.helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AAC7C,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAEnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAA;AAC3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AAKnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAA;AAE5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAKjE,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAiB,MAAM,qBAAqB,CAAA;AAC5F,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAA;AAWzE,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC3D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,SAAiB,EACjB,gBAAkC,EAClC,SAAmB,gBAAgB,EACnC,SAAkB;IAElB,MAAM,WAAW,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAA;IACjD,MAAM,GAAG,GAAG,SAAS,IAAI,kBAAkB,CAAA;IAC3C,MAAM,IAAI,GAAG;QACX,SAAS,GAAG,SAAS,GAAG,2BAA2B;QACnD,mCAAmC,GAAG,WAAW,GAAG,aAAa,GAAG,SAAS,GAAG,eAAe;QAC/F,4BAA4B,GAAG,GAAG,GAAG,GAAG;KACzC,CAAA;IAED,IAAI,gBAAgB,CAAC,SAAS,EAAE,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,iDAAiD,CAAC,CAAA;QAChE,IAAI,gBAAgB,CAAC,qBAAqB,IAAI,gBAAgB,CAAC,qBAAqB,GAAG,CAAC,EAAE,CAAC;YACzF,IAAI,CAAC,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC,qBAAqB,GAAG,mBAAmB,CAAC,CAAA;QAC5F,CAAC;QACD,IAAI,gBAAgB,CAAC,SAAS,IAAI,gBAAgB,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxE,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,gBAAgB,CAAC,SAAS,CAAC,MAAM,GAAG,qBAAqB,CAAC,CAAA;QAC/E,CAAC;QACD,IAAI,gBAAgB,CAAC,iBAAiB,IAAI,gBAAgB,CAAC,YAAY,EAAE,CAAC;YACxE,IAAI,CAAC,IAAI,CAAC,oCAAoC,GAAG,gBAAgB,CAAC,YAAY,CAAC,CAAA;QACjF,CAAC;IACH,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,4CAA4C,CAAC,CAAA;IAC3D,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,uBAAuB,CAAC,cAAsB,OAAO,CAAC,GAAG,EAAE;IACzE,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAA;IACvD,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,SAAS,CAAA;IAE9C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;QAC9C,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACvC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAA;QAE3D,MAAM,UAAU,GAAI,MAAmC,CAAC,UAAU,CAAA;QAClE,IAAI,CAAC,UAAU,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YAC/E,OAAO,SAAS,CAAA;QAClB,CAAC;QAED,OAAO,MAAM,CAAC,IAAI,CAAC,UAAqC,CAAC,CAAA;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,cAAsB;IACpD,MAAM,SAAS,GAAG,oBAAoB,CAAC,cAAc,EAAE,uBAAuB,EAAE,CAAC,CAAA;IACjF,MAAM,QAAQ,GAAa,EAAE,CAAA;IAC7B,KAAK,MAAM,MAAM,IAAI,SAAS,CAAC,qBAAqB,EAAE,CAAC;QACrD,oEAAoE;QACpE,gEAAgE;QAChE,IAAI,SAAS,CAAC,iBAAiB,EAAE,CAAC,MAAM,CAAC,KAAK,YAAY;YAAE,SAAQ;QACpE,QAAQ,CAAC,IAAI,CAAC,cAAc,GAAG,MAAM,GAAG,2CAA2C,CAAC,CAAA;IACtF,CAAC;IACD,OAAO;QACL,oBAAoB,EAAE,SAAS,CAAC,OAAO;QACvC,YAAY,EAAE,SAAS;QACvB,YAAY,EAAE,QAAQ;KACvB,CAAA;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAA+B,EAC/B,OAAe,EACf,OAAe,EACf,QAAwC;IAExC,MAAM,SAAS,GAAG,oBAAoB,CAAC,OAAO,EAAE,uBAAuB,EAAE,CAAC,CAAA;IAC1E,MAAM,MAAM,GAAG,iBAAiB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAA;IACrD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAA;IAEjC,MAAM,IAAI,GAAyB,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACtD,QAAQ,EAAE,OAAO;QACjB,QAAQ,EAAE,GAAG,CAAC,OAAO;QACrB,UAAU,EAAE,GAAG,CAAC,SAAS;QACzB,WAAW,EAAE,GAAG,CAAC,UAAU;QAC3B,UAAU,EAAE,GAAG,CAAC,SAAS;QACzB,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,QAAQ,EAAE,GAAG,CAAC,QAAQ;KACvB,CAAC,CAAC,CAAA;IAEH,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAgC,CAAA;IACxD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAA;QACnD,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAClB,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;IACxC,CAAC;IAED,KAAK,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,IAAI,QAAQ,EAAE,CAAC;QAC5C,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,UAAU,EAAE,MAA0C,CAAC,CAAA;IACvF,CAAC;IAED,OAAO,IAAI,CAAC,MAAM,CAAA;AACpB,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,MAAgB;IAC3D,OAAO,MAAM,KAAK,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,MAAM,EAAE,CAAA;AAClE,CAAC;AAED,0EAA0E;AAC1E,2EAA2E;AAC3E,mDAAmD;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAA;AAEpE;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,EAAY,EACZ,OAAe,EACf,SAAiB,EACjB,cAAsB,EACtB,SAAmB,gBAAgB;IAEnC,IAAI,CAAC;QACH,MAAM,gBAAgB,GAAG,IAAI,qBAAqB,CAAC,EAAE,EAAE;YACrD,QAAQ,EAAE,IAAI;YACd,OAAO,EAAE,OAAO;SACjB,CAAC,CAAA;QAEF,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAA;QAC3D,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAA;QAClE,MAAM,aAAa,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;QACjE,MAAM,aAAa,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QAE1D,MAAM,eAAe,GAAG,MAAM,gBAAgB,CAAC,SAAS,CACtD,OAAO,EACP,aAAa,EACb,aAAa,EACb,cAAc,EACd,MAAM,CACP,CAAA;QAED,IAAI,eAAe,CAAC,WAAW,EAAE,CAAC;YAChC,OAAO;gBACL,iBAAiB,EAAE,eAAe,CAAC,gBAAgB;gBACnD,aAAa,EAAE,eAAe,CAAC,SAAS;gBACxC,eAAe,EAAE,eAAe,CAAC,QAAQ,EAAE,OAAO;gBAClD,eAAe,EAAE,eAAe,CAAC,eAAe;gBAChD,gBAAgB,EAAE;oBAChB,SAAS,EAAE,IAAI;oBACf,SAAS,EAAE,eAAe,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;oBAC3D,iBAAiB,EAAE,CAAC,CAAC,eAAe,CAAC,QAAQ,EAAE,OAAO;oBACtD,qBAAqB,EAAE,eAAe,CAAC,KAAK,CAAC,qBAAqB;oBAClE,aAAa,EAAE,eAAe,CAAC,KAAK,CAAC,aAAa;oBAClD,cAAc,EAAE,eAAe,CAAC,KAAK,CAAC,cAAc;iBACrD;aACF,CAAA;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,yDAAyD;IAC3D,CAAC;IAED,OAAO;QACL,iBAAiB,EAAE,cAAc;QACjC,aAAa,EAAE,EAAE;QACjB,eAAe,EAAE,SAAS;QAC1B,eAAe,EAAE,SAAS;QAC1B,gBAAgB,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;KACvC,CAAA;AACH,CAAC;AAED,yEAAyE;AACzE,MAAM,oBAAoB,GAAG;IAC3B,mBAAmB;IACnB,yBAAyB;IACzB,6BAA6B;IAC7B,kBAAkB;IAClB,yBAAyB;IACzB,sBAAsB;IACtB,wBAAwB;IACxB,8BAA8B;IAC9B,sCAAsC;IACtC,uCAAuC;IACvC,2BAA2B;IAC3B,qFAAqF;IACrF,sFAAsF;IACtF,6EAA6E;IAC7E,6DAA6D;IAC7D,6BAA6B;CAC9B,CAAA;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,wEAAwE;IACxE,2EAA2E;IAC3E,yEAAyE;IACzE,oEAAoE;IACpE,IAAI,KAAK,YAAY,mBAAmB,EAAE,CAAC;QACzC,OAAO,KAAK,CAAC,OAAO,CAAA;IACtB,CAAC;IACD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,IAAI,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAChE,OAAO,KAAK,CAAC,OAAO,CAAA;QACtB,CAAC;IACH,CAAC;IACD,OAAO,8CAA8C,CAAA;AACvD,CAAC;AACD,iDAAiD;AAEjD,uEAAuE;AACvE,MAAM,UAAU,4BAA4B,CAAC,MAQ5C;IACC,OAAO,mBAAmB,CAAC;QACzB,SAAS,EAAE,MAAM,CAAC,UAAU,EAAE,SAAS,IAAI,IAAI;QAC/C,qBAAqB,EAAE,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,IAAI,CAAC;QAC9D,cAAc,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,IAAI,IAAI;QACjD,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,WAAW,EAAE,MAAM,CAAC,WAAW;KAChC,CAAC,CAAA;AACJ,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,iBAAiB,CAAC,MAMjC;IACC,IAAI,CAAC,MAAM,CAAC,WAAW;QAAE,OAAM;IAC/B,IAAI,CAAC;QACH,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC;YACxB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC,SAAS;YACtC,aAAa,EAAE,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,MAAM;YAChD,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,WAAW,EAAE;YACpD,MAAM,EAAE,MAAM,CAAC,MAAM;SACtB,CAAC,CAAA;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,kEAAkE;IACpE,CAAC;AACH,CAAC"}
|
|
@@ -4,38 +4,10 @@
|
|
|
4
4
|
* @see SMI-4745: domain-driven split to stay under the 500-line CI gate
|
|
5
5
|
*/
|
|
6
6
|
import type { ScannerOptions, ScanReport } from '../security/index.js';
|
|
7
|
-
|
|
7
|
+
export { writeInstallFiles, type WriteInstallResult } from './skill-installation.io.write.js';
|
|
8
8
|
export declare function assertNotEncrypted(content: string, filePath: string): void;
|
|
9
9
|
export declare function fetchFromGitHub(owner: string, repo: string, filePath: string, branch?: string): Promise<string>;
|
|
10
10
|
export declare function checkForModifications(skillPath: string, installedAt: string): Promise<boolean>;
|
|
11
|
-
export interface WriteInstallResult {
|
|
12
|
-
writtenFiles: string[];
|
|
13
|
-
subagentPath?: string;
|
|
14
|
-
}
|
|
15
|
-
export declare function writeInstallFiles(installPath: string, skillsDir: string, skillName: string, finalSkillContent: string, subSkillFiles: Array<{
|
|
16
|
-
filename: string;
|
|
17
|
-
content: string;
|
|
18
|
-
}>, subagentContent: string | undefined,
|
|
19
|
-
/**
|
|
20
|
-
* SMI-5980 (Wave 3): the client this companion subagent (if any) is
|
|
21
|
-
* generated for — resolves its output path via
|
|
22
|
-
* `resolveCompanionAgentPath()` (install/paths.ts) instead of a hardcoded
|
|
23
|
-
* `~/.claude/agents/` literal. Optional, defaulting to `CANONICAL_CLIENT`
|
|
24
|
-
* (`claude-code`) so pre-existing callers/tests that never pass it keep
|
|
25
|
-
* today's exact behavior unchanged.
|
|
26
|
-
*/
|
|
27
|
-
client?: ClientId,
|
|
28
|
-
/**
|
|
29
|
-
* SMI-5982 code-review fix #1: explicit base dir for resolving a RELATIVE
|
|
30
|
-
* `COMPANION_AGENT_TARGETS[client].dir` (Antigravity only — every other
|
|
31
|
-
* client's `dir` is absolute already). Threaded through to
|
|
32
|
-
* `resolveCompanionAgentPath()`'s own `baseDir` param as-is (no
|
|
33
|
-
* `?? process.cwd()` fallback here) — `resolveCompanionAgentPath()` itself
|
|
34
|
-
* now requires an explicit `baseDir` for every `directory-package`-mode
|
|
35
|
-
* client (PR-review follow-up), so whether an omitted `companionBaseDir`
|
|
36
|
-
* is acceptable is that function's call to make, not this one's.
|
|
37
|
-
*/
|
|
38
|
-
companionBaseDir?: string): Promise<WriteInstallResult>;
|
|
39
11
|
export interface OptionalInstallFilesResult {
|
|
40
12
|
/** Validation warnings from config.json (surfaced as install tips). */
|
|
41
13
|
configWarnings: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill-installation.io.d.ts","sourceRoot":"","sources":["../../../src/services/skill-installation.io.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"skill-installation.io.d.ts","sourceRoot":"","sources":["../../../src/services/skill-installation.io.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AActE,OAAO,EAAE,iBAAiB,EAAE,KAAK,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AAE7F,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAQ1E;AAkBD,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAAe,GACtB,OAAO,CAAC,MAAM,CAAC,CAyBjB;AAcD,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,OAAO,CAAC,CAkBlB;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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Symlink-safe intermediate-directory creation for writeInstallFiles.
|
|
3
|
+
* @module @skillsmith/core/services/skill-installation.io.dirs
|
|
4
|
+
* @see SMI-6529 Wave A0 (F4): split out of skill-installation.io.ts to stay
|
|
5
|
+
* under the 500-line CI gate once the F1-F4 rollback fixes landed — pure
|
|
6
|
+
* move, no behavior change.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Create every path segment of `dir` (relative to `baseDir`) via `ensureDirNoFollow()`,
|
|
10
|
+
* so no intermediate segment can be a pre-existing symlink. `dir` must already be lexically
|
|
11
|
+
* inside `baseDir` — callers are expected to have proven that (as `writeInstallFiles()`'s
|
|
12
|
+
* `installPath` already is, via its own lexical + realpath checks) before calling this.
|
|
13
|
+
*/
|
|
14
|
+
export declare function mkdirNoFollow(baseDir: string, dir: string, freshDirs: Set<string>): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=skill-installation.io.dirs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-installation.io.dirs.d.ts","sourceRoot":"","sources":["../../../src/services/skill-installation.io.dirs.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA8DH;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GACrB,OAAO,CAAC,IAAI,CAAC,CASf"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Symlink-safe intermediate-directory creation for writeInstallFiles.
|
|
3
|
+
* @module @skillsmith/core/services/skill-installation.io.dirs
|
|
4
|
+
* @see SMI-6529 Wave A0 (F4): split out of skill-installation.io.ts to stay
|
|
5
|
+
* under the 500-line CI gate once the F1-F4 rollback fixes landed — pure
|
|
6
|
+
* move, no behavior change.
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from 'fs/promises';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
/**
|
|
11
|
+
* Ensure `dirPath` exists as a real directory, never following (or silently accepting) an
|
|
12
|
+
* existing symlink there.
|
|
13
|
+
*
|
|
14
|
+
* Sol final-code-review finding #2 (confirmed): `safeWriteFile()` only `lstat`s the FINAL
|
|
15
|
+
* path component before writing — a symlinked INTERMEDIATE directory (e.g. `<installPath>/
|
|
16
|
+
* scripts` already existing as a symlink from a prior force-reinstall or a planted attack)
|
|
17
|
+
* is never checked, so a nested sub-skill filename like `"scripts/run.sh"` could write
|
|
18
|
+
* through it to an arbitrary target outside `installPath`. `fs.mkdir(dir, {recursive:true})`
|
|
19
|
+
* alone does not close this either — it silently no-ops on an existing path whether or not
|
|
20
|
+
* that path is a symlink.
|
|
21
|
+
*
|
|
22
|
+
* SMI-6529 F4: `freshDirs` records every segment that did NOT exist when this call started
|
|
23
|
+
* checking it — regardless of which concurrent sibling sub-skill write's own `fs.mkdir`
|
|
24
|
+
* ultimately wins the create below (the benign EEXIST race) — so a failed install can roll
|
|
25
|
+
* a nested directory back instead of leaving it orphaned inside a pre-existing `installPath`.
|
|
26
|
+
*/
|
|
27
|
+
async function ensureDirNoFollow(dirPath, freshDirs) {
|
|
28
|
+
const check = async () => {
|
|
29
|
+
let stats;
|
|
30
|
+
try {
|
|
31
|
+
stats = await fs.lstat(dirPath);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
if (err.code === 'ENOENT')
|
|
35
|
+
return 'missing';
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
if (stats.isSymbolicLink()) {
|
|
39
|
+
throw new Error('Refusing to write through existing symlink: ' + dirPath);
|
|
40
|
+
}
|
|
41
|
+
if (!stats.isDirectory()) {
|
|
42
|
+
throw new Error('Expected a directory, found a file: ' + dirPath);
|
|
43
|
+
}
|
|
44
|
+
return 'ok';
|
|
45
|
+
};
|
|
46
|
+
if ((await check()) === 'ok')
|
|
47
|
+
return;
|
|
48
|
+
// This segment did not exist at the point we checked it — record it as a rollback
|
|
49
|
+
// candidate BEFORE attempting to create it (mirrors F1's write-side ordering).
|
|
50
|
+
freshDirs.add(dirPath);
|
|
51
|
+
try {
|
|
52
|
+
await fs.mkdir(dirPath);
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
// EEXIST here is a benign race between our own concurrent sub-skill writes under the
|
|
56
|
+
// same new subdirectory (writeInstallFiles writes them in parallel) — re-check below
|
|
57
|
+
// rather than trust that; it ALSO re-confirms nothing hostile won the race.
|
|
58
|
+
if (err.code !== 'EEXIST')
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
61
|
+
// Re-check post-create: closes both the benign concurrent-mkdir race above and the TOCTOU
|
|
62
|
+
// window between the first check and this mkdir.
|
|
63
|
+
if ((await check()) !== 'ok') {
|
|
64
|
+
throw new Error('Failed to create directory: ' + dirPath);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Create every path segment of `dir` (relative to `baseDir`) via `ensureDirNoFollow()`,
|
|
69
|
+
* so no intermediate segment can be a pre-existing symlink. `dir` must already be lexically
|
|
70
|
+
* inside `baseDir` — callers are expected to have proven that (as `writeInstallFiles()`'s
|
|
71
|
+
* `installPath` already is, via its own lexical + realpath checks) before calling this.
|
|
72
|
+
*/
|
|
73
|
+
export async function mkdirNoFollow(baseDir, dir, freshDirs) {
|
|
74
|
+
const relative = path.relative(baseDir, dir);
|
|
75
|
+
if (relative === '' || relative.startsWith('..'))
|
|
76
|
+
return;
|
|
77
|
+
const segments = relative.split(path.sep);
|
|
78
|
+
let current = baseDir;
|
|
79
|
+
for (const segment of segments) {
|
|
80
|
+
current = path.join(current, segment);
|
|
81
|
+
await ensureDirNoFollow(current, freshDirs);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=skill-installation.io.dirs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-installation.io.dirs.js","sourceRoot":"","sources":["../../../src/services/skill-installation.io.dirs.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,MAAM,aAAa,CAAA;AACjC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAE5B;;;;;;;;;;;;;;;;GAgBG;AACH,KAAK,UAAU,iBAAiB,CAAC,OAAe,EAAE,SAAsB;IACtE,MAAM,KAAK,GAAG,KAAK,IAA+B,EAAE;QAClD,IAAI,KAAK,CAAA;QACT,IAAI,CAAC;YACH,KAAK,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QACjC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;gBAAE,OAAO,SAAS,CAAA;YACtE,MAAM,GAAG,CAAA;QACX,CAAC;QACD,IAAI,KAAK,CAAC,cAAc,EAAE,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,8CAA8C,GAAG,OAAO,CAAC,CAAA;QAC3E,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,sCAAsC,GAAG,OAAO,CAAC,CAAA;QACnE,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC,CAAA;IAED,IAAI,CAAC,MAAM,KAAK,EAAE,CAAC,KAAK,IAAI;QAAE,OAAM;IAEpC,kFAAkF;IAClF,+EAA+E;IAC/E,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAEtB,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACzB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,qFAAqF;QACrF,qFAAqF;QACrF,4EAA4E;QAC5E,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,GAAG,CAAA;IACjE,CAAC;IAED,0FAA0F;IAC1F,iDAAiD;IACjD,IAAI,CAAC,MAAM,KAAK,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,8BAA8B,GAAG,OAAO,CAAC,CAAA;IAC3D,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAAe,EACf,GAAW,EACX,SAAsB;IAEtB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;IAC5C,IAAI,QAAQ,KAAK,EAAE,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAM;IACxD,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACzC,IAAI,OAAO,GAAG,OAAO,CAAA;IACrB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QACrC,MAAM,iBAAiB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;IAC7C,CAAC;AACH,CAAC"}
|
|
@@ -5,11 +5,15 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import * as fs from 'fs/promises';
|
|
7
7
|
import * as path from 'path';
|
|
8
|
-
import { safeWriteFile } from '../utils/safe-fs.js';
|
|
9
8
|
import { SecurityScanner } from '../security/index.js';
|
|
10
|
-
import { CANONICAL_CLIENT, getCompanionAgentTarget, resolveCompanionAgentPath, } from '../install/paths.js';
|
|
11
9
|
import { validateOptionalConfig } from './skill-installation.validate.js';
|
|
12
10
|
import { BUNDLED_SCAN_FILES, classifyBundledFile, extractPackageJsonLifecycleScripts, isRejectableScan, } from './skill-installation.policy.js';
|
|
11
|
+
// SMI-6529 Wave A0 round 2: writeInstallFiles() + WriteInstallResult moved to
|
|
12
|
+
// a sibling module (pure move, no behavior change to it) to keep this file
|
|
13
|
+
// under the 500-line CI gate once the M4/M7/M10/L14/L15 fixes landed —
|
|
14
|
+
// re-exported here so every existing `from './skill-installation.io.js'`
|
|
15
|
+
// import site is unaffected.
|
|
16
|
+
export { writeInstallFiles } from './skill-installation.io.write.js';
|
|
13
17
|
export function assertNotEncrypted(content, filePath) {
|
|
14
18
|
if (content.startsWith('\x00GITCRYPT')) {
|
|
15
19
|
throw new Error('File "' +
|
|
@@ -84,180 +88,6 @@ export async function checkForModifications(skillPath, installedAt) {
|
|
|
84
88
|
return false;
|
|
85
89
|
}
|
|
86
90
|
}
|
|
87
|
-
/**
|
|
88
|
-
* Ensure `dirPath` exists as a real directory, never following (or silently accepting) an
|
|
89
|
-
* existing symlink there.
|
|
90
|
-
*
|
|
91
|
-
* Sol final-code-review finding #2 (confirmed): `safeWriteFile()` only `lstat`s the FINAL
|
|
92
|
-
* path component before writing — a symlinked INTERMEDIATE directory (e.g. `<installPath>/
|
|
93
|
-
* scripts` already existing as a symlink from a prior force-reinstall or a planted attack)
|
|
94
|
-
* is never checked, so a nested sub-skill filename like `"scripts/run.sh"` could write
|
|
95
|
-
* through it to an arbitrary target outside `installPath`. `fs.mkdir(dir, {recursive:true})`
|
|
96
|
-
* alone does not close this either — it silently no-ops on an existing path whether or not
|
|
97
|
-
* that path is a symlink.
|
|
98
|
-
*/
|
|
99
|
-
async function ensureDirNoFollow(dirPath) {
|
|
100
|
-
const check = async () => {
|
|
101
|
-
let stats;
|
|
102
|
-
try {
|
|
103
|
-
stats = await fs.lstat(dirPath);
|
|
104
|
-
}
|
|
105
|
-
catch (err) {
|
|
106
|
-
if (err.code === 'ENOENT')
|
|
107
|
-
return 'missing';
|
|
108
|
-
throw err;
|
|
109
|
-
}
|
|
110
|
-
if (stats.isSymbolicLink()) {
|
|
111
|
-
throw new Error('Refusing to write through existing symlink: ' + dirPath);
|
|
112
|
-
}
|
|
113
|
-
if (!stats.isDirectory()) {
|
|
114
|
-
throw new Error('Expected a directory, found a file: ' + dirPath);
|
|
115
|
-
}
|
|
116
|
-
return 'ok';
|
|
117
|
-
};
|
|
118
|
-
if ((await check()) === 'ok')
|
|
119
|
-
return;
|
|
120
|
-
try {
|
|
121
|
-
await fs.mkdir(dirPath);
|
|
122
|
-
}
|
|
123
|
-
catch (err) {
|
|
124
|
-
// EEXIST here is a benign race between our own concurrent sub-skill writes under the
|
|
125
|
-
// same new subdirectory (writeInstallFiles writes them in parallel) — re-check below
|
|
126
|
-
// rather than trust that; it ALSO re-confirms nothing hostile won the race.
|
|
127
|
-
if (err.code !== 'EEXIST')
|
|
128
|
-
throw err;
|
|
129
|
-
}
|
|
130
|
-
// Re-check post-create: closes both the benign concurrent-mkdir race above and the TOCTOU
|
|
131
|
-
// window between the first check and this mkdir.
|
|
132
|
-
if ((await check()) !== 'ok') {
|
|
133
|
-
throw new Error('Failed to create directory: ' + dirPath);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Create every path segment of `dir` (relative to `baseDir`) via `ensureDirNoFollow()`,
|
|
138
|
-
* so no intermediate segment can be a pre-existing symlink. `dir` must already be lexically
|
|
139
|
-
* inside `baseDir` — callers are expected to have proven that (as `writeInstallFiles()`'s
|
|
140
|
-
* `installPath` already is, via its own lexical + realpath checks) before calling this.
|
|
141
|
-
*/
|
|
142
|
-
async function mkdirNoFollow(baseDir, dir) {
|
|
143
|
-
const relative = path.relative(baseDir, dir);
|
|
144
|
-
if (relative === '' || relative.startsWith('..'))
|
|
145
|
-
return;
|
|
146
|
-
const segments = relative.split(path.sep);
|
|
147
|
-
let current = baseDir;
|
|
148
|
-
for (const segment of segments) {
|
|
149
|
-
current = path.join(current, segment);
|
|
150
|
-
await ensureDirNoFollow(current);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
export async function writeInstallFiles(installPath, skillsDir, skillName, finalSkillContent, subSkillFiles, subagentContent,
|
|
154
|
-
/**
|
|
155
|
-
* SMI-5980 (Wave 3): the client this companion subagent (if any) is
|
|
156
|
-
* generated for — resolves its output path via
|
|
157
|
-
* `resolveCompanionAgentPath()` (install/paths.ts) instead of a hardcoded
|
|
158
|
-
* `~/.claude/agents/` literal. Optional, defaulting to `CANONICAL_CLIENT`
|
|
159
|
-
* (`claude-code`) so pre-existing callers/tests that never pass it keep
|
|
160
|
-
* today's exact behavior unchanged.
|
|
161
|
-
*/
|
|
162
|
-
client = CANONICAL_CLIENT,
|
|
163
|
-
/**
|
|
164
|
-
* SMI-5982 code-review fix #1: explicit base dir for resolving a RELATIVE
|
|
165
|
-
* `COMPANION_AGENT_TARGETS[client].dir` (Antigravity only — every other
|
|
166
|
-
* client's `dir` is absolute already). Threaded through to
|
|
167
|
-
* `resolveCompanionAgentPath()`'s own `baseDir` param as-is (no
|
|
168
|
-
* `?? process.cwd()` fallback here) — `resolveCompanionAgentPath()` itself
|
|
169
|
-
* now requires an explicit `baseDir` for every `directory-package`-mode
|
|
170
|
-
* client (PR-review follow-up), so whether an omitted `companionBaseDir`
|
|
171
|
-
* is acceptable is that function's call to make, not this one's.
|
|
172
|
-
*/
|
|
173
|
-
companionBaseDir) {
|
|
174
|
-
const writtenFiles = [];
|
|
175
|
-
let subagentPath;
|
|
176
|
-
// SMI-5359 (retro): lexically reject an escaping installPath BEFORE any filesystem
|
|
177
|
-
// mutation. path.resolve normalizes `..`, so an unsanitized skillName like '..'
|
|
178
|
-
// (e.g. path.basename('foo/..')) or a compromised registry skillName cannot drive
|
|
179
|
-
// installPath outside skillsDir and reach the rollback below. Thrown here, nothing
|
|
180
|
-
// has been created, so no cleanup is needed.
|
|
181
|
-
const resolvedInstall = path.resolve(installPath);
|
|
182
|
-
const resolvedSkillsDir = path.resolve(skillsDir);
|
|
183
|
-
if (resolvedInstall !== resolvedSkillsDir &&
|
|
184
|
-
!resolvedInstall.startsWith(resolvedSkillsDir + path.sep)) {
|
|
185
|
-
throw new Error('Install path escapes skills directory (lexical): ' + installPath);
|
|
186
|
-
}
|
|
187
|
-
// Only set true once installPath is PROVEN inside skillsDir (both the lexical check
|
|
188
|
-
// above and the realpath check below). The recursive rollback rm keys off this so it
|
|
189
|
-
// can never force-delete an out-of-bounds path (e.g. a symlink-escape the realpath
|
|
190
|
-
// check rejects after mkdir).
|
|
191
|
-
let pathValidated = false;
|
|
192
|
-
try {
|
|
193
|
-
await fs.mkdir(installPath, { recursive: true });
|
|
194
|
-
// SMI-4692: realpath both sides — macOS /var/folders symlinks to /private/var/folders.
|
|
195
|
-
const realInstallPath = await fs.realpath(installPath);
|
|
196
|
-
const expectedPrefix = await fs.realpath(skillsDir).catch(() => path.resolve(skillsDir));
|
|
197
|
-
if (!realInstallPath.startsWith(expectedPrefix + path.sep) &&
|
|
198
|
-
realInstallPath !== expectedPrefix) {
|
|
199
|
-
throw new Error('Install path escapes skills directory (realpath): ' + installPath);
|
|
200
|
-
}
|
|
201
|
-
pathValidated = true;
|
|
202
|
-
const mainSkillPath = path.join(installPath, 'SKILL.md');
|
|
203
|
-
await safeWriteFile(mainSkillPath, finalSkillContent);
|
|
204
|
-
writtenFiles.push(mainSkillPath);
|
|
205
|
-
// Write sub-skills in parallel. Sol final-code-review findings #2/#4: a nested filename
|
|
206
|
-
// (e.g. "scripts/run.sh", used by private-registry content installs) needs its parent
|
|
207
|
-
// directory created symlink-safely first — writeInstallFiles previously neither created it
|
|
208
|
-
// (ENOENT on any clean install with a nested file) nor checked it for a pre-existing symlink.
|
|
209
|
-
if (subSkillFiles.length > 0) {
|
|
210
|
-
await Promise.all(subSkillFiles.map(async (subSkill) => {
|
|
211
|
-
const subPath = path.join(installPath, subSkill.filename);
|
|
212
|
-
const subDir = path.dirname(subPath);
|
|
213
|
-
if (subDir !== installPath) {
|
|
214
|
-
await mkdirNoFollow(installPath, subDir);
|
|
215
|
-
}
|
|
216
|
-
await safeWriteFile(subPath, subSkill.content);
|
|
217
|
-
writtenFiles.push(subPath);
|
|
218
|
-
}));
|
|
219
|
-
}
|
|
220
|
-
// Write companion subagent if generated
|
|
221
|
-
if (subagentContent) {
|
|
222
|
-
subagentPath = resolveCompanionAgentPath(skillName, client, companionBaseDir);
|
|
223
|
-
const agentsDir = path.dirname(subagentPath);
|
|
224
|
-
await fs.mkdir(agentsDir, { recursive: true });
|
|
225
|
-
await safeWriteFile(subagentPath, subagentContent);
|
|
226
|
-
writtenFiles.push(subagentPath);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
catch (writeError) {
|
|
230
|
-
// Rollback on failure. Unlink tracked files first (subagentPath lives OUTSIDE
|
|
231
|
-
// installPath, under the client's companion-agent dir — see
|
|
232
|
-
// resolveCompanionAgentPath()/COMPANION_AGENT_TARGETS, install/paths.ts).
|
|
233
|
-
for (const filePath of writtenFiles) {
|
|
234
|
-
await fs.unlink(filePath).catch(() => { });
|
|
235
|
-
}
|
|
236
|
-
// SMI-5982 (Wave 6): 'directory-package' mode (Antigravity) creates a
|
|
237
|
-
// skill-named subdirectory OUTSIDE installPath (<agentsDir>/<skillName>/
|
|
238
|
-
// agent.md) that 'flat' mode never needed — every other client's agents
|
|
239
|
-
// dir is a shared, pre-existing directory, never created per-skill. A
|
|
240
|
-
// failed install must not leave that now-empty per-skill directory
|
|
241
|
-
// orphaned. rmdir is non-recursive: a safe no-op if the directory is
|
|
242
|
-
// missing (never created) or non-empty (unexpected content survives).
|
|
243
|
-
if (subagentPath && getCompanionAgentTarget(client).fileMode === 'directory-package') {
|
|
244
|
-
await fs.rmdir(path.dirname(subagentPath)).catch(() => { });
|
|
245
|
-
}
|
|
246
|
-
// Only recursively remove installPath once it has been PROVEN inside skillsDir
|
|
247
|
-
// (pathValidated) — so an untracked orphan from a mid-batch Promise.all write can't
|
|
248
|
-
// survive. If mkdir or the realpath escape guard threw, installPath was never
|
|
249
|
-
// validated; fall back to a non-recursive rmdir, a safe no-op on a non-empty or
|
|
250
|
-
// out-of-bounds directory (NEVER a recursive force-delete of an unvalidated path).
|
|
251
|
-
if (pathValidated) {
|
|
252
|
-
await fs.rm(installPath, { recursive: true, force: true }).catch(() => { });
|
|
253
|
-
}
|
|
254
|
-
else {
|
|
255
|
-
await fs.rmdir(installPath).catch(() => { });
|
|
256
|
-
}
|
|
257
|
-
throw writeError;
|
|
258
|
-
}
|
|
259
|
-
return { writtenFiles, subagentPath };
|
|
260
|
-
}
|
|
261
91
|
/**
|
|
262
92
|
* SMI-5359 Gap-1 / SMI-5422 Phase 1: fetch + scan the optional install files
|
|
263
93
|
* WITHOUT writing them. The caller runs this BEFORE writeInstallFiles, rejects
|