Skip to content

Commit 1f4ac90

Browse files
committed
lint
add semgrep action
1 parent 66ea102 commit 1f4ac90

File tree

19 files changed

+2863
-249
lines changed

19 files changed

+2863
-249
lines changed

README.md

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ jobs:
2929
```
3030
3131
The workflow will:
32+
3233
1. Auto-detect languages in your repository
3334
2. Load repo-specific config from `repo-configs/` (or use defaults)
3435
3. Run CodeQL and Semgrep scans in parallel
@@ -50,13 +51,16 @@ const config = {
5051
build_mode: 'manual',
5152
build_command: './gradlew build',
5253
version: '21',
53-
distribution: 'temurin'
54-
}
54+
distribution: 'temurin',
55+
},
5556
],
5657
queries: [
5758
{ name: 'Security queries', uses: './query-suites/base.qls' },
58-
{ name: 'Custom queries', uses: './custom-queries/query-suites/custom-queries.qls' }
59-
]
59+
{
60+
name: 'Custom queries',
61+
uses: './custom-queries/query-suites/custom-queries.qls',
62+
},
63+
],
6064
};
6165
6266
export default config;
@@ -93,7 +97,7 @@ jobs:
9397
uses: metamask/security-codescanner-monorepo/.github/workflows/security-scan.yml@dev-branch
9498
with:
9599
repo: ${{ github.repository }}
96-
ref: dev-branch # Must explicitly pass the branch name
100+
ref: dev-branch # Must explicitly pass the branch name
97101
```
98102

99103
**Note**: The `@branch` in the `uses:` statement only affects which workflow file is used. The `ref` input ensures all internal monorepo checkouts use the same branch.
@@ -184,6 +188,7 @@ yarn workspaces foreach run <command>
184188
### Supported Languages
185189

186190
**CodeQL:**
191+
187192
- JavaScript/TypeScript → `javascript-typescript`
188193
- Python → `python`
189194
- Java/Kotlin → `java-kotlin`
@@ -197,43 +202,51 @@ yarn workspaces foreach run <command>
197202
## 🎯 Key Features
198203

199204
### ✅ Automatic Language Detection
205+
200206
- Detects languages via GitHub API
201207
- Maps to appropriate scanners
202208
- Configurable per-repository
203209

204210
### ✅ Optimized Execution
211+
205212
- Parallel scanning per language
206213
- Matrix-based job strategy
207214
- Fail-fast for ignored languages
208215

209216
### ✅ Flexible Configuration
217+
210218
- File-based configs (single source of truth)
211219
- Workflow input overrides
212220
- Per-language build settings
213221

214222
### ✅ Security First
223+
215224
- Minimal token permissions (`contents: read`, `security-events: write`)
216225
- Input validation and sanitization
217226
- See [SECURITY.md](./SECURITY.md) for threat model
218227

219228
## 🔍 Troubleshooting
220229

221230
### Language not detected
231+
222232
- Check GitHub's language detection (repo insights → languages)
223233
- Ensure language is in `LANGUAGE_MAPPING` in `language-detector/src/job-configurator.js`
224234
- Add manual `languages_config` in workflow input
225235

226236
### Build failures
237+
227238
- Verify `build_command` in repo config
228239
- Check if correct `version` and `distribution` are specified
229240
- Review CodeQL build logs in Actions
230241

231242
### Config not loading
243+
232244
- Repo config filename must match repo name: `owner/repo``repo.js`
233245
- Ensure config file exports with `export default config`
234246
- Check config-loader logs in workflow output
235247

