mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-18 22:36:19 +00:00
fix(blueprints): fail closed on marker ownership for apply and withdraw (#1694)
* fix(blueprints): fail closed on marker ownership for apply and withdraw Require a matching .blueprint.json under the stack lock, persist required_blueprint_id on deletion intents, remove the legacy remote apply fallback, and protect the marker in the file explorer. * fix(blueprints): add CodeQL path barriers on ownership probes Use the canonical resolve-and-startsWith sanitizer inline at the marker and stack-directory fs sinks so js/path-injection clears. * fix(blueprints): block delete on failed withdraw and defer marker write Refuse Blueprint DELETE when pre-delete withdraw does not complete, and write .blueprint.json only after a successful deploy so failed applies cannot orphan stacks or claim an unapplied revision. * test(blueprints): align lock-order assert with deferred marker write Update the per-stack lock ordering expectations to compose, cleanup, deploy, then marker after the partial-apply fix. * fix(deps): bump postcss past GHSA-r28c-9q8g-f849 for npm audit Raise the Vitest/Vite transitive postcss to 8.5.23 so Backend CI audit --audit-level=high passes.
This commit is contained in:
@@ -58,6 +58,13 @@ const PROTECTED_STACK_FILES = new Set([
|
||||
'.env',
|
||||
]);
|
||||
|
||||
// Explorer-only protection: includes the blueprint ownership marker without
|
||||
// putting it in PROTECTED_STACK_FILES (backup/rollback orphan removal).
|
||||
const EXPLORER_PROTECTED_STACK_FILES = new Set([
|
||||
...PROTECTED_STACK_FILES,
|
||||
'.blueprint.json',
|
||||
]);
|
||||
|
||||
// Bookkeeping markers Sencho writes into the backup slot. They are never copied
|
||||
// back into the stack directory on restore: `.timestamp` records when the backup
|
||||
// was taken; `.checksums` is the integrity manifest verified before a restore.
|
||||
@@ -169,7 +176,7 @@ function isProtectedRelPath(relPath: string): boolean {
|
||||
if (normalized.includes('/')) return false;
|
||||
// Fold case so e.g. a request for COMPOSE.YAML cannot dodge the gate on a
|
||||
// case-insensitive filesystem where it resolves to the real compose.yaml.
|
||||
return PROTECTED_STACK_FILES.has(fsCaseKey(normalized));
|
||||
return EXPLORER_PROTECTED_STACK_FILES.has(fsCaseKey(normalized));
|
||||
}
|
||||
|
||||
function protectedFileError(relPath: string): Error & { code: string } {
|
||||
@@ -1643,7 +1650,7 @@ export class FileSystemService {
|
||||
type,
|
||||
size,
|
||||
mtime,
|
||||
isProtected: protectedEnabled && PROTECTED_STACK_FILES.has(dirent.name),
|
||||
isProtected: protectedEnabled && EXPLORER_PROTECTED_STACK_FILES.has(dirent.name),
|
||||
};
|
||||
})
|
||||
);
|
||||
@@ -2096,7 +2103,7 @@ export class FileSystemService {
|
||||
type,
|
||||
size: stat.isDirectory() ? 0 : stat.size,
|
||||
mtime: stat.mtimeMs,
|
||||
isProtected: (scope?.protectedEnabled ?? true) && PROTECTED_STACK_FILES.has(name),
|
||||
isProtected: (scope?.protectedEnabled ?? true) && EXPLORER_PROTECTED_STACK_FILES.has(name),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user