|
| 1 | +// Module-level mock functions for fs — must be declared before jest.mock('fs') |
| 2 | +// so the factory can close over them. jest.mock is hoisted but the factory runs |
| 3 | +// lazily after module initialisation, when these variables are defined. |
| 4 | +const mockMkdirSync = jest.fn(); |
| 5 | +const mockWriteFileSync = jest.fn(); |
| 6 | +const mockChmodSync = jest.fn(); |
| 7 | + |
| 8 | +jest.mock('fs', () => { |
| 9 | + const actual = jest.requireActual<typeof import('fs')>('fs'); |
| 10 | + return { |
| 11 | + ...actual, |
| 12 | + mkdirSync: (...args: unknown[]) => mockMkdirSync(...args), |
| 13 | + writeFileSync: (...args: unknown[]) => mockWriteFileSync(...args), |
| 14 | + chmodSync: (...args: unknown[]) => mockChmodSync(...args), |
| 15 | + }; |
| 16 | +}); |
| 17 | + |
1 | 18 | import { createMainAction } from './main-action'; |
2 | 19 |
|
3 | 20 | // eslint-disable-next-line @typescript-eslint/no-require-imports |
@@ -373,4 +390,81 @@ describe('createMainAction', () => { |
373 | 390 | expect(serialized).not.toContain('gem-secret'); |
374 | 391 | }); |
375 | 392 | }); |
| 393 | + |
| 394 | + describe('resolved config artifact', () => { |
| 395 | + beforeEach(() => { |
| 396 | + mockMkdirSync.mockReset(); |
| 397 | + mockWriteFileSync.mockReset(); |
| 398 | + mockChmodSync.mockReset(); |
| 399 | + }); |
| 400 | + |
| 401 | + afterEach(() => jest.restoreAllMocks()); |
| 402 | + |
| 403 | + it('writes awf-resolved-config.json to audit dir when set', async () => { |
| 404 | + const configWithAudit = { |
| 405 | + ...STUB_CONFIG, |
| 406 | + auditDir: '/tmp/awf-audit', |
| 407 | + }; |
| 408 | + mockedValidateOptions.validateOptions.mockReturnValue( |
| 409 | + configWithAudit as unknown as import('../types').WrapperConfig |
| 410 | + ); |
| 411 | + const action = createMainAction(getOptionValueSource); |
| 412 | + await action(['echo hi'], {}); |
| 413 | + |
| 414 | + expect(mockMkdirSync).toHaveBeenCalledWith('/tmp/awf-audit', { recursive: true, mode: 0o755 }); |
| 415 | + expect(mockWriteFileSync).toHaveBeenCalledWith( |
| 416 | + '/tmp/awf-audit/awf-resolved-config.json', |
| 417 | + expect.stringContaining('"allowedDomains"'), |
| 418 | + { mode: 0o644 }, |
| 419 | + ); |
| 420 | + expect(mockChmodSync).toHaveBeenCalledWith('/tmp/awf-audit/awf-resolved-config.json', 0o644); |
| 421 | + // Verify secret key names are excluded from the artifact |
| 422 | + const written = mockWriteFileSync.mock.calls.find( |
| 423 | + (c) => String(c[0]).includes('awf-resolved-config.json') |
| 424 | + ); |
| 425 | + expect(written).toBeDefined(); |
| 426 | + const writtenJson = String(written![1]); |
| 427 | + expect(writtenJson).not.toContain('ApiKey'); |
| 428 | + expect(writtenJson).not.toContain('GithubToken'); |
| 429 | + }); |
| 430 | + |
| 431 | + it('redacts secret values in agentCommand in the artifact', async () => { |
| 432 | + const secretValue = 'super-secret-token-12345'; |
| 433 | + const configWithSecret = { |
| 434 | + ...STUB_CONFIG, |
| 435 | + auditDir: '/tmp/awf-audit', |
| 436 | + agentCommand: `my-agent --token ${secretValue}`, |
| 437 | + }; |
| 438 | + mockedValidateOptions.validateOptions.mockReturnValue( |
| 439 | + configWithSecret as unknown as import('../types').WrapperConfig |
| 440 | + ); |
| 441 | + // Make redactSecrets actually remove the secret value |
| 442 | + mockedRedactSecrets.redactSecrets.mockImplementation((s: string) => |
| 443 | + s.replace(secretValue, '[REDACTED]') |
| 444 | + ); |
| 445 | + |
| 446 | + const action = createMainAction(getOptionValueSource); |
| 447 | + await action(['echo hi'], {}); |
| 448 | + |
| 449 | + const written = mockWriteFileSync.mock.calls.find( |
| 450 | + (c) => String(c[0]).includes('awf-resolved-config.json') |
| 451 | + ); |
| 452 | + expect(written).toBeDefined(); |
| 453 | + const writtenJson = String(written![1]); |
| 454 | + expect(writtenJson).not.toContain(secretValue); |
| 455 | + expect(writtenJson).toContain('[REDACTED]'); |
| 456 | + }); |
| 457 | + |
| 458 | + it('falls back to workDir/audit when auditDir is not set', async () => { |
| 459 | + mockedValidateOptions.validateOptions.mockReturnValue(STUB_CONFIG); |
| 460 | + const action = createMainAction(getOptionValueSource); |
| 461 | + await action(['echo hi'], {}); |
| 462 | + |
| 463 | + expect(mockWriteFileSync).toHaveBeenCalledWith( |
| 464 | + '/tmp/awf-test/audit/awf-resolved-config.json', |
| 465 | + expect.any(String), |
| 466 | + { mode: 0o644 }, |
| 467 | + ); |
| 468 | + }); |
| 469 | + }); |
376 | 470 | }); |
0 commit comments