agentsys 5.10.0 → 5.12.0

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agentsys",
3
3
  "description": "20 specialized plugins for AI workflow automation - task orchestration, PR workflow, slop detection, code review, drift detection, enhancement analysis, documentation sync, unified static analysis, perf investigations, topic research, agent config linting, cross-tool AI consultation, structured AI debate, workflow pattern learning, codebase onboarding, contributor guidance, and Zig language support",
4
- "version": "5.10.0",
4
+ "version": "5.12.0",
5
5
  "owner": {
6
6
  "name": "Avi Fenesh",
7
7
  "url": "https://github.com/avifenesh"
@@ -27,11 +27,11 @@
27
27
  "source": {
28
28
  "source": "url",
29
29
  "url": "https://github.com/agent-sh/next-task.git",
30
- "ref": "v1.1.1",
31
- "commit": "9aa32b856d81ebeeb6c6ea0ab421c80d6986a7b1"
30
+ "ref": "v1.1.2",
31
+ "commit": "8feba0141a895d651a850cbe724a0e333c24a3a0"
32
32
  },
33
33
  "description": "Master workflow orchestrator: autonomous workflow with model optimization (opus/sonnet/haiku), two-file state management, workflow enforcement gates, 8 specialist agents",
34
- "version": "1.1.1",
34
+ "version": "1.1.2",
35
35
  "category": "productivity",
36
36
  "homepage": "https://github.com/agent-sh/next-task"
37
37
  },
@@ -40,10 +40,11 @@
40
40
  "source": {
41
41
  "source": "url",
42
42
  "url": "https://github.com/agent-sh/prepare-delivery.git",
43
- "commit": "2e8f400115d8df68e6e8e02466a97117c7f86fab"
43
+ "commit": "0e9685fe0e93e058af5ca9a0374f4f97e1db878c",
44
+ "ref": "v0.1.2"
44
45
  },
45
46
  "description": "Pre-ship quality gates: deslop, simplify, agnix, enhance, review loop, delivery validation, docs sync",
46
- "version": "0.1.0",
47
+ "version": "0.1.2",
47
48
  "category": "productivity",
48
49
  "homepage": "https://github.com/agent-sh/prepare-delivery"
49
50
  },
@@ -64,11 +65,11 @@
64
65
  "source": {
65
66
  "source": "url",
66
67
  "url": "https://github.com/agent-sh/ship.git",
67
- "commit": "3e65dcf5f6aa60e8e42baff14eef0c2209302751",
68
- "ref": "v1.1.1"
68
+ "commit": "189da6af2abdf67ab661098af2fc18453fe9e734",
69
+ "ref": "v1.1.2"
69
70
  },
70
71
  "description": "Complete PR workflow: commit to production, skips review when called from next-task, removes task from registry on cleanup, automatic rollback",
71
- "version": "1.1.1",
72
+ "version": "1.1.2",
72
73
  "category": "deployment",
73
74
  "homepage": "https://github.com/agent-sh/ship"
74
75
  },
@@ -77,7 +78,7 @@
77
78
  "source": {
78
79
  "source": "url",
79
80
  "url": "https://github.com/agent-sh/deslop.git",
80
- "commit": "be3ac2396dcabad450e5097e4f22f9f4e166a143"
81
+ "commit": "00301b9ce81d12caa38063e4a65b535ba5b011b2"
81
82
  },
82
83
  "description": "3-phase AI slop detection: regex patterns (HIGH), multi-pass analyzers (MEDIUM), CLI tools (LOW)",
83
84
  "version": "1.0.0",
@@ -89,10 +90,11 @@
89
90
  "source": {
90
91
  "source": "url",
91
92
  "url": "https://github.com/agent-sh/audit-project.git",
92
- "commit": "a080ebf74184ba3ad2de19100e2b3a818c8a194c"
93
+ "commit": "f703facec38765b6fd8cb5a2076c98bf14e5998e",
94
+ "ref": "v1.0.2"
93
95
  },
