Skip to content

Commit 2b8d4d1

Browse files
committed
fix(analyze): detect replacement token in xargs env assignments
The hasDynamicReplacement check now also looks at envAssignments for the replacement token, catching cases like where the token appears in an environment variable value rather than a direct argument.
1 parent cdc9fe0 commit 2b8d4d1

4 files changed

Lines changed: 22 additions & 3 deletions

File tree

dist/bin/cc-safety-net.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4187,7 +4187,7 @@ function analyzeXargs(tokens, context) {
41874187
}
41884188
if (childCommand.head === "git") {
41894189
const gitTokens = replacementToken === null ? [...childTokens, XARGS_APPENDED_INPUT] : childTokens;
4190-
const hasDynamicReplacement = replacementToken !== null && childTokens.some((token) => token.includes(replacementToken));
4190+
const hasDynamicReplacement = replacementToken !== null && (childTokens.some((token) => token.includes(replacementToken)) || Array.from(childCommand.envAssignments.values()).some((value) => value.includes(replacementToken)));
41914191
const gitResult = analyzeGit(gitTokens, {
41924192
cwd: childCommand.cwd,
41934193
envAssignments: childCommand.envAssignments,

dist/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3124,7 +3124,7 @@ function analyzeXargs(tokens, context) {
31243124
}
31253125
if (childCommand.head === "git") {
31263126
const gitTokens = replacementToken === null ? [...childTokens, XARGS_APPENDED_INPUT] : childTokens;
3127-
const hasDynamicReplacement = replacementToken !== null && childTokens.some((token) => token.includes(replacementToken));
3127+
const hasDynamicReplacement = replacementToken !== null && (childTokens.some((token) => token.includes(replacementToken)) || Array.from(childCommand.envAssignments.values()).some((value) => value.includes(replacementToken)));
31283128
const gitResult = analyzeGit(gitTokens, {
31293129
cwd: childCommand.cwd,
31303130
envAssignments: childCommand.envAssignments,

src/core/analyze/xargs.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,11 @@ export function analyzeXargs(
6666
const gitTokens =
6767
replacementToken === null ? [...childTokens, XARGS_APPENDED_INPUT] : childTokens;
6868
const hasDynamicReplacement =
69-
replacementToken !== null && childTokens.some((token) => token.includes(replacementToken));
69+
replacementToken !== null &&
70+
(childTokens.some((token) => token.includes(replacementToken)) ||
71+
Array.from(childCommand.envAssignments.values()).some((value) =>
72+
value.includes(replacementToken),
73+
));
7074
const gitResult = analyzeGit(gitTokens, {
7175
cwd: childCommand.cwd,
7276
envAssignments: childCommand.envAssignments,

tests/core/rules-git.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,21 @@ describe('git linked worktree mode', () => {
10501050
}
10511051
});
10521052

1053+
test('SAFETY_NET_WORKTREE fails closed on dynamic xargs git env assignments', () => {
1054+
const fixture = createLinkedWorktreeFixture();
1055+
try {
1056+
withEnv({ SAFETY_NET_WORKTREE: '1' }, () => {
1057+
assertBlocked(
1058+
'echo ignored | xargs -I{} env EXTRA={} git reset --hard',
1059+
'git reset --hard',
1060+
fixture.linkedWorktree,
1061+
);
1062+
});
1063+
} finally {
1064+
fixture.cleanup();
1065+
}
1066+
});
1067+
10531068
test('SAFETY_NET_WORKTREE fails closed on dynamic parallel git arguments', () => {
10541069
const fixture = createLinkedWorktreeFixture();
10551070
try {

0 commit comments

Comments
 (0)