-
Notifications
You must be signed in to change notification settings - Fork 0
[WRONG BRANCH] fix(providers): harden Alibaba migration backup #274
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,12 @@ | ||
| import { copyFileSync, existsSync, linkSync, readFileSync, rmSync } from "node:fs"; | ||
| import { chmodSync, copyFileSync, existsSync, linkSync, readFileSync, rmSync } from "node:fs"; | ||
| import { getConfigPath } from "../config"; | ||
| import { hardenSecretPath } from "../lib/windows-secret-acl"; | ||
|
|
||
| export interface AlibabaBackupIO { | ||
| exists: (path: string) => boolean; | ||
| read: (path: string) => Buffer; | ||
| copy: (source: string, destination: string) => void; | ||
| harden: (path: string) => void; | ||
| /** Publish with no-replace semantics: fails with EEXIST if the destination exists. */ | ||
| publishNoReplace: (temp: string, destination: string) => void; | ||
| remove: (path: string) => void; | ||
|
|
@@ -14,6 +16,10 @@ const DEFAULT_IO: AlibabaBackupIO = { | |
| exists: existsSync, | ||
| read: path => readFileSync(path), | ||
| copy: (source, destination) => copyFileSync(source, destination), | ||
| harden: path => { | ||
| try { chmodSync(path, 0o600); } catch { /* platform may ignore chmod */ } | ||
| if (process.platform === "win32") hardenSecretPath(path, { required: true }); | ||
| }, | ||
|
Comment on lines
+19
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
rg -n -C 12 'HardenResult|hardenEntry|hardenSecretPath|required' src/lib/windows-secret-acl.tsRepository: luvs01/opencodex Length of output: 15241 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- target file ---'
cat -n src/providers/alibaba-region-backup.ts | sed -n '1,180p'
printf '%s\n' '--- harden and backup call sites ---'
rg -n -C 5 'DEFAULT_IO|backupConfigBeforeAlibabaRegionMigration|hardenSecretPath|chmodSync' srcRepository: luvs01/opencodex Length of output: 50372 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- Alibaba backup tests and fixtures ---'
fd -i 'alibaba|backup' . --type f | sort
rg -n -C 8 'AlibabaBackup|pre-alibaba-region|backupConfigBeforeAlibabaRegionMigration|harden.*throw|chmod' --glob '*test*' --glob '*spec*' src test tests 2>/dev/null || true
printf '%s\n' '--- focused hardening tests ---'
rg -n -C 6 'hardenSecretPath|HardenResult|required: true|required: false|chmodSync' --glob '*test*' --glob '*spec*' src test tests 2>/dev/null | head -n 500 || trueRepository: luvs01/opencodex Length of output: 50373 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
cat -n tests/alibaba-region-backup.test.ts
printf '%s\n' '--- relevant hardening contract tests ---'
cat -n tests/windows-secret-acl.test.ts | sed -n '1368,1425p'Repository: luvs01/opencodex Length of output: 8326 Make POSIX hardening fail closed. At Remove the 🤖 Prompt for AI Agents |
||
| publishNoReplace: linkSync, | ||
| remove: path => rmSync(path, { force: true }), | ||
| }; | ||
|
|
@@ -57,6 +63,9 @@ export function backupConfigBeforeAlibabaRegionMigration( | |
| const temp = `${backup}.${process.pid}.tmp`; | ||
| try { | ||
| io.copy(configPath, temp); | ||
| // The snapshot contains credentials. Harden it before publication so the | ||
| // stable backup path is never exposed with inherited permissions or ACLs. | ||
| io.harden(temp); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When upgrading an installation that already ran v2.11.0, this new hardening call is never reached: a successful prior migration makes AGENTS.md reference: AGENTS.md:L218-L224 Useful? React with 👍 / 👎. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
On Windows, if this hardening call times out, Useful? React with 👍 / 👎. |
||
| // Verify before publishing: a short copy must never become the snapshot. | ||
| if (!io.read(temp).equals(source)) { | ||
| throw new AlibabaBackupIntegrityError(`failed to write a complete backup to ${temp}`); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| import { expect, test } from "bun:test"; | ||
| import { existsSync, linkSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; | ||
| import { existsSync, linkSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs"; | ||
| import { tmpdir } from "node:os"; | ||
| import { join } from "node:path"; | ||
| import { | ||
|
|
@@ -22,6 +22,7 @@ test("creates a snapshot, then never replaces it", () => { | |
| writeFileSync(configPath, '{"before":true}', "utf8"); | ||
| expect(backupConfigBeforeAlibabaRegionMigration(configPath)).toBe("created"); | ||
| expect(readFileSync(backupPath, "utf8")).toBe('{"before":true}'); | ||
| if (process.platform !== "win32") expect(statSync(backupPath).mode & 0o777).toBe(0o600); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
rg -n -C 8 'hardenSecretPath|HardenResult|required: true|ACL|process\.platform' src testsRepository: luvs01/opencodex Length of output: 50373 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- Alibaba test ---'
cat -n tests/alibaba-region-backup.test.ts | sed -n '1,125p'
printf '%s\n' '--- Alibaba implementation ---'
cat -n src/providers/alibaba-region-backup.ts | sed -n '1,110p'
printf '%s\n' '--- Windows ACL tests: declarations and relevant cases ---'
rg -n -C 6 'describe|test\(|hardenSecretPath\(|required: true|required: false|setPlatformForTests|setIcaclsRunnerForTests|HardenResult' tests/windows-secret-acl.test.ts
printf '%s\n' '--- Windows ACL implementation ---'
cat -n src/lib/windows-secret-acl.ts | sed -n '730,825p'Repository: luvs01/opencodex Length of output: 50372 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- Direct Alibaba backup coverage ---'
rg -n -C 3 'backupConfigBeforeAlibabaRegionMigration|AlibabaBackupIO|DEFAULT_IO' src tests
printf '%s\n' '--- Exact Windows ACL implementation around the requested location ---'
cat -n src/lib/windows-secret-acl.ts | sed -n '775,805p'
printf '%s\n' '--- Relevant Windows ACL failure tests only ---'
sed -n '458,545p' tests/windows-secret-acl.test.ts
printf '%s\n' '--- Test seams that can force Windows behavior ---'
rg -n -C 3 'Object\.defineProperty\(process, "platform"|setPlatformForTests\("win32"\)' tests | head -n 180Repository: luvs01/opencodex Length of output: 25575 Add default Windows ACL coverage for
🤖 Prompt for AI AgentsSource: Path instructions |
||
| expect(backupConfigBeforeAlibabaRegionMigration(configPath)).toBe("reused"); | ||
| expect(readFileSync(backupPath, "utf8")).toBe('{"before":true}'); | ||
| } finally { rmSync(dir, { recursive: true, force: true }); } | ||
|
|
@@ -52,6 +53,7 @@ test("a short copy is never published", () => { | |
| exists: existsSync, | ||
| read: path => readFileSync(path), | ||
| copy: (_source, destination) => { writeFileSync(destination, '{"bef', "utf8"); }, | ||
| harden: () => {}, | ||
| publishNoReplace: linkSync, | ||
| remove: path => rmSync(path, { force: true }), | ||
| })).toThrow(AlibabaBackupIntegrityError); | ||
|
|
@@ -69,10 +71,30 @@ test("a failed copy leaves no snapshot and no temp file", () => { | |
| exists: existsSync, | ||
| read: path => readFileSync(path), | ||
| copy: () => { throw new Error("disk full"); }, | ||
| harden: () => {}, | ||
| publishNoReplace: linkSync, | ||
| remove: path => { removed.push(path); rmSync(path, { force: true }); }, | ||
| })).toThrow("disk full"); | ||
| expect(existsSync(`${configPath}.pre-alibaba-region-v1.bak`)).toBe(false); | ||
| expect(removed).toHaveLength(1); | ||
| } finally { rmSync(dir, { recursive: true, force: true }); } | ||
| }); | ||
|
|
||
| test("a failed harden never publishes the secret-bearing snapshot", () => { | ||
| const dir = mkdtempSync(join(tmpdir(), "ocx-bak-")); | ||
| const configPath = join(dir, "config.json"); | ||
| const backupPath = `${configPath}.pre-alibaba-region-v1.bak`; | ||
| try { | ||
| writeFileSync(configPath, '{"before":true}', "utf8"); | ||
| expect(() => backupConfigBeforeAlibabaRegionMigration(configPath, { | ||
| exists: existsSync, | ||
| read: path => readFileSync(path), | ||
| copy: (source, destination) => writeFileSync(destination, readFileSync(source)), | ||
| harden: () => { throw new Error("ACL hardening failed"); }, | ||
| publishNoReplace: linkSync, | ||
| remove: path => rmSync(path, { force: true }), | ||
| })).toThrow("ACL hardening failed"); | ||
| expect(existsSync(backupPath)).toBe(false); | ||
| expect(existsSync(`${backupPath}.${process.pid}.tmp`)).toBe(false); | ||
| } finally { rmSync(dir, { recursive: true, force: true }); } | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On a non-Windows filesystem where
chmodSyncthrows, such as some network or removable mounts, this catch reports hardening as successful and the routine proceeds to publish the secret-bearing backup with its copied/inherited permissions. The added failure test only covers an injectedhardenthat throws, so it misses this production path; let chmod failures propagate on POSIX or verify that the resulting mode is0600, relying on the required ACL operation only on Windows.AGENTS.md reference: AGENTS.md:L218-L224
Useful? React with 👍 / 👎.