94
96
  "description": "Multi-agent iterative code review until zero issues remain",
95
- "version": "1.0.0",
97
+ "version": "1.0.2",
96
98
  "category": "development",
97
99
  "homepage": "https://github.com/agent-sh/audit-project"
98
100
  },
@@ -101,7 +103,7 @@
101
103
  "source": {
102
104
  "source": "url",
103
105
  "url": "https://github.com/agent-sh/drift-detect.git",
104
- "commit": "880c2ce1f0d637a947229281aea9b9a40156b6a4"
106
+ "commit": "576aca37402068aef175c8a6002584e19cb6ea74"
105
107
  },
106
108
  "description": "Deep repository analysis to realign project plans with code reality - detects drift, gaps, and creates prioritized reconstruction plans",
107
109
  "version": "1.0.0",
@@ -113,7 +115,7 @@
113
115
  "source": {
114
116
  "source": "url",
115
117
  "url": "https://github.com/agent-sh/enhance.git",
116
- "commit": "081e6b1c90bbd5b7297a18c6d01e86693825c113"
118
+ "commit": "93f299e494a3a9ea74e82bd2d15bc1c517ce8f0c"
117
119
  },
118
120
  "description": "Master enhancement orchestrator: parallel analyzer execution for plugins, agents, docs, CLAUDE.md, and prompts with unified reporting",
119
121
  "version": "1.0.0",
@@ -125,7 +127,7 @@
125
127
  "source": {
126
128
  "source": "url",
127
129
  "url": "https://github.com/agent-sh/sync-docs.git",
128
- "commit": "f8281a98f440577934b67dc9ef3f9da85d56f9de"
130
+ "commit": "410e06739da101583b7238669f4acad7f5aea7ab"
129
131
  },
130
132
  "description": "Standalone documentation sync: find outdated refs, update CHANGELOG, flag stale examples based on code changes",
131
133
  "version": "1.0.0",
@@ -150,10 +152,11 @@
150
152
  "source": {
151
153
  "source": "url",
152
154
  "url": "https://github.com/agent-sh/perf.git",
153
- "commit": "cc988ec68863a18243297d216e2fcd37802f2296"
155
+ "commit": "189eb15e22bb6678da4d773f1c52b57d8880abff",
156
+ "ref": "v1.0.1"
154
157
  },
155
158
  "description": "Rigorous performance investigation workflow with baselines, profiling, hypotheses, and evidence-backed decisions",
156
- "version": "1.0.0",
159
+ "version": "1.0.1",
157
160
  "category": "development",
158
161
  "homepage": "https://github.com/agent-sh/perf"
159
162
  },
@@ -162,7 +165,7 @@
162
165
  "source": {
163
166
  "source": "url",
164
167
  "url": "https://github.com/agent-sh/learn.git",
165
- "commit": "91983c1fe35b96bcda5360f4155465d69a0b01fc"
168
+ "commit": "b3025d376a83841078f3ab5cf53b62c6960e46c3"
166
169
  },
167
170
  "description": "Research topics online and create comprehensive learning guides with RAG-optimized indexes",
168
171
  "version": "1.0.0",
@@ -187,7 +190,7 @@
187
190
  "source": {
188
191
  "source": "url",
189
192
  "url": "https://github.com/agent-sh/consult.git",
190
- "commit": "71a08ef5566cfb189d5161c2d0e31542d2c99155"
193
+ "commit": "8ce96f86c0ae67f732383a8b45c9994a7cd64d2a"
191
194
  },
192
195
  "description": "Cross-tool AI consultation: get second opinions from Gemini CLI, Codex CLI, Claude Code, OpenCode, or Copilot CLI with model and thinking effort control",
193
196
  "version": "1.0.0",
