Repository navigation
fix(cli)!: require consent before patching user-patched packages - #1646
Mikola Lysenko (mikolalysenko) wants to merge 2 commits into
Conversation
Detect project patch ownership across pnpm, Yarn, patch-package, npm, Bun, Composer, Cargo and Go. Skip protected scan candidates and refuse explicit mutations unless --allow-user-patches is passed. Preserve native backend refusals and require explicit consent before overwriting unknown installed file contents. BREAKING CHANGE: declared project patches are protected by default and agent apply now rejects unknown file-content drift. Document the next-major-release requirement, detection limits and invocation-only consent in the CLI contract.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 4 potential issues.
Bugbot Autofix is ON, but none of the reported issues met your autofix severity threshold.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 98d2edd. Configure here.
| Err(error) => { | ||
| let code = report_apply_failure(&args, &error, &error.message, &telemetry).await; | ||
| return ApplyRunReport::run_failure(code, &error.code, error.message); | ||
| } |
There was a problem hiding this comment.
Apply holds lock during conflict telemetry
Low Severity
A user_patch_conflict in run_locked reports the failure and sends telemetry without releasing apply.lock first. The two manifest-load failures immediately above call lock.release() before report_apply_failure, because that helper can POST. Concurrent apply, scan, vendor, or repair stay blocked until the report finishes.
Reviewed by Cursor Bugbot for commit 98d2edd. Configure here.
| e.name.eq_ignore_ascii_case(name) | ||
| } else { | ||
| e.name == name | ||
| }) |
There was a problem hiding this comment.
Cargo patch names match case-sensitively
Medium Severity
conflicts treats only Composer names as case-insensitive. Cargo crate names are case-insensitive, so a [patch] or [replace] key that differs in case from the Socket PURL does not match. That package stays eligible and can be patched without --allow-user-patches.
Reviewed by Cursor Bugbot for commit 98d2edd. Configure here.
| .filter(|g| !g.starts_with('!') || g.starts_with("!(")) | ||
| { | ||
| pending.extend(index.workspace_members(&dir, pattern)); | ||
| } |
There was a problem hiding this comment.
Go workspace members are not inspected
Medium Severity
Discovery reads replace directives only in each queued directory’s go.mod / go.work. It never walks go.work use members, unlike npm workspace globs. A member-only replace is invisible from the workspace root, so that module can receive a Socket patch without consent.
Reviewed by Cursor Bugbot for commit 98d2edd. Configure here.
| silent, | ||
| all_releases: args.all_releases, | ||
| strict: args.common.strict, | ||
| allow_user_patches: args.common.allow_user_patches, |
There was a problem hiding this comment.
Scan warnings ignore silent mode
Low Severity
Scan pushes user_patch_conflict and user_patch_override through print_layout_refusals, which prints Warning: even when --silent is set. The dedicated check helper already suppresses those warnings under --silent. Human scan output therefore leaks skip/override details that silent mode should hide.
Additional Locations (1)
Triggered by learned rule: CLI warnings must be surfaced in both stderr (human) and JSON envelope (machine)
Reviewed by Cursor Bugbot for commit 98d2edd. Configure here.
The url-spelled crates-io [patch] table and ancestor .cargo/config [patch] fixtures are now caught earlier by the new UserPatchIndex, which refuses with user_patch_conflict before the cargo-specific guards run. Pass --allow-user-patches so these tests keep exercising the guards they were written for (cargo_manifest_patch_source_alias and user_authored_patch_entry); the default refusal is covered by tests/user_patches.rs. Co-Authored-By: Claude <noreply@anthropic.com>
|
Burn-down agent: CI on 98d2edd had two separate failures. Fixed (29dd054): Needs a decision (coverage job): 5 tests in
Question: when the protection comes only from an inspection failure (unparseable lockfile, workspace glob outside the root) and not from real patch evidence, should Generated by Claude Code |


A project could already have a pnpm, Yarn or patch-package fix and still receive a Socket patch without specific consent. Native npm/Bun had backend-specific refusals, but there was no shared package-ownership check. Agent apply also overwrote unknown installed file contents with the verified Socket blob by default, potentially discarding a local fix.
This change protects project patches before patch selection or mutation, even when the two fixes touch different files or the user diff has not been installed yet.
user_patch_conflictwarnings in every mode. Explicitget,apply, andvendorrefuse conflicts with exit 1. Recheck ownership before hosted/vendored writes, including hosted-to-vendored eject, and retain the full crawl for pruning.--allow-user-patches, forwarded through nested apply and reported asuser_patch_override. General--yes, UUID selection, policy settings, and--forcedo not approve declared patch composition. Existing backend safety refusals remain in force.--strictkeeps rejecting drift even with consent. Existingapply --forceremains an explicit override for file drift only. Consent is an attempt to combine changes, not an automatic merge.The CLI contract and usage docs specify diagnostics, read-only/reversal exemptions, version matching, workspace behavior, and detection limits. The ecosystem documentation links the primary package-manager documentation and source used for the research. Regression coverage includes pnpm wildcard prereleases, nested Yarn patch locators in both directions, scoped/nested/sequenced patch-package names, malformed metadata, all CLI modes, nested opt-in forwarding, and preservation of user files.
Breaking change / release coordination: the stricter mismatch default and package eligibility belong in the next major release under the CLI semver policy. Version stamps and release notes remain the release train's responsibility.
Detection is conservative where metadata is unreadable or cannot be resolved without executing scripts. Arbitrary build hooks, external patch pipelines, global-install declarations, and unrecorded edits to files a Socket patch does not touch cannot be detected reliably. A nested agent apply also checks existing in-scope manifest entries; it can refuse an older protected entry after newly selected records have been saved. These limits are explicit in the contract.
Validation passed locally:
cargo check --locked -p socket-patch-cli --testscargo clippy --locked --workspace --all-features -- -D warningscargo fmt --all -- --checkandgit diff --checkpatch/patch-commitworkflow with a local Socket test record targeting a different file: default apply and--forcerefused;--allow-user-patchesapplied the Socket fix while preserving the native diff and user-modified file.Draft until the repository-required
/code-review highworkflow has run. That workflow is not available in the authoring session; the implementation has been manually reviewed and validated as above.Note
High Risk
Major behavior change to default apply eligibility and file-mismatch policy on mutating CLI paths; incorrect detection could block or overwrite user-owned dependency changes.
Overview
Breaking (major): Socket Patch no longer applies on top of declared project patches or overwrites locally modified dependency files unless the user opts in.
Adds
--allow-user-patches(invocation-only; not env/policy) and a coreUserPatchIndexthat scans lockfiles and manifests for pnpm/Yarn/patch-package/npm/Bun/Composer/Cargo/Go patch evidence (plus workspaces).scandrops protected packages withuser_patch_conflictwarnings;get,apply,vendor, and hosted/vendored write paths exit withuser_patch_conflictunless consent is given, emittinguser_patch_overridewhen allowed.--forcestill overrides file-hash drift only; declared patches still need--allow-user-patches.Default file mismatch behavior flips: unknown content (neither
beforeHashnorafterHash) now fails closed; warn-and-overwrite with verifiedafterHashcontent requires--allow-user-patches.--strictcontinues to reject drift even with that flag.CLI contract, usage, and ecosystem docs document detection scope, diagnostics, and limits.
Reviewed by Cursor Bugbot for commit 98d2edd. Configure here.
Generated by Claude Code