Skip to content

Commit 936477d

Browse files
authored
Merge branch 'main' into dd/ql-mcp-client/2
2 parents ce4fbb1 + 2cf8b49 commit 936477d

File tree

4 files changed

+90
-7
lines changed

4 files changed

+90
-7
lines changed

server/dist/codeql-development-mcp-server.js

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

server/dist/codeql-development-mcp-server.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/src/resources/server-tools.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,50 @@ This resource provides a complete reference of the default tools exposed by the
6868
| `sarif_compare_alerts` | Compare code locations of two SARIF alerts for overlap (sink, source, any-location, full-path modes) |
6969
| `sarif_diff_runs` | Diff two SARIF files to find added, removed, and changed rules/results across analysis runs |
7070

71+
### `sarif_list_rules` Response Format
72+
73+
Returns a JSON object with per-rule result counts and metadata:
74+
75+
```json
76+
{
77+
"totalRules": 3,
78+
"totalResults": 15,
79+
"rules": [
80+
{
81+
"ruleId": "js/sql-injection",
82+
"resultCount": 8,
83+
"name": "Database query built from user-controlled sources",
84+
"kind": "path-problem",
85+
"precision": "high",
86+
"severity": "8.8",
87+
"tags": ["security", "external/cwe/cwe-089"],
88+
"tool": "CodeQL",
89+
"toolVersion": "2.20.4"
90+
}
91+
]
92+
}
93+
```
94+
95+
| Field | Type | Description |
96+
| -------------- | ------ | ------------------------------------------------ |
97+
| `totalRules` | number | Total number of distinct rules in the SARIF file |
98+
| `totalResults` | number | Sum of `resultCount` across all rules |
99+
| `rules[]` | array | Per-rule summaries (see below) |
100+
101+
Each rule object:
102+
103+
| Field | Type | Required | Description |
104+
| ------------- | -------- | -------- | ---------------------------------------------------------------------------- |
105+
| `ruleId` | string | yes | Rule identifier (matches the CodeQL query `@id`) |
106+
| `resultCount` | number | yes | Number of results (findings) for this rule; `0` if defined but not triggered |
107+
| `name` | string | no | Display name (from `shortDescription.text`, `name`, or `id`) |
108+
| `kind` | string | no | Query kind (`path-problem`, `problem`, etc.) |
109+
| `precision` | string | no | Precision level (`high`, `medium`, `low`, `very-high`) |
110+
| `severity` | string | no | Security severity score (from `security-severity` property) |
111+
| `tags` | string[] | no | Rule tags (e.g., `security`, `external/cwe/cwe-089`) |
112+
| `tool` | string | no | Tool driver name (e.g., `CodeQL`) |
113+
| `toolVersion` | string | no | Tool driver version |
114+
71115
## Common Tool Workflows
72116

73117
### Create and Test a Query

server/test/src/tools/sarif-tools.test.ts

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ function createTestSarif() {
5353
{ location: { physicalLocation: { artifactLocation: { uri: 'src/db.js' }, region: { startLine: 42 } }, message: { text: 'query(...)' } } },
5454
] }] }],
5555
},
56+
{
57+
ruleId: 'js/sql-injection',
58+
ruleIndex: 0,
59+
message: { text: 'SQL injection from request body.' },
60+
locations: [{ physicalLocation: { artifactLocation: { uri: 'src/api.js' }, region: { startLine: 15, startColumn: 3, endColumn: 40 } } }],
61+
},
5662
{
5763
ruleId: 'js/xss',
5864
ruleIndex: 1,
@@ -64,6 +70,25 @@ function createTestSarif() {
6470
};
6571
}
6672

73+
/** SARIF with a defined rule but zero results — validates resultCount: 0 */
74+
function createZeroResultsSarif() {
75+
return {
76+
version: '2.1.0',
77+
runs: [{
78+
tool: {
79+
driver: {
80+
name: 'CodeQL',
81+
version: '2.25.1',
82+
rules: [
83+
{ id: 'js/unused-variable', shortDescription: { text: 'Unused variable' } },
84+
],
85+
},
86+
},
87+
results: [],
88+
}],
89+
};
90+
}
91+
6792
// ---------------------------------------------------------------------------
6893
// Tests
6994
// ---------------------------------------------------------------------------
@@ -170,7 +195,7 @@ describe('SARIF Tools', () => {
170195
const parsed = JSON.parse(result.content[0].text);
171196

172197
expect(parsed.ruleId).toBe('js/sql-injection');
173-
expect(parsed.resultCount).toBe(1);
198+
expect(parsed.resultCount).toBe(2);
174199
expect(parsed.extractedSarif.runs[0].tool.driver.rules).toHaveLength(1);
175200
});
176201

@@ -212,15 +237,29 @@ describe('SARIF Tools', () => {
212237
});
213238

214239
describe('sarif_list_rules', () => {
215-
it('should list all rules with result counts', async () => {
240+
it('should list all rules with per-rule result counts', async () => {
216241
const result = await handlers.sarif_list_rules({ sarifPath: testSarifPath });
217242
const parsed = JSON.parse(result.content[0].text);
218243

219244
expect(parsed.totalRules).toBe(2);
220-
expect(parsed.totalResults).toBe(2);
245+
expect(parsed.totalResults).toBe(3);
221246
expect(parsed.rules[0].ruleId).toBe('js/sql-injection');
222-
expect(parsed.rules[0].resultCount).toBe(1);
247+
expect(parsed.rules[0].resultCount).toBe(2);
223248
expect(parsed.rules[1].ruleId).toBe('js/xss');
249+
expect(parsed.rules[1].resultCount).toBe(1);
250+
});
251+
252+
it('should return resultCount 0 for rules with no results', async () => {
253+
const noResultsPath = join(testStorageDir, 'no-results.sarif');
254+
writeFileSync(noResultsPath, JSON.stringify(createZeroResultsSarif()));
255+
256+
const result = await handlers.sarif_list_rules({ sarifPath: noResultsPath });
257+
const parsed = JSON.parse(result.content[0].text);
258+
259+
expect(parsed.totalRules).toBe(1);
260+
expect(parsed.totalResults).toBe(0);
261+
expect(parsed.rules[0].ruleId).toBe('js/unused-variable');
262+
expect(parsed.rules[0].resultCount).toBe(0);
224263
});
225264
});
226265

@@ -303,7 +342,7 @@ describe('SARIF Tools', () => {
303342

304343
it('should detect changed result counts', async () => {
305344
const sarifB = createTestSarif();
306-
sarifB.runs[0].results = [sarifB.runs[0].results[0]]; // remove XSS result
345+
sarifB.runs[0].results = sarifB.runs[0].results.filter(r => r.ruleId !== 'js/xss');
307346
const pathB = join(testStorageDir, 'modified.sarif');
308347
writeFileSync(pathB, JSON.stringify(sarifB));
309348

0 commit comments

Comments
 (0)