@@ -199,10 +202,11 @@
199
202
  "source": {
200
203
  "source": "url",
201
204
  "url": "https://github.com/agent-sh/debate.git",
202
- "commit": "95c0333b432aa8b0911e9accccecb219d3bc3ad7"
205
+ "commit": "aba659706bd25f7e394096acb457446e44966711",
206
+ "ref": "v1.0.1"
203
207
  },
204
208
  "description": "Structured multi-round debate between AI tools with proposer/challenger roles and verdict",
205
- "version": "1.0.0",
209
+ "version": "1.0.1",
206
210
  "category": "productivity",
207
211
  "homepage": "https://github.com/agent-sh/debate"
208
212
  },
@@ -224,10 +228,11 @@
224
228
  "source": {
225
229
  "source": "url",
226
230
  "url": "https://github.com/agent-sh/skillers.git",
227
- "commit": "88efb0346b2582a224f3f85db72450b9f3ba7507"
231
+ "commit": "e1c1a9b752c0d20a0a1f83747c26e5dea195b5ae",
232
+ "ref": "v0.2.1"
228
233
  },
229
234
  "description": "Learn from workflow patterns across sessions and suggest skills, hooks, and agents to automate repetitive work",
230
- "version": "1.0.0",
235
+ "version": "0.2.1",
231
236
  "category": "productivity",
232
237
  "homepage": "https://github.com/agent-sh/skillers"
233
238
  },
@@ -236,11 +241,11 @@
236
241
  "source": {
237
242
  "source": "url",
238
243
  "url": "https://github.com/agent-sh/onboard.git",
239
- "ref": "v0.1.0",
240
- "commit": "7444d6475055897498a348639dd0bcb12ba7906b"
244
+ "ref": "v0.1.1",
245
+ "commit": "6c2e47e567aac6249a0df6d15491cbcd42ce7717"
241
246
  },
242
247
  "description": "Codebase onboarding - automated data collection and interactive project orientation",
243
- "version": "0.1.0",
248
+ "version": "0.1.1",
244
249
  "category": "productivity",
245
250
  "homepage": "https://github.com/agent-sh/onboard"
246
251
  },
@@ -249,11 +254,11 @@
249
254
  "source": {
250
255
  "source": "url",
251
256
  "url": "https://github.com/agent-sh/can-i-help.git",
252
- "ref": "v0.1.0",
253
- "commit": "5610a54ce9200577879a0ad8a9dc174133f56abf"
257
+ "ref": "v0.1.1",
258
+ "commit": "f1364158deb359b581d7113a54e8a6aa7a6d8679"
254
259
  },
255
260
  "description": "Find where to contribute to any project - matches developer skills to test gaps, stale docs, bugspots, and open issues",
256
- "version": "0.1.0",
261
+ "version": "0.1.1",
257
262
  "category": "productivity",
258
263
  "homepage": "https://github.com/agent-sh/can-i-help"
259
264
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentsys",
3
- "version": "5.10.0",
3
+ "version": "5.12.0",
4
4
  "description": "Professional-grade slash commands for Claude Code with cross-platform support",
