Honor an explicit non-git CLAUDE_PROJECT_DIR as an isolated project scope, canonicalize and hash it consistently in the shell observer and Python CLI, and preserve the global fallback for arbitrary non-git working directories.
2026-07-26 00:04:35 -07:00
devin-ai-integration[bot]GitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>affaanDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>Affaan Mustafa
* chore(continuous-learning-v2): standardize shell shebangs to env bash
Three scripts under skills/continuous-learning-v2/ used the hardcoded
`#!/bin/bash` shebang while the other four already used the portable
`#!/usr/bin/env bash`:
- hooks/observe.sh (runs on every hook invocation)
- scripts/detect-project.sh
- agents/start-observer.sh
The hardcoded interpreter path fails to execute on systems where bash is
not installed at /bin/bash (NixOS, some Homebrew layouts, FreeBSD).
Standardize all three to `#!/usr/bin/env bash`, matching the repo-wide
majority convention, and add a regression test that asserts shebang
uniformity for every shell script in this skill so the inconsistency
cannot reappear.
Fixes#2303
* test(continuous-learning-v2): harden shebang test runner
Address review feedback on the shebang-consistency regression test:
- firstLine() now splits on /\r?\n/ so a script checked out with CRLF
line endings does not leave a trailing carriage return that would
break the shebang comparison on Windows.
- The test() helper now surfaces the full error (stack trace, not just
the message) on failure and writes pass/fail lines via
process.stdout/stderr so diagnostics are preserved.
* test(continuous-learning-v2): skip hidden dirs in shebang scan
The recursive shell-script scan now skips hidden directories (e.g. the
observer's runtime `.observer-tmp`). This keeps the shebang-consistency
check deterministic: only committed skill scripts are examined, and an
untracked local artifact left over from an observer run can no longer
cause a false failure.
Two bugs in skills/continuous-learning-v2/scripts/detect-project.sh that
silently split the same project into multiple project_id records:
1. Locale-dependent SHA-256 input (HIGH)
The project_id hash was computed with
printf '%s' "$hash_input" | python -c 'sys.stdin.buffer.read()'
which ships shell-locale-encoded bytes to Python. On a system with a
non-UTF-8 LC_ALL (e.g. ja_JP.CP932 / CP1252) the same project root
produced a different 12-char hash than the UTF-8 locale would produce,
so observations/instincts were silently written under a separate
project directory. Fixed by passing the value via an env var and
encoding as UTF-8 inside Python, making the hash locale-independent.
2. basename cannot split Windows backslash paths (MEDIUM)
basename "C:\Users\...\ECC作成" returns the whole string on POSIX
bash, so project_name was garbled whenever CLAUDE_PROJECT_DIR was
passed as a native Windows path. Normalize backslashes to forward
slashes before calling basename.
Both the primary project_id hash and the legacy-compat fallback hash
are updated to use the env-var / UTF-8 approach.
Verified: id is stable across en_US.UTF-8, ja_JP.UTF-8, ja_JP.CP932, C,
and POSIX locales; Windows-path input yields project_name=ECC作成;
ASCII-only paths regress-free.
In git worktrees, .git is a file (not a directory) containing a gitdir
pointer. The -d test fails for worktree checkouts, causing project
detection to fall through to the "global" fallback. Changing to -e
(exists) handles both regular repos and worktrees correctly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(hooks): scrub secrets and harden hook security
- Scrub common secret patterns (api_key, token, password, etc.) from
observation logs before persisting to JSONL (observe.sh)
- Auto-purge observation files older than 30 days (observe.sh)
- Strip embedded credentials from git remote URLs before saving to
projects.json (detect-project.sh)
- Add command prefix allowlist to runCommand — only git, node, npx,
which, where are permitted (utils.js)
- Sanitize CLAUDE_SESSION_ID in temp file paths to prevent path
traversal (suggest-compact.js)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(hooks): address review feedback from CodeRabbit and Cubic
- Reject shell command-chaining operators (;|&`) in runCommand, strip
quoted sections before checking to avoid false positives (utils.js)
- Remove command string from blocked error message to avoid leaking
secrets (utils.js)
- Fix Python regex quoting: switch outer shell string from double to
single quotes so regex compiles correctly (observe.sh)
- Add optional auth scheme match (Bearer, Basic) to secret scrubber
regex (observe.sh)
- Scope auto-purge to current project dir and match only archived
files (observations-*.jsonl), not live queue (observe.sh)
- Add second fallback after session ID sanitization to prevent empty
string (suggest-compact.js)
- Preserve backward compatibility when credential stripping changes
project hash — detect and migrate legacy directories
(detect-project.sh)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(hooks): block $() substitution, fix Bearer redaction, add security tests
- Add $ and \n to blocked shell metacharacters in runCommand to prevent
command substitution via $(cmd) and newline injection (utils.js)
- Make auth scheme group capturing so Bearer/Basic is preserved in
redacted output instead of being silently dropped (observe.sh)
- Add 10 unit tests covering runCommand allowlist blocking (rm, curl,
bash prefixes) and metacharacter rejection (;|&`$ chaining), plus
error message leak prevention (utils.test.js)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(hooks): scrub parse-error fallback, strengthen security tests
Address remaining reviewer feedback from CodeRabbit and Cubic:
- Scrub secrets in observe.sh parse-error fallback path (was writing
raw unsanitized input to observations file)
- Remove redundant re.IGNORECASE flag ((?i) inline flag already set)
- Add inline comment documenting quote-stripping limitation trade-off
- Fix misleading test name for error-output test
- Add 5 new security tests: single-quote passthrough, mixed
quoted+unquoted metacharacters, prefix boundary (no trailing space),
npx acceptance, and newline injection
- Improve existing quoted-metacharacter test to actually exercise
quote-stripping logic
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(security): block $() and backtick inside quotes in runCommand
Shell evaluates $() and backticks inside double quotes, so checking
only the unquoted portion was insufficient. Now $ and ` are rejected
anywhere in the command string, while ; | & remain quote-aware.
Addresses CodeRabbit and Cubic review feedback on PR #348.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>