fix(#4132): verify durable runtime surface sources (#4182)

* fix(#4132): verify durable runtime surface sources

* chore(#4132): record PR number in changeset

* test(#4132): cover rejected commands source alias

* fix(#4132): reject aliased package fallback

* test(#4132): cover rejected agents source alias

* test(#4132): cover partially aliased marker provider

* fix(#4132): reject partially aliased source providers

* test(#4132): cover routed source identity probes

* fix(#4132): route installed source identity probes

* refactor(#4132): tighten installer source metadata

* test(#4132): cover corpus trust boundary attacks

* fix(#4132): close installed corpus trust gaps

* refactor(#4132): keep installer authority private

* fix(#4132): preserve private installer fallback

* test(#4132): preserve fixture source authority

* fix(#4132): reject overlapping source fallback

* fix(#4132): avoid redundant installed corpus reads

* refactor(#4132): simplify provider resolution

* test(#4132): sync install tree fixtures after rebase

---------

Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
This commit is contained in:
Zy Deng
2026-09-05 21:32:52 +02:00
committed by GitHub
parent 1017898cb9
commit 4c60879b5d
49 changed files with 3956 additions and 307 deletions

View File

@@ -815,6 +815,10 @@ const {
const {
resolveRuntimeArtifactLayout,
} = require(path.join(_gsdLibDir, 'runtime-artifact-layout.cjs'));
const {
readSurface,
resolveSurface,
} = require(path.join(_gsdLibDir, 'surface.cjs'));
const {
assertDestWithinConfigHome,
createRuntimeArtifactInstallPlan,
@@ -10620,7 +10624,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
// one) is honored on every profile, `full` included (#2322 blocker 2).
const _commandsDir = path.join(src, 'commands', 'gsd');
const _skillsManifest = _isCoreProfileAlias ? new Map() : loadSkillsManifest(_commandsDir);
const _resolvedProfile = resolveProfile({
let _resolvedProfile = resolveProfile({
modes: [_activeProfileName],
manifest: _skillsManifest,
registry: _installedCapabilityRegistry,
@@ -10971,6 +10975,28 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
return Array.isArray(scopeLayout) && scopeLayout.length > 0;
})();
// Install the distribution-owned gsd-core tree before layout materialization.
// installRuntimeArtifacts then provisions the durable Runtime Surface corpus
// exactly once into this final tree instead of having that corpus overwritten
// by a later whole-tree copy and needing a second provisioning pass.
const skillSrc = path.join(src, 'gsd-core');
const skillDest = path.join(targetDir, 'gsd-core');
const _gsdArtifactsStagingRoot = _tryResolveUserArtifactStagingRoot(targetDir);
if (_gsdArtifactsStagingRoot === null) {
console.warn(` ${yellow}!${reset} Skipping gsd-core/${USER_OWNED_ARTIFACTS.join(', gsd-core/')} preservation (staging unavailable) — it will be lost if present.`);
copyWithPathReplacement(skillSrc, skillDest, pathPrefix, runtime, false, isGlobal, targetDir);
} else {
const stagedGsdArtifacts = stageUserArtifacts(skillDest, USER_OWNED_ARTIFACTS, _gsdArtifactsStagingRoot);
copyWithPathReplacement(skillSrc, skillDest, pathPrefix, runtime, false, isGlobal, targetDir);
restoreStagedUserArtifacts(skillDest, stagedGsdArtifacts);
discardStagedUserArtifacts(stagedGsdArtifacts);
}
if (verifyInstalled(skillDest, 'gsd-core')) {
console.log(` ${green}✓${reset} Installed workflow assets`);
} else {
failures.push('gsd-core');
}
// #2624: write the .gsd-source marker. Extracted from its former late position so it can be
// called BEFORE staging reads the marker (see the call site below). Scoped to the Claude-global
// layout (issue #1477) — the only install path that ships the skills layout without a
@@ -10987,6 +11013,9 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
// ADR-1239 Phase B write-confinement: the descriptor-sourced marker filename
// must resolve under targetDir (parity with the other descriptor-driven writes).
const _markerPath = assertDestWithinConfigHome(targetDir, _hostBehaviors(runtime).sourceMarkerFile);
if (hasExistingSymlinkBetween(path.resolve(targetDir), _markerPath, { allowOptInFollow: isSymlinkedDestOptIn() })) {
throw new Error(`compatibility marker "${_markerPath}" contains an untrusted symlink`);
}
fs.writeFileSync(_markerPath, gsdSourceCommands + '\n', 'utf8');
} catch (err) {
// Non-fatal: install proceeds. But on the Claude-global layout walk-up
@@ -11013,6 +11042,19 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
if (_isSkillsRuntime) {
// Layout-driven install for skills-based runtimes (full and minimal modes)
const scope = _installScopeId;
// Preserve an existing Runtime Surface selection during upgrades. The
// installer refreshes the source corpus first, then emits exactly the
// already-committed selection instead of widening it to the base profile.
const _committedSurface = readSurface(targetDir);
if (_committedSurface) {
_resolvedProfile = resolveSurface(
targetDir,
loadSkillsManifest(_commandsDir),
undefined,
_installedCapabilityRegistry,
_committedSurface,
);
}
// ADR-1239 upgrade 3 / #2088: a kind may declare an alternate install `home`
// (e.g. Codex skills -> $HOME/.agents/skills) instead of the runtime's normal
// configDir. Resolve the ACTUAL on-disk skills root here, descriptor-driven
@@ -11305,36 +11347,6 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
_installNativePluginIfDeclared(runtime, targetDir, _hostBehaviors(runtime), src);
}
// Copy gsd-core skill with path replacement
// Stage user-generated files DURABLY to disk before the wipe-and-copy so
// they survive re-install even if the process dies mid-copy (#2875 /
// #1874-F19) — copyWithPathReplacement wipes and recursively re-copies the
// entire gsd-core/ tree, the single longest operation in the install, on
// the path every user takes (40-design.md "Site 4 is far worse...").
const skillSrc = path.join(src, 'gsd-core');
const skillDest = path.join(targetDir, 'gsd-core');
// #2875 defect fix: this IS the mainline install step (installing
// gsd-core/ itself) — unlike the optional legacy-cleanup blocks above,
// install must still be able to proceed and actually write gsd-core/ even
// when the staging root cannot be resolved. Degrade by skipping ONLY the
// USER_OWNED_ARTIFACTS preserve/restore wrapper around the copy (warn),
// never the copy itself.
const _gsdArtifactsStagingRoot = _tryResolveUserArtifactStagingRoot(targetDir);
if (_gsdArtifactsStagingRoot === null) {
console.warn(` ${yellow}!${reset} Skipping gsd-core/${USER_OWNED_ARTIFACTS.join(', gsd-core/')} preservation (staging unavailable) — it will be lost if present.`);
copyWithPathReplacement(skillSrc, skillDest, pathPrefix, runtime, false, isGlobal, targetDir);
} else {
const stagedGsdArtifacts = stageUserArtifacts(skillDest, USER_OWNED_ARTIFACTS, _gsdArtifactsStagingRoot);
copyWithPathReplacement(skillSrc, skillDest, pathPrefix, runtime, false, isGlobal, targetDir);
restoreStagedUserArtifacts(skillDest, stagedGsdArtifacts);
discardStagedUserArtifacts(stagedGsdArtifacts);
}
if (verifyInstalled(skillDest, 'gsd-core')) {
console.log(` ${green}✓${reset} Installed workflow assets`);
} else {
failures.push('gsd-core');
}
// #2624: the .gsd-source marker is now written by _writeGsdSourceMarker()
// BEFORE staging reads it (see the early call above the _isSkillsRuntime
// block). The former write lived here — AFTER staging — which on an upgrade