5
5
  "keywords": [
6
6
  "workflow",
package/CHANGELOG.md CHANGED
@@ -9,6 +9,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [5.12.0] - 2026-04-26
13
+
14
+ ### Propagated upstream releases
15
+ - agent-core v0.4.4 (fixer.js symlink + TOCTOU) -> v0.4.5 (client-side SLSA verification + sync allowlist) synced into all 13 consumers.
16
+ - agent-analyzer v0.8.0 -> v0.8.1 (cargo-deny CI).
17
+ - prepare-delivery v0.1.2, audit-project v1.0.2 (reviewer-contract markers + orchestrator blocked handling).
18
+
19
+ ## [5.11.0] - 2026-04-26
20
+
21
+ ### Changed
22
+ - **Upgraded marketplace sub-plugin pins from SHA-only to tag+SHA** after each downstream plugin cut security releases. Post-run totals: 12 pinned to tags, 8 fell back to default-branch SHA (up from 7/13 in v5.10.0). New tag pins in this wave: `prepare-delivery` v0.1.1, `audit-project` v1.0.1, `next-task` v1.1.2, `ship` v1.1.2, `skillers` v0.2.1, `onboard` v0.1.1, `can-i-help` v0.1.1, `perf` v1.0.1, `debate` v1.0.1. Consumers now install from verifiable release tags for these plugins.
23
+
24
+ ### Propagated upstream security fixes
25
+ - agent-core v0.4.4 synced into all 13 consumers via `lib/`: fixer.js symlink + TOCTOU guards (#14 agent-core), earlier v0.4.3 code-point-safe truncate + sync-workflow test-file exclusion, v0.4.2 additive sync + upstreamed workflow-state/queries, v0.4.1 binary SHA-256 + zip-slip defenses.
26
+ - prepare-delivery + audit-project: falsePositive review-bypass cap (50% ratio + required reason).
27
+ - next-task: worktree-manager TASK_ID/BASE_BRANCH validation.
28
+ - ship: platform-API health checks instead of log-grep rollback DoS.
29
+ - skillers: transcript redaction pipeline (ported from consult).
30
+ - onboard + can-i-help: explicit argv arrays in collector git invocations.
31
+ - perf: command-parser error message accuracy.
32
+ - debate: SKILL.md routes AI CLI invocations through consult's hardened ACP transport.
33
+
12
34
  ## [5.10.0] - 2026-04-26
13
35
 
14
36
  ### Security
@@ -19,6 +19,22 @@
19
19
  * script validates every zip entry before extracting it and rejects
20
20
  * absolute, UNC, and parent-traversal entries.
21
21
  *
22
+ * Verification chain (in order, each gate must pass to proceed):
23
+ * 1. TLS - https.get() pins the GitHub CA chain at the OS level.
24
+ * 2. SHA-256 sidecar - `<asset>.sha256` fetched from the same release and
25
+ * verified against the downloaded bytes. Closes basic tampering.
26
+ * 3. SLSA build provenance (optional / required) - `gh attestation verify`
27
+ * checks the Sigstore-signed attestation that agent-analyzer's release
28
+ * workflow publishes via `actions/attest-build-provenance`. This closes
29
+ * the "stolen release token uploads attacker binary + attacker sha256"
30
+ * hole that steps 1 and 2 cannot see.
31
+ *
32
+ * SLSA verification is SOFT by default: if `gh` is not on PATH we log
33
+ * a warning and proceed with just SHA-256. Set env var
34
+ * `AGENT_ANALYZER_REQUIRE_ATTESTATION=1` to make a missing `gh` a hard
35
+ * failure (recommended for CI). A present `gh` that reports a failed
36
+ * verification is ALWAYS a hard failure regardless of the env var.
37
+ *
22
38
  * @module lib/binary
23
39
  */
24
40
 
@@ -572,6 +588,117 @@ function findBinaryInScratch(scratch, binaryBaseName) {
572
588
  return null;
573
589
  }
574
590
 
591
+ // ---------------------------------------------------------------------------
592
+ // SLSA build provenance verification
593
+ // ---------------------------------------------------------------------------
594
+
595
+ /**
596
+ * Result of an attempted SLSA attestation verification.
597
+ * @typedef {Object} SlsaResult
598
+ * @property {'verified'|'skipped'|'failed'} status
599
+ * @property {string} [reason] human-readable detail (for skipped/failed)
600
+ * @property {string} [stderr] captured stderr from `gh` (failed only)
601
+ */
602
+
603
+ /**
604
+ * Default runner: spawn `gh attestation verify` and return the captured
605
+ * exit code, stdout, and stderr. Injectable for tests.
606
+ * @param {string} filePath
607
+ * @param {string} repo e.g. `agent-sh/agent-analyzer`
608
+ * @returns {{ status: number|null, stdout: string, stderr: string }}
609
+ */
610
+ function defaultGhRunner(filePath, repo) {
611
+ try {
612
+ const stdout = cp.execFileSync(
613
+ 'gh',
614
+ ['attestation', 'verify', filePath, '--repo', repo, '--format', 'json'],
615
+ {
616
+ encoding: 'utf8',
617
+ stdio: ['ignore', 'pipe', 'pipe'],
618
+ timeout: 60000,
619
+ windowsHide: true
620
+ }
621
+ );
622
+ return { status: 0, stdout: stdout || '', stderr: '' };
623
+ } catch (err) {
624
+ return {
625
+ status: typeof err.status === 'number' ? err.status : null,
626
+ stdout: err.stdout ? String(err.stdout) : '',
627
+ stderr: err.stderr ? String(err.stderr) : (err.message || '')
628
+ };
629
+ }
630
+ }
631
+
632
+ /**
633
+ * Returns true if the `gh` CLI is on PATH. Uses a short, non-privileged probe.
634
+ * @param {function} [runner] optional probe; defaults to real `gh --version`
635
+ * @returns {boolean}
636
+ */
637
+ function isGhAvailable(runner) {
638
+ if (typeof runner === 'function') {
639
+ try { return !!runner(); } catch (e) { return false; }
640
+ }
641
+ try {
642
+ cp.execFileSync('gh', ['--version'], {
643
+ stdio: 'ignore',
644
+ timeout: 5000,
645
+ windowsHide: true
646
+ });
647
+ return true;
648
+ } catch (e) {
649
+ return false;
650
+ }
651
+ }
652
+
653
+ /**
654
+ * Verify a downloaded asset's SLSA build provenance attestation via the
655
+ * GitHub CLI. The check is SOFT by default: if `gh` is not installed the
656
+ * function returns { status: 'skipped' } and the caller logs a warning. Set
657
+ * `requireAttestation` (or the env var) to make a missing `gh` a failure.
658
+ *
659
+ * A present `gh` that reports verification failure ALWAYS returns
660
+ * { status: 'failed' } regardless of `requireAttestation`; the caller is
661
+ * expected to abort in that case.
662
+ *
663
+ * @param {string} filePath absolute path to the downloaded archive
664
+ * @param {Object} [options]
665
+ * @param {string} [options.repo] e.g. `agent-sh/agent-analyzer`
666
+ * @param {boolean} [options.requireAttestation] defaults to env
667
+ * `AGENT_ANALYZER_REQUIRE_ATTESTATION === '1'`
668
+ * @param {function} [options.ghRunner] injectable runner for tests. Receives
669
+ * (filePath, repo), returns { status, stdout, stderr }.
670
+ * @param {function} [options.ghProbe] injectable gh-on-PATH probe for tests.
671
+ * @returns {SlsaResult}
672
+ */
673
+ function verifySlsaAttestation(filePath, options) {
674
+ const opts = options || {};
675
+ const repo = opts.repo || GITHUB_REPO;
676
+ const runner = typeof opts.ghRunner === 'function' ? opts.ghRunner : defaultGhRunner;
677
+ const require_ = typeof opts.requireAttestation === 'boolean'
678
+ ? opts.requireAttestation
679
+ : process.env.AGENT_ANALYZER_REQUIRE_ATTESTATION === '1';
680
+
681
+ const ghPresent = isGhAvailable(opts.ghProbe);
682
+ if (!ghPresent) {
683
+ const reason = '`gh` CLI not found on PATH';
684
+ if (require_) {
685
+ return { status: 'failed', reason: reason + ' (AGENT_ANALYZER_REQUIRE_ATTESTATION=1)' };
686
+ }
687
+ return { status: 'skipped', reason: reason };
688
+ }
689
+
690
+ const result = runner(filePath, repo);
691
+ if (result && result.status === 0) {
692
+ return { status: 'verified' };
693
+ }
694
+ return {
695
+ status: 'failed',
696
+ reason: 'gh attestation verify exited with status ' +
697
+ (result && result.status !== null ? result.status : 'unknown'),
698
+ stderr: (result && result.stderr) || ''
699
+ };
700
+ }
701
+
575
702
  // ---------------------------------------------------------------------------
576
703
  // Download + install
577
704
  // ---------------------------------------------------------------------------
@@ -582,11 +709,19 @@ function findBinaryInScratch(scratch, binaryBaseName) {
582
709
  * @param {Object} [options]
583
710
  * @param {boolean} [options.skipChecksum=false] LOCAL DEV ONLY. Skips the
584
711
  * `.sha256` sidecar fetch and verification. NEVER set this in production.
712
+ * @param {boolean} [options.skipAttestation=false] LOCAL DEV ONLY. Skips the
713
+ * SLSA attestation check entirely.
714
+ * @param {boolean} [options.requireAttestation] when true, a missing `gh`
715
+ * CLI becomes a hard failure. Defaults to
716
+ * `process.env.AGENT_ANALYZER_REQUIRE_ATTESTATION === '1'`.
717
+ * @param {function} [options.ghRunner] injectable runner for tests.
718
+ * @param {function} [options.ghProbe] injectable gh-on-PATH probe for tests.
585
719
  * @returns {Promise<string>} path to the installed binary
586
720
  */
587
721
  async function downloadBinary(ver, options) {
588
722
  const opts = options || {};
589
723
  const skipChecksum = opts.skipChecksum === true;
724
+ const skipAttestation = opts.skipAttestation === true;
590
725
 
591
726
  const platformKey = getPlatformKey();
592
727
  if (!platformKey) {
@@ -643,6 +778,47 @@ async function downloadBinary(ver, options) {
643
778
  verifySha256(buf, expected, filename);
644
779
  }
645
780
 
781
+ // --- 2b. Verify SLSA build provenance (optional / required) ------------
782
+ if (skipAttestation) {
783
+ process.stderr.write(
784
+ '[WARN] skipAttestation=true - SLSA verification disabled. ' +
785
+ 'This is LOCAL DEV ONLY and MUST NOT be used in production.\n'
786
+ );
787
+ } else {
788
+ // `gh attestation verify` needs a real file. Persist buf to a tmp path,
789
+ // verify, then drop it. Extraction continues from the in-memory buf so
790
+ // we don't need the tmp file beyond the verify call.
791
+ const attestDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-analyzer-slsa-'));
792
+ const attestFile = path.join(attestDir, filename);
793
+ try {
794
+ fs.writeFileSync(attestFile, buf);
795
+ const result = verifySlsaAttestation(attestFile, {
796
+ repo: GITHUB_REPO,
797
+ requireAttestation: opts.requireAttestation,
798
+ ghRunner: opts.ghRunner,
799
+ ghProbe: opts.ghProbe
800
+ });
801
+ if (result.status === 'verified') {
802
+ process.stderr.write('[OK] SLSA attestation verified for ' + filename + '\n');
803
+ } else if (result.status === 'skipped') {
804
+ process.stderr.write(
805
+ '[WARN] SLSA attestation check skipped: ' + result.reason + '. ' +
806
+ 'Install the GitHub CLI (`gh`) to enable provenance verification. ' +
807
+ 'Set AGENT_ANALYZER_REQUIRE_ATTESTATION=1 to require it.\n'
808
+ );
809
+ } else {
810
+ // 'failed'
811
+ throw new Error(
812
+ 'SLSA attestation verification failed for ' + filename + ': ' +
813
+ result.reason + '. Refusing to execute binary.' +
814
+ (result.stderr ? '\n--- gh stderr ---\n' + result.stderr : '')
815
+ );
816
+ }
817
+ } finally {
818
+ rmrf(attestDir);
819
+ }
820
+ }
821
+
646
822
  // --- 3. Extract to isolated scratch dir + validate entries -------------
647
823
  const binaryBaseName = path.basename(binPath);
648
824
  let scratch;
@@ -707,7 +883,13 @@ async function ensureBinary(options) {
707
883
  }
708
884
  }
709
885
 
710
- return downloadBinary(targetVer, { skipChecksum: opts.skipChecksum === true });
886
+ return downloadBinary(targetVer, {
887
+ skipChecksum: opts.skipChecksum === true,
888
+ skipAttestation: opts.skipAttestation === true,
889
+ requireAttestation: opts.requireAttestation,
890
+ ghRunner: opts.ghRunner,
891
+ ghProbe: opts.ghProbe
892
+ });
711
893
  }
712
894
 
713
895
  /**
@@ -730,11 +912,27 @@ function ensureBinarySync(options) {
730
912
 
731
913
  const targetVer = (options && options.version) || ANALYZER_MIN_VERSION;
732
914
  const skipChecksum = !!(options && options.skipChecksum);
915
+ const skipAttestation = !!(options && options.skipAttestation);
916
+ // Forward requireAttestation when explicitly set (tri-state: undefined
917
+ // lets the child fall back to the AGENT_ANALYZER_REQUIRE_ATTESTATION
918
+ // env var, matching ensureBinary()). Without this forwarding, a sync
919
+ // caller with requireAttestation:true would silently lose the hard-fail
920
+ // intent when gh is missing.
921
+ const requireAttestation = options && typeof options.requireAttestation === 'boolean'
922
+ ? options.requireAttestation
923
+ : undefined;
733
924
  const selfPath = __filename;
925
+ const ensureOpts = {
926
+ version: targetVer,
927
+ skipChecksum: skipChecksum,
928
+ skipAttestation: skipAttestation
929
+ };
930
+ if (requireAttestation !== undefined) {
931
+ ensureOpts.requireAttestation = requireAttestation;
932
+ }
734
933
  const helperLines = [
735
934
  'var b = require(' + JSON.stringify(selfPath) + ');',
736
- 'b.ensureBinary({ version: ' + JSON.stringify(targetVer) +
737
- ', skipChecksum: ' + JSON.stringify(skipChecksum) + ' })',
935
+ 'b.ensureBinary(' + JSON.stringify(ensureOpts) + ')',
738
936
  ' .then(function(p) { process.stdout.write(p); })',
739
937
  ' .catch(function(e) { process.stderr.write(e.message); process.exit(1); });'
740
938
  ];
@@ -798,6 +996,8 @@ module.exports = {
798
996
  assertSafeArchiveEntry,
799
997
  assertInsideRoot,
800
998
  downloadBinary,
999
+ verifySlsaAttestation,
1000
+ isGhAvailable,
801
1001
  // Exported for tests only
802
1002
  extractTarGzToScratch,
803
1003
  extractZipToScratch,
@@ -7,6 +7,37 @@
7
7
  const fs = require('fs');
8
8
  const path = require('path');
9
9
 
10
+ /**
11
+ * Reject symlinks before read/write operations.
12
+ *
13
+ * Security: A hostile repo could symlink a fixable file (e.g. `agent.md`) to a
14
+ * sensitive target (e.g. `~/.ssh/authorized_keys`). A HIGH-certainty auto-fix
15
+ * would then silently overwrite that target. We refuse to follow symlinks on
16
+ * any path we intend to read from or write to, including `.backup` siblings.
17
+ *
18
+ * This is called both before opening and immediately before writing, which
19
+ * narrows - though does not fully close - the TOCTOU window between calls.
20
+ * Node's fs module does not expose a portable `O_NOFOLLOW` open flag, so
21
+ * repeated lstat is the cleanest available mitigation for text-file edits.
22
+ *
23
+ * @param {string} targetPath - Path to check.
24
+ * @throws {Error} If the path exists and is a symlink.
25
+ */
26
+ function assertNotSymlink(targetPath) {
27
+ let stat;
28
+ try {
29
+ stat = fs.lstatSync(targetPath);
30
+ } catch (err) {
31
+ if (err.code === 'ENOENT') return; // Path does not yet exist - fine.
32
+ throw err;
33
+ }
34
+ if (stat.isSymbolicLink()) {
35
+ const err = new Error('target is a symlink; refusing to follow');
36
+ err.code = 'ESYMLINK_REFUSED';
37
+ throw err;
38
+ }
39
+ }
40
+
10
41
  function applyFixes(issues, options = {}) {
11
42
  const { dryRun = false, backup = true } = options;
12
43
 
@@ -59,6 +90,23 @@ function applyFixes(issues, options = {}) {
59
90
  continue;
60
91
  }
61
92
 
93
+ // Security: refuse symlinks before we read, so a hostile repo can't
94
+ // redirect a HIGH-certainty fix at ~/.ssh/authorized_keys or similar.
95
+ try {
96
+ assertNotSymlink(filePath);
97
+ } catch (err) {
98
+ if (err.code === 'ESYMLINK_REFUSED') {
99
+ results.errors.push({
100
+ filePath,
101
+ error: err.message,
102
+ success: false,
103
+ reason: 'target is a symlink; refusing to follow'
104
+ });
105
+ continue;
106
+ }
107
+ throw err;
108
+ }
109
+
62
110
  const content = fs.readFileSync(filePath, 'utf8');
63
111
  let data;
64
112
 
@@ -135,6 +183,8 @@ function applyFixes(issues, options = {}) {
135
183
  // Create backup
136
184
  if (backup) {
137
185
  const backupPath = `${filePath}.backup`;
186
+ // Refuse if the backup slot itself is a pre-existing symlink.
187
+ assertNotSymlink(backupPath);
138
188
  fs.writeFileSync(backupPath, content, 'utf8');
139
189
  }
140
190
 
@@ -145,6 +195,11 @@ function applyFixes(issues, options = {}) {
145
195
  } else {
146
196
  newContent = JSON.stringify(modified, null, 2);
147
197
  }
198
+ // Re-check immediately before write. Narrows the TOCTOU window
199
+ // between the initial lstat and this writeFileSync (an attacker
200
+ // who swaps the regular file for a symlink between calls will
201
+ // be caught here).
202
+ assertNotSymlink(filePath);
148
203
  fs.writeFileSync(filePath, newContent, 'utf8');
149
204
  }
150
205
 
@@ -280,7 +335,14 @@ function restoreFromBackup(filePath) {
280
335
  return false;
281
336
  }
282
337
 
338
+ // Security: refuse if either the backup or the restore target is a
339
+ // symlink. Same threat model as applyFixes - a malicious post-hoc swap
340
+ // could redirect the restore at a sensitive file.
341
+ assertNotSymlink(backupPath);
342
+ assertNotSymlink(filePath);
343
+
283
344
  const backupContent = fs.readFileSync(backupPath, 'utf8');
345
+ assertNotSymlink(filePath);
284
346
  fs.writeFileSync(filePath, backupContent, 'utf8');
285
347
  fs.unlinkSync(backupPath);
286
348
 
@@ -717,5 +779,6 @@ module.exports = {
717
779
  fixAggressiveEmphasis,
718
780
  previewFixes,
719
781
  restoreFromBackup,
720
- cleanupBackups
782
+ cleanupBackups,
783
+ assertNotSymlink
721
784
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentsys",
3
- "version": "5.10.0",
3
+ "version": "5.12.0",
4
4
  "description": "A modular runtime and orchestration system for AI agents - works with Claude Code, OpenCode, and Codex CLI",
5
5
  "main": "lib/platform/detect-platform.js",
6
6
  "type": "commonjs",
package/site/content.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "url": "https://agent-sh.github.io/agentsys",
6
6
  "repo": "https://github.com/agent-sh/agentsys",
7
7
  "npm": "https://www.npmjs.com/package/agentsys",
8
- "version": "5.10.0",
8
+ "version": "5.12.0",
9
9
  "author": "Avi Fenesh",
10
10
  "author_url": "https://github.com/avifenesh"
11
11
  },