chore: cross-crate correctness and security fixes#196
Conversation
security
- workspace:<range> now validates against local version, any workspace: prefix used to short-circuit so `workspace:^2.0.0` linked a local 1.0.0 without complaint
- allowlist bypass via `npm:` alias closed at every policy.decide site, `h3-safe: npm:h3@0.19.0` no longer sneaks past an `h3` entry
- aube dlx forces ignore-scripts so transient installs do not inherit user's global allow-builds
- aube run args shell-quoted per platform, no more `$` / backtick / `;` injection when forwarding args
- publish ignore list covers .env / .env.* / .ssh / .aws / .gnupg / id_rsa / *.pem / *.key / *.p12
- install ancestor walk stops at $HOME, empty-dir install cannot climb into user home and attach to a parent project
- libc detection runs at runtime (probes /lib/ld-musl-* vs /lib*/ld-linux-*), not compile-time cfg
- catalog entry pointing at another catalog rejected with a clear error
- peer-context max-iter is error level not warn, convergence failures no longer silent in CI
- aube audit --ignore-registry-errors exits 2 with degraded status instead of claiming clean
correctness
- atomic writes for aube-lock.yaml (all 6 formats), .aube-state, .aube-applied-patches.json, package.json mutations in add/remove
- CAS import fsyncs before close, shard-missing fallback switched from truncate-in-place to atomic create_new
- detect_strategy probes cross-FS (store vs project) so store on a different mount no longer silently falls back to per-file copy
- tarball extract rejects Windows reserved names / trailing dot-space / control chars on win32 only
- parser tolerates leading UTF-8 BOM, null dep maps, string-form workspaces ("packages/*"), with targeted error for package.json JSONC comments
- workspace glob skips node_modules/ at any depth so `packages/**` stops picking up installed deps
- workspace member without a version is a hard error instead of silent 0.0.0 fallback
- latest-tag-missing packument falls back to highest stable version, no more spurious NoMatch
- 0.0.0-0 workspace placeholder matches bare `*` range
- graph_hash allow_build fed registry_name, not in-tree alias
resources / concurrency
- fetch loops use JoinSet, first-error aborts outstanding siblings instead of detaching them
- virtual store sweeps stale .tmp-<pid>-* dirs on each install to reclaim prior-crash leftovers
- pnpmfile afterAllResolved child has kill_on_drop
- Windows remove_dir_all retries on sharing violations / access-denied with backoff
ux
- signal-aware exit codes (128+signum) in aube run / dlx / exec, SIGKILL no longer collapses to plain 1
- aube run <missing> lists available scripts from package.json
- policy warnings and peer errors routed through a progress-safe writer so output does not smear with the install bar
- branch-lockfile merge prints conflicts per line, integrity mismatches flagged explicitly as supply-chain signal
- aube run sets npm_lifecycle_event / npm_package_name / npm_package_version / INIT_CWD, was a bare env before
- aube run and aube exec take allow_hyphen_values so `aube run build --flag` works without the `--` separator
- root script failure error includes hook name, no more bare "exit status 1"
compat
- publishConfig.registry / .tag / .access honored with CLI > manifest precedence
- .npmrc backslash line continuation supported (npm ini semantics)
- HOME falls back to USERPROFILE on Windows in expand_setting_path, user_npmrc_path, registry config home_dir
- frozen fast-path state hash now covers .npmrc bytes, host (os+arch+libc), and patches dir + sidecar
- find_workspace_root and find_workspace_yaml_root also cap the ancestor walk at $HOME
Greptile SummaryThis PR is a broad cross-crate hardening pass covering security fixes (allowlist bypass via
Confidence Score: 4/5Safe to merge after fixing the scoped-package pruning bug in One P1 finding: crates/aube/src/commands/remove.rs —
|
| Filename | Overview |
|---|---|
| crates/aube/src/commands/remove.rs | Adds atomic manifest write and prune_sidecar_entries on remove; the rsplit_once('@') logic fails to match bare scoped package names in array-shaped fields, leaving stale build-approval entries. |
| crates/aube-linker/Cargo.toml | Adds serde_json and tempfile deps; tempfile uses "3" instead of { workspace = true }, creating an inconsistency with other workspace crates. |
| crates/aube-linker/src/lib.rs | Adds sweep_stale_tmp_dirs, remove_dir_all_with_retry (Windows backoff), cross-FS strategy probe, and atomic write_applied_patches; logic looks correct. |
| crates/aube-lockfile/src/lib.rs | Adds atomic_write_lockfile helper used by all 6 lockfile format writers; temp-file + fsync + rename pattern is correct. |
| crates/aube-resolver/src/lib.rs | Fixes workspace:<range> validation against local version, adds catalog-chain rejection, and adds highest_stable_version fallback for missing latest dist-tag; logic is correct. |
| crates/aube/src/commands/run.rs | Extracts shared build_script_command helper, adds npm-compat env vars, fixes INIT_CWD to use actual invocation dir via dirs::cwd(), and applies exit_code_from_status. |
| crates/aube-scripts/src/lib.rs | Adds shell_quote_arg (Unix single-quote / Windows double-quote with %% for %VAR%) and exit_code_from_status (128+signum on Unix); both are correct. |
| crates/aube/src/commands/install/mod.rs | Switches fetch loops to JoinSet for abort-on-first-error, fixes policy.decide callers to use registry_name(), uses cross-FS strategy probe, and surfaces merge conflicts per-line via safe_eprintln. |
| crates/aube/src/state.rs | Switches write_state to atomic tempfile+persist and extends the frozen fast-path hash to cover .npmrc bytes, host triple, and patches dir. |
| crates/aube/src/dirs.rs | Caps ancestor walks in find_project_root, find_workspace_root, and find_workspace_yaml_root at $HOME/USERPROFILE; logic is correct. |
Comments Outside Diff (1)
-
crates/aube/src/commands/remove.rs, line 302-312 (link)Scoped packages silently skipped in array-shaped sidecar pruning
rsplit_once('@')on a bare scoped name like"@types/node"splits at the only@(position 0), yieldingSome(("", "types/node")). The resultingbase = ""never equals"@types/node", so the entry is retained instead of pruned.The security invariant the code is trying to enforce — removing a package also removes its build-approval sidecar entries — is broken for any scoped package that appears without an explicit version suffix (the common form in
trustedDependencies/neverBuiltDependencies).Fix: detect the empty-left-side case and fall back to the whole string as the base name:
Reviews (2): Last reviewed commit: "run: INIT_CWD uses invocation cwd, dedup..." | Re-trigger Greptile
|
@greptileai is the P1 issue resolved? |
|
Yes — the P1 thread (JSON injection via unescaped error message in Tip: You can customize Greptile's behavior for this repo with |
security
workspace:^2.0.0linked a local 1.0.0 without complaintnpm:alias closed at every policy.decide site,h3-safe: npm:h3@0.19.0no longer sneaks past anh3entry$/ backtick /;injection when forwarding argscorrectness
packages/**stops picking up installed deps*rangeresources / concurrency
ux
aube run build --flagworks without the--separatorcompat