agentic-workflow-manager 6.5.0 → 6.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,7 @@ exports.parseCoverageContract = parseCoverageContract;
5
5
  exports.parseCoverageManifest = parseCoverageManifest;
6
6
  exports.COVERAGE_SCHEMA_VERSION = 1;
7
7
  exports.MAX_COVERAGE_FILE_BYTES = 1024 * 1024;
8
+ const SAFE_EVIDENCE_DOTFILES = Object.freeze(['.semgrep.awm.yml', '.dep-cruiser.awm.js']);
8
9
  function isRecord(value) {
9
10
  return typeof value === 'object' && value !== null && !Array.isArray(value);
10
11
  }
@@ -37,6 +38,13 @@ function safeName(value, source, location) {
37
38
  }
38
39
  return name;
39
40
  }
41
+ function safeEvidenceFilename(value, source, location) {
42
+ const name = nonEmptyString(value, source, location);
43
+ if (name === '.' || name === '..' || name.includes('..') || /[/\\]/.test(name) || !/^[A-Za-z0-9._-]+$/.test(name) || (name.startsWith('.') && !SAFE_EVIDENCE_DOTFILES.includes(name))) {
44
+ invalid(source, `${location} must be a safe evidence filename component`);
45
+ }
46
+ return name;
47
+ }
40
48
  function stringArray(value, source, location, allowEmpty) {
41
49
  if (!Array.isArray(value) || (!allowEmpty && value.length === 0)) {
42
50
  invalid(source, `${location} must be ${allowEmpty ? 'an array' : 'a nonempty array'}`);
@@ -46,7 +54,7 @@ function stringArray(value, source, location, allowEmpty) {
46
54
  function parseFileRequirement(input, source, location) {
47
55
  const value = record(input, source, location);
48
56
  fields(value, ['path', 'containsAll'], source, location);
49
- const path = safeName(value.path, source, `${location}.path`);
57
+ const path = safeEvidenceFilename(value.path, source, `${location}.path`);
50
58
  const containsAll = stringArray(value.containsAll, source, `${location}.containsAll`, true);
51
59
  return { path, containsAll };
52
60
  }
@@ -32,7 +32,7 @@ describe('coverage contract v1', () => {
32
32
  ])('rejects malformed contract %j', (input, message) => {
33
33
  expect(() => (0, contract_1.parseCoverageContract)(input, 'coverage.json')).toThrow(message);
34
34
  });
35
- test.each(['', '.', '..', '../secret', 'a/../../secret', '/etc/passwd', 'C:\\secret', 'a\\..\\secret', ' report.txt', 'report!.txt'])('rejects hostile evidence path %p', (path) => {
35
+ test.each(['', '.', '..', 'a..b', '../secret', 'a/../../secret', '/etc/passwd', 'C:\\secret', 'a\\..\\secret', ' report.txt', 'report!.txt', 'ñ.txt', '.env', '.gitignore'])('rejects hostile evidence path %p', (path) => {
36
36
  const input = {
37
37
  schemaVersion: 1,
38
38
  classes: {
@@ -45,6 +45,19 @@ describe('coverage contract v1', () => {
45
45
  };
46
46
  expect(() => (0, contract_1.parseCoverageContract)(input, 'coverage.json')).toThrow('path');
47
47
  });
48
+ test.each(['.semgrep.awm.yml', '.dep-cruiser.awm.js'])('accepts safe dotfile evidence path %p', (path) => {
49
+ const input = {
50
+ schemaVersion: 1,
51
+ classes: {
52
+ valid: {
53
+ description: 'x',
54
+ detectors: [{ sensor: 'test', evidence: { files: [{ path, containsAll: [] }] } }],
55
+ remedy: { summary: 'x', command: 'x' },
56
+ },
57
+ },
58
+ };
59
+ expect((0, contract_1.parseCoverageContract)(input, 'coverage.json')).toEqual(input);
60
+ });
48
61
  it('rejects whitespace-only evidence text', () => {
49
62
  const input = {
50
63
  schemaVersion: 1,
@@ -76,13 +76,17 @@ describe('R2 provider evidence integrity', () => {
76
76
  expect(claude.semanticContract).toEqual(codex.semanticContract);
77
77
  });
78
78
  it('does not write Claude evidence when its required executable is unavailable', () => {
79
+ // Certified evidence is now committed at this path (RNF-T.2), so this test cannot
80
+ // assume the file is absent. Instead, force the attestation to fail by hiding
81
+ // `claude` from PATH and assert the file is left byte-for-byte untouched.
79
82
  const output = path_1.default.join(evidenceDir, 'claude-code.json');
80
- expect(fs_1.default.existsSync(output)).toBe(false);
83
+ const before = fs_1.default.existsSync(output) ? fs_1.default.readFileSync(output, 'utf8') : null;
81
84
  const result = (0, child_process_1.spawnSync)(process.execPath, [runner, 'claude-code', path_1.default.resolve(__dirname, '../..')], {
82
- cwd: path_1.default.resolve(__dirname, '../../..'), encoding: 'utf8',
85
+ cwd: path_1.default.resolve(__dirname, '../../..'), encoding: 'utf8', env: { ...process.env, PATH: '' },
83
86
  });
84
87
  expect(result.status).not.toBe(0);
85
88
  expect(`${result.stdout}${result.stderr}`).toContain('claude');
86
- expect(fs_1.default.existsSync(output)).toBe(false);
89
+ const after = fs_1.default.existsSync(output) ? fs_1.default.readFileSync(output, 'utf8') : null;
90
+ expect(after).toBe(before);
87
91
  });
88
92
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-workflow-manager",
3
- "version": "6.5.0",
3
+ "version": "6.5.1",
4
4
  "main": "dist/src/index.js",
5
5
  "bin": {
6
6
  "awm": "./dist/src/index.js"