236248
### Permissions errors
249+
237250
- Add required permissions to calling workflow:
238251
```yaml
239252
permissions:

packages/codeql-action/README.md

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Custom CodeQL analysis action with repository-specific configurations and custom
55
## Overview
66

77
This action provides flexible CodeQL scanning with:
8+
89
- **Automatic language detection** via GitHub API
910
- **Repository-specific configurations** in `repo-configs/`
1011
- **Custom query suites** for specialized security analysis
@@ -21,16 +22,16 @@ The action works as part of the security scanning workflow:
2122

2223
## Inputs
2324

24-
| Input | Required | Description |
25-
|-------|----------|-------------|
26-
| `repo` || Repository name (format: `owner/repo`) |
27-
| `language` || Language to scan (e.g., `javascript-typescript`, `java-kotlin`, `python`) |
28-
| `paths_ignored` || Newline-delimited paths to ignore |
29-
| `rules_excluded` || Newline-delimited CodeQL rule IDs to exclude |
30-
| `build_mode` || Build mode: `none`, `autobuild`, or `manual` |
31-
| `build_command` || Build command for `manual` build mode |
32-
| `version` || Language/runtime version (e.g., `21` for Java, `3.10` for Python) |
33-
| `distribution` || Distribution (e.g., `temurin`, `zulu` for Java) |
25+
| Input | Required | Description |
26+
| ---------------- | -------- | ------------------------------------------------------------------------- |
27+
| `repo` | | Repository name (format: `owner/repo`) |
28+
| `language` | | Language to scan (e.g., `javascript-typescript`, `java-kotlin`, `python`) |
29+
| `paths_ignored` | | Newline-delimited paths to ignore |
30+
| `rules_excluded` | | Newline-delimited CodeQL rule IDs to exclude |
31+
| `build_mode` | | Build mode: `none`, `autobuild`, or `manual` |
32+
| `build_command` | | Build command for `manual` build mode |
33+
| `version` | | Language/runtime version (e.g., `21` for Java, `3.10` for Python) |
34+
| `distribution` | | Distribution (e.g., `temurin`, `zulu` for Java) |
3435

3536
## Usage
3637

@@ -87,23 +88,26 @@ const config = {
8788
build_mode: 'manual',
8889
build_command: './gradlew :coordinator:app:build',
8990
version: '21',
90-
distribution: 'temurin'
91+
distribution: 'temurin',
9192
},
9293
{
9394
language: 'javascript-typescript',
9495
// Uses default config (no build needed)
9596
},
9697
{
9798
language: 'cpp',
98-
ignore: true // Skip C++ scanning
99-
}
99+
ignore: true, // Skip C++ scanning
100+
},
100101
],
101102
102103
// CodeQL query suites
103104
queries: [
104105
{ name: 'Base security queries', uses: './query-suites/base.qls' },
105-
{ name: 'Custom queries', uses: './custom-queries/query-suites/custom-queries.qls' }
106-
]
106+
{
107+
name: 'Custom queries',
108+
uses: './custom-queries/query-suites/custom-queries.qls',
109+
},
110+
],
107111
};
108112
109113
export default config;
@@ -121,45 +125,49 @@ The action includes sensible defaults for common languages:
121125

122126
```javascript
123127
const DEFAULT_CONFIGS = {
124-
'javascript': { language: 'javascript-typescript' },
125-
'typescript': { language: 'javascript-typescript' },
126-
'python': { language: 'python' },
127-
'go': { language: 'go' },
128-
'java': {
128+
javascript: { language: 'javascript-typescript' },
129+
typescript: { language: 'javascript-typescript' },
130+
python: { language: 'python' },
131+
go: { language: 'go' },
132+
java: {
129133
language: 'java-kotlin',
130134
build_mode: 'manual',
131-
build_command: './mvnw compile'
135+
build_command: './mvnw compile',
132136
},
133-
'cpp': { language: 'cpp' },
134-
'csharp': { language: 'csharp' },
135-
'ruby': { language: 'ruby' }
137+
cpp: { language: 'cpp' },
138+
csharp: { language: 'csharp' },
139+
ruby: { language: 'ruby' },
136140
};
137141
```
138142

139143
## Supported Languages
140144

141-
| GitHub Language | CodeQL Language | Build Required |
142-
|-----------------|-----------------|----------------|
143-
| JavaScript | `javascript-typescript` | No |
144-
| TypeScript | `javascript-typescript` | No |
145-
| Python | `python` | No |
146-
| Java | `java-kotlin` | Yes (defaults to `./mvnw compile`) |
147-
| Kotlin | `java-kotlin` | Yes |
148-
| Go | `go` | No |
149-
| C/C++ | `cpp` | Yes |
150-
| C# | `csharp` | Yes |
151-
| Ruby | `ruby` | No |
145+
| GitHub Language | CodeQL Language | Build Required |
146+
| --------------- | ----------------------- | ---------------------------------- |
147+
| JavaScript | `javascript-typescript` | No |
148+
| TypeScript | `javascript-typescript` | No |
149+
| Python | `python` | No |
150+
| Java | `java-kotlin` | Yes (defaults to `./mvnw compile`) |
151+
| Kotlin | `java-kotlin` | Yes |
152+
| Go | `go` | No |
153+
| C/C++ | `cpp` | Yes |
154+
| C# | `csharp` | Yes |
155+
| Ruby | `ruby` | No |
152156

153157
## Build Modes
154158

155159
### `none`
160+
156161
No build needed (interpreted languages like JavaScript, Python)
157162

158163
### `autobuild`
164+
159165
CodeQL automatically detects and runs build (works for simple projects)
160166

161167
### `manual`
168+
162169
Specify exact build command:
170+
163171
```javascript
164172
{
165173
language: 'java-kotlin',
@@ -173,38 +181,45 @@ Specify exact build command:
173181
Query suites define which CodeQL queries to run:
174182

175183
**Built-in suites:**
184+
176185
- `./query-suites/base.qls` - Standard security queries
177186
- `./query-suites/linea-monorepo.qls` - Project-specific queries
178187

179188
**Custom queries:**
189+
180190
- Checked out from `metamask/CodeQL-Queries` repository
181191
- Available at `./custom-queries/query-suites/custom-queries.qls`
182192

183193
## Troubleshooting
184194

185195
### Config not loading
196+
186197
- Filename must match repo: `owner/repo` → `repo.js`
187198
- Must use ESM: `export default config`
188199
- Check logs: `[config-loader] Loading config for repository: ...`
189200

190201
### Build failures
202+
191203
- Verify `build_command` works locally
192204
- Check Java version matches `version` input
193205
- Review build step logs in Actions
194206

195207
### Language not detected
208+
196209
- Check GitHub language stats (repo → Insights → Languages)
197210
- Add language manually via `languages_config` in repo config
198211
- Verify language mapping in `language-detector/src/job-configurator.js`
199212

200213
### SARIF upload errors
214+
201215
- Ensure workflow has `security-events: write` permission
202216
- Check SARIF file is generated in `${{ steps.codeql-analysis.outputs.sarif-output }}`
203217
- Review CodeQL analysis logs
204218

205219
## Security
206220

207221
See [SECURITY.md](../../SECURITY.md) for:
222+
208223
- Threat model and security boundaries
209224
- Input validation approach
210225
- Token permissions model

packages/codeql-action/__tests__/config-loader.test.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,10 @@ describe('config-loader', () => {
6666
try {
6767
// Write a config that will cause an error when evaluated (but is valid JS)
6868
const badConfigPath = path.join(tempDir, 'errorconfig.js');
69-
fs.writeFileSync(badConfigPath, 'throw new Error("Config error"); export default {};');
69+
fs.writeFileSync(
70+
badConfigPath,
71+
'throw new Error("Config error"); export default {};',
72+
);
7073

7174
const config = await loadRepoConfig('owner/errorconfig', tempDir);
7275

packages/codeql-action/__tests__/validation.test.js

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {
22
validateRequiredInputs,
33
sanitizePath,
44
sanitizeRuleId,
5-
escapeOutput
5+
escapeOutput,
66
} from '../src/validation.js';
77

88
describe('validation', () => {
@@ -14,27 +14,37 @@ describe('validation', () => {
1414

1515
test('throws when repo is missing', () => {
1616
const inputs = { language: 'javascript' };
17-
expect(() => validateRequiredInputs(inputs)).toThrow('Missing required inputs');
17+
expect(() => validateRequiredInputs(inputs)).toThrow(
18+
'Missing required inputs',
19+
);
1820
});
1921

2022
test('throws when language is missing', () => {
2123
const inputs = { repo: 'owner/repo' };
22-
expect(() => validateRequiredInputs(inputs)).toThrow('Missing required inputs');
24+
expect(() => validateRequiredInputs(inputs)).toThrow(
25+
'Missing required inputs',
26+
);
2327
});
2428

2529
test('throws when both are missing', () => {
2630
const inputs = {};
27-
expect(() => validateRequiredInputs(inputs)).toThrow('Missing required inputs');
31+
expect(() => validateRequiredInputs(inputs)).toThrow(
32+
'Missing required inputs',
33+
);
2834
});
2935

3036
test('throws when repo is empty string', () => {
3137
const inputs = { repo: '', language: 'javascript' };
32-
expect(() => validateRequiredInputs(inputs)).toThrow('Missing required inputs');
38+
expect(() => validateRequiredInputs(inputs)).toThrow(
39+
'Missing required inputs',
40+
);
3341
});
3442

3543
test('throws when language is empty string', () => {
3644
const inputs = { repo: 'owner/repo', language: '' };
37-
expect(() => validateRequiredInputs(inputs)).toThrow('Missing required inputs');
45+
expect(() => validateRequiredInputs(inputs)).toThrow(
46+
'Missing required inputs',
47+
);
3848
});
3949
});
4050

@@ -76,9 +86,15 @@ describe('validation', () => {
7686
});
7787

7888
test('preserves valid path characters', () => {
79-
expect(sanitizePath('src/components/Button.tsx')).toBe('src/components/Button.tsx');
80-
expect(sanitizePath('test-utils/helpers_v2.js')).toBe('test-utils/helpers_v2.js');
81-
expect(sanitizePath('./node_modules/@types')).toBe('./node_modules/@types');
89+
expect(sanitizePath('src/components/Button.tsx')).toBe(
90+
'src/components/Button.tsx',
91+
);
92+
expect(sanitizePath('test-utils/helpers_v2.js')).toBe(
93+
'test-utils/helpers_v2.js',
94+
);
95+
expect(sanitizePath('./node_modules/@types')).toBe(
96+
'./node_modules/@types',
97+
);
8298
});
8399

84100
test('handles multiple dangerous characters', () => {
@@ -90,11 +106,15 @@ describe('validation', () => {
90106
test('preserves valid rule IDs', () => {
91107
expect(sanitizeRuleId('js/log-injection')).toBe('js/log-injection');
92108
expect(sanitizeRuleId('py/sql-injection')).toBe('py/sql-injection');
93-
expect(sanitizeRuleId('java/unsafe-deserialization')).toBe('java/unsafe-deserialization');
109+
expect(sanitizeRuleId('java/unsafe-deserialization')).toBe(
110+
'java/unsafe-deserialization',
111+
);
94112
});
95113

96114
test('preserves underscores', () => {
97-
expect(sanitizeRuleId('js/unsafe_dynamic_access')).toBe('js/unsafe_dynamic_access');
115+
expect(sanitizeRuleId('js/unsafe_dynamic_access')).toBe(
116+
'js/unsafe_dynamic_access',
117+
);
98118
});
99119

100120
test('removes special characters', () => {
@@ -132,7 +152,9 @@ describe('validation', () => {
132152
});
133153

134154
test('escapes multiple special characters', () => {
135-
expect(escapeOutput('line1\nline2\rvalue%')).toBe('line1%0Aline2%0Dvalue%25');
155+
expect(escapeOutput('line1\nline2\rvalue%')).toBe(
156+
'line1%0Aline2%0Dvalue%25',
157+
);
136158
});
137159

138160
test('escapes percent before newlines (order matters)', () => {

0 commit comments

Comments
 (0)