ai-consultants 2.17.0 → 2.17.2

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.
package/CLAUDE.md CHANGED
@@ -6,7 +6,7 @@ AI Consultants is a multi-model AI deliberation system that queries up to 15 AI
6
6
 
7
7
  **Self-Exclusion**: The invoking agent is automatically excluded from the panel to prevent self-consultation. Claude Code won't query Claude, Codex CLI won't query Codex, etc.
8
8
 
9
- **Version**: 2.17.0
9
+ **Version**: 2.17.2
10
10
 
11
11
  ## Distribution
12
12
 
@@ -826,6 +826,19 @@ curl -fsSL https://raw.githubusercontent.com/matteoscurati/ai-consultants/main/s
826
826
 
827
827
  ## Changelog
828
828
 
829
+ ### v2.17.2
830
+ - **SECURITY: revert v2.17.1's `allow_absolute=true` for context files — it opened a secret-exfiltration surface (caught in `/code-review max`).** v2.17.1 fixed the macOS `/private/tmp` drop by letting `build_context.sh` accept *any* absolute context path behind `validate_file_path`'s prefix-only blocklist (`/etc /root /var/log /proc /sys /dev`). That blocklist covers no home secrets, so a context arg of `~/.ssh/id_rsa`, `~/.aws/credentials`, `~/.netrc`, or `~/.config/gh/hosts.yml` was read verbatim and **sent to the external AI providers**. It also matched literal non-canonical prefixes, so `/private/etc/master.passwd` (the real `/etc` on macOS, via the same symlink aliasing) bypassed the `/etc` rule.
831
+ - **Fix (correct altitude — allowlist, not blocklist)**: context files are now accepted only when (a) relative and in-tree (`validate_file_path ... false` — rejects absolute and `..`), or (b) under a recognized temp root via the new `_is_temp_path` helper (`/tmp`, `/private/tmp`, `$TMPDIR`, and the `/private`-prefixed macOS alias of `$TMPDIR`; `..` rejected so a temp prefix can't traverse out). Still fixes the original macOS scratch-file regression (Claude Code writes `/private/tmp/...`) **without** widening to the whole filesystem. Verified: `/tmp`, `/private/tmp`, `$TMPDIR/...`, relative in-tree → accepted; `~/.ssh/id_rsa`, `/etc/passwd`, `/private/etc/master.passwd`, `/tmp/../etc/passwd` → rejected.
832
+ - **`build_context.sh` OUTPUT_FILE**: removed the `/tmp/*` short-circuit that jumped past `validate_file_path` (so `/tmp/../etc/x` bypassed the `..` check). Output paths now always run through `validate_file_path "$OUTPUT_FILE" "true"` — absolute allowed (output lives under the XDG cache or `/tmp`), traversal + sensitive-path guards enforced. The two validation sites are intentionally different now (OUTPUT is tool-chosen; context files are untrusted input) and the comments say so.
833
+ - **Test**: rewrote `test_context_optimization.sh` Test 15 as a *boundary* test — temp-root accepted, `~/.ssh/id_rsa` rejected (exfiltration blocked), `/etc/passwd` rejected. Uses the auto-cleaned `$_TMPDIR` (no `$HOME` litter) and `QUESTION_CATEGORY=SECURITY` to skip the project-tree `find` the assertions don't need. Fixed the stale suite-header comment that contradicted the new behavior. 8 suites pass; shellcheck clean.
834
+ - **Note**: v2.17.1 was tagged and GitHub-released but **never published to npm** (npm stayed at 2.17.0), so no npm user received the vulnerable version. Publish **2.17.2**, not 2.17.1.
835
+
836
+ ### v2.17.1
837
+ - **Fix: context files at absolute paths outside `/tmp` were silently dropped.** `build_context.sh`'s context-file gate accepted only relative paths or a literal `/tmp/*` prefix (`if [[ "$_PARSED_PATH" == /tmp/* ]] || validate_file_path "$_PARSED_PATH" "false"`). On macOS `/tmp` is a symlink to `/private/tmp`, so Claude Code scratch files arrive as `/private/tmp/...` and matched neither branch → `log_warn "Skipping invalid file path"` and the file was excluded, with `build_context.sh` falling back to a generic repo auto-context. Net effect: a consultation looked like it ran with the user's context but the consultants never received it. (Reported from a real session where the passed context was dropped and an auto-context substituted.)
838
+ - **Fix**: the gate now uses `validate_file_path "$_PARSED_PATH" "true"` (allow absolute), mirroring the OUTPUT_FILE handling a few lines above. Context files are explicitly user/agent-provided, so absolute paths are legitimate; the sensitive-path blocklist (`/etc /root /var/log /proc /sys /dev`), path-traversal (`..`), and null-byte guards in `validate_file_path` still apply. Verified: `/private/tmp/...`, `/tmp/...`, relative, and `$HOME/...` paths accepted; `/etc/passwd`, `/var/log/...`, `../../etc/...` still rejected.
839
+ - **Test**: `test_context_optimization.sh` Test 15 — an absolute context path outside `/tmp` (a `$HOME` file) is now included in the built context, and `/etc/passwd` is still skipped. 8 suites pass; shellcheck clean.
840
+ - **Note (not a code change)**: the same session also showed Gemini/Codex failing immediately — because the *installed* skill at `~/.claude/skills/ai-consultants` was **v2.10.0** (pre-agy migration), so Gemini called the deprecated `gemini` binary. Updating the installed skill to ≥v2.15 (now v2.17.1) resolves that; it's an install-staleness issue, not a current-code bug.
841
+
829
842
  ### v2.17.0
830
843
  - **Model catalog refresh (June 2026)** across all three tiers + cost rates, for every agent. Source of truth stays `config.sh::get_model_for_tier` (+ default `*_MODEL` vars) mirrored by `docs/cost_rates.json` (`model_tiers` + `consultant_fallbacks` + per-1K `models` rates). CLI-addressed models verified by querying the installed binaries (`agy models`, `agent --list-models`, kimi config); API/provider models + pricing researched against official sources. Superseded IDs kept in `cost_rates.json` for historical/pinned lookups.
831
844
  - **Changed models** (premium / standard / economy):
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # AI Consultants v2.17.0
1
+ # AI Consultants v2.17.2
2
2
 
3
3
  > **A harness for every question.** A panel of up to 15 frontier models that writes its own playbook per question — fan out, debate to convergence, cross-examine under adversarial review, or run a tournament — and checks its work before it reaches you.
4
4
 
5
- [![Version](https://img.shields.io/badge/version-2.17.0-blue.svg)](https://github.com/matteoscurati/ai-consultants)
5
+ [![Version](https://img.shields.io/badge/version-2.17.2-blue.svg)](https://github.com/matteoscurati/ai-consultants)
6
6
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
7
7
  [![Claude Code Skill](https://img.shields.io/badge/Claude%20Code-Skill-orange.svg)](https://docs.anthropic.com/en/docs/claude-code/skills)
8
8
  [![GitHub stars](https://img.shields.io/github/stars/matteoscurati/ai-consultants?style=social)](https://github.com/matteoscurati/ai-consultants)
package/SKILL.md CHANGED
@@ -5,10 +5,10 @@ license: MIT
5
5
  compatibility: Requires bash, jq, and at least 2 AI CLI tools (agy, codex, vibe, etc.). macOS and Linux.
6
6
  metadata:
7
7
  author: matteoscurati
8
- version: 2.17.0
8
+ version: 2.17.2
9
9
  ---
10
10
 
11
- # AI Consultants v2.17.0 - AI Expert Panel
11
+ # AI Consultants v2.17.2 - AI Expert Panel
12
12
 
13
13
  **A harness for every question.** Convene a panel of AI "consultants" for coding questions — and let the engine pick *how* they deliberate: a quick read, a convergence loop, an adversarial refutation gate, a tournament, or an exhaustive sweep. Each consultant has a **configurable persona** that shapes its analysis.
14
14
 
@@ -1,4 +1,4 @@
1
- # Cost Rates - AI Consultants v2.17.0
1
+ # Cost Rates - AI Consultants v2.17.2
2
2
 
3
3
  This page documents the per-token rates used by the cost tracking system.
4
4
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.17.0",
2
+ "version": "2.17.2",
3
3
  "updated": "2026-06-24",
4
4
  "models": {
5
5
  "_comment_units": "All rates are USD per 1,000 tokens (per-1K). lib/costs.sh computes cost as (token_count / 1000) * rate, so every value here MUST be a per-1K rate = (provider $/1M price) / 1000. Do NOT paste $/MTok figures directly.",
@@ -0,0 +1,46 @@
1
+ # Release v2.17.1
2
+
3
+ **Date:** 2026-06-25
4
+ **Type:** Patch — context-file path fix
5
+ **Previous:** v2.17.0
6
+
7
+ ## Highlights
8
+
9
+ - **Fixes a silent failure where context files you passed were dropped** and replaced with a generic auto-context, so consultants never saw the context you intended.
10
+
11
+ ## What's New
12
+
13
+ ### Absolute context paths are now accepted
14
+
15
+ `build_context.sh` (which assembles the context bundle sent to the consultants) only accepted context files at **relative paths** or under a literal **`/tmp/`** prefix. Any other absolute path was rejected with `Skipping invalid file path` and excluded — and the script quietly fell back to building a generic context from the repository.
16
+
17
+ The most common way to hit this: on macOS, `/tmp` is a symlink to `/private/tmp`, so scratch files (e.g. the ones Claude Code writes) arrive as `/private/tmp/...` and missed the `/tmp/` check. The result looked like a normal consultation, but the consultants received auto-generated repo context instead of what you passed.
18
+
19
+ Context files are explicitly provided by you (or the calling agent), so absolute paths are now accepted. The safety guards are unchanged: the sensitive-path blocklist (`/etc`, `/root`, `/var/log`, `/proc`, `/sys`, `/dev`), path-traversal (`..`), and null-byte checks still reject dangerous paths.
20
+
21
+ ## Breaking Changes
22
+
23
+ None.
24
+
25
+ ## Upgrade Guide
26
+
27
+ ```bash
28
+ # If installed via git clone
29
+ cd ~/.claude/skills/ai-consultants && git pull
30
+
31
+ # If installed via curl | bash
32
+ curl -fsSL https://raw.githubusercontent.com/matteoscurati/ai-consultants/main/scripts/install.sh | bash
33
+
34
+ # Or via npx (always latest)
35
+ npx ai-consultants@latest install
36
+ ```
37
+
38
+ > If your installed skill is well behind (e.g. v2.10.x), update it: pre-v2.15 versions still call the now-deprecated `gemini` CLI, which is why Gemini (and possibly Codex) fail immediately. v2.15+ uses the Antigravity CLI (`agy`).
39
+
40
+ ## Commits
41
+
42
+ - `_______` fix(context): accept absolute context-file paths (macOS /private/tmp was dropped)
43
+
44
+ ## Contributors
45
+
46
+ - @matteoscurati
@@ -0,0 +1,51 @@
1
+ # Release v2.17.2
2
+
3
+ **Date:** 2026-06-25
4
+ **Type:** Patch — security fix (supersedes v2.17.1)
5
+ **Previous:** v2.17.1
6
+
7
+ ## Highlights
8
+
9
+ - **Security fix.** v2.17.1's context-path change was too broad: it let a context file argument point at **any** absolute path — including secrets like `~/.ssh/id_rsa`, `~/.aws/credentials`, or `~/.netrc` — whose contents would then be sent to the external AI consultants. v2.17.2 closes that.
10
+ - The original bug v2.17.1 set out to fix (macOS `/private/tmp` scratch files being dropped) is still fixed — just with a safe allowlist instead of an open door.
11
+
12
+ > ⚠️ **v2.17.1 was tagged and GitHub-released but never published to npm** (npm stayed at 2.17.0), so no npm user received the vulnerable build. Install **2.17.2**.
13
+
14
+ ## What's New
15
+
16
+ ### Context files: allowlist instead of blocklist
17
+
18
+ `build_context.sh` assembles the files you pass and sends their contents to the consultant models. v2.17.1 accepted any absolute path and relied on a small blocklist (`/etc`, `/root`, …) to keep dangerous paths out — but that list covers no home-directory secrets, and it matched non-canonical prefixes (so `/private/etc/master.passwd`, the real `/etc` on macOS, slipped through).
19
+
20
+ v2.17.2 restricts context files to:
21
+ - **relative, in-tree paths** (absolute paths and `..` traversal rejected), or
22
+ - **recognized temp roots**: `/tmp`, `/private/tmp`, and `$TMPDIR` (plus the `/private`-prefixed macOS alias), with `..` rejected so a temp prefix can't traverse out.
23
+
24
+ Everything else — `~/.ssh/id_rsa`, `/etc/passwd`, `/private/etc/master.passwd`, `/tmp/../etc/passwd` — is rejected. The macOS scratch-file case that v2.17.1 targeted (`/private/tmp/...`) still works.
25
+
26
+ ### Output-path hardening
27
+
28
+ `build_context.sh`'s output path no longer short-circuits past the traversal/sensitive-path guards for `/tmp` outputs; all output paths run through `validate_file_path` uniformly.
29
+
30
+ ## Breaking Changes
31
+
32
+ If you relied on passing an **absolute context path outside a temp directory** (introduced for one patch in v2.17.1), it is now rejected. Pass the file by a relative path from the project root, or place it under a temp dir. This is the same boundary as v2.17.0 and earlier.
33
+
34
+ ## Upgrade Guide
35
+
36
+ ```bash
37
+ # git clone
38
+ cd ~/.claude/skills/ai-consultants && git pull
39
+ # curl | bash
40
+ curl -fsSL https://raw.githubusercontent.com/matteoscurati/ai-consultants/main/scripts/install.sh | bash
41
+ # npx
42
+ npx ai-consultants@latest install
43
+ ```
44
+
45
+ ## Commits
46
+
47
+ - `_______` fix(security): restrict context files to relative/temp paths (revert v2.17.1 allow-absolute)
48
+
49
+ ## Contributors
50
+
51
+ - @matteoscurati
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-consultants",
3
- "version": "2.17.0",
3
+ "version": "2.17.2",
4
4
  "description": "A harness for every question - a panel of up to 15 AI models that picks its own deliberation strategy per question (convergence, adversarial verification, tournament) and checks its work before answering",
5
5
  "bin": {
6
6
  "ai-consultants": "bin/ai-consultants"
@@ -475,10 +475,31 @@ shift
475
475
  QUERY="$1"
476
476
  shift
477
477
 
478
- # Validate output file path (allow /tmp for output)
479
- if [[ "$OUTPUT_FILE" == /tmp/* ]]; then
480
- : # Allow /tmp paths for output
481
- elif ! validate_file_path "$OUTPUT_FILE" "true"; then
478
+ # Returns 0 if PATH is under a recognized temp root: /tmp, /private/tmp (macOS:
479
+ # /tmp is a symlink to /private/tmp), $TMPDIR, or the /private-prefixed macOS
480
+ # alias of $TMPDIR. Rejects any path containing ".." so a temp prefix cannot be
481
+ # used to traverse out (e.g. /tmp/../etc/...).
482
+ _is_temp_path() {
483
+ local p="$1"
484
+ [[ "$p" == *".."* ]] && return 1
485
+ case "$p" in
486
+ /tmp/*|/private/tmp/*) return 0 ;;
487
+ esac
488
+ if [[ -n "${TMPDIR:-}" ]]; then
489
+ local t="${TMPDIR%/}"
490
+ [[ "$p" == "$t"/* ]] && return 0
491
+ [[ "$p" == /private"$t"/* ]] && return 0
492
+ fi
493
+ return 1
494
+ }
495
+
496
+ # Validate output file path. OUTPUT_FILE is tool-chosen (consult_all picks the
497
+ # output dir — under the XDG cache or /tmp), so absolute is allowed, but the
498
+ # traversal + sensitive-path guards in validate_file_path must still apply.
499
+ # (Previously a literal "/tmp/*" prefix short-circuited past those guards, so an
500
+ # output path like /tmp/../etc/x bypassed the ".." check — fixed by routing all
501
+ # output paths through validate_file_path uniformly.)
502
+ if ! validate_file_path "$OUTPUT_FILE" "true"; then
482
503
  log_error "Invalid output file path: $OUTPUT_FILE"
483
504
  exit 1
484
505
  fi
@@ -492,8 +513,20 @@ while [[ $# -gt 0 ]]; do
492
513
  file_arg="$1"
493
514
  shift
494
515
  _parse_tagged_path "$file_arg"
495
- # Allow relative paths and /tmp paths for context files
496
- if [[ "$_PARSED_PATH" == /tmp/* ]] || validate_file_path "$_PARSED_PATH" "false" 2>/dev/null; then
516
+ # SECURITY: build_context sends file contents to EXTERNAL AI providers, so a
517
+ # context arg must NOT be able to read arbitrary absolute paths (e.g.
518
+ # ~/.ssh/id_rsa, ~/.aws/credentials, ~/.netrc). Accept only:
519
+ # (a) relative, in-tree paths — validate_file_path with allow_absolute=false
520
+ # rejects absolute paths and ".." traversal, keeping them under cwd; or
521
+ # (b) files under a recognized temp root (_is_temp_path).
522
+ # This fixes the macOS scratch-file regression (Claude Code writes
523
+ # /private/tmp/...; /tmp is a symlink to /private/tmp, and $TMPDIR is
524
+ # /var/folders/...) WITHOUT widening to the whole filesystem.
525
+ # NOTE: v2.17.1 briefly used allow_absolute=true here, which let any absolute
526
+ # path through a prefix-only blocklist — a secret-exfiltration surface. Do not
527
+ # reintroduce it. (OUTPUT_FILE above is tool-chosen, not user input, so it is
528
+ # validated differently — the two sites are intentionally not identical.)
529
+ if _is_temp_path "$_PARSED_PATH" || validate_file_path "$_PARSED_PATH" "false" 2>/dev/null; then
497
530
  FILES+=("$_PARSED_PATH")
498
531
  FILE_TAGS+=("$_PARSED_TAG")
499
532
  else
package/scripts/config.sh CHANGED
@@ -858,4 +858,4 @@ EOF
858
858
  # VERSION
859
859
  # =============================================================================
860
860
 
861
- AI_CONSULTANTS_VERSION="2.17.0"
861
+ AI_CONSULTANTS_VERSION="2.17.2"
@@ -9,8 +9,9 @@ set -uo pipefail
9
9
 
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
12
- # Relative path is required: lib/common.sh::validate_file_path rejects
13
- # absolute paths in non-/tmp locations.
12
+ # Fixtures are referenced by RELATIVE path: build_context.sh's context gate
13
+ # accepts only relative in-tree paths or temp-root paths (/tmp, /private/tmp,
14
+ # $TMPDIR) and rejects arbitrary absolute paths (see Test 15 for the boundary).
14
15
  FIXTURES_REL="scripts/test_fixtures/context"
15
16
 
16
17
  # Always invoke build_context.sh from the project root so relative fixture
@@ -282,6 +283,37 @@ test_legacy_inlined_query() {
282
283
  # Suite runner
283
284
  # -----------------------------------------------------------------------------
284
285
  echo "test_context_optimization.sh — v2.14 context handoff regression suite"
286
+ # -----------------------------------------------------------------------------
287
+ # Test 15: context-file path boundary (v2.17.2)
288
+ # -----------------------------------------------------------------------------
289
+ # build_context.sh sends file contents to external AI providers, so the context
290
+ # gate must (a) ACCEPT temp-root paths — fixing the macOS /private/tmp scratch-file
291
+ # regression — and (b) REJECT arbitrary absolute paths so secrets like ~/.ssh/id_rsa
292
+ # can't be exfiltrated. ($_TMPDIR is itself a temp root: /var/folders/... on macOS,
293
+ # /tmp/... on Linux — the exact accept case, auto-cleaned, no $HOME litter.)
294
+ # QUESTION_CATEGORY=SECURITY skips the project-tree find these assertions don't need.
295
+ test_context_path_boundary() {
296
+ local tmpfile="$_TMPDIR/aic_ctx_abs.txt"
297
+ printf 'UNIQUE_CTX_MARKER_42 lorem ipsum\n' > "$tmpfile"
298
+ QUESTION_CATEGORY=SECURITY "$SCRIPT_DIR/build_context.sh" "$_TMPDIR/ctx_tmp.md" "summarize this" "$tmpfile" >/dev/null 2>&1
299
+ local content
300
+ content=$(cat "$_TMPDIR/ctx_tmp.md" 2>/dev/null)
301
+ assert_match "UNIQUE_CTX_MARKER_42" "$content" \
302
+ "temp-root context path is included (macOS /private/tmp regression class)"
303
+
304
+ # Arbitrary absolute path outside the temp roots must be REJECTED (no secret
305
+ # exfiltration). Path need not exist — rejection happens at validation.
306
+ local stderr
307
+ stderr=$(QUESTION_CATEGORY=SECURITY "$SCRIPT_DIR/build_context.sh" "$_TMPDIR/ctx_sec.md" "q" "$HOME/.ssh/id_rsa" 2>&1)
308
+ assert_match "Skipping invalid file path: $HOME/.ssh/id_rsa" "$stderr" \
309
+ "absolute path outside temp roots is rejected (secret exfiltration blocked)"
310
+
311
+ # Sensitive system path still rejected.
312
+ stderr=$(QUESTION_CATEGORY=SECURITY "$SCRIPT_DIR/build_context.sh" "$_TMPDIR/ctx_etc.md" "q" "/etc/passwd" 2>&1)
313
+ assert_match "Skipping invalid file path: /etc/passwd" "$stderr" \
314
+ "sensitive /etc path is still rejected"
315
+ }
316
+
285
317
  run_test "Test 1: @TAG defaults to PRIMARY" test_tag_default_primary
286
318
  run_test "Test 2: @TAG honored when explicit" test_tag_explicit
287
319
  run_test "Test 3: @TAG unknown falls back to PRIMARY with warning" test_tag_unknown_fallback
@@ -296,5 +328,6 @@ run_test "Test 11: --query-file is parsed correctly" test_query_file_flag
296
328
  run_test "Test 12: --query-file with missing file is rejected" test_query_file_missing
297
329
  run_test "Test 13: --query-file conflicts with positional query" test_query_file_conflict
298
330
  run_test "Test 14: Legacy no-FILES path still works" test_legacy_inlined_query
331
+ run_test "Test 15: context-file path boundary (v2.17.2)" test_context_path_boundary
299
332
 
300
333
  test_summary "test_context_optimization.sh"