



























@@ -87,6 +87,27 @@ function listExecApprovalTempFiles(homeDir: string): string[] {
8787return fs.readdirSync(dir).filter((name) => name.endsWith(".tmp"));
8888}
898990+function requireRecord(value: unknown): Record<string, unknown> {
91+expect(value).toBeTruthy();
92+expect(typeof value).toBe("object");
93+expect(Array.isArray(value)).toBe(false);
94+return value as Record<string, unknown>;
95+}
96+97+function allowlistEntries(homeDir: string, agentId: string): Record<string, unknown>[] {
98+const file = readApprovalsFile(homeDir);
99+return (file.agents?.[agentId]?.allowlist ?? []).map((entry) => requireRecord(entry));
100+}
101+102+function expectAllowlistEntryFields(
103+entry: Record<string, unknown>,
104+fields: Record<string, unknown>,
105+): void {
106+for (const [key, value] of Object.entries(fields)) {
107+expect(entry[key]).toEqual(value);
108+}
109+}
110+90111describe("exec approvals store helpers", () => {
91112it("expands home-prefixed default file and socket paths", () => {
92113const dir = createHomeDir();
@@ -417,13 +438,13 @@ describe("exec approvals store helpers", () => {
417438addAllowlistEntry(approvals, "worker", "/usr/bin/rg");
418439addAllowlistEntry(approvals, "worker", " ");
419440420-expect(readApprovalsFile(dir).agents?.worker?.allowlist).toEqual([
421- expect.objectContaining({
422- pattern: "/usr/bin/rg",
423- lastUsedAt: 123_456,
424-}),
425-]);
426-expect(readApprovalsFile(dir).agents?.worker?.allowlist?.[0]?.id).toMatch(/^[0-9a-f-]{36}$/i);
441+const allowlist = allowlistEntries(dir, "worker");
442+expect(allowlist).toHaveLength(1);
443+expectAllowlistEntryFields(allowlist[0] ?? {}, {
444+pattern: "/usr/bin/rg",
445+lastUsedAt: 123_456,
446+});
447+expect(allowlist[0]?.id).toMatch(/^[0-9a-f-]{36}$/i);
427448});
428449429450it("persists durable command approvals without storing plaintext command text", () => {
@@ -433,56 +454,36 @@ describe("exec approvals store helpers", () => {
433454const approvals = ensureExecApprovals();
434455addDurableCommandApproval(approvals, "worker", 'printenv API_KEY="secret-value"');
435456436-expect(readApprovalsFile(dir).agents?.worker?.allowlist).toEqual([
437-expect.objectContaining({
438-source: "allow-always",
439-lastUsedAt: 321_000,
440-}),
441-]);
442-expect(readApprovalsFile(dir).agents?.worker?.allowlist?.[0]?.pattern).toMatch(
443-/^=command:[0-9a-f]{16}$/i,
444-);
445-expect(readApprovalsFile(dir).agents?.worker?.allowlist?.[0]).not.toHaveProperty("commandText");
457+const allowlist = allowlistEntries(dir, "worker");
458+expect(allowlist).toHaveLength(1);
459+expectAllowlistEntryFields(allowlist[0] ?? {}, {
460+source: "allow-always",
461+lastUsedAt: 321_000,
462+});
463+expect(allowlist[0]?.pattern).toMatch(/^=command:[0-9a-f]{16}$/i);
464+expect(allowlist[0]).not.toHaveProperty("commandText");
446465});
447466448467it("strips legacy plaintext command text during normalization", () => {
449-expect(
450-normalizeExecApprovals({
451-version: 1,
452-agents: {
453-main: {
454-allowlist: [
455-{
456-pattern: "=command:test",
457-source: "allow-always",
458-commandText: "echo secret-token",
459-},
460-],
461-},
462-},
463-}).agents?.main?.allowlist,
464-).toEqual([
465-expect.objectContaining({
466-pattern: "=command:test",
467-source: "allow-always",
468-}),
469-]);
470-expect(
471-normalizeExecApprovals({
472-version: 1,
473-agents: {
474-main: {
475-allowlist: [
476-{
477-pattern: "=command:test",
478-source: "allow-always",
479-commandText: "echo secret-token",
480-},
481-],
482-},
468+const normalized = normalizeExecApprovals({
469+version: 1,
470+agents: {
471+main: {
472+allowlist: [
473+{
474+pattern: "=command:test",
475+source: "allow-always",
476+commandText: "echo secret-token",
477+},
478+],
483479},
484-}).agents?.main?.allowlist?.[0],
485-).not.toHaveProperty("commandText");
480+},
481+});
482+const allowlist = normalized.agents?.main?.allowlist ?? [];
483+expect(allowlist).toHaveLength(1);
484+expect(allowlist[0]?.pattern).toBe("=command:test");
485+expect(allowlist[0]?.source).toBe("allow-always");
486+expect(allowlist[0]).not.toHaveProperty("commandText");
486487});
487488488489it("preserves source and argPattern metadata for allow-always entries", () => {
@@ -503,20 +504,20 @@ describe("exec approvals store helpers", () => {
503504source: "allow-always",
504505});
505506506-expect(readApprovalsFile(dir).agents?.worker?.allowlist).toEqual([
507- expect.objectContaining({
508- pattern: "/usr/bin/python3",
509- argPattern: "^script\\.py\x00$",
510- source: "allow-always",
511- lastUsedAt: 321_000,
512-}),
513- expect.objectContaining({
514- pattern: "/usr/bin/python3",
515- argPattern: "^other\\.py\x00$",
516- source: "allow-always",
517- lastUsedAt: 321_000,
518-}),
519-]);
507+const allowlist = allowlistEntries(dir, "worker");
508+expect(allowlist).toHaveLength(2);
509+expectAllowlistEntryFields(allowlist[0] ?? {}, {
510+pattern: "/usr/bin/python3",
511+argPattern: "^script\\.py\x00$",
512+source: "allow-always",
513+lastUsedAt: 321_000,
514+});
515+expectAllowlistEntryFields(allowlist[1] ?? {}, {
516+pattern: "/usr/bin/python3",
517+argPattern: "^other\\.py\x00$",
518+source: "allow-always",
519+lastUsedAt: 321_000,
520+});
520521});
521522522523it("records allowlist usage on the matching entry and backfills missing ids", () => {
@@ -542,16 +543,16 @@ describe("exec approvals store helpers", () => {
542543"/opt/homebrew/bin/rg",
543544);
544545545-expect(readApprovalsFile(dir).agents?.main?.allowlist).toEqual([
546- expect.objectContaining({
547- pattern: "/usr/bin/rg",
548- lastUsedAt: 999_000,
549- lastUsedCommand: "rg needle",
550- lastResolvedPath: "/opt/homebrew/bin/rg",
551-}),
552- { pattern: "/usr/bin/jq", id: "keep-id" },
553-]);
554-expect(readApprovalsFile(dir).agents?.main?.allowlist?.[0]?.id).toMatch(/^[0-9a-f-]{36}$/i);
546+const allowlist = allowlistEntries(dir, "main");
547+expect(allowlist).toHaveLength(2);
548+expectAllowlistEntryFields(allowlist[0] ?? {}, {
549+pattern: "/usr/bin/rg",
550+lastUsedAt: 999_000,
551+lastUsedCommand: "rg needle",
552+lastResolvedPath: "/opt/homebrew/bin/rg",
553+});
554+expect(allowlist[0]?.id).toMatch(/^[0-9a-f-]{36}$/i);
555+expect(allowlist[1]).toEqual({ pattern: "/usr/bin/jq", id: "keep-id" });
555556});
556557557558it("dedupes allowlist usage by pattern and argPattern", () => {
@@ -584,18 +585,18 @@ describe("exec approvals store helpers", () => {
584585resolvedPath: "/usr/bin/python3",
585586});
586587587-expect(readApprovalsFile(dir).agents?.main?.allowlist).toEqual([
588- expect.objectContaining({
589- pattern: "/usr/bin/python3",
590- argPattern: "^a\\.py\x00$",
591- lastUsedAt: 777_000,
592-}),
593- expect.objectContaining({
594- pattern: "/usr/bin/python3",
595- argPattern: "^b\\.py\x00$",
596- lastUsedAt: 777_000,
597-}),
598-]);
588+const allowlist = allowlistEntries(dir, "main");
589+expect(allowlist).toHaveLength(2);
590+expectAllowlistEntryFields(allowlist[0] ?? {}, {
591+pattern: "/usr/bin/python3",
592+argPattern: "^a\\.py\x00$",
593+lastUsedAt: 777_000,
594+});
595+expectAllowlistEntryFields(allowlist[1] ?? {}, {
596+pattern: "/usr/bin/python3",
597+argPattern: "^b\\.py\x00$",
598+lastUsedAt: 777_000,
599+});
599600});
600601601602it("persists allow-always patterns with shared helper", () => {
@@ -633,14 +634,14 @@ describe("exec approvals store helpers", () => {
633634argPattern: "^a\\.py\x00$",
634635},
635636]);
636-expect(readApprovalsFile(dir).agents?.worker?.allowlist).toEqual([
637- expect.objectContaining({
638- pattern: "/usr/bin/custom-tool.exe",
639- argPattern: "^a\\.py\x00$",
640- source: "allow-always",
641- lastUsedAt: 654_321,
642-}),
643-]);
637+const allowlist = allowlistEntries(dir, "worker");
638+expect(allowlist).toHaveLength(1);
639+expectAllowlistEntryFields(allowlist[0] ?? {}, {
640+pattern: "/usr/bin/custom-tool.exe",
641+argPattern: "^a\\.py\x00$",
642+source: "allow-always",
643+lastUsedAt: 654_321,
644+});
644645});
645646646647it("returns null when approval socket credentials are missing", async () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。