Skip to content

Commit abf5be6

Browse files
committed
feat: add rule to catch npx usage in JS/TS/YAML
1 parent b678707 commit abf5be6

File tree

7 files changed

+354
-8
lines changed

7 files changed

+354
-8
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
rules:
2+
- id: npx-usage-js
3+
languages:
4+
- javascript
5+
- typescript
6+
severity: WARNING
7+
metadata:
8+
tags: [security]
9+
shortDescription: "npx usage introduces supply chain security risks"
10+
confidence: HIGH
11+
help: |
12+
Using npx to install and run packages introduces significant supply chain security risks for the following reasons:
13+
14+
1. **Unpinned by default**: Running `npx <package>` fetches the latest release outside of your lockfile. If a malicious version of a package is published ([example])(https://socket.dev/blog/npm-author-qix-compromised-in-major-supply-chain-attack), `npx` will install and execute it the next time it is run.
15+
16+
2. **Bypasses lockfile guarantees**: Packages executed with npx are not added to your project's package.json or lockfile. As a result, their versions and lockfile integrity hashes are not captured for reproducibility, making builds non-deterministic and harder to audit
17+
18+
### Recommended practice
19+
- Add packages as dependencies or devDependencies in `package.json`.
20+
- Use your package manager to install and execute them (e.g., `yarn add <package> [--dev]` followed by `yarn <package> <command>`).
21+
22+
**Bad example (using npx):**
23+
```javascript
24+
const cmd = `npx jest --coverage`;
25+
execSync(cmd);
26+
```
27+
28+
**Good example (proper dependency):**
29+
```javascript
30+
// Add jest as a dependency /devDependency in package.json
31+
const cmd = `yarn jest --coverage`;
32+
execSync(cmd);
33+
```
34+
35+
message: >-
36+
Avoid using 'npx' to run packages due to supply chain security risks. Instead, install the package
37+
as a dependency / devDependency and invoke it using your package manager to ensure version pinning
38+
and reproducibility.
39+
patterns:
40+
- pattern: "$STRING"
41+
- metavariable-regex:
42+
metavariable: $STRING
43+
regex: '.*\bnpx\s'

packages/semgrep-action/rules/src/generic/npx-usage/npx-usage-json.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ rules:
55
severity: WARNING
66
metadata:
77
tags: [security]
8-
shortDescription: 'npx usage introduces supply chain security risks'
8+
shortDescription: "npx usage introduces supply chain security risks"
99
confidence: HIGH
1010
help: |
1111
Using npx to install and run packages introduces significant supply chain security risks for the following reasons:
@@ -17,7 +17,7 @@ rules:
1717
### Recommended practice
1818
- Add packages as dependencies or devDependencies in `package.json`.
1919
- Use your package manager to install and execute them (e.g., `yarn add <package> --dev` followed by `yarn <package> <command>`).
20-
20+
2121
**Bad example (using npx):**
2222
```json
2323
{
@@ -26,7 +26,7 @@ rules:
2626
}
2727
}
2828
```
29-
29+
3030
**Good example (proper dependency):**
3131
```json
3232
{
@@ -38,9 +38,9 @@ rules:
3838
}
3939
}
4040
```
41-
41+
4242
message: >-
4343
Avoid using 'npx' to run packages due to supply chain security risks. Instead, install the package
4444
as a dependency / devDependency and invoke it using your package manager to ensure version pinning
4545
and reproducibility.
46-
pattern-regex: '"[^"]*":\s*"(\s*npx\s|npx\s)[^"]*"'
46+
pattern-regex: '"[^"]*":\s*"(\s*npx\s|npx\s)[^"]*"'

packages/semgrep-action/rules/src/generic/npx-usage/npx-usage-shell.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ rules:
66
severity: WARNING
77
metadata:
88
tags: [security]
9-
shortDescription: 'npx usage introduces supply chain security risks'
9+
shortDescription: "npx usage introduces supply chain security risks"
1010
confidence: HIGH
1111
help: |
1212
Using npx to install and run packages introduces significant supply chain security risks for the following reasons:
@@ -18,8 +18,8 @@ rules:
1818
1919
### Recommended practice
2020
- Add packages as dependencies or devDependencies in `package.json`.
21-
- Use your package manager to install and execute them (e.g., `yarn add <package> --dev` followed by `yarn <package> <command>`).
22-
21+
- Use your package manager to install and execute them (e.g., `yarn add <package> --dev` followed by `yarn <package> <command>`).
22+
2323
message: >-
2424
Avoid using 'npx' to run packages due to supply chain security risks. Instead, install the package
2525
as a dependency / devDependency and invoke it using your package manager to ensure version pinning
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
rules:
2+
- id: npx-usage-yml
3+
languages:
4+
- yaml
5+
severity: WARNING
6+
metadata:
7+
tags: [security]
8+
shortDescription: "npx usage introduces supply chain security risks"
9+
confidence: HIGH
10+
help: |
11+
Using npx to install and run packages introduces significant supply chain security risks for the following reasons:
12+
13+
1. **Unpinned by default**: Running `npx <package>` fetches the latest release outside of your lockfile. If a malicious version of a package is published ([example])(https://socket.dev/blog/npm-author-qix-compromised-in-major-supply-chain-attack), `npx` will install and execute it the next time it is run.
14+
15+
2. **Bypasses lockfile guarantees**: Packages executed with npx are not added to your project's package.json or lockfile. As a result, their versions and lockfile integrity hashes are not captured for reproducibility, making builds non-deterministic and harder to audit
16+
17+
### Recommended practice
18+
- Add packages as dependencies or devDependencies in `package.json`.
19+
- Use your package manager to install and execute them (e.g., `yarn add <package> --dev` followed by `yarn <package> <command>`).
20+
21+
**Bad example (using npx):**
22+
```yaml
23+
- name: Run tests
24+
run: npx jest --coverage
25+
```
26+
27+
**Good example (proper dependency):**
28+
```yaml
29+
- name: Run tests
30+
run: yarn jest --coverage
31+
```
32+
33+
message: >-
34+
Avoid using 'npx' to run packages due to supply chain security risks. Instead, install the package
35+
as a dependency / devDependency and invoke it using your package manager to ensure version pinning
36+
and reproducibility.
37+
patterns:
38+
- pattern: |
39+
run: $CMD
40+
- metavariable-pattern:
41+
metavariable: $CMD
42+
language: sh
43+
pattern: npx ...
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
const { execSync, exec, spawn, spawnSync } = require('child_process');
2+
3+
// Test cases that should be flagged
4+
5+
// Template literal with interpolation (like coverage-analysis.js:234)
6+
function runTests() {
7+
const testArgs = 'test/*.js';
8+
// ruleid: npx-usage-js
9+
const cmd = `npx jest ${testArgs} --coverage --coverageReporters=lcov`;
10+
execSync(cmd);
11+
}
12+
13+
// Template literal passed directly to exec
14+
function lintCode() {
15+
// ruleid: npx-usage-js
16+
execSync(`npx eslint src/`);
17+
}
18+
19+
// String literal in error message (like global.setup.ts:72)
20+
// Now caught with regex - flags npx usage anywhere in strings including docs/examples
21+
function throwError() {
22+
throw new Error(
23+
// ruleid: npx-usage-js
24+
'Please specify a project name with --project flag. Example: npx playwright test --project dummy-test-local'
25+
);
26+
}
27+
28+
// String literal with scoped package
29+
function formatCode() {
30+
// ruleid: npx-usage-js
31+
const command = "npx @typescript-eslint/parser --version";
32+
exec(command);
33+
}
34+
35+
// Template literal with output redirection
36+
function generateFingerprint() {
37+
// ruleid: npx-usage-js
38+
exec(`npx @expo/fingerprint ./ > fingerprint.json`);
39+
}
40+
41+
// Template literal with flags
42+
function setupTool() {
43+
// ruleid: npx-usage-js
44+
const setupCmd = `npx --yes create-react-app my-app`;
45+
execSync(setupCmd);
46+
}
47+
48+
// Template literal with environment variables
49+
function runWithEnv() {
50+
const workspace = process.env.GITHUB_WORKSPACE;
51+
// ruleid: npx-usage-js
52+
spawn(`npx jest ${workspace} --coverage`);
53+
}
54+
55+
// Template literal in command chain
56+
function buildAndTest() {
57+
// ruleid: npx-usage-js
58+
execSync(`yarn build && npx jest --coverage`);
59+
}
60+
61+
// String literal assigned to variable
62+
function assignCommand() {
63+
// ruleid: npx-usage-js
64+
let cmd = "npx prettier --write .";
65+
return cmd;
66+
}
67+
68+
// Test cases that should NOT be flagged
69+
70+
// Using yarn instead
71+
function goodYarnUsage() {
72+
// ok: npx-usage-js
73+
execSync(`yarn jest --coverage`);
74+
}
75+
76+
// Using npm scripts
77+
function goodNpmUsage() {
78+
// ok: npx-usage-js
79+
execSync('npm run test');
80+
}
81+
82+
// Using yarn dlx
83+
function goodYarnDlx() {
84+
// ok: npx-usage-js
85+
const cmd = `yarn dlx create-react-app my-app`;
86+
execSync(cmd);
87+
}
88+
89+
// Direct node execution
90+
function goodNodeUsage() {
91+
// ok: npx-usage-js
92+
exec('node scripts/build.js');
93+
}
94+
95+
// Comment mentioning npx - should be ignored automatically
96+
// This comment talks about npx but isn't code execution
97+
function withComment() {
98+
// ok: npx-usage-js
99+
execSync('yarn test');
100+
}
101+
102+
// Variable name contains "npx" but not executing it
103+
function variableName() {
104+
// ok: npx-usage-js
105+
const shouldUseNpx = false;
106+
const npxWarning = "Dont use npx!";
107+
console.log(npxWarning);
108+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { execSync, exec, spawn } from 'child_process';
2+
3+
// Test cases that should be flagged in TypeScript
4+
5+
// Template literal with type annotation (like global.setup.ts scenario)
6+
function runPlaywrightTests(): void {
7+
const project: string = 'test-project';
8+
// ruleid: npx-usage-js
9+
const command: string = `npx playwright test --project ${project}`;
10+
execSync(command);
11+
}
12+
13+
// Error message with npx example
14+
function throwConfigError(): never {
15+
throw new Error(
16+
// ruleid: npx-usage-js
17+
'Please specify a project name with --project flag. Example: npx playwright test --project dummy-test-local'
18+
);
19+
}
20+
21+
// Arrow function with template literal
22+
const buildApp = (): void => {
23+
// ruleid: npx-usage-js
24+
execSync(`npx tsc --build`);
25+
};
26+
27+
// Async function
28+
async function deployApp(): Promise<void> {
29+
// ruleid: npx-usage-js
30+
await exec(`npx vercel deploy`);
31+
}
32+
33+
// String literal with type assertion
34+
function formatFiles(): void {
35+
// ruleid: npx-usage-js
36+
const cmd = "npx prettier --write ." as const;
37+
execSync(cmd);
38+
}
39+
40+
// Template literal in class method
41+
class TestRunner {
42+
runTests(): void {
43+
const coverage: boolean = true;
44+
// ruleid: npx-usage-js
45+
execSync(`npx jest ${coverage ? '--coverage' : ''}`);
46+
}
47+
}
48+
49+
// Good examples that should NOT be flagged
50+
51+
function useYarnProperly(): void {
52+
// ok: npx-usage-js
53+
execSync('yarn test --coverage');
54+
}
55+
56+
function useNpmScript(): void {
57+
// ok: npx-usage-js
58+
exec('npm run build');
59+
}
60+
61+
function useYarnDlx(): void {
62+
// ok: npx-usage-js
63+
const cmd: string = `yarn dlx create-next-app my-app`;
64+
execSync(cmd);
65+
}

0 commit comments

Comments
 (0)