refactor(permission-system): simplify policy and autoreview flow

This commit is contained in:
云服务部-叶林立
2026-09-03 02:01:06 +08:00
parent 180da95ab7
commit 6e7effa792
112 changed files with 911 additions and 10346 deletions
+129
View File
@@ -0,0 +1,129 @@
import assert from "node:assert/strict";
import { mkdtemp, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import {
LEGACY_PERMISSION_POLICY_HASHES,
migratePermissionPolicy,
permissionPolicySemanticHash,
} from "../scripts/migrate-permission-policy.mjs";
const knownLegacyHash =
"9abf020e80fceae8c52de3e8b758822dfc86cb90e9083381e2569dde1e29400a";
async function fixture(t: test.TestContext) {
const directory = await mkdtemp(join(tmpdir(), "permission-policy-migration-"));
t.after(() => rm(directory, { recursive: true, force: true }));
const policyPath = join(directory, "policy.jsonc");
const seedPath = join(directory, "seed.jsonc");
const seed = `{
// bundled policy
"permission": {
"*": "ask",
"read": "allow"
}
}\n`;
await writeFile(seedPath, seed, { mode: 0o600 });
return { directory, policyPath, seedPath, seed };
}
test("recognizes the exact previously installed policy snapshot", () => {
assert.equal(LEGACY_PERMISSION_POLICY_HASHES.has(knownLegacyHash), true);
});
test("semantic hashes ignore JSONC comments, whitespace, and object key order", () => {
const left = `{
// comment
"permission": { "read": "allow", "*": "ask" },
"analysis": { "enabled": true }
}`;
const right = `{"analysis":{"enabled":true},"permission":{"*":"ask","read":"allow"}}`;
assert.equal(permissionPolicySemanticHash(left), permissionPolicySemanticHash(right));
});
test("migrates only an exact legacy semantic snapshot and creates private files", async (t) => {
const { directory, policyPath, seedPath, seed } = await fixture(t);
const legacy = `{
// formatting does not matter
"permission": { "*": "allow", "path": { "**/.env": "deny" } }
}\n`;
await writeFile(policyPath, legacy, { mode: 0o644 });
const result = await migratePermissionPolicy(policyPath, seedPath, {
legacyHashes: new Set([permissionPolicySemanticHash(legacy)]),
now: new Date("2026-09-02T03:04:05.000Z"),
});
assert.equal(result.status, "migrated");
assert.equal(await readFile(policyPath, "utf8"), seed);
assert.equal((await stat(policyPath)).mode & 0o777, 0o600);
assert.ok(result.backupPath);
assert.equal(await readFile(result.backupPath!, "utf8"), legacy);
assert.equal((await stat(result.backupPath!)).mode & 0o777, 0o600);
assert.equal(
(await readdir(directory)).some((name) => name.includes(".tmp.")),
false,
);
});
test("preserves customized policy content", async (t) => {
const { policyPath, seedPath } = await fixture(t);
const custom = `{"permission":{"*":"deny"}}\n`;
await writeFile(policyPath, custom, { mode: 0o600 });
const result = await migratePermissionPolicy(policyPath, seedPath);
assert.deepEqual(result, { status: "custom" });
assert.equal(await readFile(policyPath, "utf8"), custom);
});
test("treats the bundled seed as current and repairs its mode", async (t) => {
const { policyPath, seedPath, seed } = await fixture(t);
await writeFile(policyPath, seed, { mode: 0o644 });
const result = await migratePermissionPolicy(policyPath, seedPath);
assert.deepEqual(result, { status: "current" });
assert.equal((await stat(policyPath)).mode & 0o777, 0o600);
});
test("returns missing without creating a policy", async (t) => {
const { policyPath, seedPath } = await fixture(t);
assert.deepEqual(await migratePermissionPolicy(policyPath, seedPath), {
status: "missing",
});
});
test("rejects invalid current JSONC without replacing it", async (t) => {
const { policyPath, seedPath } = await fixture(t);
const invalid = "{ invalid\n";
await writeFile(policyPath, invalid, { mode: 0o600 });
await assert.rejects(
migratePermissionPolicy(policyPath, seedPath),
/invalid JSONC/,
);
assert.equal(await readFile(policyPath, "utf8"), invalid);
});
test("aborts if the policy changes before commit", async (t) => {
const { directory, policyPath, seedPath } = await fixture(t);
const legacy = `{"permission":{"*":"allow"}}\n`;
const concurrent = `{"permission":{"*":"deny"}}\n`;
await writeFile(policyPath, legacy, { mode: 0o600 });
await assert.rejects(
migratePermissionPolicy(policyPath, seedPath, {
legacyHashes: new Set([permissionPolicySemanticHash(legacy)]),
beforeCommit: () => writeFile(policyPath, concurrent, "utf8"),
}),
/changed during migration/,
);
assert.equal(await readFile(policyPath, "utf8"), concurrent);
const entries = await readdir(directory);
assert.equal(entries.some((name) => name.includes(".bak.")), true);
assert.equal(entries.some((name) => name.includes(".tmp.")), false);
});