codeql-development-mcp-server 2.25.1-next.1 → 2.25.1-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/codeql-development-mcp-server.js +1206 -144
- package/dist/codeql-development-mcp-server.js.map +4 -4
- package/package.json +2 -2
- package/ql/actions/tools/src/codeql-pack.yml +1 -1
- package/ql/cpp/tools/src/codeql-pack.yml +1 -1
- package/ql/csharp/tools/src/codeql-pack.yml +1 -1
- package/ql/go/tools/src/codeql-pack.yml +1 -1
- package/ql/java/tools/src/codeql-pack.yml +1 -1
- package/ql/javascript/tools/src/codeql-pack.yml +1 -1
- package/ql/python/tools/src/codeql-pack.yml +1 -1
- package/ql/ruby/tools/src/codeql-pack.yml +1 -1
- package/ql/rust/tools/src/codeql-pack.yml +1 -1
- package/ql/swift/tools/src/codeql-pack.yml +1 -1
|
@@ -30698,7 +30698,8 @@ var require_dist2 = __commonJS({
|
|
|
30698
30698
|
}
|
|
30699
30699
|
function pathToRegexp(path3, options = {}) {
|
|
30700
30700
|
const { delimiter: delimiter7 = DEFAULT_DELIMITER, end = true, sensitive = false, trailing = true } = options;
|
|
30701
|
-
const
|
|
30701
|
+
const keys = [];
|
|
30702
|
+
const sources = [];
|
|
30702
30703
|
const paths = [path3];
|
|
30703
30704
|
let combinations = 0;
|
|
30704
30705
|
while (paths.length) {
|
|
@@ -30712,39 +30713,15 @@ var require_dist2 = __commonJS({
|
|
|
30712
30713
|
if (combinations++ >= 256) {
|
|
30713
30714
|
throw new PathError("Too many path combinations", data.originalPath);
|
|
30714
30715
|
}
|
|
30715
|
-
|
|
30716
|
-
for (const part of toRegExpSource(tokens, delimiter7, data.originalPath)) {
|
|
30717
|
-
node = node.add(part.source, part.key);
|
|
30718
|
-
}
|
|
30719
|
-
node.add("");
|
|
30716
|
+
sources.push(toRegExpSource(tokens, delimiter7, keys, data.originalPath));
|
|
30720
30717
|
});
|
|
30721
30718
|
}
|
|
30722
|
-
|
|
30723
|
-
let pattern = toRegExp(root, keys);
|
|
30719
|
+
let pattern = `^(?:${sources.join("|")})`;
|
|
30724
30720
|
if (trailing)
|
|
30725
30721
|
pattern += "(?:" + escape2(delimiter7) + "$)?";
|
|
30726
30722
|
pattern += end ? "$" : "(?=" + escape2(delimiter7) + "|$)";
|
|
30727
30723
|
return { regexp: new RegExp(pattern, sensitive ? "" : "i"), keys };
|
|
30728
30724
|
}
|
|
30729
|
-
function toRegExp(node, keys) {
|
|
30730
|
-
if (node.key)
|
|
30731
|
-
keys.push(node.key);
|
|
30732
|
-
const children = Object.keys(node.children);
|
|
30733
|
-
const text = children.map((id) => toRegExp(node.children[id], keys)).join("|");
|
|
30734
|
-
return node.source + (children.length < 2 ? text : `(?:${text})`);
|
|
30735
|
-
}
|
|
30736
|
-
var SourceNode = class _SourceNode {
|
|
30737
|
-
constructor(source, key) {
|
|
30738
|
-
this.source = source;
|
|
30739
|
-
this.key = key;
|
|
30740
|
-
this.children = /* @__PURE__ */ Object.create(null);
|
|
30741
|
-
}
|
|
30742
|
-
add(source, key) {
|
|
30743
|
-
var _a;
|
|
30744
|
-
const id = source + ":" + (key ? key.name : "");
|
|
30745
|
-
return (_a = this.children)[id] || (_a[id] = new _SourceNode(source, key));
|
|
30746
|
-
}
|
|
30747
|
-
};
|
|
30748
30725
|
function flatten(tokens, index, result, callback) {
|
|
30749
30726
|
while (index < tokens.length) {
|
|
30750
30727
|
const token = tokens[index++];
|
|
@@ -30756,8 +30733,8 @@ var require_dist2 = __commonJS({
|
|
|
30756
30733
|
}
|
|
30757
30734
|
callback(result);
|
|
30758
30735
|
}
|
|
30759
|
-
function toRegExpSource(tokens, delimiter7, originalPath) {
|
|
30760
|
-
let result =
|
|
30736
|
+
function toRegExpSource(tokens, delimiter7, keys, originalPath) {
|
|
30737
|
+
let result = "";
|
|
30761
30738
|
let backtrack = "";
|
|
30762
30739
|
let wildcardBacktrack = "";
|
|
30763
30740
|
let prevCaptureType = 0;
|
|
@@ -30788,7 +30765,7 @@ var require_dist2 = __commonJS({
|
|
|
30788
30765
|
while (index < tokens.length) {
|
|
30789
30766
|
const token = tokens[index++];
|
|
30790
30767
|
if (token.type === "text") {
|
|
30791
|
-
result
|
|
30768
|
+
result += escape2(token.value);
|
|
30792
30769
|
backtrack += token.value;
|
|
30793
30770
|
if (prevCaptureType === 2)
|
|
30794
30771
|
wildcardBacktrack += token.value;
|
|
@@ -30801,19 +30778,14 @@ var require_dist2 = __commonJS({
|
|
|
30801
30778
|
throw new PathError(`Missing text before "${token.name}" ${token.type}`, originalPath);
|
|
30802
30779
|
}
|
|
30803
30780
|
if (token.type === "param") {
|
|
30804
|
-
result
|
|
30805
|
-
source: hasSegmentCapture ? `(${negate(delimiter7, backtrack)}+?)` : hasInSegment(index, "wildcard") ? `(${negate(delimiter7, peekText(index))}+?)` : `(${negate(delimiter7, "")}+?)`,
|
|
30806
|
-
key: token
|
|
30807
|
-
});
|
|
30781
|
+
result += hasSegmentCapture & 2 ? `(${negate(delimiter7, backtrack)}+)` : hasInSegment(index, "wildcard") ? `(${negate(delimiter7, peekText(index))}+)` : hasSegmentCapture & 1 ? `(${negate(delimiter7, backtrack)}+|${escape2(backtrack)})` : `(${negate(delimiter7, "")}+)`;
|
|
30808
30782
|
hasSegmentCapture |= prevCaptureType = 1;
|
|
30809
30783
|
} else {
|
|
30810
|
-
result
|
|
30811
|
-
source: hasSegmentCapture & 2 ? `(${negate(backtrack, "")}+?)` : hasSegmentCapture & 1 ? `(${negate(wildcardBacktrack, "")}+?)` : wildcardBacktrack ? `(${negate(wildcardBacktrack, "")}+?|${negate(delimiter7, "")}+?)` : `([^]+?)`,
|
|
30812
|
-
key: token
|
|
30813
|
-
});
|
|
30784
|
+
result += hasSegmentCapture & 2 ? `(${negate(backtrack, "")}+)` : wildcardBacktrack ? `(${negate(wildcardBacktrack, "")}+|${negate(delimiter7, "")}+)` : `([^]+)`;
|
|
30814
30785
|
wildcardBacktrack = "";
|
|
30815
30786
|
hasSegmentCapture |= prevCaptureType = 2;
|
|
30816
30787
|
}
|
|
30788
|
+
keys.push(token);
|
|
30817
30789
|
backtrack = "";
|
|
30818
30790
|
continue;
|
|
30819
30791
|
}
|
|
@@ -165001,7 +164973,11 @@ var require_utils5 = __commonJS({
|
|
|
165001
164973
|
try {
|
|
165002
164974
|
stat = self2.fs.statSync(resolvedPath);
|
|
165003
164975
|
} catch (e) {
|
|
165004
|
-
|
|
164976
|
+
if (e.message && e.message.startsWith("ENOENT")) {
|
|
164977
|
+
self2.fs.mkdirSync(resolvedPath);
|
|
164978
|
+
} else {
|
|
164979
|
+
throw e;
|
|
164980
|
+
}
|
|
165005
164981
|
}
|
|
165006
164982
|
if (stat && stat.isFile()) throw Errors.FILE_IN_THE_WAY(`"${resolvedPath}"`);
|
|
165007
164983
|
});
|
|
@@ -165198,9 +165174,9 @@ var require_utils5 = __commonJS({
|
|
|
165198
165174
|
}
|
|
165199
165175
|
};
|
|
165200
165176
|
Utils.readBigUInt64LE = function(buffer, index) {
|
|
165201
|
-
|
|
165202
|
-
|
|
165203
|
-
return
|
|
165177
|
+
const lo = buffer.readUInt32LE(index);
|
|
165178
|
+
const hi = buffer.readUInt32LE(index + 4);
|
|
165179
|
+
return hi * 4294967296 + lo;
|
|
165204
165180
|
};
|
|
165205
165181
|
Utils.fromDOS2Date = function(val) {
|
|
165206
165182
|
return new Date((val >> 25 & 127) + 1980, Math.max((val >> 21 & 15) - 1, 0), Math.max(val >> 16 & 31, 1), val >> 11 & 31, val >> 5 & 63, (val & 31) << 1);
|
|
@@ -165382,6 +165358,7 @@ var require_entryHeader = __commonJS({
|
|
|
165382
165358
|
return Utils.fromDOS2Date(this.timeval);
|
|
165383
165359
|
},
|
|
165384
165360
|
set time(val) {
|
|
165361
|
+
val = new Date(val);
|
|
165385
165362
|
this.timeval = Utils.fromDate2DOS(val);
|
|
165386
165363
|
},
|
|
165387
165364
|
get timeval() {
|
|
@@ -165482,6 +165459,7 @@ var require_entryHeader = __commonJS({
|
|
|
165482
165459
|
}
|
|
165483
165460
|
_localHeader.version = data.readUInt16LE(Constants.LOCVER);
|
|
165484
165461
|
_localHeader.flags = data.readUInt16LE(Constants.LOCFLG);
|
|
165462
|
+
_localHeader.flags_desc = (_localHeader.flags & Constants.FLG_DESC) > 0;
|
|
165485
165463
|
_localHeader.method = data.readUInt16LE(Constants.LOCHOW);
|
|
165486
165464
|
_localHeader.time = data.readUInt32LE(Constants.LOCTIM);
|
|
165487
165465
|
_localHeader.crc = data.readUInt32LE(Constants.LOCCRC);
|
|
@@ -165891,7 +165869,7 @@ var require_zipEntry = __commonJS({
|
|
|
165891
165869
|
return input.slice(_centralHeader.realDataOffset, _centralHeader.realDataOffset + _centralHeader.compressedSize);
|
|
165892
165870
|
}
|
|
165893
165871
|
function crc32OK(data) {
|
|
165894
|
-
if (!_centralHeader.flags_desc) {
|
|
165872
|
+
if (!_centralHeader.flags_desc && !_centralHeader.localHeader.flags_desc) {
|
|
165895
165873
|
if (Utils.crc32(data) !== _centralHeader.localHeader.crc) {
|
|
165896
165874
|
return false;
|
|
165897
165875
|
}
|
|
@@ -166020,7 +165998,7 @@ var require_zipEntry = __commonJS({
|
|
|
166020
165998
|
}
|
|
166021
165999
|
}
|
|
166022
166000
|
function readUInt64LE(buffer, offset) {
|
|
166023
|
-
return (buffer
|
|
166001
|
+
return Utils.readBigUInt64LE(buffer, offset);
|
|
166024
166002
|
}
|
|
166025
166003
|
function parseExtra(data) {
|
|
166026
166004
|
try {
|
|
@@ -166612,7 +166590,7 @@ var require_adm_zip = __commonJS({
|
|
|
166612
166590
|
}
|
|
166613
166591
|
function fixPath(zipPath) {
|
|
166614
166592
|
const { join: join22, normalize: normalize2, sep: sep3 } = pth.posix;
|
|
166615
|
-
return join22(".", normalize2(sep3 + zipPath.split("\\").join(sep3) + sep3));
|
|
166593
|
+
return join22(pth.isAbsolute(zipPath) ? "/" : ".", normalize2(sep3 + zipPath.split("\\").join(sep3) + sep3));
|
|
166616
166594
|
}
|
|
166617
166595
|
function filenameFilter(filterfn) {
|
|
166618
166596
|
if (filterfn instanceof RegExp) {
|
|
@@ -176078,10 +176056,9 @@ var ProgressTokenSchema = union([string2(), number2().int()]);
|
|
|
176078
176056
|
var CursorSchema = string2();
|
|
176079
176057
|
var TaskCreationParamsSchema = looseObject({
|
|
176080
176058
|
/**
|
|
176081
|
-
*
|
|
176082
|
-
* If null, the task has unlimited lifetime until manually cleaned up.
|
|
176059
|
+
* Requested duration in milliseconds to retain task from creation.
|
|
176083
176060
|
*/
|
|
176084
|
-
ttl:
|
|
176061
|
+
ttl: number2().optional(),
|
|
176085
176062
|
/**
|
|
176086
176063
|
* Time in milliseconds to wait between task status requests.
|
|
176087
176064
|
*/
|
|
@@ -176381,7 +176358,11 @@ var ClientCapabilitiesSchema = object2({
|
|
|
176381
176358
|
/**
|
|
176382
176359
|
* Present if the client supports task creation.
|
|
176383
176360
|
*/
|
|
176384
|
-
tasks: ClientTasksCapabilitySchema.optional()
|
|
176361
|
+
tasks: ClientTasksCapabilitySchema.optional(),
|
|
176362
|
+
/**
|
|
176363
|
+
* Extensions that the client supports. Keys are extension identifiers (vendor-prefix/extension-name).
|
|
176364
|
+
*/
|
|
176365
|
+
extensions: record(string2(), AssertObjectSchema).optional()
|
|
176385
176366
|
});
|
|
176386
176367
|
var InitializeRequestParamsSchema = BaseRequestParamsSchema.extend({
|
|
176387
176368
|
/**
|
|
@@ -176443,7 +176424,11 @@ var ServerCapabilitiesSchema = object2({
|
|
|
176443
176424
|
/**
|
|
176444
176425
|
* Present if the server supports task creation.
|
|
176445
176426
|
*/
|
|
176446
|
-
tasks: ServerTasksCapabilitySchema.optional()
|
|
176427
|
+
tasks: ServerTasksCapabilitySchema.optional(),
|
|
176428
|
+
/**
|
|
176429
|
+
* Extensions that the server supports. Keys are extension identifiers (vendor-prefix/extension-name).
|
|
176430
|
+
*/
|
|
176431
|
+
extensions: record(string2(), AssertObjectSchema).optional()
|
|
176447
176432
|
});
|
|
176448
176433
|
var InitializeResultSchema = ResultSchema.extend({
|
|
176449
176434
|
/**
|
|
@@ -176635,6 +176620,12 @@ var ResourceSchema = object2({
|
|
|
176635
176620
|
* The MIME type of this resource, if known.
|
|
176636
176621
|
*/
|
|
176637
176622
|
mimeType: optional(string2()),
|
|
176623
|
+
/**
|
|
176624
|
+
* The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known.
|
|
176625
|
+
*
|
|
176626
|
+
* This can be used by Hosts to display file sizes and estimate context window usage.
|
|
176627
|
+
*/
|
|
176628
|
+
size: optional(number2()),
|
|
176638
176629
|
/**
|
|
176639
176630
|
* Optional annotations for the client.
|
|
176640
176631
|
*/
|
|
@@ -181540,7 +181531,7 @@ var StdioServerTransport = class {
|
|
|
181540
181531
|
};
|
|
181541
181532
|
|
|
181542
181533
|
// ../node_modules/@hono/node-server/dist/index.mjs
|
|
181543
|
-
import { Http2ServerRequest as Http2ServerRequest2 } from "http2";
|
|
181534
|
+
import { Http2ServerRequest as Http2ServerRequest2, constants as h2constants } from "http2";
|
|
181544
181535
|
import { Http2ServerRequest } from "http2";
|
|
181545
181536
|
import { Readable } from "stream";
|
|
181546
181537
|
import crypto2 from "crypto";
|
|
@@ -181863,6 +181854,50 @@ if (typeof global.crypto === "undefined") {
|
|
|
181863
181854
|
global.crypto = crypto2;
|
|
181864
181855
|
}
|
|
181865
181856
|
var outgoingEnded = /* @__PURE__ */ Symbol("outgoingEnded");
|
|
181857
|
+
var incomingDraining = /* @__PURE__ */ Symbol("incomingDraining");
|
|
181858
|
+
var DRAIN_TIMEOUT_MS = 500;
|
|
181859
|
+
var MAX_DRAIN_BYTES = 64 * 1024 * 1024;
|
|
181860
|
+
var drainIncoming = (incoming) => {
|
|
181861
|
+
const incomingWithDrainState = incoming;
|
|
181862
|
+
if (incoming.destroyed || incomingWithDrainState[incomingDraining]) {
|
|
181863
|
+
return;
|
|
181864
|
+
}
|
|
181865
|
+
incomingWithDrainState[incomingDraining] = true;
|
|
181866
|
+
if (incoming instanceof Http2ServerRequest2) {
|
|
181867
|
+
try {
|
|
181868
|
+
;
|
|
181869
|
+
incoming.stream?.close?.(h2constants.NGHTTP2_NO_ERROR);
|
|
181870
|
+
} catch {
|
|
181871
|
+
}
|
|
181872
|
+
return;
|
|
181873
|
+
}
|
|
181874
|
+
let bytesRead = 0;
|
|
181875
|
+
const cleanup = () => {
|
|
181876
|
+
clearTimeout(timer);
|
|
181877
|
+
incoming.off("data", onData);
|
|
181878
|
+
incoming.off("end", cleanup);
|
|
181879
|
+
incoming.off("error", cleanup);
|
|
181880
|
+
};
|
|
181881
|
+
const forceClose = () => {
|
|
181882
|
+
cleanup();
|
|
181883
|
+
const socket = incoming.socket;
|
|
181884
|
+
if (socket && !socket.destroyed) {
|
|
181885
|
+
socket.destroySoon();
|
|
181886
|
+
}
|
|
181887
|
+
};
|
|
181888
|
+
const timer = setTimeout(forceClose, DRAIN_TIMEOUT_MS);
|
|
181889
|
+
timer.unref?.();
|
|
181890
|
+
const onData = (chunk) => {
|
|
181891
|
+
bytesRead += chunk.length;
|
|
181892
|
+
if (bytesRead > MAX_DRAIN_BYTES) {
|
|
181893
|
+
forceClose();
|
|
181894
|
+
}
|
|
181895
|
+
};
|
|
181896
|
+
incoming.on("data", onData);
|
|
181897
|
+
incoming.on("end", cleanup);
|
|
181898
|
+
incoming.on("error", cleanup);
|
|
181899
|
+
incoming.resume();
|
|
181900
|
+
};
|
|
181866
181901
|
var handleRequestError = () => new Response(null, {
|
|
181867
181902
|
status: 400
|
|
181868
181903
|
});
|
|
@@ -182034,14 +182069,18 @@ var getRequestListener = (fetchCallback, options = {}) => {
|
|
|
182034
182069
|
setTimeout(() => {
|
|
182035
182070
|
if (!incomingEnded) {
|
|
182036
182071
|
setTimeout(() => {
|
|
182037
|
-
incoming
|
|
182038
|
-
outgoing.destroy();
|
|
182072
|
+
drainIncoming(incoming);
|
|
182039
182073
|
});
|
|
182040
182074
|
}
|
|
182041
182075
|
});
|
|
182042
182076
|
}
|
|
182043
182077
|
};
|
|
182044
182078
|
}
|
|
182079
|
+
outgoing.on("finish", () => {
|
|
182080
|
+
if (!incomingEnded) {
|
|
182081
|
+
drainIncoming(incoming);
|
|
182082
|
+
}
|
|
182083
|
+
});
|
|
182045
182084
|
}
|
|
182046
182085
|
outgoing.on("close", () => {
|
|
182047
182086
|
const abortController = req[abortControllerKey];
|
|
@@ -182056,7 +182095,7 @@ var getRequestListener = (fetchCallback, options = {}) => {
|
|
|
182056
182095
|
setTimeout(() => {
|
|
182057
182096
|
if (!incomingEnded) {
|
|
182058
182097
|
setTimeout(() => {
|
|
182059
|
-
incoming
|
|
182098
|
+
drainIncoming(incoming);
|
|
182060
182099
|
});
|
|
182061
182100
|
}
|
|
182062
182101
|
});
|
|
@@ -182798,7 +182837,7 @@ var StreamableHTTPServerTransport = class {
|
|
|
182798
182837
|
var import_express = __toESM(require_express2(), 1);
|
|
182799
182838
|
var import_cors = __toESM(require_lib4(), 1);
|
|
182800
182839
|
var import_dotenv = __toESM(require_main(), 1);
|
|
182801
|
-
import { realpathSync as
|
|
182840
|
+
import { realpathSync as realpathSync3 } from "fs";
|
|
182802
182841
|
import { resolve as resolve14 } from "path";
|
|
182803
182842
|
import { pathToFileURL as pathToFileURL5 } from "url";
|
|
182804
182843
|
|
|
@@ -182808,7 +182847,7 @@ init_cli_executor();
|
|
|
182808
182847
|
// src/lib/database-resolver.ts
|
|
182809
182848
|
init_logger();
|
|
182810
182849
|
import { basename as basename2, join as join6 } from "path";
|
|
182811
|
-
import { existsSync as existsSync4, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
182850
|
+
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
|
|
182812
182851
|
var databasePathCache = /* @__PURE__ */ new Map();
|
|
182813
182852
|
function resolveDatabasePath(dbPath) {
|
|
182814
182853
|
const cached2 = databasePathCache.get(dbPath);
|
|
@@ -182848,6 +182887,41 @@ function resolveDatabasePath(dbPath) {
|
|
|
182848
182887
|
databasePathCache.set(dbPath, dbPath);
|
|
182849
182888
|
return dbPath;
|
|
182850
182889
|
}
|
|
182890
|
+
function readDatabaseMetadata(databasePath) {
|
|
182891
|
+
for (const filename of ["codeql-database.yml", "codeql-database.yaml"]) {
|
|
182892
|
+
try {
|
|
182893
|
+
const content = readFileSync4(join6(databasePath, filename), "utf8");
|
|
182894
|
+
return parseDatabaseYmlContent(content);
|
|
182895
|
+
} catch {
|
|
182896
|
+
}
|
|
182897
|
+
}
|
|
182898
|
+
return {};
|
|
182899
|
+
}
|
|
182900
|
+
function parseDatabaseYmlContent(content) {
|
|
182901
|
+
const metadata = {};
|
|
182902
|
+
for (const line of content.split("\n")) {
|
|
182903
|
+
const trimmed = line.trim();
|
|
182904
|
+
const colonIdx = trimmed.indexOf(":");
|
|
182905
|
+
if (colonIdx === -1) continue;
|
|
182906
|
+
const key = trimmed.substring(0, colonIdx).trim();
|
|
182907
|
+
const value = trimmed.substring(colonIdx + 1).trim().replace(/^["']|["']$/g, "");
|
|
182908
|
+
switch (key) {
|
|
182909
|
+
case "primaryLanguage":
|
|
182910
|
+
metadata.language = value;
|
|
182911
|
+
break;
|
|
182912
|
+
case "sourceLocationPrefix":
|
|
182913
|
+
metadata.sourceLocationPrefix = value;
|
|
182914
|
+
break;
|
|
182915
|
+
case "cliVersion":
|
|
182916
|
+
metadata.cliVersion = value;
|
|
182917
|
+
break;
|
|
182918
|
+
case "creationTime":
|
|
182919
|
+
metadata.creationTime = value;
|
|
182920
|
+
break;
|
|
182921
|
+
}
|
|
182922
|
+
}
|
|
182923
|
+
return metadata;
|
|
182924
|
+
}
|
|
182851
182925
|
|
|
182852
182926
|
// src/lib/cli-tool-registry.ts
|
|
182853
182927
|
init_logger();
|
|
@@ -182940,13 +183014,13 @@ async function resolveQueryPath(params, logger2) {
|
|
|
182940
183014
|
// src/lib/result-processor.ts
|
|
182941
183015
|
init_cli_executor();
|
|
182942
183016
|
import { basename as basename4, dirname as dirname4 } from "path";
|
|
182943
|
-
import { mkdirSync as mkdirSync7, readFileSync as
|
|
183017
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync7 } from "fs";
|
|
182944
183018
|
import { createHash as createHash2 } from "crypto";
|
|
182945
183019
|
|
|
182946
183020
|
// src/lib/query-results-evaluator.ts
|
|
182947
183021
|
init_cli_executor();
|
|
182948
183022
|
init_logger();
|
|
182949
|
-
import { closeSync, fstatSync, mkdirSync as mkdirSync4, openSync, readFileSync as
|
|
183023
|
+
import { closeSync, fstatSync, mkdirSync as mkdirSync4, openSync, readFileSync as readFileSync5, writeFileSync } from "fs";
|
|
182950
183024
|
import { dirname as dirname3, isAbsolute as isAbsolute3 } from "path";
|
|
182951
183025
|
var BUILT_IN_EVALUATORS = {
|
|
182952
183026
|
"json-decode": "JSON format decoder for query results",
|
|
@@ -182968,7 +183042,7 @@ async function extractQueryMetadata(queryPath) {
|
|
|
182968
183042
|
metadataCache.set(queryPath, cached2);
|
|
182969
183043
|
return cached2.metadata;
|
|
182970
183044
|
}
|
|
182971
|
-
queryContent =
|
|
183045
|
+
queryContent = readFileSync5(fd, "utf-8");
|
|
182972
183046
|
} finally {
|
|
182973
183047
|
closeSync(fd);
|
|
182974
183048
|
}
|
|
@@ -183223,6 +183297,444 @@ async function evaluateWithCustomScript(_bqrsPath, _queryPath, _scriptPath, _out
|
|
|
183223
183297
|
};
|
|
183224
183298
|
}
|
|
183225
183299
|
|
|
183300
|
+
// src/lib/sarif-utils.ts
|
|
183301
|
+
function getRuleDisplayName(rule) {
|
|
183302
|
+
return rule.shortDescription?.text ?? rule.name ?? rule.id;
|
|
183303
|
+
}
|
|
183304
|
+
function collectAllRules(run) {
|
|
183305
|
+
const driverRules = run.tool.driver.rules ?? [];
|
|
183306
|
+
const extensionRules = [];
|
|
183307
|
+
for (const ext of run.tool.extensions ?? []) {
|
|
183308
|
+
if (ext.rules) {
|
|
183309
|
+
extensionRules.push(...ext.rules);
|
|
183310
|
+
}
|
|
183311
|
+
}
|
|
183312
|
+
return [...driverRules, ...extensionRules];
|
|
183313
|
+
}
|
|
183314
|
+
function extractPrimaryLocations(result) {
|
|
183315
|
+
return (result.locations ?? []).filter((loc) => loc.physicalLocation?.artifactLocation?.uri).map((loc) => ({
|
|
183316
|
+
endColumn: loc.physicalLocation.region?.endColumn,
|
|
183317
|
+
endLine: loc.physicalLocation.region?.endLine ?? loc.physicalLocation.region?.startLine,
|
|
183318
|
+
startColumn: loc.physicalLocation.region?.startColumn,
|
|
183319
|
+
startLine: loc.physicalLocation.region?.startLine,
|
|
183320
|
+
uri: loc.physicalLocation.artifactLocation.uri
|
|
183321
|
+
}));
|
|
183322
|
+
}
|
|
183323
|
+
function extractSourceLocations(result) {
|
|
183324
|
+
const flows = result.codeFlows ?? [];
|
|
183325
|
+
const sources = [];
|
|
183326
|
+
for (const flow of flows) {
|
|
183327
|
+
for (const tf of flow.threadFlows ?? []) {
|
|
183328
|
+
const steps = tf.locations ?? [];
|
|
183329
|
+
if (steps.length > 0) {
|
|
183330
|
+
const firstStep = steps[0];
|
|
183331
|
+
const loc = firstStep.location?.physicalLocation;
|
|
183332
|
+
if (loc?.artifactLocation?.uri) {
|
|
183333
|
+
sources.push({
|
|
183334
|
+
endColumn: loc.region?.endColumn,
|
|
183335
|
+
endLine: loc.region?.endLine ?? loc.region?.startLine,
|
|
183336
|
+
startColumn: loc.region?.startColumn,
|
|
183337
|
+
startLine: loc.region?.startLine,
|
|
183338
|
+
uri: loc.artifactLocation.uri
|
|
183339
|
+
});
|
|
183340
|
+
}
|
|
183341
|
+
}
|
|
183342
|
+
}
|
|
183343
|
+
}
|
|
183344
|
+
return sources;
|
|
183345
|
+
}
|
|
183346
|
+
function extractAllLocations(result) {
|
|
183347
|
+
const locs = [...extractPrimaryLocations(result)];
|
|
183348
|
+
for (const rl of result.relatedLocations ?? []) {
|
|
183349
|
+
if (rl.physicalLocation?.artifactLocation?.uri) {
|
|
183350
|
+
locs.push({
|
|
183351
|
+
endColumn: rl.physicalLocation.region?.endColumn,
|
|
183352
|
+
endLine: rl.physicalLocation.region?.endLine ?? rl.physicalLocation.region?.startLine,
|
|
183353
|
+
startColumn: rl.physicalLocation.region?.startColumn,
|
|
183354
|
+
startLine: rl.physicalLocation.region?.startLine,
|
|
183355
|
+
uri: rl.physicalLocation.artifactLocation.uri
|
|
183356
|
+
});
|
|
183357
|
+
}
|
|
183358
|
+
}
|
|
183359
|
+
for (const flow of result.codeFlows ?? []) {
|
|
183360
|
+
for (const tf of flow.threadFlows ?? []) {
|
|
183361
|
+
for (const step of tf.locations ?? []) {
|
|
183362
|
+
const loc = step.location?.physicalLocation;
|
|
183363
|
+
if (loc?.artifactLocation?.uri) {
|
|
183364
|
+
locs.push({
|
|
183365
|
+
endColumn: loc.region?.endColumn,
|
|
183366
|
+
endLine: loc.region?.endLine ?? loc.region?.startLine,
|
|
183367
|
+
startColumn: loc.region?.startColumn,
|
|
183368
|
+
startLine: loc.region?.startLine,
|
|
183369
|
+
uri: loc.artifactLocation.uri
|
|
183370
|
+
});
|
|
183371
|
+
}
|
|
183372
|
+
}
|
|
183373
|
+
}
|
|
183374
|
+
}
|
|
183375
|
+
return locs;
|
|
183376
|
+
}
|
|
183377
|
+
function extractFullPathLocations(result) {
|
|
183378
|
+
const flows = result.codeFlows ?? [];
|
|
183379
|
+
const locs = [];
|
|
183380
|
+
for (const flow of flows) {
|
|
183381
|
+
for (const tf of flow.threadFlows ?? []) {
|
|
183382
|
+
for (const step of tf.locations ?? []) {
|
|
183383
|
+
const loc = step.location?.physicalLocation;
|
|
183384
|
+
if (loc?.artifactLocation?.uri) {
|
|
183385
|
+
locs.push({
|
|
183386
|
+
endColumn: loc.region?.endColumn,
|
|
183387
|
+
endLine: loc.region?.endLine ?? loc.region?.startLine,
|
|
183388
|
+
startColumn: loc.region?.startColumn,
|
|
183389
|
+
startLine: loc.region?.startLine,
|
|
183390
|
+
uri: loc.artifactLocation.uri
|
|
183391
|
+
});
|
|
183392
|
+
}
|
|
183393
|
+
}
|
|
183394
|
+
}
|
|
183395
|
+
}
|
|
183396
|
+
return locs;
|
|
183397
|
+
}
|
|
183398
|
+
function normalizeUri(uri) {
|
|
183399
|
+
let normalized = uri;
|
|
183400
|
+
if (normalized.startsWith("file:///")) {
|
|
183401
|
+
normalized = normalized.substring(7);
|
|
183402
|
+
} else if (normalized.startsWith("file://")) {
|
|
183403
|
+
normalized = normalized.substring(5);
|
|
183404
|
+
}
|
|
183405
|
+
try {
|
|
183406
|
+
normalized = decodeURIComponent(normalized);
|
|
183407
|
+
} catch {
|
|
183408
|
+
}
|
|
183409
|
+
normalized = normalized.replace(/\\/g, "/");
|
|
183410
|
+
normalized = normalized.replace(/^\/+/, "");
|
|
183411
|
+
return normalized;
|
|
183412
|
+
}
|
|
183413
|
+
function urisMatch(uriA, uriB) {
|
|
183414
|
+
const a = normalizeUri(uriA);
|
|
183415
|
+
const b = normalizeUri(uriB);
|
|
183416
|
+
if (a === b) return true;
|
|
183417
|
+
return a.endsWith(b) || b.endsWith(a);
|
|
183418
|
+
}
|
|
183419
|
+
function regionsOverlap(a, b) {
|
|
183420
|
+
if (!urisMatch(a.uri, b.uri)) return false;
|
|
183421
|
+
const aStartLine = a.startLine ?? 0;
|
|
183422
|
+
const aEndLine = a.endLine ?? aStartLine;
|
|
183423
|
+
const bStartLine = b.startLine ?? 0;
|
|
183424
|
+
const bEndLine = b.endLine ?? bStartLine;
|
|
183425
|
+
if (aEndLine < bStartLine || bEndLine < aStartLine) return false;
|
|
183426
|
+
if (aStartLine === aEndLine && bStartLine === bEndLine && aStartLine === bStartLine) {
|
|
183427
|
+
const aStartCol = a.startColumn ?? 0;
|
|
183428
|
+
const aEndCol = a.endColumn ?? Infinity;
|
|
183429
|
+
const bStartCol = b.startColumn ?? 0;
|
|
183430
|
+
const bEndCol = b.endColumn ?? Infinity;
|
|
183431
|
+
if (aEndCol < bStartCol || bEndCol < aStartCol) return false;
|
|
183432
|
+
}
|
|
183433
|
+
return true;
|
|
183434
|
+
}
|
|
183435
|
+
function locationKey(loc) {
|
|
183436
|
+
return `${normalizeUri(loc.uri)}:${loc.startLine ?? "?"}:${loc.startColumn ?? "?"}`;
|
|
183437
|
+
}
|
|
183438
|
+
function findSharedLocations(locsA, locsB) {
|
|
183439
|
+
const shared = [];
|
|
183440
|
+
const seen = /* @__PURE__ */ new Set();
|
|
183441
|
+
for (const a of locsA) {
|
|
183442
|
+
for (const b of locsB) {
|
|
183443
|
+
if (regionsOverlap(a, b)) {
|
|
183444
|
+
const key = `${locationKey(a)}|${locationKey(b)}`;
|
|
183445
|
+
if (!seen.has(key)) {
|
|
183446
|
+
seen.add(key);
|
|
183447
|
+
shared.push({
|
|
183448
|
+
endColumn: a.endColumn,
|
|
183449
|
+
endLine: a.endLine,
|
|
183450
|
+
startColumn: a.startColumn,
|
|
183451
|
+
startLine: a.startLine,
|
|
183452
|
+
uri: a.uri
|
|
183453
|
+
});
|
|
183454
|
+
}
|
|
183455
|
+
}
|
|
183456
|
+
}
|
|
183457
|
+
}
|
|
183458
|
+
return shared;
|
|
183459
|
+
}
|
|
183460
|
+
function extractRuleFromSarif(sarif, ruleId) {
|
|
183461
|
+
const run = sarif.runs[0];
|
|
183462
|
+
if (!run) {
|
|
183463
|
+
return { ...sarif, runs: [{ tool: { driver: { name: "CodeQL", rules: [] } }, results: [] }] };
|
|
183464
|
+
}
|
|
183465
|
+
const allRules = collectAllRules(run);
|
|
183466
|
+
const matchingRules = allRules.filter((r) => r.id === ruleId);
|
|
183467
|
+
const matchingResults = (run.results ?? []).filter((r) => r.ruleId === ruleId).map((r) => ({ ...r, ruleIndex: 0 }));
|
|
183468
|
+
const extractedRun = {
|
|
183469
|
+
tool: {
|
|
183470
|
+
driver: {
|
|
183471
|
+
...run.tool.driver,
|
|
183472
|
+
rules: matchingRules
|
|
183473
|
+
},
|
|
183474
|
+
extensions: run.tool.extensions
|
|
183475
|
+
},
|
|
183476
|
+
results: matchingResults
|
|
183477
|
+
};
|
|
183478
|
+
if (run.properties) {
|
|
183479
|
+
extractedRun.properties = run.properties;
|
|
183480
|
+
}
|
|
183481
|
+
return {
|
|
183482
|
+
version: sarif.version,
|
|
183483
|
+
$schema: sarif.$schema,
|
|
183484
|
+
runs: [extractedRun]
|
|
183485
|
+
};
|
|
183486
|
+
}
|
|
183487
|
+
function decomposeSarifByRule(sarif) {
|
|
183488
|
+
const run = sarif.runs[0];
|
|
183489
|
+
if (!run) return /* @__PURE__ */ new Map();
|
|
183490
|
+
const results = run.results ?? [];
|
|
183491
|
+
const ruleIds = new Set(results.map((r) => r.ruleId));
|
|
183492
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
183493
|
+
for (const ruleId of ruleIds) {
|
|
183494
|
+
map2.set(ruleId, extractRuleFromSarif(sarif, ruleId));
|
|
183495
|
+
}
|
|
183496
|
+
return map2;
|
|
183497
|
+
}
|
|
183498
|
+
function sanitizeMermaidLabel(text) {
|
|
183499
|
+
let sanitized = text.replace(/"/g, "#quot;");
|
|
183500
|
+
if (sanitized.length > 60) {
|
|
183501
|
+
sanitized = sanitized.substring(0, 57) + "...";
|
|
183502
|
+
}
|
|
183503
|
+
return sanitized;
|
|
183504
|
+
}
|
|
183505
|
+
function sarifResultToMermaid(result, _rule) {
|
|
183506
|
+
const flows = result.codeFlows;
|
|
183507
|
+
if (!flows || flows.length === 0) return "";
|
|
183508
|
+
const threadFlow = flows[0]?.threadFlows?.[0];
|
|
183509
|
+
if (!threadFlow || !threadFlow.locations || threadFlow.locations.length === 0) return "";
|
|
183510
|
+
const steps = threadFlow.locations;
|
|
183511
|
+
const lines = ["flowchart LR"];
|
|
183512
|
+
const nodeIds = [];
|
|
183513
|
+
for (let i = 0; i < steps.length; i++) {
|
|
183514
|
+
const step = steps[i];
|
|
183515
|
+
const loc = step.location?.physicalLocation;
|
|
183516
|
+
const message = step.location?.message?.text ?? "(step)";
|
|
183517
|
+
const uri = loc?.artifactLocation?.uri ?? "unknown";
|
|
183518
|
+
const line = loc?.region?.startLine ?? "?";
|
|
183519
|
+
const fileName = uri.split("/").pop() ?? uri;
|
|
183520
|
+
const nodeId = String.fromCharCode(65 + i % 26) + (i >= 26 ? String(Math.floor(i / 26)) : "");
|
|
183521
|
+
nodeIds.push(nodeId);
|
|
183522
|
+
const label = sanitizeMermaidLabel(message);
|
|
183523
|
+
lines.push(` ${nodeId}["${label}<br/>${fileName}:${line}"]`);
|
|
183524
|
+
}
|
|
183525
|
+
for (let i = 0; i < nodeIds.length - 1; i++) {
|
|
183526
|
+
lines.push(` ${nodeIds[i]} --> ${nodeIds[i + 1]}`);
|
|
183527
|
+
}
|
|
183528
|
+
if (nodeIds.length >= 2) {
|
|
183529
|
+
lines.push(` style ${nodeIds[0]} fill:#d4edda`);
|
|
183530
|
+
lines.push(` style ${nodeIds[nodeIds.length - 1]} fill:#f8d7da`);
|
|
183531
|
+
}
|
|
183532
|
+
return lines.join("\n");
|
|
183533
|
+
}
|
|
183534
|
+
function sarifRuleToMarkdown(sarif, ruleId) {
|
|
183535
|
+
const extracted = extractRuleFromSarif(sarif, ruleId);
|
|
183536
|
+
const run = extracted.runs[0];
|
|
183537
|
+
const results = run.results ?? [];
|
|
183538
|
+
const rules = run.tool.driver.rules ?? [];
|
|
183539
|
+
if (results.length === 0 && rules.length === 0) return "";
|
|
183540
|
+
const rule = rules[0];
|
|
183541
|
+
const lines = [];
|
|
183542
|
+
lines.push(`## ${ruleId}`);
|
|
183543
|
+
lines.push("");
|
|
183544
|
+
if (rule) {
|
|
183545
|
+
const name = getRuleDisplayName(rule);
|
|
183546
|
+
lines.push(`**Name**: ${name}`);
|
|
183547
|
+
const props = rule.properties;
|
|
183548
|
+
if (props) {
|
|
183549
|
+
const parts = [];
|
|
183550
|
+
if (props.kind) parts.push(`**Kind**: ${props.kind}`);
|
|
183551
|
+
if (props.precision) parts.push(`**Precision**: ${props.precision}`);
|
|
183552
|
+
if (props["security-severity"]) parts.push(`**Security Severity**: ${props["security-severity"]}`);
|
|
183553
|
+
if (Array.isArray(props.tags) && props.tags.length > 0) {
|
|
183554
|
+
parts.push(`**Tags**: ${props.tags.join(", ")}`);
|
|
183555
|
+
}
|
|
183556
|
+
if (parts.length > 0) {
|
|
183557
|
+
lines.push(parts.join(" | "));
|
|
183558
|
+
}
|
|
183559
|
+
}
|
|
183560
|
+
lines.push(`**Results**: ${results.length}`);
|
|
183561
|
+
lines.push("");
|
|
183562
|
+
if (rule.help?.markdown) {
|
|
183563
|
+
lines.push("### Query Help");
|
|
183564
|
+
lines.push("");
|
|
183565
|
+
lines.push(rule.help.markdown);
|
|
183566
|
+
lines.push("");
|
|
183567
|
+
}
|
|
183568
|
+
}
|
|
183569
|
+
if (results.length > 0) {
|
|
183570
|
+
lines.push("### Results");
|
|
183571
|
+
lines.push("");
|
|
183572
|
+
lines.push("| # | File | Line | Message |");
|
|
183573
|
+
lines.push("|---|------|------|---------|");
|
|
183574
|
+
for (let i = 0; i < results.length; i++) {
|
|
183575
|
+
const r = results[i];
|
|
183576
|
+
const loc = r.locations?.[0]?.physicalLocation;
|
|
183577
|
+
const uri = loc?.artifactLocation?.uri ?? "(unknown)";
|
|
183578
|
+
const line = loc?.region?.startLine ?? "-";
|
|
183579
|
+
const msg = r.message.text.replace(/([\\|])/g, "\\$1");
|
|
183580
|
+
lines.push(`| ${i + 1} | ${uri} | ${line} | ${msg} |`);
|
|
183581
|
+
}
|
|
183582
|
+
lines.push("");
|
|
183583
|
+
}
|
|
183584
|
+
const pathResults = results.filter((r) => r.codeFlows && r.codeFlows.length > 0);
|
|
183585
|
+
if (pathResults.length > 0 && rule) {
|
|
183586
|
+
lines.push("### Dataflow Paths");
|
|
183587
|
+
lines.push("");
|
|
183588
|
+
for (let i = 0; i < pathResults.length; i++) {
|
|
183589
|
+
const r = pathResults[i];
|
|
183590
|
+
const loc = r.locations?.[0]?.physicalLocation;
|
|
183591
|
+
const uri = loc?.artifactLocation?.uri ?? "(unknown)";
|
|
183592
|
+
const line = loc?.region?.startLine ?? "?";
|
|
183593
|
+
lines.push(`#### Path ${i + 1}: ${uri}:${line}`);
|
|
183594
|
+
lines.push("");
|
|
183595
|
+
const mermaid = sarifResultToMermaid(r, rule);
|
|
183596
|
+
if (mermaid) {
|
|
183597
|
+
lines.push("```mermaid");
|
|
183598
|
+
lines.push(mermaid);
|
|
183599
|
+
lines.push("```");
|
|
183600
|
+
lines.push("");
|
|
183601
|
+
}
|
|
183602
|
+
}
|
|
183603
|
+
}
|
|
183604
|
+
return lines.join("\n");
|
|
183605
|
+
}
|
|
183606
|
+
function listSarifRules(sarif) {
|
|
183607
|
+
const run = sarif.runs[0];
|
|
183608
|
+
if (!run) return [];
|
|
183609
|
+
const allRules = collectAllRules(run);
|
|
183610
|
+
const results = run.results ?? [];
|
|
183611
|
+
const toolName = run.tool.driver.name;
|
|
183612
|
+
const toolVersion = run.tool.driver.version;
|
|
183613
|
+
const countByRule = /* @__PURE__ */ new Map();
|
|
183614
|
+
for (const r of results) {
|
|
183615
|
+
countByRule.set(r.ruleId, (countByRule.get(r.ruleId) ?? 0) + 1);
|
|
183616
|
+
}
|
|
183617
|
+
const summaries = [];
|
|
183618
|
+
const seenRuleIds = /* @__PURE__ */ new Set();
|
|
183619
|
+
for (const rule of allRules) {
|
|
183620
|
+
seenRuleIds.add(rule.id);
|
|
183621
|
+
const props = rule.properties;
|
|
183622
|
+
summaries.push({
|
|
183623
|
+
kind: props?.kind,
|
|
183624
|
+
name: getRuleDisplayName(rule),
|
|
183625
|
+
precision: props?.precision,
|
|
183626
|
+
resultCount: countByRule.get(rule.id) ?? 0,
|
|
183627
|
+
ruleId: rule.id,
|
|
183628
|
+
severity: props?.["security-severity"],
|
|
183629
|
+
tags: Array.isArray(props?.tags) ? props.tags : void 0,
|
|
183630
|
+
tool: toolName,
|
|
183631
|
+
toolVersion
|
|
183632
|
+
});
|
|
183633
|
+
}
|
|
183634
|
+
for (const [ruleId, count] of countByRule) {
|
|
183635
|
+
if (!seenRuleIds.has(ruleId)) {
|
|
183636
|
+
summaries.push({
|
|
183637
|
+
resultCount: count,
|
|
183638
|
+
ruleId,
|
|
183639
|
+
tool: toolName,
|
|
183640
|
+
toolVersion
|
|
183641
|
+
});
|
|
183642
|
+
}
|
|
183643
|
+
}
|
|
183644
|
+
return summaries;
|
|
183645
|
+
}
|
|
183646
|
+
function diffSarifRules(sarifA, sarifB) {
|
|
183647
|
+
const rulesA = listSarifRules(sarifA);
|
|
183648
|
+
const rulesB = listSarifRules(sarifB);
|
|
183649
|
+
const mapA = new Map(rulesA.map((r) => [r.ruleId, r]));
|
|
183650
|
+
const mapB = new Map(rulesB.map((r) => [r.ruleId, r]));
|
|
183651
|
+
const addedRules = [];
|
|
183652
|
+
const removedRules = [];
|
|
183653
|
+
const changedRules = [];
|
|
183654
|
+
const unchangedRules = [];
|
|
183655
|
+
for (const [ruleId, ruleA] of mapA) {
|
|
183656
|
+
const ruleB = mapB.get(ruleId);
|
|
183657
|
+
if (!ruleB) {
|
|
183658
|
+
removedRules.push(ruleA);
|
|
183659
|
+
} else if (ruleA.resultCount !== ruleB.resultCount) {
|
|
183660
|
+
changedRules.push({
|
|
183661
|
+
countA: ruleA.resultCount,
|
|
183662
|
+
countB: ruleB.resultCount,
|
|
183663
|
+
delta: ruleB.resultCount - ruleA.resultCount,
|
|
183664
|
+
ruleId
|
|
183665
|
+
});
|
|
183666
|
+
} else {
|
|
183667
|
+
unchangedRules.push(ruleA);
|
|
183668
|
+
}
|
|
183669
|
+
}
|
|
183670
|
+
for (const [ruleId, ruleB] of mapB) {
|
|
183671
|
+
if (!mapA.has(ruleId)) {
|
|
183672
|
+
addedRules.push(ruleB);
|
|
183673
|
+
}
|
|
183674
|
+
}
|
|
183675
|
+
const runA = sarifA.runs[0];
|
|
183676
|
+
const runB = sarifB.runs[0];
|
|
183677
|
+
return {
|
|
183678
|
+
addedRules,
|
|
183679
|
+
changedRules,
|
|
183680
|
+
removedRules,
|
|
183681
|
+
summary: {
|
|
183682
|
+
toolA: runA?.tool.driver.name,
|
|
183683
|
+
toolB: runB?.tool.driver.name,
|
|
183684
|
+
toolVersionA: runA?.tool.driver.version,
|
|
183685
|
+
toolVersionB: runB?.tool.driver.version,
|
|
183686
|
+
totalResultsA: rulesA.reduce((sum, r) => sum + r.resultCount, 0),
|
|
183687
|
+
totalResultsB: rulesB.reduce((sum, r) => sum + r.resultCount, 0),
|
|
183688
|
+
totalRulesA: rulesA.length,
|
|
183689
|
+
totalRulesB: rulesB.length
|
|
183690
|
+
},
|
|
183691
|
+
unchangedRules
|
|
183692
|
+
};
|
|
183693
|
+
}
|
|
183694
|
+
function computeLocationOverlap(resultA, resultB, mode = "sink") {
|
|
183695
|
+
let locsA;
|
|
183696
|
+
let locsB;
|
|
183697
|
+
switch (mode) {
|
|
183698
|
+
case "sink":
|
|
183699
|
+
locsA = extractPrimaryLocations(resultA);
|
|
183700
|
+
locsB = extractPrimaryLocations(resultB);
|
|
183701
|
+
break;
|
|
183702
|
+
case "source":
|
|
183703
|
+
locsA = extractSourceLocations(resultA);
|
|
183704
|
+
locsB = extractSourceLocations(resultB);
|
|
183705
|
+
break;
|
|
183706
|
+
case "any-location":
|
|
183707
|
+
locsA = extractAllLocations(resultA);
|
|
183708
|
+
locsB = extractAllLocations(resultB);
|
|
183709
|
+
break;
|
|
183710
|
+
case "full-path": {
|
|
183711
|
+
const pathA = extractFullPathLocations(resultA);
|
|
183712
|
+
const pathB = extractFullPathLocations(resultB);
|
|
183713
|
+
const shared2 = findSharedLocations(pathA, pathB);
|
|
183714
|
+
const allKeysA = new Set(pathA.map(locationKey));
|
|
183715
|
+
const allKeysB = new Set(pathB.map(locationKey));
|
|
183716
|
+
const unionSize = (/* @__PURE__ */ new Set([...allKeysA, ...allKeysB])).size;
|
|
183717
|
+
const intersectionSize = [...allKeysA].filter((k) => allKeysB.has(k)).length;
|
|
183718
|
+
const similarity = unionSize > 0 ? intersectionSize / unionSize : 0;
|
|
183719
|
+
return {
|
|
183720
|
+
overlaps: shared2.length > 0,
|
|
183721
|
+
overlapMode: mode,
|
|
183722
|
+
pathSimilarity: Math.round(similarity * 1e3) / 1e3,
|
|
183723
|
+
sharedLocations: shared2
|
|
183724
|
+
};
|
|
183725
|
+
}
|
|
183726
|
+
default:
|
|
183727
|
+
locsA = extractPrimaryLocations(resultA);
|
|
183728
|
+
locsB = extractPrimaryLocations(resultB);
|
|
183729
|
+
}
|
|
183730
|
+
const shared = findSharedLocations(locsA, locsB);
|
|
183731
|
+
return {
|
|
183732
|
+
overlaps: shared.length > 0,
|
|
183733
|
+
overlapMode: mode,
|
|
183734
|
+
sharedLocations: shared
|
|
183735
|
+
};
|
|
183736
|
+
}
|
|
183737
|
+
|
|
183226
183738
|
// src/lib/session-data-manager.ts
|
|
183227
183739
|
init_temp_dir();
|
|
183228
183740
|
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
@@ -183232,7 +183744,7 @@ import { randomUUID as randomUUID2 } from "crypto";
|
|
|
183232
183744
|
// src/lib/sqlite-store.ts
|
|
183233
183745
|
var import_sql_asm = __toESM(require_sql_asm(), 1);
|
|
183234
183746
|
init_logger();
|
|
183235
|
-
import { mkdirSync as mkdirSync5, readFileSync as
|
|
183747
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync6, renameSync, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
183236
183748
|
import { join as join8 } from "path";
|
|
183237
183749
|
var SqliteStore = class _SqliteStore {
|
|
183238
183750
|
db = null;
|
|
@@ -183257,7 +183769,7 @@ var SqliteStore = class _SqliteStore {
|
|
|
183257
183769
|
mkdirSync5(this.storageDir, { recursive: true });
|
|
183258
183770
|
const SQL = await (0, import_sql_asm.default)();
|
|
183259
183771
|
try {
|
|
183260
|
-
const fileBuffer =
|
|
183772
|
+
const fileBuffer = readFileSync6(this.dbPath);
|
|
183261
183773
|
this.db = new SQL.Database(fileBuffer);
|
|
183262
183774
|
} catch {
|
|
183263
183775
|
this.db = new SQL.Database();
|
|
@@ -183357,10 +183869,28 @@ var SqliteStore = class _SqliteStore {
|
|
|
183357
183869
|
CREATE INDEX IF NOT EXISTS idx_qrc_language
|
|
183358
183870
|
ON query_result_cache (language);
|
|
183359
183871
|
`);
|
|
183872
|
+
this.migrateAddColumn("query_result_cache", "rule_id", "TEXT");
|
|
183873
|
+
this.exec(`
|
|
183874
|
+
CREATE INDEX IF NOT EXISTS idx_qrc_rule_id
|
|
183875
|
+
ON query_result_cache (rule_id);
|
|
183876
|
+
`);
|
|
183877
|
+
this.migrateAddColumn("query_result_cache", "run_id", "TEXT NOT NULL DEFAULT ''");
|
|
183360
183878
|
}
|
|
183361
183879
|
// ---------------------------------------------------------------------------
|
|
183362
183880
|
// Low-level helpers
|
|
183363
183881
|
// ---------------------------------------------------------------------------
|
|
183882
|
+
/**
|
|
183883
|
+
* Add a column to a table if it does not already exist (safe migration).
|
|
183884
|
+
*/
|
|
183885
|
+
migrateAddColumn(table, column, type2) {
|
|
183886
|
+
const db = this.ensureDb();
|
|
183887
|
+
const result = db.exec(`PRAGMA table_info(${table})`);
|
|
183888
|
+
const columns = result.length > 0 ? result[0].values.map((row) => row[1]) : [];
|
|
183889
|
+
if (!columns.includes(column)) {
|
|
183890
|
+
db.run(`ALTER TABLE ${table} ADD COLUMN ${column} ${type2}`);
|
|
183891
|
+
this.dirty = true;
|
|
183892
|
+
}
|
|
183893
|
+
}
|
|
183364
183894
|
ensureDb() {
|
|
183365
183895
|
if (!this.db) throw new Error("SqliteStore not initialized \u2014 call initialize() first");
|
|
183366
183896
|
return this.db;
|
|
@@ -183584,8 +184114,9 @@ var SqliteStore = class _SqliteStore {
|
|
|
183584
184114
|
params.$entity_key_prefix = filter.entityKeyPrefix + "%";
|
|
183585
184115
|
}
|
|
183586
184116
|
if (filter?.search) {
|
|
183587
|
-
conditions.push("id IN (SELECT rowid FROM annotations_fts WHERE annotations_fts MATCH $search)");
|
|
184117
|
+
conditions.push("(id IN (SELECT rowid FROM annotations_fts WHERE annotations_fts MATCH $search) OR category = $search_cat COLLATE NOCASE)");
|
|
183588
184118
|
params.$search = filter.search;
|
|
184119
|
+
params.$search_cat = filter.search;
|
|
183589
184120
|
}
|
|
183590
184121
|
let sql = "SELECT * FROM annotations";
|
|
183591
184122
|
if (conditions.length > 0) {
|
|
@@ -183696,11 +184227,11 @@ var SqliteStore = class _SqliteStore {
|
|
|
183696
184227
|
`INSERT OR REPLACE INTO query_result_cache
|
|
183697
184228
|
(cache_key, query_name, query_path, database_path, language, codeql_version,
|
|
183698
184229
|
external_predicates, output_format, result_content, result_count,
|
|
183699
|
-
bqrs_path, interpreted_path, execution_time_ms, created_at)
|
|
184230
|
+
bqrs_path, interpreted_path, execution_time_ms, rule_id, run_id, created_at)
|
|
183700
184231
|
VALUES ($cache_key, $query_name, $query_path, $database_path, $language,
|
|
183701
184232
|
$codeql_version, $external_predicates, $output_format, $result_content,
|
|
183702
184233
|
$result_count, $bqrs_path, $interpreted_path, $execution_time_ms,
|
|
183703
|
-
datetime('now'))`,
|
|
184234
|
+
$rule_id, $run_id, datetime('now'))`,
|
|
183704
184235
|
{
|
|
183705
184236
|
$cache_key: entry.cacheKey,
|
|
183706
184237
|
$query_name: entry.queryName,
|
|
@@ -183714,7 +184245,9 @@ var SqliteStore = class _SqliteStore {
|
|
|
183714
184245
|
$result_count: entry.resultCount ?? null,
|
|
183715
184246
|
$bqrs_path: entry.bqrsPath ?? null,
|
|
183716
184247
|
$interpreted_path: entry.interpretedPath ?? null,
|
|
183717
|
-
$execution_time_ms: entry.executionTimeMs ?? null
|
|
184248
|
+
$execution_time_ms: entry.executionTimeMs ?? null,
|
|
184249
|
+
$rule_id: entry.ruleId ?? null,
|
|
184250
|
+
$run_id: entry.runId ?? ""
|
|
183718
184251
|
}
|
|
183719
184252
|
);
|
|
183720
184253
|
this.dirty = true;
|
|
@@ -183727,7 +184260,7 @@ var SqliteStore = class _SqliteStore {
|
|
|
183727
184260
|
const db = this.ensureDb();
|
|
183728
184261
|
const stmt = db.prepare(
|
|
183729
184262
|
`SELECT cache_key, query_name, database_path, language, output_format,
|
|
183730
|
-
result_count, created_at
|
|
184263
|
+
result_count, rule_id, run_id, created_at
|
|
183731
184264
|
FROM query_result_cache WHERE cache_key = $key`
|
|
183732
184265
|
);
|
|
183733
184266
|
stmt.bind({ $key: cacheKey2 });
|
|
@@ -183741,6 +184274,8 @@ var SqliteStore = class _SqliteStore {
|
|
|
183741
184274
|
language: row.language,
|
|
183742
184275
|
outputFormat: row.output_format,
|
|
183743
184276
|
resultCount: row.result_count,
|
|
184277
|
+
ruleId: row.rule_id ?? null,
|
|
184278
|
+
runId: row.run_id ?? "",
|
|
183744
184279
|
createdAt: row.created_at
|
|
183745
184280
|
};
|
|
183746
184281
|
}
|
|
@@ -183871,8 +184406,12 @@ var SqliteStore = class _SqliteStore {
|
|
|
183871
184406
|
conditions.push("language = $language");
|
|
183872
184407
|
params.$language = filter.language;
|
|
183873
184408
|
}
|
|
184409
|
+
if (filter?.ruleId) {
|
|
184410
|
+
conditions.push("rule_id = $rule_id");
|
|
184411
|
+
params.$rule_id = filter.ruleId;
|
|
184412
|
+
}
|
|
183874
184413
|
let sql = `SELECT cache_key, query_name, database_path, language, output_format,
|
|
183875
|
-
result_count, execution_time_ms, created_at
|
|
184414
|
+
result_count, execution_time_ms, rule_id, run_id, created_at
|
|
183876
184415
|
FROM query_result_cache`;
|
|
183877
184416
|
if (conditions.length > 0) {
|
|
183878
184417
|
sql += " WHERE " + conditions.join(" AND ");
|
|
@@ -183895,6 +184434,8 @@ var SqliteStore = class _SqliteStore {
|
|
|
183895
184434
|
outputFormat: row.output_format,
|
|
183896
184435
|
resultCount: row.result_count,
|
|
183897
184436
|
executionTimeMs: row.execution_time_ms,
|
|
184437
|
+
ruleId: row.rule_id ?? null,
|
|
184438
|
+
runId: row.run_id ?? "",
|
|
183898
184439
|
createdAt: row.created_at
|
|
183899
184440
|
});
|
|
183900
184441
|
}
|
|
@@ -183925,6 +184466,10 @@ var SqliteStore = class _SqliteStore {
|
|
|
183925
184466
|
conditions.push("database_path = $database_path");
|
|
183926
184467
|
params.$database_path = filter.databasePath;
|
|
183927
184468
|
}
|
|
184469
|
+
if (filter?.ruleId) {
|
|
184470
|
+
conditions.push("rule_id = $rule_id");
|
|
184471
|
+
params.$rule_id = filter.ruleId;
|
|
184472
|
+
}
|
|
183928
184473
|
if (conditions.length === 0) return 0;
|
|
183929
184474
|
const db = this.ensureDb();
|
|
183930
184475
|
db.run(
|
|
@@ -184480,10 +185025,10 @@ Interpreted output saved to: ${outputFilePath}`;
|
|
|
184480
185025
|
try {
|
|
184481
185026
|
const config2 = sessionDataManager.getConfig();
|
|
184482
185027
|
if (config2.enableAnnotationTools && outputFilePath && queryPath) {
|
|
184483
|
-
const resultContent =
|
|
185028
|
+
const resultContent = readFileSync7(outputFilePath, "utf8");
|
|
184484
185029
|
const codeqlVersion = getActualCodeqlVersion();
|
|
184485
185030
|
const dbPath = params.database || "";
|
|
184486
|
-
const lang = queryLanguage || "unknown";
|
|
185031
|
+
const lang = queryLanguage || (dbPath ? readDatabaseMetadata(dbPath).language ?? "unknown" : "unknown");
|
|
184487
185032
|
const extPreds = {};
|
|
184488
185033
|
if (params.sourceFiles) extPreds.sourceFiles = params.sourceFiles;
|
|
184489
185034
|
if (params.sourceFunction) extPreds.sourceFunction = params.sourceFunction;
|
|
@@ -184496,6 +185041,24 @@ Interpreted output saved to: ${outputFilePath}`;
|
|
|
184496
185041
|
queryPath
|
|
184497
185042
|
});
|
|
184498
185043
|
const store = sessionDataManager.getStore();
|
|
185044
|
+
let resultCount = null;
|
|
185045
|
+
let ruleId = null;
|
|
185046
|
+
let sarifQueryName = null;
|
|
185047
|
+
if (outputFormat.includes("sarif")) {
|
|
185048
|
+
try {
|
|
185049
|
+
const sarif = JSON.parse(resultContent);
|
|
185050
|
+
resultCount = sarif?.runs?.[0]?.results?.length ?? 0;
|
|
185051
|
+
const run = sarif?.runs?.[0];
|
|
185052
|
+
if (run) {
|
|
185053
|
+
const allRules = collectAllRules(run);
|
|
185054
|
+
if (allRules.length > 0) {
|
|
185055
|
+
ruleId = allRules[0].id ?? null;
|
|
185056
|
+
sarifQueryName = getRuleDisplayName(allRules[0]);
|
|
185057
|
+
}
|
|
185058
|
+
}
|
|
185059
|
+
} catch {
|
|
185060
|
+
}
|
|
185061
|
+
}
|
|
184499
185062
|
store.putCacheEntry({
|
|
184500
185063
|
bqrsPath,
|
|
184501
185064
|
cacheKey: cacheKey2,
|
|
@@ -184505,9 +185068,11 @@ Interpreted output saved to: ${outputFilePath}`;
|
|
|
184505
185068
|
interpretedPath: outputFilePath,
|
|
184506
185069
|
language: lang,
|
|
184507
185070
|
outputFormat,
|
|
184508
|
-
queryName: queryName || basename4(queryPath, ".ql"),
|
|
185071
|
+
queryName: sarifQueryName || queryName || basename4(queryPath, ".ql"),
|
|
184509
185072
|
queryPath,
|
|
184510
|
-
resultContent
|
|
185073
|
+
resultContent,
|
|
185074
|
+
resultCount,
|
|
185075
|
+
ruleId
|
|
184511
185076
|
});
|
|
184512
185077
|
enhancedOutput += `
|
|
184513
185078
|
Results cached with key: ${cacheKey2}`;
|
|
@@ -184574,10 +185139,94 @@ Warning: Query processing error - ${error2}`
|
|
|
184574
185139
|
};
|
|
184575
185140
|
}
|
|
184576
185141
|
}
|
|
185142
|
+
function cacheDatabaseAnalyzeResults(params, logger2) {
|
|
185143
|
+
try {
|
|
185144
|
+
const config2 = sessionDataManager.getConfig();
|
|
185145
|
+
if (!config2.enableAnnotationTools) return;
|
|
185146
|
+
const outputPath = params.output;
|
|
185147
|
+
const format = params.format;
|
|
185148
|
+
const dbPath = params.database;
|
|
185149
|
+
const queries = params.queries;
|
|
185150
|
+
if (!outputPath || !format || !dbPath) return;
|
|
185151
|
+
if (!format.includes("sarif")) return;
|
|
185152
|
+
let resultContent;
|
|
185153
|
+
try {
|
|
185154
|
+
resultContent = readFileSync7(outputPath, "utf8");
|
|
185155
|
+
} catch {
|
|
185156
|
+
return;
|
|
185157
|
+
}
|
|
185158
|
+
const codeqlVersion = getActualCodeqlVersion();
|
|
185159
|
+
const queryName = queries ? basename4(queries, ".qls") : "database-analyze";
|
|
185160
|
+
let resultCount = null;
|
|
185161
|
+
let parsedSarif = null;
|
|
185162
|
+
try {
|
|
185163
|
+
parsedSarif = JSON.parse(resultContent);
|
|
185164
|
+
const runs = parsedSarif?.runs;
|
|
185165
|
+
resultCount = runs?.[0]?.results?.length ?? 0;
|
|
185166
|
+
} catch {
|
|
185167
|
+
}
|
|
185168
|
+
const language = params.language || (readDatabaseMetadata(dbPath).language ?? "unknown");
|
|
185169
|
+
const cacheKey2 = computeQueryCacheKey({
|
|
185170
|
+
codeqlVersion,
|
|
185171
|
+
databasePath: dbPath,
|
|
185172
|
+
outputFormat: format,
|
|
185173
|
+
queryPath: queries || "database-analyze"
|
|
185174
|
+
});
|
|
185175
|
+
const store = sessionDataManager.getStore();
|
|
185176
|
+
store.putCacheEntry({
|
|
185177
|
+
cacheKey: cacheKey2,
|
|
185178
|
+
codeqlVersion,
|
|
185179
|
+
databasePath: dbPath,
|
|
185180
|
+
interpretedPath: outputPath,
|
|
185181
|
+
language,
|
|
185182
|
+
outputFormat: format,
|
|
185183
|
+
queryName,
|
|
185184
|
+
queryPath: queries || "database-analyze",
|
|
185185
|
+
resultContent,
|
|
185186
|
+
resultCount
|
|
185187
|
+
});
|
|
185188
|
+
logger2.info(`Cached database-analyze results with key: ${cacheKey2} (${resultCount != null ? `${resultCount} results` : "result count unknown"})`);
|
|
185189
|
+
if (parsedSarif) {
|
|
185190
|
+
try {
|
|
185191
|
+
const decomposed = decomposeSarifByRule(parsedSarif);
|
|
185192
|
+
for (const [ruleId, ruleSarif] of decomposed) {
|
|
185193
|
+
const ruleResults = ruleSarif.runs[0]?.results ?? [];
|
|
185194
|
+
const ruleContent = JSON.stringify(ruleSarif, null, 2);
|
|
185195
|
+
const ruleDef = ruleSarif.runs[0]?.tool.driver.rules?.[0];
|
|
185196
|
+
const ruleDisplayName = ruleDef ? getRuleDisplayName(ruleDef) : ruleId;
|
|
185197
|
+
const ruleCacheKey = computeQueryCacheKey({
|
|
185198
|
+
codeqlVersion,
|
|
185199
|
+
databasePath: dbPath,
|
|
185200
|
+
outputFormat: format,
|
|
185201
|
+
queryPath: `${queries || "database-analyze"}#${ruleId}`
|
|
185202
|
+
});
|
|
185203
|
+
store.putCacheEntry({
|
|
185204
|
+
cacheKey: ruleCacheKey,
|
|
185205
|
+
codeqlVersion,
|
|
185206
|
+
databasePath: dbPath,
|
|
185207
|
+
interpretedPath: outputPath,
|
|
185208
|
+
language,
|
|
185209
|
+
outputFormat: format,
|
|
185210
|
+
queryName: ruleDisplayName,
|
|
185211
|
+
queryPath: queries || "database-analyze",
|
|
185212
|
+
resultContent: ruleContent,
|
|
185213
|
+
resultCount: ruleResults.length,
|
|
185214
|
+
ruleId
|
|
185215
|
+
});
|
|
185216
|
+
logger2.info(`Cached per-rule entry for ${ruleId} with key: ${ruleCacheKey} (${ruleResults.length} results)`);
|
|
185217
|
+
}
|
|
185218
|
+
} catch (decomposeErr) {
|
|
185219
|
+
logger2.error("Failed to decompose SARIF by rule:", decomposeErr);
|
|
185220
|
+
}
|
|
185221
|
+
}
|
|
185222
|
+
} catch (err) {
|
|
185223
|
+
logger2.error("Failed to cache database-analyze results:", err);
|
|
185224
|
+
}
|
|
185225
|
+
}
|
|
184577
185226
|
|
|
184578
185227
|
// src/lib/cli-tool-registry.ts
|
|
184579
185228
|
init_package_paths();
|
|
184580
|
-
import {
|
|
185229
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync8, realpathSync, rmSync, writeFileSync as writeFileSync4 } from "fs";
|
|
184581
185230
|
import { delimiter as delimiter5, dirname as dirname5, isAbsolute as isAbsolute4, join as join10, resolve as resolve4 } from "path";
|
|
184582
185231
|
|
|
184583
185232
|
// ../node_modules/js-yaml/dist/js-yaml.mjs
|
|
@@ -187168,6 +187817,26 @@ var safeDump = renamed("safeDump", "dump");
|
|
|
187168
187817
|
|
|
187169
187818
|
// src/lib/cli-tool-registry.ts
|
|
187170
187819
|
init_temp_dir();
|
|
187820
|
+
var databaseLocks = /* @__PURE__ */ new Map();
|
|
187821
|
+
function acquireDatabaseLock(dbPath) {
|
|
187822
|
+
const key = dbPath;
|
|
187823
|
+
const previous = databaseLocks.get(key) ?? Promise.resolve();
|
|
187824
|
+
let resolveGate;
|
|
187825
|
+
const gate = new Promise((resolve15) => {
|
|
187826
|
+
resolveGate = resolve15;
|
|
187827
|
+
});
|
|
187828
|
+
databaseLocks.set(key, gate);
|
|
187829
|
+
return {
|
|
187830
|
+
ready: previous.then(() => {
|
|
187831
|
+
}),
|
|
187832
|
+
release: () => {
|
|
187833
|
+
resolveGate();
|
|
187834
|
+
if (databaseLocks.get(key) === gate) {
|
|
187835
|
+
databaseLocks.delete(key);
|
|
187836
|
+
}
|
|
187837
|
+
}
|
|
187838
|
+
};
|
|
187839
|
+
}
|
|
187171
187840
|
var defaultCLIResultProcessor = (result, _params) => {
|
|
187172
187841
|
if (!result.success) {
|
|
187173
187842
|
return `Command failed (exit code ${result.exitCode || "unknown"}):
|
|
@@ -187276,8 +187945,23 @@ function registerCLITool(server, definition) {
|
|
|
187276
187945
|
if (files && Array.isArray(files)) {
|
|
187277
187946
|
positionalArgs = [...positionalArgs, ...files];
|
|
187278
187947
|
}
|
|
187279
|
-
if (file && name.startsWith("codeql_bqrs_")) {
|
|
187280
|
-
|
|
187948
|
+
if (file !== void 0 && name.startsWith("codeql_bqrs_")) {
|
|
187949
|
+
let cleanFile = Array.isArray(file) ? file.length > 0 ? String(file[0]) : "" : String(file);
|
|
187950
|
+
if (cleanFile.startsWith("[") && cleanFile.endsWith("]")) {
|
|
187951
|
+
try {
|
|
187952
|
+
const parsed = JSON.parse(cleanFile);
|
|
187953
|
+
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
187954
|
+
cleanFile = String(parsed[0]);
|
|
187955
|
+
} else {
|
|
187956
|
+
cleanFile = "";
|
|
187957
|
+
}
|
|
187958
|
+
} catch {
|
|
187959
|
+
}
|
|
187960
|
+
}
|
|
187961
|
+
if (!cleanFile.trim()) {
|
|
187962
|
+
throw new Error('The "file" parameter for BQRS tools must be a non-empty string path to a .bqrs file.');
|
|
187963
|
+
}
|
|
187964
|
+
positionalArgs = [...positionalArgs, cleanFile];
|
|
187281
187965
|
}
|
|
187282
187966
|
if (qlref && name === "codeql_resolve_qlref") {
|
|
187283
187967
|
positionalArgs = [...positionalArgs, qlref];
|
|
@@ -187407,6 +188091,37 @@ function registerCLITool(server, definition) {
|
|
|
187407
188091
|
}
|
|
187408
188092
|
break;
|
|
187409
188093
|
}
|
|
188094
|
+
case "codeql_bqrs_interpret": {
|
|
188095
|
+
const dbValue = options.database;
|
|
188096
|
+
delete options.database;
|
|
188097
|
+
if (dbValue !== void 0) {
|
|
188098
|
+
const dbStr = String(dbValue).trim();
|
|
188099
|
+
if (!dbStr) {
|
|
188100
|
+
throw new Error('The "database" parameter must be a non-empty path to a CodeQL database.');
|
|
188101
|
+
}
|
|
188102
|
+
const dbPath = resolveDatabasePath(dbStr);
|
|
188103
|
+
if (!options["source-archive"]) {
|
|
188104
|
+
const srcZipPath = join10(dbPath, "src.zip");
|
|
188105
|
+
const srcDirPath = join10(dbPath, "src");
|
|
188106
|
+
if (existsSync6(srcZipPath)) {
|
|
188107
|
+
options["source-archive"] = srcZipPath;
|
|
188108
|
+
} else if (existsSync6(srcDirPath)) {
|
|
188109
|
+
options["source-archive"] = srcDirPath;
|
|
188110
|
+
} else {
|
|
188111
|
+
throw new Error(
|
|
188112
|
+
`CodeQL database at "${dbPath}" does not contain a source archive (expected "src.zip" file or "src" directory).`
|
|
188113
|
+
);
|
|
188114
|
+
}
|
|
188115
|
+
}
|
|
188116
|
+
if (!options["source-location-prefix"]) {
|
|
188117
|
+
const dbMeta = readDatabaseMetadata(dbPath);
|
|
188118
|
+
if (dbMeta.sourceLocationPrefix) {
|
|
188119
|
+
options["source-location-prefix"] = dbMeta.sourceLocationPrefix;
|
|
188120
|
+
}
|
|
188121
|
+
}
|
|
188122
|
+
}
|
|
188123
|
+
break;
|
|
188124
|
+
}
|
|
187410
188125
|
case "codeql_query_compile":
|
|
187411
188126
|
case "codeql_resolve_metadata":
|
|
187412
188127
|
if (query) {
|
|
@@ -187501,7 +188216,24 @@ function registerCLITool(server, definition) {
|
|
|
187501
188216
|
if (name === "codeql_test_run") {
|
|
187502
188217
|
options["keep-databases"] = true;
|
|
187503
188218
|
}
|
|
187504
|
-
|
|
188219
|
+
let dbLock;
|
|
188220
|
+
if (name === "codeql_database_analyze") {
|
|
188221
|
+
const resolvedDb = typeof params.database === "string" ? resolveDatabasePath(params.database) : positionalArgs.length > 0 ? positionalArgs[0] : void 0;
|
|
188222
|
+
if (resolvedDb) {
|
|
188223
|
+
let lockKey = resolve4(resolvedDb);
|
|
188224
|
+
try {
|
|
188225
|
+
lockKey = realpathSync(lockKey);
|
|
188226
|
+
} catch {
|
|
188227
|
+
}
|
|
188228
|
+
dbLock = acquireDatabaseLock(lockKey);
|
|
188229
|
+
await dbLock.ready;
|
|
188230
|
+
}
|
|
188231
|
+
}
|
|
188232
|
+
try {
|
|
188233
|
+
result = await executeCodeQLCommand(subcommand, options, [...positionalArgs, ...userAdditionalArgs], cwd);
|
|
188234
|
+
} finally {
|
|
188235
|
+
dbLock?.release();
|
|
188236
|
+
}
|
|
187505
188237
|
} else if (command === "qlt") {
|
|
187506
188238
|
result = await executeQLTCommand(subcommand, options, [...positionalArgs, ...userAdditionalArgs]);
|
|
187507
188239
|
} else {
|
|
@@ -187531,6 +188263,10 @@ function registerCLITool(server, definition) {
|
|
|
187531
188263
|
}
|
|
187532
188264
|
}
|
|
187533
188265
|
}
|
|
188266
|
+
if (name === "codeql_database_analyze" && result.success && options.output) {
|
|
188267
|
+
const resolvedDb = typeof params.database === "string" ? resolveDatabasePath(params.database) : params.database;
|
|
188268
|
+
cacheDatabaseAnalyzeResults({ ...params, database: resolvedDb, output: options.output, format: options.format }, logger);
|
|
188269
|
+
}
|
|
187534
188270
|
const processedResult = resultProcessor(result, params);
|
|
187535
188271
|
return {
|
|
187536
188272
|
content: [{
|
|
@@ -187654,7 +188390,7 @@ var codeqlBqrsInfoTool = {
|
|
|
187654
188390
|
command: "codeql",
|
|
187655
188391
|
subcommand: "bqrs info",
|
|
187656
188392
|
inputSchema: {
|
|
187657
|
-
|
|
188393
|
+
file: external_exports.string().describe("BQRS file to examine"),
|
|
187658
188394
|
format: external_exports.enum(["text", "json"]).optional().describe("Output format: text (default) or json. Use json for machine-readable output and pagination offset computation."),
|
|
187659
188395
|
"paginate-rows": external_exports.number().optional().describe("Compute byte offsets for pagination at intervals of this many rows. Use with --format=json. Offsets can be passed to codeql_bqrs_decode --start-at."),
|
|
187660
188396
|
"paginate-result-set": external_exports.string().optional().describe("Compute pagination offsets only for this result set name"),
|
|
@@ -187679,7 +188415,8 @@ var codeqlBqrsInterpretTool = {
|
|
|
187679
188415
|
file: external_exports.string().describe("The BQRS file to interpret"),
|
|
187680
188416
|
format: external_exports.enum(["csv", "sarif-latest", "sarifv2.1.0", "graphtext", "dgml", "dot"]).describe("Output format: csv (comma-separated), sarif-latest/sarifv2.1.0 (SARIF), graphtext/dgml/dot (graph formats, only for @kind graph queries)"),
|
|
187681
188417
|
output: createCodeQLSchemas.output(),
|
|
187682
|
-
|
|
188418
|
+
database: external_exports.string().optional().describe("Path to the CodeQL database. When provided, auto-resolves --source-archive (for file contents/snippets) and --source-location-prefix (from codeql-database.yml metadata) so SARIF results include full source context"),
|
|
188419
|
+
t: external_exports.array(external_exports.string()).describe('Query metadata key=value pairs in KEY=VALUE format. At least "kind" and "id" must be specified. Example: ["kind=problem", "id=js/sql-injection"]. Common keys: kind (problem|path-problem|graph|metric|diagnostic), id (query identifier like js/xss)'),
|
|
187683
188420
|
"max-paths": external_exports.number().optional().describe("Maximum number of paths to produce for each alert with paths (default: 4)"),
|
|
187684
188421
|
"sarif-add-file-contents": external_exports.boolean().optional().describe("[SARIF only] Include full file contents for all files referenced in results"),
|
|
187685
188422
|
"sarif-add-snippets": external_exports.boolean().optional().describe("[SARIF only] Include code snippets for each location with context"),
|
|
@@ -188236,7 +188973,7 @@ var codeqlGenerateQueryHelpTool = {
|
|
|
188236
188973
|
};
|
|
188237
188974
|
|
|
188238
188975
|
// src/tools/codeql/list-databases.ts
|
|
188239
|
-
import { existsSync as existsSync8, readdirSync as readdirSync4,
|
|
188976
|
+
import { existsSync as existsSync8, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
|
|
188240
188977
|
import { join as join12 } from "path";
|
|
188241
188978
|
|
|
188242
188979
|
// src/lib/discovery-config.ts
|
|
@@ -188259,33 +188996,6 @@ function getQueryRunResultsDirs() {
|
|
|
188259
188996
|
|
|
188260
188997
|
// src/tools/codeql/list-databases.ts
|
|
188261
188998
|
init_logger();
|
|
188262
|
-
function parseDatabaseYml(ymlPath) {
|
|
188263
|
-
try {
|
|
188264
|
-
const content = readFileSync8(ymlPath, "utf-8");
|
|
188265
|
-
const info = {};
|
|
188266
|
-
for (const line of content.split("\n")) {
|
|
188267
|
-
const trimmed = line.trim();
|
|
188268
|
-
const colonIdx = trimmed.indexOf(":");
|
|
188269
|
-
if (colonIdx === -1) continue;
|
|
188270
|
-
const key = trimmed.substring(0, colonIdx).trim();
|
|
188271
|
-
const value = trimmed.substring(colonIdx + 1).trim().replace(/^["']|["']$/g, "");
|
|
188272
|
-
switch (key) {
|
|
188273
|
-
case "primaryLanguage":
|
|
188274
|
-
info.language = value;
|
|
188275
|
-
break;
|
|
188276
|
-
case "cliVersion":
|
|
188277
|
-
info.cliVersion = value;
|
|
188278
|
-
break;
|
|
188279
|
-
case "creationTime":
|
|
188280
|
-
info.creationTime = value;
|
|
188281
|
-
break;
|
|
188282
|
-
}
|
|
188283
|
-
}
|
|
188284
|
-
return info;
|
|
188285
|
-
} catch {
|
|
188286
|
-
return {};
|
|
188287
|
-
}
|
|
188288
|
-
}
|
|
188289
188999
|
async function discoverDatabases(baseDirs, language) {
|
|
188290
189000
|
const databases = [];
|
|
188291
189001
|
for (const baseDir of baseDirs) {
|
|
@@ -188307,11 +189017,10 @@ async function discoverDatabases(baseDirs, language) {
|
|
|
188307
189017
|
} catch {
|
|
188308
189018
|
continue;
|
|
188309
189019
|
}
|
|
188310
|
-
|
|
188311
|
-
if (!existsSync8(ymlPath)) {
|
|
189020
|
+
if (!existsSync8(join12(entryPath, "codeql-database.yml")) && !existsSync8(join12(entryPath, "codeql-database.yaml"))) {
|
|
188312
189021
|
continue;
|
|
188313
189022
|
}
|
|
188314
|
-
const metadata =
|
|
189023
|
+
const metadata = readDatabaseMetadata(entryPath);
|
|
188315
189024
|
if (language && metadata.language !== language) {
|
|
188316
189025
|
continue;
|
|
188317
189026
|
}
|
|
@@ -190276,7 +190985,7 @@ var codeqlResolveTestsTool = {
|
|
|
190276
190985
|
};
|
|
190277
190986
|
|
|
190278
190987
|
// src/tools/codeql/search-ql-code.ts
|
|
190279
|
-
import { closeSync as closeSync2, createReadStream as createReadStream3, fstatSync as fstatSync2, lstatSync, openSync as openSync2, readdirSync as readdirSync8, readFileSync as readFileSync12, realpathSync } from "fs";
|
|
190988
|
+
import { closeSync as closeSync2, createReadStream as createReadStream3, fstatSync as fstatSync2, lstatSync, openSync as openSync2, readdirSync as readdirSync8, readFileSync as readFileSync12, realpathSync as realpathSync2 } from "fs";
|
|
190280
190989
|
import { basename as basename8, extname as extname2, join as join19, resolve as resolve9 } from "path";
|
|
190281
190990
|
import { createInterface as createInterface3 } from "readline";
|
|
190282
190991
|
init_logger();
|
|
@@ -190306,7 +191015,7 @@ function collectFiles(paths, extensions, fileCount) {
|
|
|
190306
191015
|
if (SKIP_DIRS2.has(basename8(p))) return;
|
|
190307
191016
|
let realPath;
|
|
190308
191017
|
try {
|
|
190309
|
-
realPath =
|
|
191018
|
+
realPath = realpathSync2(p);
|
|
190310
191019
|
} catch {
|
|
190311
191020
|
return;
|
|
190312
191021
|
}
|
|
@@ -190841,13 +191550,13 @@ var security_templates_default = '# Security Query Templates\n\nThis resource pr
|
|
|
190841
191550
|
var server_overview_default = '# CodeQL Development MCP Server \u2014 Getting Started\n\nThis resource is the primary onboarding guide for LLM clients connecting to the CodeQL Development MCP Server. It explains what the server provides, which tools and prompts are available, and how to orchestrate common workflows.\n\n## What This Server Does\n\nThe CodeQL Development MCP Server wraps the CodeQL CLI and supporting utilities behind the Model Context Protocol (MCP). It exposes **tools** (executable actions), **prompts** (reusable workflow templates), and **resources** (reference material) that enable an LLM to develop, test, and analyze CodeQL queries without direct shell access.\n\n## Available Resources\n\nRead these resources via `resources/read` to deepen your understanding:\n\n| URI | Purpose |\n| --------------------------------------------- | --------------------------------------------------- |\n| `codeql://server/overview` | This guide \u2014 MCP server orientation |\n| `codeql://server/queries` | Bundled tools queries (PrintAST, PrintCFG, etc.) |\n| `codeql://server/tools` | Complete default tool reference |\n| `codeql://server/prompts` | Complete prompt reference |\n| `codeql://learning/query-basics` | QL query writing reference (syntax, metadata, etc.) |\n| `codeql://learning/test-driven-development` | TDD theory and workflow for CodeQL |\n| `codeql://templates/security` | Security query templates (multi-language) |\n| `codeql://patterns/performance` | Performance profiling and optimization |\n| `codeql://guides/query-unit-testing` | Guide for creating and running CodeQL query tests |\n| `codeql://guides/dataflow-migration-v1-to-v2` | Migrating from v1 to v2 dataflow API |\n| `codeql://languages/{language}/ast` | Language-specific AST class reference |\n| `codeql://languages/{language}/security` | Language-specific security patterns |\n\n## Quick-Start Workflows\n\n### 1. Create a New Query (TDD Approach)\n\nUse the `test_driven_development` prompt (or `ql_tdd_basic` / `ql_tdd_advanced`):\n\n1. `create_codeql_query` \u2014 scaffold query, test files, and `.qlref`\n2. `codeql_pack_install` \u2014 install pack dependencies\n3. Write test code with positive and negative cases\n4. `codeql_test_run` \u2014 run tests (expect failure initially)\n5. Implement query logic\n6. `codeql_query_compile` \u2014 validate syntax\n7. `codeql_test_run` \u2014 iterate until tests pass\n8. `codeql_test_accept` \u2014 accept correct results as baseline\n\n### 2. Understand Code Structure\n\nUse the `tools_query_workflow` prompt:\n\n1. `codeql_query_run` with `queryName="PrintAST"` \u2014 visualize the AST\n2. `codeql_query_run` with `queryName="PrintCFG"` \u2014 visualize control flow\n3. `codeql_query_run` with `queryName="CallGraphFrom"` / `"CallGraphTo"` \u2014 trace call relationships\n\n### 3. Analyze Query Quality\n\n1. `codeql_database_analyze` \u2014 run queries against a database\n2. `profile_codeql_query` or `profile_codeql_query_from_logs` \u2014 analyze performance\n3. `run_query_and_summarize_false_positives` prompt \u2014 assess precision\n4. `sarif_rank_false_positives` / `sarif_rank_true_positives` prompts \u2014 rank results\n\n### 4. Iterative Development with LSP\n\nUse the `ql_lsp_iterative_development` prompt:\n\n1. `codeql_lsp_completion` \u2014 get code completions while writing QL\n2. `codeql_lsp_definition` \u2014 navigate to symbol definitions\n3. `codeql_lsp_references` \u2014 find all references to a symbol\n4. `codeql_lsp_diagnostics` \u2014 real-time syntax and semantic validation\n\n## Tool Categories\n\nThe server provides default tools across these categories (see `codeql://server/tools` for the full reference):\n\n- **CodeQL CLI tools** \u2014 Database creation, query compilation, execution, result decoding, pack management\n- **LSP tools** \u2014 Code completion, go-to-definition, find references, diagnostics\n- **Query development tools** \u2014 Scaffolding, validation, profiling, quick evaluation, database registration\n\n## Prompt Categories\n\nThe server provides **11 prompts** (see `codeql://server/prompts` for the full reference):\n\n- **Test-driven development** \u2014 `test_driven_development`, `ql_tdd_basic`, `ql_tdd_advanced`\n- **Code understanding** \u2014 `tools_query_workflow`, `explain_codeql_query`\n- **Iterative development** \u2014 `ql_lsp_iterative_development`\n- **Documentation and quality** \u2014 `document_codeql_query`, `run_query_and_summarize_false_positives`, `sarif_rank_false_positives`, `sarif_rank_true_positives`\n- **Workshop creation** \u2014 `workshop_creation_workflow`\n\n## Key Concepts\n\n- **CodeQL database**: A relational representation of source code created by `codeql_database_create`. All queries execute against a database.\n- **QL pack**: A directory containing `codeql-pack.yml` with query or library code. Use `codeql_pack_install` to resolve dependencies.\n- **`.qlref` file**: A test reference that points from a test directory to the query being tested.\n- **`.expected` file**: The expected output of a query test. Use `codeql_test_accept` to update it when results are correct.\n- **BQRS**: Binary Query Result Sets \u2014 the native output format of `codeql_query_run`. Decode with `codeql_bqrs_decode` or interpret with `codeql_bqrs_interpret`.\n- **SARIF**: Static Analysis Results Interchange Format \u2014 the standard output format for `codeql_database_analyze`.\n\n## Supported Languages\n\nThe server supports CodeQL queries for: `actions`, `cpp`, `csharp`, `go`, `java`, `javascript`, `python`, `ruby`, `rust`, `swift`.\n';
|
|
190842
191551
|
|
|
190843
191552
|
// src/resources/server-prompts.md
|
|
190844
|
-
var server_prompts_default = "# MCP Server Prompts\n\nThis resource provides a complete reference of the prompts exposed by the CodeQL Development MCP Server. Prompts are reusable workflow templates that guide the LLM through common CodeQL development tasks. Invoke a prompt via the MCP `prompts/get` protocol.\n\n## Prompt Reference\n\n| Prompt | Description |\n| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------- |\n| `document_codeql_query` | Create or update standardized markdown documentation for a CodeQL query |\n| `explain_codeql_query` | Generate a detailed explanation of a CodeQL query with Mermaid evaluation diagrams |\n| `ql_lsp_iterative_development` | Iterative CodeQL query development using LSP tools for completion, navigation, and validation |\n| `ql_tdd_advanced` | Advanced test-driven CodeQL development with AST visualization, control flow, and call graph analysis |\n| `ql_tdd_basic` | Test-driven CodeQL query development checklist \u2014 write tests first, implement query, iterate until tests pass |\n| `run_query_and_summarize_false_positives` | Run a CodeQL query and summarize its false positives by root cause |\n| `sarif_rank_false_positives` | Analyze SARIF results to identify and rank likely false positives |\n| `sarif_rank_true_positives` | Analyze SARIF results to identify and rank likely true positives |\n| `test_driven_development` | End-to-end test-driven development workflow for CodeQL queries using MCP tools |\n| `tools_query_workflow` | Guide for using PrintAST, PrintCFG, CallGraphFrom, and CallGraphTo tool queries to understand code structure |\n| `workshop_creation_workflow` | Guide for creating multi-exercise CodeQL query development workshops from production-grade queries |\n\n## Prompt Categories\n\n### Test-Driven Development\n\n- **`test_driven_development`** \u2014 The primary TDD prompt. Requires a `language` parameter and optionally accepts `queryName`. Loads the `ql-tdd-basic.prompt.md` template and walks through the complete TDD cycle: scaffold \u2192 write tests \u2192 implement \u2192 compile \u2192 test \u2192 iterate.\n- **`ql_tdd_basic`** \u2014 A standalone TDD checklist. All parameters are optional. Covers the core loop: write test cases, implement the query, run tests, iterate.\n- **`ql_tdd_advanced`** \u2014 Extends basic TDD with AST visualization (`PrintAST`), control flow graph analysis (`PrintCFG`), and call graph exploration (`CallGraphFrom`, `CallGraphTo`). Optionally accepts a `database` path for immediate analysis.\n\n### Code Understanding\n\n- **`tools_query_workflow`** \u2014 Orchestrates the four built-in tool queries (PrintAST, PrintCFG, CallGraphFrom, CallGraphTo) to explore how source code is represented in a CodeQL database. Requires `language` and `database` parameters.\n- **`explain_codeql_query`** \u2014 Produces a verbal explanation of a query's logic and generates Mermaid diagrams showing the evaluation flow. Requires `queryPath` and `language`.\n\n### Iterative Development\n\n- **`ql_lsp_iterative_development`** \u2014 Combines LSP-based code completions (`codeql_lsp_completion`), go-to-definition (`codeql_lsp_definition`), find-references (`codeql_lsp_references`), and diagnostics (`codeql_lsp_diagnostics`) for an interactive development loop.\n\n### Documentation and Quality\n\n- **`document_codeql_query`** \u2014 Generates standardized markdown documentation as a sibling `.md` file to a query. Requires `queryPath` and `language`.\n- **`run_query_and_summarize_false_positives`** \u2014 Runs a CodeQL query on a database and groups results into false-positive categories by root cause.\n- **`sarif_rank_false_positives`** / **`sarif_rank_true_positives`** \u2014 Analyze SARIF output to assess query precision by ranking results as likely true or false positives.\n\n### Workshop Creation\n\n- **`workshop_creation_workflow`** \u2014 Guides the creation of multi-exercise workshops that teach CodeQL query development. Requires `queryPath` and `language`, optionally accepts `workshopName` and `numStages`.\n\n## Related Resources\n\n- `codeql://server/overview` \u2014 MCP server orientation guide\n- `codeql://server/tools` \u2014 Complete tool reference\n- `codeql://learning/test-driven-development` \u2014 TDD theory and workflow overview\n";
|
|
191553
|
+
var server_prompts_default = "# MCP Server Prompts\n\nThis resource provides a complete reference of the prompts exposed by the CodeQL Development MCP Server. Prompts are reusable workflow templates that guide the LLM through common CodeQL development tasks. Invoke a prompt via the MCP `prompts/get` protocol.\n\n## Prompt Reference\n\n| Prompt | Description |\n| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------- |\n| `compare_overlapping_alerts` | Compare CodeQL SARIF alerts across rules, files, runs, databases, or CodeQL versions |\n| `document_codeql_query` | Create or update standardized markdown documentation for a CodeQL query |\n| `explain_codeql_query` | Generate a detailed explanation of a CodeQL query with Mermaid evaluation diagrams |\n| `ql_lsp_iterative_development` | Iterative CodeQL query development using LSP tools for completion, navigation, and validation |\n| `ql_tdd_advanced` | Advanced test-driven CodeQL development with AST visualization, control flow, and call graph analysis |\n| `ql_tdd_basic` | Test-driven CodeQL query development checklist \u2014 write tests first, implement query, iterate until tests pass |\n| `run_query_and_summarize_false_positives` | Run a CodeQL query and summarize its false positives by root cause |\n| `sarif_rank_false_positives` | Analyze SARIF results to identify and rank likely false positives |\n| `sarif_rank_true_positives` | Analyze SARIF results to identify and rank likely true positives |\n| `test_driven_development` | End-to-end test-driven development workflow for CodeQL queries using MCP tools |\n| `tools_query_workflow` | Guide for using PrintAST, PrintCFG, CallGraphFrom, and CallGraphTo tool queries to understand code structure |\n| `workshop_creation_workflow` | Guide for creating multi-exercise CodeQL query development workshops from production-grade queries |\n\n## Prompt Categories\n\n### Test-Driven Development\n\n- **`test_driven_development`** \u2014 The primary TDD prompt. Requires a `language` parameter and optionally accepts `queryName`. Loads the `ql-tdd-basic.prompt.md` template and walks through the complete TDD cycle: scaffold \u2192 write tests \u2192 implement \u2192 compile \u2192 test \u2192 iterate.\n- **`ql_tdd_basic`** \u2014 A standalone TDD checklist. All parameters are optional. Covers the core loop: write test cases, implement the query, run tests, iterate.\n- **`ql_tdd_advanced`** \u2014 Extends basic TDD with AST visualization (`PrintAST`), control flow graph analysis (`PrintCFG`), and call graph exploration (`CallGraphFrom`, `CallGraphTo`). Optionally accepts a `database` path for immediate analysis.\n\n### Code Understanding\n\n- **`tools_query_workflow`** \u2014 Orchestrates the four built-in tool queries (PrintAST, PrintCFG, CallGraphFrom, CallGraphTo) to explore how source code is represented in a CodeQL database. Requires `language` and `database` parameters.\n- **`explain_codeql_query`** \u2014 Produces a verbal explanation of a query's logic and generates Mermaid diagrams showing the evaluation flow. Requires `queryPath` and `language`.\n\n### Iterative Development\n\n- **`ql_lsp_iterative_development`** \u2014 Combines LSP-based code completions (`codeql_lsp_completion`), go-to-definition (`codeql_lsp_definition`), find-references (`codeql_lsp_references`), and diagnostics (`codeql_lsp_diagnostics`) for an interactive development loop.\n\n### Documentation and Quality\n\n- **`document_codeql_query`** \u2014 Generates standardized markdown documentation as a sibling `.md` file to a query. Requires `queryPath` and `language`.\n- **`run_query_and_summarize_false_positives`** \u2014 Runs a CodeQL query on a database and groups results into false-positive categories by root cause. Uses `query_results_cache_lookup`, `sarif_list_rules`, `sarif_extract_rule`, `sarif_rule_to_markdown`, and `read_database_source` for structured analysis.\n- **`sarif_rank_false_positives`** / **`sarif_rank_true_positives`** \u2014 Analyze SARIF output to assess query precision by ranking results as likely true or false positives. Uses `sarif_list_rules`, `sarif_extract_rule`, `sarif_rule_to_markdown`, `read_database_source`, `sarif_compare_alerts`, and `sarif_diff_runs` for context gathering.\n\n### Alert Analysis and Comparison\n\n- **`compare_overlapping_alerts`** \u2014 Compares CodeQL SARIF alerts across any combination of SARIF files, analysis runs, CodeQL databases, or query packs. Classifies findings as redundant, complementary, false overlap, behavioral regression, or new coverage. Uses `sarif_list_rules`, `sarif_extract_rule`, `sarif_rule_to_markdown`, `sarif_compare_alerts`, `sarif_diff_runs`, and `read_database_source` tools. Requires `sarifPathA`; optionally accepts `sarifPathB` for cross-file comparison, `ruleIdA`/`ruleIdB` to narrow to specific rules, and `databasePath` for source code context.\n\n### Workshop Creation\n\n- **`workshop_creation_workflow`** \u2014 Guides the creation of multi-exercise workshops that teach CodeQL query development. Requires `queryPath` and `language`, optionally accepts `workshopName` and `numStages`.\n\n## Related Resources\n\n- `codeql://server/overview` \u2014 MCP server orientation guide\n- `codeql://server/tools` \u2014 Complete tool reference\n- `codeql://learning/test-driven-development` \u2014 TDD theory and workflow overview\n";
|
|
190845
191554
|
|
|
190846
191555
|
// src/resources/server-queries.md
|
|
190847
|
-
var server_queries_default = '# MCP Server Bundled Queries\n\nThis resource describes the tools queries bundled with the CodeQL Development MCP Server. These queries run via `codeql_query_run` and provide structural insight into how source code is represented in a CodeQL database. Use them to understand code structure before writing detection queries.\n\nFor general QL query writing guidance (syntax, metadata, `from`/`where`/`select`, testing conventions), see `codeql://learning/query-basics`.\n\n## Bundled Tools Queries\n\nThe server bundles
|
|
191556
|
+
var server_queries_default = '# MCP Server Bundled Queries\n\nThis resource describes the tools queries bundled with the CodeQL Development MCP Server. These queries run via `codeql_query_run` and provide structural insight into how source code is represented in a CodeQL database. Use them to understand code structure before writing detection queries.\n\nFor general QL query writing guidance (syntax, metadata, `from`/`where`/`select`, testing conventions), see `codeql://learning/query-basics`.\n\n## Bundled Tools Queries\n\nThe server bundles five tools queries that operate on CodeQL databases:\n\n| Query | Purpose | Output Format |\n| ----------------- | ------------------------------------------------------- | ------------------------- |\n| `PrintAST` | Visualize the Abstract Syntax Tree of source code | `@kind graph` (graphtext) |\n| `PrintCFG` | Visualize the Control Flow Graph of a function | `@kind graph` (graphtext) |\n| `CallGraphFrom` | Show all functions called FROM a given function | `@kind graph` (graphtext) |\n| `CallGraphTo` | Show all call sites that call TO a given function | `@kind graph` (graphtext) |\n| `CallGraphFromTo` | Show calls FROM one function TO another (bidirectional) | `@kind graph` (graphtext) |\n\nAll five queries use `@kind graph` metadata and produce output in graphtext format.\n\n## PrintAST\n\n**Purpose**: Outputs a hierarchical representation of the Abstract Syntax Tree showing parent-child relationships between declarations, statements, and expressions.\n\n**When to use**: Before writing any CodeQL query, run `PrintAST` on your test source code to understand which QL classes represent which source constructs and which predicates are available for matching.\n\n**How to run**:\n\n```text\nTool: codeql_query_run\nParameters:\n queryName: "PrintAST"\n queryLanguage: "<language>"\n database: "<path-to-database>"\n sourceFiles: "<comma-separated-filenames>" (optional \u2014 filter to specific files)\n format: "graphtext"\n interpretedOutput: "<output-directory>"\n```\n\n**Output**: A tree showing each AST node with its QL class name, properties, and position in the hierarchy. This reveals exactly which QL classes and predicates to use in `from`/`where`/`select` clauses.\n\n## PrintCFG\n\n**Purpose**: Produces a Control Flow Graph representation showing the order in which statements and expressions execute, including branching paths.\n\n**When to use**: When writing queries that reason about execution order, reachability, or branching logic (e.g., "is this check always performed before this call?").\n\n**How to run**:\n\n```text\nTool: codeql_query_run\nParameters:\n queryName: "PrintCFG"\n queryLanguage: "<language>"\n database: "<path-to-database>"\n sourceFunction: "<function-name>" (optional \u2014 target a specific function)\n format: "graphtext"\n interpretedOutput: "<output-directory>"\n```\n\n**Output**: Nodes representing CFG basic blocks and edges representing possible execution transitions (successor relationships).\n\n## CallGraphFrom\n\n**Purpose**: Shows all functions called FROM a specified source function \u2014 the outbound call dependencies.\n\n**When to use**: When analyzing what a function does by tracing the functions it invokes. Useful for understanding call chains and identifying potential data flow paths.\n\n**How to run**:\n\n```text\nTool: codeql_query_run\nParameters:\n queryName: "CallGraphFrom"\n queryLanguage: "<language>"\n database: "<path-to-database>"\n sourceFunction: "<function-name>"\n format: "graphtext"\n interpretedOutput: "<output-directory>"\n```\n\n**Output**: A graph showing each call site within the source function and the target function being called.\n\n## CallGraphTo\n\n**Purpose**: Shows all call sites that invoke a specified target function \u2014 the inbound callers.\n\n**When to use**: When performing impact analysis to understand where a function is used, or when identifying all locations that pass data to a particular sink function.\n\n**How to run**:\n\n```text\nTool: codeql_query_run\nParameters:\n queryName: "CallGraphTo"\n queryLanguage: "<language>"\n database: "<path-to-database>"\n targetFunction: "<function-name>"\n format: "graphtext"\n interpretedOutput: "<output-directory>"\n```\n\n**Output**: A graph showing each caller function and the specific call site where the target function is invoked.\n\n## CallGraphFromTo\n\n**Purpose**: Shows the call relationship between two specific functions \u2014 whether function A calls function B (directly or transitively) and what the call path looks like.\n\n**When to use**: When investigating whether a specific source function can reach a specific sink function through the call graph. Useful for validating dataflow hypotheses before writing taint-tracking queries.\n\n**How to run**:\n\n```text\nTool: codeql_query_run\nParameters:\n queryName: "CallGraphFromTo"\n queryLanguage: "<language>"\n database: "<path-to-database>"\n sourceFunction: "<calling-function-name>"\n targetFunction: "<called-function-name>"\n format: "graphtext"\n interpretedOutput: "<output-directory>"\n```\n\n**Output**: A graph showing the call path from the source function to the target function, including intermediate call sites.\n\n## Language Support\n\n| Language | PrintAST | PrintCFG | CallGraphFrom | CallGraphTo | CallGraphFromTo |\n| ---------- | :------: | :------: | :-----------: | :---------: | :-------------: |\n| actions | \u2713 | \u2713 | | | |\n| cpp | \u2713 | \u2713 | \u2713 | \u2713 | \u2713 |\n| csharp | \u2713 | \u2713 | \u2713 | \u2713 | \u2713 |\n| go | \u2713 | \u2713 | \u2713 | \u2713 | \u2713 |\n| java | \u2713 | \u2713 | \u2713 | \u2713 | \u2713 |\n| javascript | \u2713 | \u2713 | \u2713 | \u2713 | \u2713 |\n| python | \u2713 | \u2713 | \u2713 | \u2713 | \u2713 |\n| ruby | \u2713 | \u2713 | \u2713 | \u2713 | \u2713 |\n| rust | \u2713 | \u2713 | \u2713 | \u2713 | \u2713 |\n| swift | \u2713 | \u2713 | \u2713 | \u2713 | \u2713 |\n\nNote: The `actions` language supports PrintAST and PrintCFG only (no call graph queries).\n\n## Recommended Workflow\n\nUse the `tools_query_workflow` prompt for a guided step-by-step workflow:\n\n1. **Identify or create a database**: Use `list_codeql_databases` or `codeql_database_create`\n2. **Run PrintAST**: Understand how the source code maps to QL classes\n3. **Run PrintCFG**: Understand control flow for the functions of interest\n4. **Run CallGraphFrom / CallGraphTo**: Trace call relationships to identify sources and sinks\n5. **Run CallGraphFromTo**: Verify specific source-to-sink call paths\n6. **Write detection queries**: Use the insights from steps 2\u20135 to select the right QL classes and predicates\n\n## Related Resources\n\n- `codeql://learning/query-basics` \u2014 QL query writing reference (syntax, metadata, patterns, testing)\n- `codeql://server/overview` \u2014 MCP server orientation guide\n- `codeql://server/tools` \u2014 Complete tool reference\n- `codeql://learning/test-driven-development` \u2014 TDD workflow for CodeQL queries\n- `codeql://languages/{language}/ast` \u2014 Language-specific AST class reference\n';
|
|
190848
191557
|
|
|
190849
191558
|
// src/resources/server-tools.md
|
|
190850
|
-
var server_tools_default = '# MCP Server Tools\n\nThis resource provides a complete reference of the default tools exposed by the CodeQL Development MCP Server. These tools wrap the CodeQL CLI and supporting utilities, enabling an LLM to develop, test, and analyze CodeQL queries programmatically.\n\n## CodeQL CLI Tools\n\n| Tool | Description |\n| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |\n| `codeql_bqrs_decode` | Decode BQRS result files to human-readable formats (text, csv, json). Supports `--result-set` and `--rows` for pagination |\n| `codeql_bqrs_info` | Get metadata about BQRS result files: result sets, column types, row counts |\n| `codeql_bqrs_interpret` | Interpret BQRS result files according to query metadata and generate output in specified formats (CSV, SARIF, graph formats) |\n| `codeql_database_analyze` | Run queries or query suites against CodeQL databases. Produces evaluator logs, BQRS, and SARIF output |\n| `codeql_database_create` | Create a CodeQL database from source code |\n| `codeql_generate_log-summary` | Create a summary of a structured JSON evaluator event log file |\n| `codeql_generate_query-help` | Generate query help documentation from QLDoc comments |\n| `codeql_pack_install` | Install CodeQL pack dependencies |\n| `codeql_pack_ls` | List CodeQL packs under a local directory path |\n| `codeql_query_compile` | Compile and validate CodeQL queries |\n| `codeql_query_format` | Automatically format CodeQL source code files |\n| `codeql_query_run` | Execute a CodeQL query against a database |\n| `codeql_resolve_database` | Resolve database path and validate database structure |\n| `codeql_resolve_files` | Find files in a directory tree, filtered by extension and glob patterns. Useful for discovering QL library files |\n| `codeql_resolve_languages` | List installed CodeQL extractor packs |\n| `codeql_resolve_library-path` | Resolve library path for CodeQL queries and libraries |\n| `codeql_resolve_metadata` | Resolve and return key-value metadata pairs from a CodeQL query source file |\n| `codeql_resolve_qlref` | Resolve `.qlref` files to their corresponding query files |\n| `codeql_resolve_queries` | List available CodeQL queries found on the local filesystem |\n| `codeql_resolve_tests` | Resolve the local filesystem paths of unit tests and/or queries under a base directory |\n| `codeql_test_accept` | Accept new test results as the expected baseline |\n| `codeql_test_extract` | Extract test databases for CodeQL query tests |\n| `codeql_test_run` | Run CodeQL query tests |\n\n## Language Server Protocol (LSP) Tools\n\n| Tool | Description |\n| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `codeql_lsp_completion` | Get code completions at a cursor position in a CodeQL file |\n| `codeql_lsp_definition` | Go to the definition of a CodeQL symbol at a given position |\n| `codeql_lsp_diagnostics` | Syntax and semantic validation of CodeQL code via the Language Server. Note: inline `ql_code` cannot resolve pack imports; use `codeql_query_compile` for files with imports |\n| `codeql_lsp_references` | Find all references to a CodeQL symbol at a given position |\n\n## Query Development Tools\n\n| Tool | Description |\n| -------------------------------- | ------------------------------------------------------------------------------------------------------------ |\n| `create_codeql_query` | Create directory structure and files for a new CodeQL query with tests |\n| `find_class_position` | Find the start/end line and column of a class for quick evaluation |\n| `find_codeql_query_files` | Find and track all files and directories related to a CodeQL query, including resolved metadata |\n| `find_predicate_position` | Find the start/end line and column of a predicate for quick evaluation |\n| `list_codeql_databases` | List CodeQL databases discovered in configured base directories |\n| `list_mrva_run_results` | List MRVA (Multi-Repository Variant Analysis) run results with per-repo details |\n| `list_query_run_results` | List query run result directories with artifact inventory. Filter by `queryName`, `language`, or `queryPath` |\n| `profile_codeql_query` | Profile the performance of a CodeQL query run against a specific database by analyzing the evaluator log |\n| `profile_codeql_query_from_logs` | Parse evaluator logs into a compact profile with line-indexed detail file for targeted read_file access |\n| `quick_evaluate` | Quick evaluate either a class or a predicate in a CodeQL query for debugging |\n| `read_database_source` | Read source file contents from a CodeQL database source archive. Omit `filePath` to list all files |\n| `register_database` | Register a CodeQL database given a local path to the database directory |\n| `search_ql_code` | Search QL source files for text or regex patterns with structured results (replaces grep for QL code) |\n| `validate_codeql_query` | Quick heuristic validation for CodeQL query structure (does not compile the query) |\n\n## Common Tool Workflows\n\n### Create and Test a Query\n\n1. `create_codeql_query` \u2014 scaffold files\n2. `codeql_pack_install` \u2014 install dependencies\n3. `codeql_query_compile` \u2014 validate syntax\n4. `codeql_test_run` \u2014 run tests\n5. `codeql_test_accept` \u2014 accept correct results\n\n### Understand Code Structure\n\n1. `codeql_query_run` with `queryName="PrintAST"` \u2014 visualize the AST\n2. `codeql_query_run` with `queryName="PrintCFG"` \u2014 visualize control flow\n3. `codeql_query_run` with `queryName="CallGraphFrom"` / `"CallGraphTo"` \u2014 trace call relationships\n\n### Profile Query Performance\n\n1. `codeql_query_run` with `evaluationOutput` \u2014 run query and capture evaluator logs\n2. `profile_codeql_query_from_logs` \u2014 analyze evaluator logs: slowest predicates, RA operations, tuple count progressions, dependencies\n\n### Discover and Search QL Code\n\n1. `codeql_resolve_files` \u2014 find QL files by extension and glob patterns in library packs\n2. `search_ql_code` \u2014 search QL source files for classes, predicates, or patterns by text/regex\n3. `codeql_lsp_definition` \u2014 navigate to the definition of a discovered symbol\n4. `codeql_lsp_references` \u2014 find all usages of a symbol across a pack\n\n### Interactive Development\n\n1. `codeql_lsp_completion` \u2014 get QL code completions\n2. `codeql_lsp_definition` \u2014 navigate to definitions\n3. `codeql_lsp_references` \u2014 find all references\n4. `codeql_lsp_diagnostics` \u2014 real-time validation\n\n## Tool Input Conventions\n\n- **LSP tools** use **0-based** line and column positions for input. Output uses 1-based `startLine`/`endLine`.\n- **`find_predicate_position`** and **`find_class_position`** return **1-based** positions.\n- **`workspace_uri`** for LSP tools must be a **plain directory path** to the pack root containing `codeql-pack.yml`, not a `file://` URI.\n\n## Related Resources\n\n- `codeql://server/overview` \u2014 MCP server orientation guide\n- `codeql://server/prompts` \u2014 Complete prompt reference\n- `codeql://learning/query-basics` \u2014 Query writing reference\n- `codeql://patterns/performance` \u2014 Performance profiling guide\n';
|
|
191559
|
+
var server_tools_default = '# MCP Server Tools\n\nThis resource provides a complete reference of the default tools exposed by the CodeQL Development MCP Server. These tools wrap the CodeQL CLI and supporting utilities, enabling an LLM to develop, test, and analyze CodeQL queries programmatically.\n\n## CodeQL CLI Tools\n\n| Tool | Description |\n| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |\n| `codeql_bqrs_decode` | Decode BQRS result files to human-readable formats (text, csv, json). Supports `--result-set` and `--rows` for pagination |\n| `codeql_bqrs_info` | Get metadata about BQRS result files: result sets, column types, row counts |\n| `codeql_bqrs_interpret` | Interpret BQRS result files according to query metadata and generate output in specified formats (CSV, SARIF, graph formats) |\n| `codeql_database_analyze` | Run queries or query suites against CodeQL databases. Produces evaluator logs, BQRS, and SARIF output |\n| `codeql_database_create` | Create a CodeQL database from source code |\n| `codeql_generate_log-summary` | Create a summary of a structured JSON evaluator event log file |\n| `codeql_generate_query-help` | Generate query help documentation from QLDoc comments |\n| `codeql_pack_install` | Install CodeQL pack dependencies |\n| `codeql_pack_ls` | List CodeQL packs under a local directory path |\n| `codeql_query_compile` | Compile and validate CodeQL queries |\n| `codeql_query_format` | Automatically format CodeQL source code files |\n| `codeql_query_run` | Execute a CodeQL query against a database |\n| `codeql_resolve_database` | Resolve database path and validate database structure |\n| `codeql_resolve_files` | Find files in a directory tree, filtered by extension and glob patterns. Useful for discovering QL library files |\n| `codeql_resolve_languages` | List installed CodeQL extractor packs |\n| `codeql_resolve_library-path` | Resolve library path for CodeQL queries and libraries |\n| `codeql_resolve_metadata` | Resolve and return key-value metadata pairs from a CodeQL query source file |\n| `codeql_resolve_qlref` | Resolve `.qlref` files to their corresponding query files |\n| `codeql_resolve_queries` | List available CodeQL queries found on the local filesystem |\n| `codeql_resolve_tests` | Resolve the local filesystem paths of unit tests and/or queries under a base directory |\n| `codeql_test_accept` | Accept new test results as the expected baseline |\n| `codeql_test_extract` | Extract test databases for CodeQL query tests |\n| `codeql_test_run` | Run CodeQL query tests |\n\n## Language Server Protocol (LSP) Tools\n\n| Tool | Description |\n| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `codeql_lsp_completion` | Get code completions at a cursor position in a CodeQL file |\n| `codeql_lsp_definition` | Go to the definition of a CodeQL symbol at a given position |\n| `codeql_lsp_diagnostics` | Syntax and semantic validation of CodeQL code via the Language Server. Note: inline `ql_code` cannot resolve pack imports; use `codeql_query_compile` for files with imports |\n| `codeql_lsp_references` | Find all references to a CodeQL symbol at a given position |\n\n## Query Development Tools\n\n| Tool | Description |\n| -------------------------------- | ------------------------------------------------------------------------------------------------------------ |\n| `create_codeql_query` | Create directory structure and files for a new CodeQL query with tests |\n| `find_class_position` | Find the start/end line and column of a class for quick evaluation |\n| `find_codeql_query_files` | Find and track all files and directories related to a CodeQL query, including resolved metadata |\n| `find_predicate_position` | Find the start/end line and column of a predicate for quick evaluation |\n| `list_codeql_databases` | List CodeQL databases discovered in configured base directories |\n| `list_mrva_run_results` | List MRVA (Multi-Repository Variant Analysis) run results with per-repo details |\n| `list_query_run_results` | List query run result directories with artifact inventory. Filter by `queryName`, `language`, or `queryPath` |\n| `profile_codeql_query` | Profile the performance of a CodeQL query run against a specific database by analyzing the evaluator log |\n| `profile_codeql_query_from_logs` | Parse evaluator logs into a compact profile with line-indexed detail file for targeted read_file access |\n| `quick_evaluate` | Quick evaluate either a class or a predicate in a CodeQL query for debugging |\n| `read_database_source` | Read source file contents from a CodeQL database source archive. Omit `filePath` to list all files |\n| `register_database` | Register a CodeQL database given a local path to the database directory |\n| `search_ql_code` | Search QL source files for text or regex patterns with structured results (replaces grep for QL code) |\n| `validate_codeql_query` | Quick heuristic validation for CodeQL query structure (does not compile the query) |\n\n## SARIF Analysis Tools\n\n| Tool | Description |\n| ------------------------ | ---------------------------------------------------------------------------------------------------- |\n| `sarif_extract_rule` | Extract all data for a specific rule from multi-rule SARIF. Returns a valid SARIF JSON subset |\n| `sarif_list_rules` | List all rules in a SARIF file with result counts, severity, precision, and tags |\n| `sarif_rule_to_markdown` | Convert per-rule SARIF data to markdown with Mermaid dataflow diagrams |\n| `sarif_compare_alerts` | Compare code locations of two SARIF alerts for overlap (sink, source, any-location, full-path modes) |\n| `sarif_diff_runs` | Diff two SARIF files to find added, removed, and changed rules/results across analysis runs |\n\n## Common Tool Workflows\n\n### Create and Test a Query\n\n1. `create_codeql_query` \u2014 scaffold files\n2. `codeql_pack_install` \u2014 install dependencies\n3. `codeql_query_compile` \u2014 validate syntax\n4. `codeql_test_run` \u2014 run tests\n5. `codeql_test_accept` \u2014 accept correct results\n\n### Understand Code Structure\n\n1. `codeql_query_run` with `queryName="PrintAST"` \u2014 visualize the AST\n2. `codeql_query_run` with `queryName="PrintCFG"` \u2014 visualize control flow\n3. `codeql_query_run` with `queryName="CallGraphFrom"` / `"CallGraphTo"` \u2014 trace call relationships\n4. `codeql_query_run` with `queryName="CallGraphFromTo"` \u2014 verify source-to-sink call paths\n\n### Profile Query Performance\n\n1. `codeql_query_run` with `evaluationOutput` \u2014 run query and capture evaluator logs\n2. `profile_codeql_query_from_logs` \u2014 analyze evaluator logs: slowest predicates, RA operations, tuple count progressions, dependencies\n\n### Discover and Search QL Code\n\n1. `codeql_resolve_files` \u2014 find QL files by extension and glob patterns in library packs\n2. `search_ql_code` \u2014 search QL source files for classes, predicates, or patterns by text/regex\n3. `codeql_lsp_definition` \u2014 navigate to the definition of a discovered symbol\n4. `codeql_lsp_references` \u2014 find all usages of a symbol across a pack\n\n### Interactive Development\n\n1. `codeql_lsp_completion` \u2014 get QL code completions\n2. `codeql_lsp_definition` \u2014 navigate to definitions\n3. `codeql_lsp_references` \u2014 find all references\n4. `codeql_lsp_diagnostics` \u2014 real-time validation\n\n### Analyze and Compare Results\n\n1. `codeql_database_analyze` \u2014 run query packs and produce SARIF\n2. `sarif_list_rules` \u2014 discover rules and result counts in the SARIF\n3. `query_results_cache_lookup` with `ruleId` \u2014 find cached results by CodeQL query `@id`\n4. `sarif_extract_rule` \u2014 extract results for a specific rule from multi-rule SARIF\n5. `sarif_rule_to_markdown` \u2014 generate markdown report with Mermaid dataflow diagrams\n6. `sarif_compare_alerts` \u2014 compare two alerts for location overlap\n7. `sarif_diff_runs` \u2014 diff two SARIF files to detect behavioral changes across runs\n8. `query_results_cache_compare` with `ruleId` \u2014 compare results across databases\n\n## Tool Input Conventions\n\n- **LSP tools** use **0-based** line and column positions for input. Output uses 1-based `startLine`/`endLine`.\n- **`find_predicate_position`** and **`find_class_position`** return **1-based** positions.\n- **`workspace_uri`** for LSP tools must be a **plain directory path** to the pack root containing `codeql-pack.yml`, not a `file://` URI.\n\n## Related Resources\n\n- `codeql://server/overview` \u2014 MCP server orientation guide\n- `codeql://server/prompts` \u2014 Complete prompt reference\n- `codeql://learning/query-basics` \u2014 Query writing reference\n- `codeql://patterns/performance` \u2014 Performance profiling guide\n';
|
|
190851
191560
|
|
|
190852
191561
|
// src/lib/resources.ts
|
|
190853
191562
|
function getLearningQueryBasics() {
|
|
@@ -191710,6 +192419,9 @@ import { fileURLToPath as fileURLToPath3 } from "url";
|
|
|
191710
192419
|
// src/prompts/check-for-duplicated-code.prompt.md
|
|
191711
192420
|
var check_for_duplicated_code_prompt_default = '---\nagent: agent\n---\n\n# Check for Duplicated Code\n\nUse the MCP server tools to identify classes, modules, and predicates defined in a\n`.ql` or `.qll` file and check for possible "duplicated code," where duplicated code\nis defined to be:\n\n- Reimplementing functionality that already exists in the standard library, or shared project `.qll` files, and\n- The local definition is identical, or semantically equivalent, or superior to the library definition, or\n- The local definition could be simplified by reusing the existing definition (e.g. a base class already exists that captures some of the shared logic)\n\nHere are some examples:\n\n```ql\nimport cpp\n\n// Duplicated: `StandardNamespace` already exists in the standard library and is identical\nclass NamespaceStd extends Namespace {\n NamespaceStd() { this.getName() = "std" }\n}\n\n// Duplicated: class should extend `Operator`, not `Function`\nclass ThrowingOperator extends Function {\n ThrowingOperator() {\n // Duplicated: this check is implied by using base class `Operator`\n this.getName().matches("%operator%") and\n and exists(ThrowExpr te |\n // Duplicated: this is equivalent to `te.getEnclosingFunction() = this`\n te.getParent*() = this.getAChild()\n )\n }\n\n // Duplicated: member predicate `getDeclaringType()` already does this.\n Class getDefiningClass() { ... }\n}\n\n// Duplicated: `ControlFlowNode.getASuccessor()` already exists in `cpp` and is superior\npredicate getASuccessor(Stmt a, Stmt b) {\n exists(Block b, int i | a = b.getChild(i) and b = b.getChild(i + 1))\n}\n\n// Duplicated: prefer to import `semmle.code.cpp.controlflow.Dominance`, defined in dependency pack `cpp-all`\npredicate dominates(Block a, Block b) { ... }\n```\n\nDuplicate code removal isn\'t done arbitrarily, but for several key reasons:\n\n- **Maintainability**: Duplicated code must be maintained separately, may diverge and have different bugs\n- **Simplicity**: Relying on existing definitions reduces the amount of code to read and understand\n- **Readability**: Existing definitions map wrap complex ideas into readable names\n- **Consistency**: A single source of truth makes for a more consistent user experience across queries\n- **Completeness/Correctness**: Recreating an already-existing definition can miss edge cases, resulting in false positives or false negatives\n\n## Use This Prompt When\n\n- A query file defines a class or predicate whose name sounds generic (e.g.\n `StandardNamespace`, `Callable`, `SecurityFeature`)\n- Refactoring a query that was written before a relevant library predicate existed\n- Reviewing a shared `.qll` file to check whether its helpers have been upstreamed\n into the standard library in a newer CodeQL version\n- Performing a code-quality audit across a suite of custom queries\n\n## Prerequisites\n\n1. The file path of the `.ql` or `.qll` file to audit for code duplication\n2. Understand which packs are imported by `qlpack.yml`\n3. Understand where the relevant language library packs are located (e.g. `~/.codeql`)\n4. Understand where project-specific library packs are located (e.g. `$LANGUAGE/lib` or `$LANGUAGE/common`)\n5. Understand where pack-specific shared `.qll` files are located (e.g. `$PACKROOT/common/*.qll`)\n\n## Overview of the Approach\n\nThe core idea is to enumerate every top-level name defined in the file under review.\nThen, find candidate `.qll` files, based on file name and path, that are available to\nthat `.ql` file in review. Then, enumerate the top-level names in each candidate\n`.qll` file, to find potential duplicates, dive further if necessary, and then report\nthe findings as code improvement recommendations to the user.\n\n1. **Read the file** to see its imports and top-level structure\n2. **Enumerate top-level definitions** with `codeql_lsp_document_symbols`\n3. **Find available .qll files** in the `.ql` file\'s pack, and its dependencies, including the standard library\n4. **Identify promising .qll file candidates** based on their file name and path\n5. **Enumerate top-level definitions in candidate `.qll` files** with `codeql_lsp_document_symbols`\n6. **Detect overlap, comparing definitions if unclear** (e.g. by using `find_predicate_position` and `find_class_position` tools)\n7. **Report findings** as a set of recommendations to the user about which definitions could be improved, by reusing which existing definitions\n\n## Step 1: Read the File and Note Its Imports\n\n```text\nTool: read_file\nParameters:\n file_path: /path/to/query.ql\n start_line: 1\n end_line: 60 # enough to see all imports\n```\n\nRecord every `import` statement. These are the namespaces the standard library\nexposes; duplication is only meaningful for libraries that are already imported (or\neasily importable).\n\n## Step 2: Enumerate Top-Level Definitions\n\nUse `codeql_lsp_document_symbols` to retrieve every class, predicate, and module\ndefined at the top level of the file in a single call:\n\n```text\nTool: codeql_lsp_document_symbols\nParameters:\n file_path: /path/to/query.ql\n names_only: true # provides significantly smaller response payload\n workspace_uri: /path/to/pack-root # directory containing codeql-pack.yml\n```\n\nThe response contains a `symbols` array. Each entry has:\n\n- `name` \u2014 the identifier as written in source\n- `kind` \u2014 numeric SymbolKind (5 = Class, 12 = Function/predicate, 2 = Module, etc.)\n- `range` \u2014 the full definition range (0-based lines)\n- `selectionRange` \u2014 the range of just the name token\n- `children` \u2014 nested members (for classes and modules)\n\nTop-level symbols are the root nodes of the array; `children` hold member\npredicates and fields.\n\n## Step 3. Read the filesystem to find candidate library `.qll` files\n\nRun the tool `codeql_resolve_library-path` with the given ql query file to find where\nthe available library sources live.\n\nFor each source root, run `find $ROOT -name "*.qll"` to find all `.qll` files\navailable in that pack. Do not preemptively filter this list of qll files. The names\nof the files may be broad or nondescriptive. Read all file names for each project to\nunderstand its structure and responsibilities before proceeding to step 3d.\n\nChoose promising candidate `.qll` files you found in the previous step. Pick\ncandidates that may potentially define behavior relevant to the current query and its\nenumerated definitions, based on the candidate filename, path, and priority.\n\nPrioritize as follows:\n\n- `.qll` files in the same directory as the query file have the absolute highest priority\n- `.qll` files in the same pack have the next extremely high priority\n- `.qll` files in project-specific library packs have the very high priority\n- `.qll` files in downloaded direct dependencies have standard priority\n- `.qll` files in transitive dependencies have the least priority.\n\n## Step 4: Identify candidate terms in the candidate library `.qll` files\n\nEnumerate the top-level definitions for each candidate `.qll` file using the tool\n`codeql_lsp_document_symbols` again. Some top levels may clearly match the name or\npurpose of a definition in the query file, while others may only appear as possibly\nrelated.\n\n```text\nTool: codeql_lsp_document_symbols\nParameters:\n file_path: /path/to/library/file.qll\n workspace_uri: /path/to/pack-root\n```\n\n## Step 5: Perform final overlap analysis\n\nFor each promising candidate, identify the predicate or class definitions that may overlap. One definition will be in the query file (`.ql`) and the other will be in the library file (`.qll`).\n\nUsing the tools `find_predicate_position` and `find_class_position`, you can retrieve the full definition of each predicate or class, and compare them to determine whether they are identical, equivalent, overlapping, or if one is a superior implementation that could be reused by the query file.\n\nDutifully analyze whether the shared library file definition would reduce code duplication in the categories identified before: maintenance, simplicity, readability, consistency, and completeness/correctness. Consider contextual factors such as comments explaining why the local definition differs from the library one, or whether the local definition is a thin wrapper around the library definition that adds value (e.g. by improving naming or adding extra checks).\n\nDo not go on a wild goose chase trying to find every possible overlap. Consider the likelihood of overlap based on the broadness of functionality, and the value that would be brought be reuse. Do not waste significant time on unimportant or unlikely overlaps.\n\n## Step 6: Report Findings\n\nFor each duplicate found, report:\n\n| Local name | Local file | import path | Notes |\n| ------------------- | ------------- | -------------------------------- | ---------- |\n| `StandardNamespace` | `query.ql:42` | already imported in `import cpp` | Identical |\n| `myHelper` | `query.ql:80` | `import myproject.Helpers` | Equivalent |\n| `myHelper` | `query.ql:80` | `import myproject.Helpers` | Equivalent |\n\nRecommend one of:\n\n- **Replace**: remove the local definition and use the standard definition directly instead\n- **Integrate**: refactor and simplify the local definition by making use of the standard definition\n- **Annotate**: add comments to the local definition to explain how it differs from the standard definition and why the duplication is necessary\n\nAdditionally, report if any issues came up in using the tools, or finding the qll files.\n\nFor each concept for which no duplicate was found, provide at most a **brief** description of what the concept is. Do not provide a long detailed explanation of a non-finding.\n\n# Conclusion\n\nDo **not** perform any updates to any code during this analysis.\n\nAs you work through completing this task, ask yourself:\n\n- Have I changed any code, even though that was not my task, or am I about to? Stop, do not change any code!\n- Did I sufficiently analyze the definitions such that I likely found most overlapping definitions?\n- Will my suggestions improve the maintainability, simplicity, readability, consistency, or completeness/correctness of the codebase?\n- Did I report my findings clearly?\n- Did I use the suggested LLM tools to their fullest extent?\n- Did I follow the steps in the recommended order, and not skip any steps?\n- Did I report any issues I had in finding the relevant `.qll` files, or using the tools to analyze definitions?\n';
|
|
191712
192421
|
|
|
192422
|
+
// src/prompts/compare-overlapping-alerts.prompt.md
|
|
192423
|
+
var compare_overlapping_alerts_prompt_default = '---\nagent: agent\n---\n\n# Compare Overlapping Alerts\n\n## Goal\n\nCompare CodeQL SARIF alerts across **any combination** of SARIF files, analysis runs, CodeQL databases, CodeQL versions, or query packs. Detect overlapping, redundant, or divergent results and classify each finding to guide query development decisions.\n\nThis workflow supports:\n\n- Comparing alerts from **different rules** in the same SARIF (e.g., standard vs custom queries)\n- Comparing alerts from **different SARIF files** (e.g., two separate `codeql database analyze` runs)\n- Checking for **behavioral deviations** across CodeQL CLI versions or database rebuilds\n- Detecting **redundancy** between custom query packs and standard query packs\n\n## Workflow\n\n### Step 1: Discover available rules\n\nUse #sarif_list_rules on each SARIF source to understand what rules and result counts are present. When comparing within a single file, call it once:\n\n```\nsarif_list_rules(sarifPath="{{sarifPathA}}")\n```\n\nWhen comparing two different SARIF files, call it on both:\n\n```\nsarif_list_rules(sarifPath="{{sarifPathA}}")\nsarif_list_rules(sarifPath="{{sarifPathB}}")\n```\n\nThe response includes `ruleId`, `resultCount`, `kind`, `precision`, `severity`, and `tags` for each rule.\n\n### Step 2: Choose a comparison strategy\n\nDepending on the use case, choose the appropriate strategy:\n\n**Same-file, different rules** (custom vs standard overlap):\n\n- Use #sarif_extract_rule to extract each rule\'s results from the same file\n- Use #sarif_compare_alerts to compare individual alert pairs\n\n**Different files, same rule** (behavioral deviation across runs):\n\n- Use #sarif_diff_runs to get a high-level diff of added/removed/changed rules\n- For changed rules, use #sarif_extract_rule on both files and compare results\n\n**Different files, different rules** (cross-pack overlap):\n\n- Use #sarif_extract_rule on each file with the respective rule IDs\n- Use #sarif_compare_alerts with different `sarifPath` values for alertA and alertB\n\n### Step 3: Diff runs (for cross-run comparison)\n\nWhen comparing two analysis runs, start with a structural diff:\n\n```\nsarif_diff_runs(\n sarifPathA="{{sarifPathA}}",\n sarifPathB="{{sarifPathB}}",\n labelA="baseline",\n labelB="comparison"\n)\n```\n\nThis returns `addedRules`, `removedRules`, `changedRules` (with result count deltas), and `unchangedRules`. Focus investigation on `changedRules` and `addedRules`.\n\n### Step 4: Extract and visualize results\n\nFor rules of interest, extract full results and generate markdown reports:\n\n```\nsarif_extract_rule(sarifPath="{{sarifPathA}}", ruleId="<ruleId>")\nsarif_rule_to_markdown(sarifPath="{{sarifPathA}}", ruleId="<ruleId>")\n```\n\nThe markdown report includes:\n\n- Rule summary with severity, precision, tags\n- Query help text\n- Results table with file, line, and message\n- Mermaid `flowchart LR` diagrams for each dataflow path\n\n### Step 5: Compare specific alerts for overlap\n\nUse #sarif_compare_alerts to compare individual results between rules or files. Each alert specifier can reference a **different SARIF file**:\n\n```\nsarif_compare_alerts(\n alertA={sarifPath="{{sarifPathA}}", ruleId="<ruleIdA>", resultIndex=0},\n alertB={sarifPath="{{sarifPathB}}", ruleId="<ruleIdB>", resultIndex=0},\n overlapMode="sink"\n)\n```\n\nIf sink overlap is found, re-check with `source` and `full-path` modes:\n\n- **sink**: same primary alert location (file + line + column range overlap)\n- **source**: same dataflow source (first step in threadFlow)\n- **any-location**: any location in alertA overlaps any location in alertB (including intermediate steps)\n- **full-path**: Jaccard similarity on dataflow path steps; `pathSimilarity` 0.0\u20131.0\n\n### Step 6: Read source code context\n\nFor each overlapping pair, use #read_database_source to read the relevant source file from the CodeQL database. **Note**: the `filePath` parameter uses the URI from the SARIF alert location, not an absolute path:\n\n```\nread_database_source(databasePath="{{databasePath}}", filePath="<uri-from-alert>")\n```\n\nRead 10\u201320 lines around the flagged location for context. When comparing across databases, read from each database separately.\n\n### Step 7: Classify each finding\n\nFor each overlapping or divergent pair, classify as:\n\n1. **Redundant** \u2014 Same problem, same or very similar code path (`pathSimilarity > 0.7`). One query subsumes the other.\n - **Action**: add a `not` exclusion predicate to the more specific query, or configure alert suppression\n\n2. **Complementary** \u2014 Same sink but different source models or taint configurations (`pathSimilarity < 0.3`). Both queries add defensive value.\n - **Action**: keep both; document the overlap in query help text\n\n3. **False overlap** \u2014 Same file and line but semantically different issues (different arguments, different properties).\n - **Action**: no change needed\n\n4. **Behavioral regression** \u2014 A rule that previously found N results now finds fewer (or zero). Visible via #sarif_diff_runs `changedRules`.\n - **Action**: investigate query or library changes between CodeQL versions\n\n5. **New coverage** \u2014 A rule appears in `addedRules` or has increased results. Indicates improved detection.\n - **Action**: review new results for false positive rate\n\n### Step 8: Produce summary\n\nCreate a structured summary with:\n\n- SARIF sources compared (file paths, labels, tool versions)\n- Total alerts per rule per source\n- Run diff summary: added/removed/changed rule counts\n- For each overlap: classification, both alert messages, shared locations, path similarity\n- Recommendations: which queries to prioritize, exclusion predicates to add, follow-up analysis needed\n\n## Notes\n\n- `ruleId` values correspond to CodeQL query `@id` metadata (e.g., `js/sql-injection`)\n- #sarif_compare_alerts supports **cross-file** comparison: `alertA.sarifPath` and `alertB.sarifPath` can be different files\n- #sarif_diff_runs compares by rule ID, not by result content \u2014 use it for high-level structural comparison, then drill into individual alerts\n- #read_database_source requires the database path \u2014 pass via the `databasePath` parameter or resolve it with #list_codeql_databases\n- When working from cached results, substitute `cacheKey` for `sarifPath` in all tool calls\n- Path similarity above 0.7 usually indicates redundancy; below 0.3 indicates complementary coverage\n- For cross-version comparison, run `codeql database analyze` with two different CodeQL CLI versions against the same database, save both SARIF files, and use #sarif_diff_runs to compare\n';
|
|
192424
|
+
|
|
191713
192425
|
// src/prompts/document-codeql-query.prompt.md
|
|
191714
192426
|
var document_codeql_query_prompt_default = '---\nagent: agent\n---\n\n# Document a CodeQL Query\n\nThis prompt guides you through creating or updating documentation for a CodeQL query file. The documentation is stored as a sibling file to the query with a standardized markdown format.\n\n## Purpose\n\nThe `document_codeql_query` prompt creates/updates **query documentation files** for a specific version of a CodeQL query. Documentation files are stored alongside the query file and provide concise yet comprehensive information about what the query does.\n\nFor creating **workshop learning content** with detailed explanations and visual diagrams, use the `explain_codeql_query` prompt instead.\n\n## Required Inputs\n\n- **queryPath**: Path to the CodeQL query file (`.ql` or `.qlref`)\n- **language**: Target programming language (actions, cpp, csharp, go, java, javascript, python, ruby, rust, swift)\n\n## Documentation File Conventions\n\n### File Location and Naming\n\nFor a query file `QueryFileBaseName.ql`, the documentation file should be:\n\n- **Primary**: `QueryFileBaseName.md` (markdown format, preferred)\n- **Legacy**: `QueryFileBaseName.qhelp` (XML-based query help format)\n\nDocumentation files are **siblings** to the query file (same directory).\n\n### Handling Existing Documentation\n\n1. **No documentation exists**: Create new `QueryFileBaseName.md` file\n2. **`.md` file exists**: Update the existing markdown file\n3. **`.qhelp` file exists**: Use #codeql_generate_query-help tool to convert to markdown, then update\n\n## Workflow Checklist\n\nUse the following MCP server tools to gather context before creating documentation:\n\n### Phase 1: Query Discovery\n\n- [ ] **Step 1: Locate query files**\n - Tool: #find_codeql_query_files\n - Parameters: `queryPath` = provided query path\n - Gather: Query source file path, existing documentation files, test files\n - Check: Does `QueryFileBaseName.md` or `QueryFileBaseName.qhelp` exist?\n\n- [ ] **Step 2: Read query metadata**\n - Tool: #codeql_resolve_metadata\n - Parameters: `query` = query file path\n - Gather: @name, @description, @kind, @id, @tags, @precision, @severity\n\n### Phase 2: Convert Existing qhelp (if needed)\n\n- [ ] **Step 3: Convert qhelp to markdown** (only if `.qhelp` exists)\n - Tool: #codeql_generate_query-help\n - Parameters: `query` = query file path, `format` = "markdown"\n - Use output as starting point for updated documentation\n\n### Phase 3: Gather Query Context\n\n- [ ] **Step 4: Validate query structure**\n - Tool: #validate_codeql_query\n - Parameters: `query` = query source code\n - Gather: Structural validation, suggestions\n - Note: This is a heuristic check only \u2014 for full validation, use #codeql_query_compile\n\n- [ ] **Step 5: Explore query types** (if deeper understanding needed)\n - Tool: #codeql_lsp_definition \u2014 navigate to class/predicate definitions\n - Tool: #codeql_lsp_completion \u2014 explore member predicates on types used in the query\n - Parameters: `file_path`, `line` (0-based), `character` (0-based), `workspace_uri` (pack root)\n - Run #codeql_pack_install first \u2014 LSP tools require resolved dependencies\n\n- [ ] **Step 6: Run tests** (if tests exist from Step 1)\n - Tool: #codeql_test_run\n - Parameters: `tests` = test directories\n - Gather: Pass/fail status, confirms query behavior\n\n### Phase 4: Create/Update Documentation\n\nBased on gathered context, create or update the documentation file.\n\n## Documentation Format\n\nThe documentation file (`QueryFileBaseName.md`) should follow this standardized format with these sections:\n\n### Section 1: Title and Description\n\n- H1 heading with the query name from @name metadata\n- One paragraph description from @description, expanded if needed\n\n### Section 2: Metadata Table\n\nA table with these rows:\n\n- ID: The @id value in backticks\n- Kind: The @kind value (problem, path-problem, etc.)\n- Severity: The @severity value\n- Precision: The @precision value\n- Tags: The @tags values\n\n### Section 3: Overview\n\nConcise explanation of what vulnerability/issue this query detects and why it matters. 2-4 sentences.\n\n### Section 4: Recommendation\n\nBrief guidance on how developers should fix issues flagged by this query. Include code patterns to use or avoid.\n\n### Section 5: Example\n\nTwo subsections:\n\n- **Vulnerable Code**: A code block showing a pattern that would be flagged by this query\n- **Fixed Code**: A code block showing the corrected version of the code\n\nUse the appropriate language identifier for the code blocks (e.g., `javascript`, `python`, `java`).\n\n### Section 6: References\n\nA list of links to:\n\n- Relevant CWE if security query\n- Relevant documentation or standards\n- CodeQL documentation for related concepts\n\n## Output Actions\n\nAfter generating documentation content:\n\n1. **For new documentation**: Create the file at `[QueryDirectory]/QueryFileBaseName.md`\n2. **For existing `.md` file**: Update the file with new content, preserving any custom sections\n3. **For existing `.qhelp` file**: Create new `.md` file (keeping `.qhelp` for backward compatibility)\n\n## Important Notes\n\n- **Be concise**: Documentation should be brief but complete. This is reference documentation, not tutorial content.\n- **Keep it current**: Documentation should reflect the current behavior of the query.\n- **Use examples from tests**: If unit tests exist, use those code patterns as examples.\n- **Standard format**: Always use the format above for consistency across all query documentation.\n- **Metadata accuracy**: Ensure documented metadata matches actual query metadata.\n- **For workshops**: Use `explain_codeql_query` prompt when creating workshop content that requires deeper explanations and visual diagrams.\n';
|
|
191715
192427
|
|
|
@@ -191729,13 +192441,13 @@ var ql_tdd_advanced_prompt_default = '---\nagent: agent\n---\n\n# Advanced Test-
|
|
|
191729
192441
|
var ql_tdd_basic_prompt_default = '---\nagent: agent\n---\n\n# Test-Driven CodeQL Query Development Checklist\n\nUse this checklist to guide test-driven development of CodeQL queries. Follow the TDD cycle: write tests first, implement query logic, and iterate until tests pass.\n\nFor advanced techniques including AST/CFG visualization, see: `codeql://prompts/ql-tdd-advanced`\nFor detailed guidance, reference the MCP resource: `codeql://learning/test-driven-development`\n\n## TDD Workflow Checklist\n\n### Phase 1: Project Setup\n\n- [ ] **Create Query Structure**\n - Tool: #create_codeql_query\n - Specify: basePath, queryName, language, description (optional)\n - Creates: src/QueryName/QueryName.ql, test/QueryName/QueryName.qlref, test/QueryName/test-code-file\n - The .qlref file will contain the relative path: `QueryName/QueryName.ql`\n - Verify: directory structure follows CodeQL conventions with intermediate directories\n\n- [ ] **Install Pack Dependencies**\n - Tool: #codeql_pack_install\n - Install src pack dependencies\n - Install test pack dependencies\n - Verify: imports resolve without errors\n\n### Phase 2: Test Design (Red Phase)\n\n- [ ] **Design Test Cases**\n - Create positive test cases (should match)\n - Create negative test cases (should not match)\n - Create edge case tests\n - Document expected behavior in comments\n\n- [ ] **Define Expected Results**\n - Create .expected file with anticipated matches\n - Specify exact match locations (file, line, column)\n - Include expected alert messages\n\n- [ ] **Extract Test Database**\n - Tool: #codeql_test_extract\n - Extract database from test code\n - Verify: .testproj directory created\n\n### Phase 3: Analysis and Understanding\n\n- [ ] **Analyze Test Code AST**\n - Tool: #codeql_query_run with queryName: "PrintAST"\n - Use format: "graphtext" for @kind graph queries\n - Review AST structure and identify relevant classes\n\n- [ ] **Explore Available Classes and Predicates**\n - Tool: #codeql_lsp_completion at the position in the `from` clause\n - Parameters: `file_path`, `line` (0-based), `character` (0-based)\n - Set `workspace_uri` to the pack root directory (containing `codeql-pack.yml`)\n - Request completions after a dot (e.g., `pw.`) to see member predicates with docs\n - Tip: Run #codeql_pack_install first \u2014 LSP tools require resolved dependencies\n\n- [ ] **Navigate to Type Definitions**\n - Tool: #codeql_lsp_definition on a class or predicate name\n - Parameters: `file_path`, `line` (0-based), `character` (0-based), `workspace_uri`\n - Returns file URI and line range \u2014 even into library pack files\n - Read the definition source to understand available member predicates\n - Tool: #codeql_lsp_references to find usage examples across the pack\n\n For the full iterative LSP workflow, see: `codeql://prompts/ql_lsp_iterative_development`\n\n- [ ] **Reference Language Documentation**\n - Resource: `codeql://languages/{language}/ast`\n - Resource: `codeql://languages/{language}/security` (if applicable)\n - Identify AST classes and predicates needed\n\n### Phase 4: Implementation (Green Phase)\n\n- [ ] **Write Query Metadata**\n - Add @name annotation\n - Add @description annotation\n - Add @kind annotation (problem, path-problem, graph, etc.)\n - Add @id and other required metadata\n\n- [ ] **Implement Query Logic**\n - Import required libraries\n - Define necessary classes (if any)\n - Define helper predicates\n - Implement main query clause\n\n- [ ] **Compile Query**\n - Tool: #codeql_query_compile\n - Fix any compilation errors\n - Verify: query compiles successfully\n\n- [ ] **Run Tests**\n - Tool: #codeql_test_run\n - Compare actual vs expected results\n - If tests fail: adjust query logic and recompile\n - If tests pass: proceed to validation\n\n### Phase 5: Validation and Acceptance\n\n- [ ] **Verify Test Results**\n - Review all test matches\n - Confirm no false positives\n - Confirm no false negatives\n - Check edge cases behave correctly\n\n- [ ] **Accept Test Results** (only when correct)\n - Tool: #codeql_test_accept\n - Update .expected files\n - Commit accepted results\n\n### Phase 6: Refactoring and Enhancement\n\n- [ ] **Refactor Query**\n - Improve code clarity\n - Extract common logic to predicates\n - Add code comments and documentation\n - Tool: #codeql_query_format for consistent formatting\n\n- [ ] **Optimize Performance** (if needed)\n - Run with evaluator-log enabled\n - Tool: #profile_codeql_query_from_logs\n - Resource: `codeql://patterns/performance`\n - Optimize expensive operations\n\n- [ ] **Generate Documentation**\n - Tool: #codeql_generate_query_help\n - Review and enhance QLDoc comments\n - Document query purpose and limitations\n\n### Phase 7: Additional Testing\n\n- [ ] **Add More Test Cases**\n - Identify additional scenarios\n - Add tests for new edge cases\n - Extract new test databases\n - Run expanded test suite\n\n- [ ] **Validate Against Real Code** (optional)\n - Tool: #codeql_database_create for real codebase\n - Tool: #codeql_query_run against real database\n - Review results for false positives/negatives\n\n## Quick Command Reference\n\n### Essential Tools\n\n```typescript\n// Create query structure\ncreate_codeql_query: {\n basePath: "/path/to/query/base",\n queryName: "MySecurityQuery",\n language: "javascript",\n description: "Detects security vulnerability X"\n}\n\n// Install dependencies\ncodeql_pack_install: {\n packPath: "/path/to/pack"\n}\n\n// Extract test database\ncodeql_test_extract: {\n testPath: "/path/to/test/QueryName",\n searchPath: "/path/to/base"\n}\n\n// Analyze AST (for @kind graph queries)\ncodeql_query_run: {\n queryName: "PrintAST",\n queryLanguage: "javascript",\n database: "/path/to/test.testproj",\n format: "graphtext",\n interpretedOutput: "/path/to/ast-output/"\n}\n\n// Compile query\ncodeql_query_compile: {\n query: "/path/to/Query.ql",\n searchPath: "/path/to/base",\n checkOnly: true\n}\n\n// Run tests\ncodeql_test_run: {\n testPath: "/path/to/test/Query.qlref",\n searchPath: "/path/to/base"\n}\n\n// Accept results\ncodeql_test_accept: {\n testPath: "/path/to/test/Query",\n searchPath: "/path/to/base"\n}\n```\n\n## TDD Principles to Remember\n\n1. **Red \u2192 Green \u2192 Refactor**: Always start with failing tests\n2. **Test First**: Write tests before implementation\n3. **Small Steps**: Make minimal changes to pass each test\n4. **Frequent Testing**: Run tests after each change\n5. **One Concept Per Test**: Each test should verify one behavior\n6. **Keep Tests Simple**: Test code should be easy to understand\n7. **Refactor Confidently**: Tests enable safe refactoring\n\n## Common Pitfalls to Avoid\n\n- \u274C Writing query before tests\n- \u274C Accepting test results without verification\n- \u274C Skipping compilation step\n- \u274C Not using PrintAST to understand test code\n- \u274C Not using #codeql_lsp_completion to discover available types\n- \u274C Not setting `workspace_uri` when using LSP tools (completions will be empty)\n- \u274C Creating tests that are too complex\n- \u274C Ignoring false positives in results\n- \u274C Not refactoring after tests pass\n\n## Success Criteria\n\nYour query development is complete when:\n\n- \u2705 All tests pass\n- \u2705 No false positives in test results\n- \u2705 No false negatives (all expected cases caught)\n- \u2705 Query compiles without errors or warnings\n- \u2705 Code is well-documented with QLDoc comments\n- \u2705 Performance is acceptable\n- \u2705 Edge cases are covered by tests\n- \u2705 Query follows CodeQL best practices\n\n## Next Steps After Completion\n\n1. **Integration Testing**: Test against real codebases\n2. **Peer Review**: Have another developer review the query\n3. **Documentation**: Update project documentation\n4. **Regression Testing**: Add to CI/CD pipeline\n5. **Monitor Performance**: Track query performance over time\n';
|
|
191730
192442
|
|
|
191731
192443
|
// src/prompts/run-query-and-summarize-false-positives.prompt.md
|
|
191732
|
-
var run_query_and_summarize_false_positives_prompt_default = '---\nagent: agent\n---\n\n# Run a query and describe its false positives\n\n## Task\n\nHelp a developer discover what kinds of false positives are produced by their current CodeQL query, and which of those false positive cases are most common.\n\n### Task steps:\n\n1. Read the provided CodeQL query to understand what patterns it is designed to detect.\n2. Discover the results of this query on a real database, by:\n - Running the tool #list_query_run_results to find existing runs for this query\n - If no existing runs are found, run the query on a relevant database using #codeql_query_run tool\n3. Analyze and group the results into what appear to be similar types of results. This may mean:\n - Grouping results in the same file\n - Grouping results that reference the same elements\n - Grouping results with similar messages\n4. For each group, explore the actual code for a sample of alerts in that group, using the #read_database_source tool to triage the results and determine which groups appear to be false positives\n5. For each false positive case discovered in this exploration, group them into categories of similar root causes. For example, a query might not properly account for unreachable code, or there may be a commonly used library that violates the query\'s assumptions but is actually safe.\n6. Explain these results to the user in order of most common to least common, so they can understand where their query may need improvement to reduce false positives.\n\n## Input Context\n\nYou will be provided with:\n\n- **Query Path**: The path to the CodeQL query\n\n## Analysis Guidelines\n\n### Exploring code paths\n\nThe tool #read_database_source can be used to read the code of a particular finding. A good strategy to explore the code paths of a finding is:\n\n1. Read in the immediate context of the violation.\n - Some queries may depend on later context (e.g., an "unused variable" may only be used after its declaration)\n - Some queries may depend on earlier context (e.g., "mutex not initialized" requires observing code that ran before the violation)\n - Some queries may rely on exploring other function calls.\n2. Read the interprocedural context of the violation.\n - If understanding a violation requires checking other source locations, read the beginning of the file to find what it imports, and then read those imported files to find the relevant code.\n - Selectively scan interprocedural paths. A false positive that requires tremendous code exploration to verify is less problematic than a false positive that only requires checking a small amount of code to verify.\n3. Stop early.\n - Grouping the potential false positive cases is more important than exhaustively verifying every single finding.\n - A common false positive likely introduces some false positives that are very hard to verify, so it is usually better to focus on simple cases first.\n - Truly hard-to-verify false positive cases are often in code that users don\'t expect to be conducive to static analysis, and query authors often don\'t expect their queries to work well in those cases.\n - Suggest a chainsaw approach rather than a scalpel - if a result may be a false positive, identify some simple heuristics to eliminate all such complex cases, even if such a heuristic could introduce false negatives.\n\n### What Makes a Result Likely to be a False Positive?\n\n1. **Pattern Mismatches**\n - Query pattern doesn\'t accurately match the actual code behavior\n - Missing context that would show the code is safe\n - Overly broad query logic catching benign variations\n\n2. **Safe Code Patterns**\n - Code includes proper validation before the flagged operation\n - Results in test code, example code, or mock implementations\n - Variable/function names suggesting test/example context (e.g., `testFunc`, `exampleVar`, `mockData`)\n - Defensive programming patterns present but not recognized by query\n\n3. **Context Indicators**\n - File paths suggesting test/example files (e.g., `test/`, `examples/`, `mock/`)\n - Comments indicating intentional patterns or safe usage\n - Framework-specific patterns that are safe in context\n\n4. **Technical Factors**\n - Type mismatches that make the vulnerability impossible\n - Control flow that prevents exploitation\n - Data flow interrupted by sanitization not captured in query\n\n### Important Constraints\n\n- **Be objective**: Don\'t be influenced by variable/function naming suggesting importance (e.g., "prodOnly" vs "test")\n- **Require evidence**: Base conclusions on actual code patterns, not assumptions\n- **Mark uncertainty**: Use lower confidence scores when code snippets are missing\n- **Avoid false confidence**: If you cannot determine FP status, mark confidence as low\n\n## Output Format\n\nReturn a JSON array of false-positive _groups_, ordered by the estimated prevalence or importance of the false-positive pattern (highest first):\n\n```json\n[\n {\n "groupLabel": "Test and example code with safe validation",\n "patternSummary": "Results where the flagged operation occurs in test/example files and is always preceded by input validation.",\n "resultCount": 23,\n "estimatedFpProportion": 0.9,\n "confidence": 0.85,\n "commonRootCause": "The query does not recognize common test/example locations or custom validation helpers as making the pattern safe.",\n "sampleResults": [\n {\n "sourceFile": "results-1.sarif",\n "resultIndex": 42,\n "ruleId": "query-id",\n "message": { "text": "original result message" },\n "locations": []\n }\n ],\n "reasoning": "Based on manual inspection of several alerts in this group: (1) results are consistently located under \'test/\' or \'examples/\' directories, (2) there is clear validation before the flagged operation, and (3) the query does not model the custom validation helpers used in these files."\n }\n]\n```\n\n### Field Descriptions\n\n- **groupLabel**: Short human-readable name for the false-positive category.\n- **patternSummary**: One-sentence description of the common pattern shared by results in this group.\n- **resultCount**: Number of SARIF results that belong to this group.\n- **estimatedFpProportion**: Estimated proportion of results in this group that are false positives (0.0\u20131.0).\n- **confidence**: Confidence in the group-level assessment (see guidelines below).\n- **commonRootCause**: Why the query produces false positives for this pattern.\n- **sampleResults**: A small representative sample of results illustrating the pattern.\n- **reasoning**: Detailed explanation of how the FP determination was made.\n\n### Confidence Score Guidelines\n\n- **0.8-1.0**: Strong evidence of FP (e.g., test code with clear safety patterns)\n- **0.6-0.8**: Good evidence of FP (e.g., defensive patterns present)\n- **0.4-0.6**: Moderate evidence of FP (e.g., context suggests safety but not conclusive)\n- **0.2-0.4**: Weak evidence of FP (e.g., minor indicators, missing code snippets)\n- **0.0-0.2**: Minimal evidence (uncertain, need more information)\n\n## Examples\n\n### High-Confidence FP Group Example\n\n```json\n{\n "groupLabel": "Sanitised inputs in test harnesses",\n "patternSummary": "Results in test files where sanitizeInput() is called before the database query.",\n "resultCount": 15,\n "estimatedFpProportion": 0.95,\n "confidence": 0.9,\n "commonRootCause": "Query does not model sanitizeInput() as a sanitizer.",\n "sampleResults": [\n {\n "sourceFile": "results-1.sarif",\n "resultIndex": 7,\n "ruleId": "js/sql-injection",\n "message": { "text": "Potential SQL injection" },\n "locations": []\n }\n ],\n "reasoning": "False positive group: (1) File paths like \'test/unit/database-mock.test.js\' indicate test code, (2) Query doesn\'t recognize that \'sanitizeInput()\' is called before database query, (3) All 15 results share this pattern."\n}\n```\n\n### Low-Confidence FP Group Example\n\n```json\n{\n "groupLabel": "File utility handlers with unclear validation",\n "patternSummary": "Results in src/utils/ files where path validation may or may not be present.",\n "resultCount": 4,\n "estimatedFpProportion": 0.5,\n "confidence": 0.3,\n "commonRootCause": "Unclear whether custom path validation is sufficient.",\n "sampleResults": [\n {\n "sourceFile": "results-1.sarif",\n "resultIndex": 22,\n "ruleId": "js/path-injection",\n "message": { "text": "Potential path traversal" },\n "locations": []\n }\n ],\n "reasoning": "Possibly false positive: (1) No code snippets available in SARIF, (2) File path \'src/utils/fileHandler.js\' doesn\'t indicate test code, (3) Cannot verify if proper path validation exists. Low confidence due to missing context."\n}\n```\n\n## Processing Instructions\n\n1. **Review SARIF results** and group them by common patterns\n2. **Analyze available context** (code snippets, file paths, messages) for each group\n3. **Compare against query logic** to understand what pattern was detected\n4. **Identify FP indicators** based on guidelines above\n5. **Assign group-level confidence scores** reflecting evidence strength\n6. **Write clear reasoning** for each group explaining the assessment\n7. **Sort groups** by estimated prevalence / importance (descending)\n\n## Important Notes\n\n- A result should belong to at most one FP group\n- When in doubt, prefer lower confidence scores\n- Missing code snippets should always reduce confidence\n- Test/example code is more likely to be FP but not always\n- Focus on technical evidence, not code naming conventions\n';
|
|
192444
|
+
var run_query_and_summarize_false_positives_prompt_default = '---\nagent: agent\n---\n\n# Run a query and describe its false positives\n\n## Task\n\nHelp a developer discover what kinds of false positives are produced by their current CodeQL query, and which of those false positive cases are most common.\n\n### Task steps:\n\n1. Read the provided CodeQL query to understand what patterns it is designed to detect.\n2. Discover the results of this query on a real database, by:\n - Using #query_results_cache_lookup with `ruleId` (the query\'s `@id` metadata) to find previously cached results\n - Running #list_query_run_results to find existing run artifacts\n - If no existing results are found, run the query using #codeql_query_run with `format: "sarif-latest"` to produce SARIF output (results are auto-cached with `ruleId` for later lookup)\n3. Use #sarif_list_rules on the SARIF output to confirm rule metadata and result counts. For multi-rule SARIF, use #sarif_extract_rule to isolate the specific query\'s results.\n4. Use #sarif_rule_to_markdown to generate a structured overview with a results table and Mermaid dataflow diagrams for path-problem results. This gives an immediate visual summary of all findings.\n5. Analyze and group the results into what appear to be similar types of results. This may mean:\n - Grouping results in the same file\n - Grouping results that reference the same elements\n - Grouping results with similar messages\n6. For each group, explore the actual code for a sample of alerts using #read_database_source with the `filePath` from the SARIF alert URI and 10\u201320 lines of context around the flagged location.\n7. For each false positive case discovered, group them into categories of similar root causes.\n8. Explain these results to the user in order of most common to least common.\n\n## Input Context\n\nYou will be provided with:\n\n- **Query Path**: The path to the CodeQL query\n\n## Analysis Guidelines\n\n### Exploring code paths\n\nUse #read_database_source with the `filePath` from the SARIF alert\'s `physicalLocation.artifactLocation.uri` and the `databasePath` of the CodeQL database. Read 10\u201320 lines around the flagged location:\n\n1. Read in the immediate context of the violation.\n - Some queries may depend on later context (e.g., an "unused variable" may only be used after its declaration)\n - Some queries may depend on earlier context (e.g., "mutex not initialized" requires observing code that ran before the violation)\n - Some queries may rely on exploring other function calls.\n2. Read the interprocedural context of the violation.\n - If understanding a violation requires checking other source locations, read the beginning of the file to find what it imports, and then read those imported files to find the relevant code.\n - Selectively scan interprocedural paths. A false positive that requires tremendous code exploration to verify is less problematic than a false positive that only requires checking a small amount of code to verify.\n3. Stop early.\n - Grouping the potential false positive cases is more important than exhaustively verifying every single finding.\n - A common false positive likely introduces some false positives that are very hard to verify, so it is usually better to focus on simple cases first.\n - Truly hard-to-verify false positive cases are often in code that users don\'t expect to be conducive to static analysis, and query authors often don\'t expect their queries to work well in those cases.\n - Suggest a chainsaw approach rather than a scalpel - if a result may be a false positive, identify some simple heuristics to eliminate all such complex cases, even if such a heuristic could introduce false negatives.\n\n### What Makes a Result Likely to be a False Positive?\n\n1. **Pattern Mismatches**\n - Query pattern doesn\'t accurately match the actual code behavior\n - Missing context that would show the code is safe\n - Overly broad query logic catching benign variations\n\n2. **Safe Code Patterns**\n - Code includes proper validation before the flagged operation\n - Results in test code, example code, or mock implementations\n - Variable/function names suggesting test/example context (e.g., `testFunc`, `exampleVar`, `mockData`)\n - Defensive programming patterns present but not recognized by query\n\n3. **Context Indicators**\n - File paths suggesting test/example files (e.g., `test/`, `examples/`, `mock/`)\n - Comments indicating intentional patterns or safe usage\n - Framework-specific patterns that are safe in context\n\n4. **Technical Factors**\n - Type mismatches that make the vulnerability impossible\n - Control flow that prevents exploitation\n - Data flow interrupted by sanitization not captured in query\n\n### Important Constraints\n\n- **Be objective**: Don\'t be influenced by variable/function naming suggesting importance (e.g., "prodOnly" vs "test")\n- **Require evidence**: Base conclusions on actual code patterns, not assumptions\n- **Mark uncertainty**: Use lower confidence scores when code snippets are missing\n- **Avoid false confidence**: If you cannot determine FP status, mark confidence as low\n\n## Output Format\n\nReturn a JSON array of false-positive _groups_, ordered by the estimated prevalence or importance of the false-positive pattern (highest first):\n\n```json\n[\n {\n "groupLabel": "Test and example code with safe validation",\n "patternSummary": "Results where the flagged operation occurs in test/example files and is always preceded by input validation.",\n "resultCount": 23,\n "estimatedFpProportion": 0.9,\n "confidence": 0.85,\n "commonRootCause": "The query does not recognize common test/example locations or custom validation helpers as making the pattern safe.",\n "sampleResults": [\n {\n "sourceFile": "results-1.sarif",\n "resultIndex": 42,\n "ruleId": "query-id",\n "message": { "text": "original result message" },\n "locations": []\n }\n ],\n "reasoning": "Based on manual inspection of several alerts in this group: (1) results are consistently located under \'test/\' or \'examples/\' directories, (2) there is clear validation before the flagged operation, and (3) the query does not model the custom validation helpers used in these files."\n }\n]\n```\n\n### Field Descriptions\n\n- **groupLabel**: Short human-readable name for the false-positive category.\n- **patternSummary**: One-sentence description of the common pattern shared by results in this group.\n- **resultCount**: Number of SARIF results that belong to this group.\n- **estimatedFpProportion**: Estimated proportion of results in this group that are false positives (0.0\u20131.0).\n- **confidence**: Confidence in the group-level assessment (see guidelines below).\n- **commonRootCause**: Why the query produces false positives for this pattern.\n- **sampleResults**: A small representative sample of results illustrating the pattern.\n- **reasoning**: Detailed explanation of how the FP determination was made.\n\n### Confidence Score Guidelines\n\n- **0.8-1.0**: Strong evidence of FP (e.g., test code with clear safety patterns)\n- **0.6-0.8**: Good evidence of FP (e.g., defensive patterns present)\n- **0.4-0.6**: Moderate evidence of FP (e.g., context suggests safety but not conclusive)\n- **0.2-0.4**: Weak evidence of FP (e.g., minor indicators, missing code snippets)\n- **0.0-0.2**: Minimal evidence (uncertain, need more information)\n\n## Examples\n\n### High-Confidence FP Group Example\n\n```json\n{\n "groupLabel": "Sanitised inputs in test harnesses",\n "patternSummary": "Results in test files where sanitizeInput() is called before the database query.",\n "resultCount": 15,\n "estimatedFpProportion": 0.95,\n "confidence": 0.9,\n "commonRootCause": "Query does not model sanitizeInput() as a sanitizer.",\n "sampleResults": [\n {\n "sourceFile": "results-1.sarif",\n "resultIndex": 7,\n "ruleId": "js/sql-injection",\n "message": { "text": "Potential SQL injection" },\n "locations": []\n }\n ],\n "reasoning": "False positive group: (1) File paths like \'test/unit/database-mock.test.js\' indicate test code, (2) Query doesn\'t recognize that \'sanitizeInput()\' is called before database query, (3) All 15 results share this pattern."\n}\n```\n\n### Low-Confidence FP Group Example\n\n```json\n{\n "groupLabel": "File utility handlers with unclear validation",\n "patternSummary": "Results in src/utils/ files where path validation may or may not be present.",\n "resultCount": 4,\n "estimatedFpProportion": 0.5,\n "confidence": 0.3,\n "commonRootCause": "Unclear whether custom path validation is sufficient.",\n "sampleResults": [\n {\n "sourceFile": "results-1.sarif",\n "resultIndex": 22,\n "ruleId": "js/path-injection",\n "message": { "text": "Potential path traversal" },\n "locations": []\n }\n ],\n "reasoning": "Possibly false positive: (1) No code snippets available in SARIF, (2) File path \'src/utils/fileHandler.js\' doesn\'t indicate test code, (3) Cannot verify if proper path validation exists. Low confidence due to missing context."\n}\n```\n\n## Processing Instructions\n\n1. **Review SARIF results** and group them by common patterns\n2. **Analyze available context** (code snippets, file paths, messages) for each group\n3. **Compare against query logic** to understand what pattern was detected\n4. **Identify FP indicators** based on guidelines above\n5. **Assign group-level confidence scores** reflecting evidence strength\n6. **Write clear reasoning** for each group explaining the assessment\n7. **Sort groups** by estimated prevalence / importance (descending)\n\n## Important Notes\n\n- A result should belong to at most one FP group\n- When in doubt, prefer lower confidence scores\n- Missing code snippets should always reduce confidence\n- Test/example code is more likely to be FP but not always\n- Focus on technical evidence, not code naming conventions\n';
|
|
191733
192445
|
|
|
191734
192446
|
// src/prompts/sarif-rank-false-positives.prompt.md
|
|
191735
|
-
var sarif_rank_false_positives_prompt_default = '---\nagent: agent\n---\n\n# Evaluate SARIF Results for False Positives\n\n## Task\n\nAnalyze SARIF results from a CodeQL query and identify the most likely **False Positives (FPs)** - results that incorrectly flag benign code as problematic.\n\n## Input Context\n\nYou will be provided with:\n\n- **Query ID**: The CodeQL query/rule identifier\n- **Query Name**: Human-readable name of the query\n- **Query Content**: Full CodeQL query implementation\n- **SARIF Results**: Array of results to analyze\n- **Code Snippets**: When available, code snippets from SARIF physical locations\n\n## Analysis Guidelines\n\n### What Makes a Result Likely to be a False Positive?\n\n1. **Pattern Mismatches**\n - Query pattern doesn\'t accurately match the actual code behavior\n - Missing context that would show the code is safe\n - Overly broad query logic catching benign variations\n\n2. **Safe Code Patterns**\n - Code includes proper validation before the flagged operation\n - Results in test code, example code, or mock implementations\n - Variable/function names suggesting test/example context (e.g., `testFunc`, `exampleVar`, `mockData`)\n - Defensive programming patterns present but not recognized by query\n\n3. **Context Indicators**\n - File paths suggesting test/example files (e.g., `test/`, `examples/`, `mock/`)\n - Comments indicating intentional patterns or safe usage\n - Framework-specific patterns that are safe in context\n\n4. **Technical Factors**\n - Type mismatches that make the vulnerability impossible\n - Control flow that prevents exploitation\n - Data flow interrupted by sanitization not captured in query\n\n### Important Constraints\n\n- **Be objective**: Don\'t be influenced by variable/function naming suggesting importance (e.g., "prodOnly" vs "test")\n- **Require evidence**: Base conclusions on actual code patterns, not assumptions\n- **Mark uncertainty**: Use lower confidence scores when code snippets are missing\n- **Avoid false confidence**: If you cannot determine FP status, mark confidence as low\n\n### When Code Snippets Are Missing\n\nIf SARIF results lack `physicalLocation.region.snippet` or `contextRegion`:\n\n- **Lower confidence scores** (typically 0.3-0.5 instead of 0.6-0.9)\n- **Note the limitation** in reasoning\n- **Rely more on**:\n - Result message text\n - File path analysis\n - Query logic understanding\n\n## Output Format\n\nReturn a JSON array of ranked results, ordered by FP likelihood (highest first):\n\n```json\n[\n {\n "ruleId": "query-id",\n "message": { "text": "original result message" },\n "locations": [...],\n "confidence": 0.85,\n "reasoning": "This appears to be a false positive because: (1) the file path \'test/examples/mock-data.js\' indicates test code, (2) variable name \'testInput\' suggests this is test data, (3) the query doesn\'t account for the validation on line X.",\n "sourceFile": "results-1.sarif",\n "resultIndex": 42\n }\n]\n```\n\n### Confidence Score Guidelines\n\n- **0.8-1.0**: Strong evidence of FP (e.g., test code with clear safety patterns)\n- **0.6-0.8**: Good evidence of FP (e.g., defensive patterns present)\n- **0.4-0.6**: Moderate evidence of FP (e.g., context suggests safety but not conclusive)\n- **0.2-0.4**: Weak evidence of FP (e.g., minor indicators, missing code snippets)\n- **0.0-0.2**: Minimal evidence (uncertain, need more information)\n\n## Examples\n\n### High-Confidence FP Example\n\n```json\n{\n "ruleId": "js/sql-injection",\n "message": { "text": "Potential SQL injection" },\n "confidence": 0.9,\n "reasoning": "False positive: (1) File path \'test/unit/database-mock.test.js\' indicates test code, (2) Query doesn\'t recognize that \'sanitizeInput()\' function is called before database query, (3) Variable name \'mockUserInput\' suggests test data. Missing code snippet limits certainty to 0.9 instead of 1.0."\n}\n```\n\n### Low-Confidence FP Example\n\n```json\n{\n "ruleId": "js/path-injection",\n "message": { "text": "Potential path traversal" },\n "confidence": 0.3,\n "reasoning": "Possibly a false positive: (1) No code snippets available in SARIF, (2) File path \'src/utils/fileHandler.js\' doesn\'t indicate test code, (3) Cannot verify if proper path validation exists. Low confidence due to missing context."\n}\n```\n\n## Processing Instructions\n\n1. **Review each SARIF result** in the provided array\n2. **Analyze available context** (code snippets, file paths, messages)\n3. **Compare against query logic** to understand what pattern was detected\n4. **Identify FP indicators** based on guidelines above\n5. **Assign confidence score** reflecting evidence strength\n6. **Write clear reasoning** explaining your assessment\n7. **Sort results** by confidence score (descending)\n8. **Return top N results** as requested (or all if N not specified)\n\n## Important Notes\n\n- A result should never appear in both FP and TP rankings\n- When in doubt, prefer lower confidence scores\n- Missing code snippets should always reduce confidence\n- Test/example code is more likely to be FP but not always\n- Focus on technical evidence, not code naming conventions\n';
|
|
192447
|
+
var sarif_rank_false_positives_prompt_default = '---\nagent: agent\n---\n\n# Evaluate SARIF Results for False Positives\n\n## Task\n\nAnalyze SARIF results from a CodeQL query and identify the most likely **False Positives (FPs)** - results that incorrectly flag benign code as problematic.\n\n## Input Context\n\nYou will be provided with:\n\n- **Query ID**: The CodeQL query/rule identifier\n- **Query Name**: Human-readable name of the query\n- **Query Content**: Full CodeQL query implementation\n- **SARIF Results**: Array of results to analyze\n- **Code Snippets**: When available, code snippets from SARIF physical locations\n\n## Recommended Tool Usage\n\nUse these tools to gather context for each result:\n\n1. **#sarif_list_rules** \u2014 Discover all rules and result counts in the SARIF data. Helps scope the analysis.\n2. **#sarif_extract_rule** \u2014 Extract results for the specific query `@id` from multi-rule SARIF. Returns a clean SARIF subset.\n3. **#sarif_rule_to_markdown** \u2014 Generate a structured markdown report with results table and Mermaid dataflow diagrams. Provides an immediate visual overview of all findings.\n4. **#read_database_source** \u2014 Read source code from the CodeQL database using the `filePath` from the SARIF alert URI. Read 10\u201320 lines around each flagged location for context.\n5. **#query_results_cache_lookup** with `ruleId` \u2014 Check if results for this query are already cached from previous runs.\n6. **#sarif_compare_alerts** \u2014 When the same codebase has results from both standard and custom queries, compare alert locations to distinguish unique findings from overlapping ones.\n7. **#sarif_diff_runs** \u2014 Compare against a baseline SARIF run to identify new results vs previously known findings.\n\n## Analysis Guidelines\n\n### What Makes a Result Likely to be a False Positive?\n\n1. **Pattern Mismatches**\n - Query pattern doesn\'t accurately match the actual code behavior\n - Missing context that would show the code is safe\n - Overly broad query logic catching benign variations\n\n2. **Safe Code Patterns**\n - Code includes proper validation before the flagged operation\n - Results in test code, example code, or mock implementations\n - Variable/function names suggesting test/example context (e.g., `testFunc`, `exampleVar`, `mockData`)\n - Defensive programming patterns present but not recognized by query\n\n3. **Context Indicators**\n - File paths suggesting test/example files (e.g., `test/`, `examples/`, `mock/`)\n - Comments indicating intentional patterns or safe usage\n - Framework-specific patterns that are safe in context\n\n4. **Technical Factors**\n - Type mismatches that make the vulnerability impossible\n - Control flow that prevents exploitation\n - Data flow interrupted by sanitization not captured in query\n\n### Important Constraints\n\n- **Be objective**: Don\'t be influenced by variable/function naming suggesting importance (e.g., "prodOnly" vs "test")\n- **Require evidence**: Base conclusions on actual code patterns, not assumptions\n- **Mark uncertainty**: Use lower confidence scores when code snippets are missing\n- **Avoid false confidence**: If you cannot determine FP status, mark confidence as low\n\n### When Code Snippets Are Missing\n\nIf SARIF results lack `physicalLocation.region.snippet` or `contextRegion`:\n\n- **Lower confidence scores** (typically 0.3-0.5 instead of 0.6-0.9)\n- **Note the limitation** in reasoning\n- **Rely more on**:\n - Result message text\n - File path analysis\n - Query logic understanding\n\n## Output Format\n\nReturn a JSON array of ranked results, ordered by FP likelihood (highest first):\n\n```json\n[\n {\n "ruleId": "query-id",\n "message": { "text": "original result message" },\n "locations": [...],\n "confidence": 0.85,\n "reasoning": "This appears to be a false positive because: (1) the file path \'test/examples/mock-data.js\' indicates test code, (2) variable name \'testInput\' suggests this is test data, (3) the query doesn\'t account for the validation on line X.",\n "sourceFile": "results-1.sarif",\n "resultIndex": 42\n }\n]\n```\n\n### Confidence Score Guidelines\n\n- **0.8-1.0**: Strong evidence of FP (e.g., test code with clear safety patterns)\n- **0.6-0.8**: Good evidence of FP (e.g., defensive patterns present)\n- **0.4-0.6**: Moderate evidence of FP (e.g., context suggests safety but not conclusive)\n- **0.2-0.4**: Weak evidence of FP (e.g., minor indicators, missing code snippets)\n- **0.0-0.2**: Minimal evidence (uncertain, need more information)\n\n## Examples\n\n### High-Confidence FP Example\n\n```json\n{\n "ruleId": "js/sql-injection",\n "message": { "text": "Potential SQL injection" },\n "confidence": 0.9,\n "reasoning": "False positive: (1) File path \'test/unit/database-mock.test.js\' indicates test code, (2) Query doesn\'t recognize that \'sanitizeInput()\' function is called before database query, (3) Variable name \'mockUserInput\' suggests test data. Missing code snippet limits certainty to 0.9 instead of 1.0."\n}\n```\n\n### Low-Confidence FP Example\n\n```json\n{\n "ruleId": "js/path-injection",\n "message": { "text": "Potential path traversal" },\n "confidence": 0.3,\n "reasoning": "Possibly a false positive: (1) No code snippets available in SARIF, (2) File path \'src/utils/fileHandler.js\' doesn\'t indicate test code, (3) Cannot verify if proper path validation exists. Low confidence due to missing context."\n}\n```\n\n## Processing Instructions\n\n1. **Review each SARIF result** in the provided array\n2. **Analyze available context** (code snippets, file paths, messages)\n3. **Compare against query logic** to understand what pattern was detected\n4. **Identify FP indicators** based on guidelines above\n5. **Assign confidence score** reflecting evidence strength\n6. **Write clear reasoning** explaining your assessment\n7. **Sort results** by confidence score (descending)\n8. **Return top N results** as requested (or all if N not specified)\n\n## Important Notes\n\n- A result should never appear in both FP and TP rankings\n- When in doubt, prefer lower confidence scores\n- Missing code snippets should always reduce confidence\n- Test/example code is more likely to be FP but not always\n- Focus on technical evidence, not code naming conventions\n';
|
|
191736
192448
|
|
|
191737
192449
|
// src/prompts/sarif-rank-true-positives.prompt.md
|
|
191738
|
-
var sarif_rank_true_positives_prompt_default = '---\nagent: agent\n---\n\n# Evaluate SARIF Results for True Positives\n\n## Task\n\nAnalyze SARIF results from a CodeQL query and identify the most likely **True Positives (TPs)** - results that correctly identify real security vulnerabilities or code quality issues.\n\n## Input Context\n\nYou will be provided with:\n\n- **Query ID**: The CodeQL query/rule identifier\n- **Query Name**: Human-readable name of the query\n- **Query Content**: Full CodeQL query implementation\n- **SARIF Results**: Array of results to analyze\n- **Code Snippets**: When available, code snippets from SARIF physical locations\n\n## Analysis Guidelines\n\n### What Makes a Result Likely to be a True Positive?\n\n1. **Pattern Matches**\n - Query pattern accurately identifies the problematic code behavior\n - Result shows clear evidence of the vulnerability or issue\n - Code matches the exact pattern the query was designed to detect\n\n2. **Vulnerable Code Patterns**\n - User input flows to dangerous sink without proper sanitization\n - Missing security checks or validation\n - Incorrect use of security-sensitive APIs\n - Clear violation of security best practices\n\n3. **Production Code Context**\n - File paths suggesting production code (e.g., `src/`, `lib/`, `app/`)\n - NOT in test/example directories\n - Function/variable names suggesting real business logic\n - Code appears to be part of actual application functionality\n\n4. **Technical Factors**\n - Data flow from untrusted source to dangerous operation\n - Missing or insufficient sanitization\n - Exploitable control flow\n - Real security or quality implications\n\n### Important Constraints\n\n- **Be objective**: Don\'t be influenced by variable/function naming suggesting importance\n- **Require evidence**: Base conclusions on actual vulnerability patterns, not assumptions\n- **Mark uncertainty**: Use lower confidence scores when code snippets are missing\n- **Avoid false confidence**: If you cannot determine TP status, mark confidence as low\n- **Consider false positives**: Be skeptical - real vulnerabilities often have subtle mitigations\n\n### When Code Snippets Are Missing\n\nIf SARIF results lack `physicalLocation.region.snippet` or `contextRegion`:\n\n- **Lower confidence scores** (typically 0.3-0.5 instead of 0.6-0.9)\n- **Note the limitation** in reasoning\n- **Rely more on**:\n - Result message text\n - File path analysis\n - Query logic understanding\n\n## Output Format\n\nReturn a JSON array of ranked results, ordered by TP likelihood (highest first):\n\n```json\n[\n {\n "ruleId": "query-id",\n "message": { "text": "original result message" },\n "locations": [...],\n "confidence": 0.85,\n "reasoning": "This appears to be a true positive because: (1) the file path \'src/api/user-controller.js\' indicates production code, (2) user input from request parameter flows directly to SQL query without sanitization, (3) no validation or prepared statement usage detected.",\n "sourceFile": "results-1.sarif",\n "resultIndex": 15\n }\n]\n```\n\n### Confidence Score Guidelines\n\n- **0.8-1.0**: Strong evidence of TP (e.g., clear vulnerability with exploit path)\n- **0.6-0.8**: Good evidence of TP (e.g., production code with problematic pattern)\n- **0.4-0.6**: Moderate evidence of TP (e.g., pattern matches but context unclear)\n- **0.2-0.4**: Weak evidence of TP (e.g., possible issue but missing context)\n- **0.0-0.2**: Minimal evidence (uncertain, need more information)\n\n## Examples\n\n### High-Confidence TP Example\n\n```json\n{\n "ruleId": "js/sql-injection",\n "message": { "text": "Unsanitized user input in SQL query" },\n "confidence": 0.9,\n "reasoning": "True positive: (1) File path \'src/controllers/UserController.js\' indicates production code, (2) User input from req.query.userId flows directly to SQL query string concatenation, (3) No prepared statements or sanitization visible in code snippet, (4) Classic SQL injection pattern. Confidence is 0.9 not 1.0 due to possibility of sanitization elsewhere in call chain."\n}\n```\n\n### Moderate-Confidence TP Example\n\n```json\n{\n "ruleId": "js/path-injection",\n "message": { "text": "Potential path traversal vulnerability" },\n "confidence": 0.6,\n "reasoning": "Likely true positive: (1) File path \'src/utils/fileHandler.js\' suggests production utility code, (2) User-controlled filename parameter used in fs.readFile(), (3) No visible path validation, but code snippet is limited. Moderate confidence due to incomplete context - may have validation in calling code."\n}\n```\n\n### Low-Confidence TP Example (Missing Snippets)\n\n```json\n{\n "ruleId": "java/unsafe-deserialization",\n "message": { "text": "Unsafe deserialization of user data" },\n "confidence": 0.4,\n "reasoning": "Possibly a true positive: (1) File path \'src/main/java/com/app/handlers/DataHandler.java\' indicates production code, (2) Message suggests unsafe deserialization pattern, (3) No code snippets available in SARIF to verify actual vulnerability or confirm absence of mitigations. Confidence is low due to missing context."\n}\n```\n\n## Processing Instructions\n\n1. **Review each SARIF result** in the provided array\n2. **Analyze available context** (code snippets, file paths, messages)\n3. **Compare against query logic** to understand what pattern was detected\n4. **Identify TP indicators** based on guidelines above\n5. **Look for counter-evidence** (mitigations, safe patterns) that would make it an FP\n6. **Assign confidence score** reflecting evidence strength\n7. **Write clear reasoning** explaining your assessment\n8. **Sort results** by confidence score (descending)\n9. **Return top N results** as requested (or all if N not specified)\n\n## Important Notes\n\n- A result should never appear in both FP and TP rankings\n- When in doubt, prefer lower confidence scores\n- Missing code snippets should always reduce confidence\n- Production code location increases TP likelihood but is not conclusive\n- Focus on technical vulnerability evidence, not code naming conventions\n- Consider that even production code can have intentional patterns that look vulnerable but are safe\n';
|
|
192450
|
+
var sarif_rank_true_positives_prompt_default = '---\nagent: agent\n---\n\n# Evaluate SARIF Results for True Positives\n\n## Task\n\nAnalyze SARIF results from a CodeQL query and identify the most likely **True Positives (TPs)** - results that correctly identify real security vulnerabilities or code quality issues.\n\n## Input Context\n\nYou will be provided with:\n\n- **Query ID**: The CodeQL query/rule identifier\n- **Query Name**: Human-readable name of the query\n- **Query Content**: Full CodeQL query implementation\n- **SARIF Results**: Array of results to analyze\n- **Code Snippets**: When available, code snippets from SARIF physical locations\n\n## Recommended Tool Usage\n\nUse these tools to gather context for each result:\n\n1. **#sarif_list_rules** \u2014 Discover all rules and result counts in the SARIF data. Helps scope the analysis.\n2. **#sarif_extract_rule** \u2014 Extract results for the specific query `@id` from multi-rule SARIF. Returns a clean SARIF subset.\n3. **#sarif_rule_to_markdown** \u2014 Generate a structured markdown report with results table and Mermaid dataflow diagrams. Provides quick visual triage of dataflow paths.\n4. **#read_database_source** \u2014 Read source code from the CodeQL database using the `filePath` from the SARIF alert URI. Read 10\u201320 lines around each flagged location to verify vulnerability patterns.\n5. **#query_results_cache_lookup** with `ruleId` \u2014 Check if results for this query are already cached from previous runs.\n6. **#sarif_compare_alerts** \u2014 Compare results across different SARIF files (e.g., standard vs custom query packs) to identify which findings are unique to the query under analysis.\n7. **#sarif_diff_runs** \u2014 Compare against a baseline SARIF run to distinguish new findings from regressions.\n\n## Analysis Guidelines\n\n### What Makes a Result Likely to be a True Positive?\n\n1. **Pattern Matches**\n - Query pattern accurately identifies the problematic code behavior\n - Result shows clear evidence of the vulnerability or issue\n - Code matches the exact pattern the query was designed to detect\n\n2. **Vulnerable Code Patterns**\n - User input flows to dangerous sink without proper sanitization\n - Missing security checks or validation\n - Incorrect use of security-sensitive APIs\n - Clear violation of security best practices\n\n3. **Production Code Context**\n - File paths suggesting production code (e.g., `src/`, `lib/`, `app/`)\n - NOT in test/example directories\n - Function/variable names suggesting real business logic\n - Code appears to be part of actual application functionality\n\n4. **Technical Factors**\n - Data flow from untrusted source to dangerous operation\n - Missing or insufficient sanitization\n - Exploitable control flow\n - Real security or quality implications\n\n### Important Constraints\n\n- **Be objective**: Don\'t be influenced by variable/function naming suggesting importance\n- **Require evidence**: Base conclusions on actual vulnerability patterns, not assumptions\n- **Mark uncertainty**: Use lower confidence scores when code snippets are missing\n- **Avoid false confidence**: If you cannot determine TP status, mark confidence as low\n- **Consider false positives**: Be skeptical - real vulnerabilities often have subtle mitigations\n\n### When Code Snippets Are Missing\n\nIf SARIF results lack `physicalLocation.region.snippet` or `contextRegion`:\n\n- **Lower confidence scores** (typically 0.3-0.5 instead of 0.6-0.9)\n- **Note the limitation** in reasoning\n- **Rely more on**:\n - Result message text\n - File path analysis\n - Query logic understanding\n\n## Output Format\n\nReturn a JSON array of ranked results, ordered by TP likelihood (highest first):\n\n```json\n[\n {\n "ruleId": "query-id",\n "message": { "text": "original result message" },\n "locations": [...],\n "confidence": 0.85,\n "reasoning": "This appears to be a true positive because: (1) the file path \'src/api/user-controller.js\' indicates production code, (2) user input from request parameter flows directly to SQL query without sanitization, (3) no validation or prepared statement usage detected.",\n "sourceFile": "results-1.sarif",\n "resultIndex": 15\n }\n]\n```\n\n### Confidence Score Guidelines\n\n- **0.8-1.0**: Strong evidence of TP (e.g., clear vulnerability with exploit path)\n- **0.6-0.8**: Good evidence of TP (e.g., production code with problematic pattern)\n- **0.4-0.6**: Moderate evidence of TP (e.g., pattern matches but context unclear)\n- **0.2-0.4**: Weak evidence of TP (e.g., possible issue but missing context)\n- **0.0-0.2**: Minimal evidence (uncertain, need more information)\n\n## Examples\n\n### High-Confidence TP Example\n\n```json\n{\n "ruleId": "js/sql-injection",\n "message": { "text": "Unsanitized user input in SQL query" },\n "confidence": 0.9,\n "reasoning": "True positive: (1) File path \'src/controllers/UserController.js\' indicates production code, (2) User input from req.query.userId flows directly to SQL query string concatenation, (3) No prepared statements or sanitization visible in code snippet, (4) Classic SQL injection pattern. Confidence is 0.9 not 1.0 due to possibility of sanitization elsewhere in call chain."\n}\n```\n\n### Moderate-Confidence TP Example\n\n```json\n{\n "ruleId": "js/path-injection",\n "message": { "text": "Potential path traversal vulnerability" },\n "confidence": 0.6,\n "reasoning": "Likely true positive: (1) File path \'src/utils/fileHandler.js\' suggests production utility code, (2) User-controlled filename parameter used in fs.readFile(), (3) No visible path validation, but code snippet is limited. Moderate confidence due to incomplete context - may have validation in calling code."\n}\n```\n\n### Low-Confidence TP Example (Missing Snippets)\n\n```json\n{\n "ruleId": "java/unsafe-deserialization",\n "message": { "text": "Unsafe deserialization of user data" },\n "confidence": 0.4,\n "reasoning": "Possibly a true positive: (1) File path \'src/main/java/com/app/handlers/DataHandler.java\' indicates production code, (2) Message suggests unsafe deserialization pattern, (3) No code snippets available in SARIF to verify actual vulnerability or confirm absence of mitigations. Confidence is low due to missing context."\n}\n```\n\n## Processing Instructions\n\n1. **Review each SARIF result** in the provided array\n2. **Analyze available context** (code snippets, file paths, messages)\n3. **Compare against query logic** to understand what pattern was detected\n4. **Identify TP indicators** based on guidelines above\n5. **Look for counter-evidence** (mitigations, safe patterns) that would make it an FP\n6. **Assign confidence score** reflecting evidence strength\n7. **Write clear reasoning** explaining your assessment\n8. **Sort results** by confidence score (descending)\n9. **Return top N results** as requested (or all if N not specified)\n\n## Important Notes\n\n- A result should never appear in both FP and TP rankings\n- When in doubt, prefer lower confidence scores\n- Missing code snippets should always reduce confidence\n- Production code location increases TP likelihood but is not conclusive\n- Focus on technical vulnerability evidence, not code naming conventions\n- Consider that even production code can have intentional patterns that look vulnerable but are safe\n';
|
|
191739
192451
|
|
|
191740
192452
|
// src/prompts/tools-query-workflow.prompt.md
|
|
191741
192453
|
var tools_query_workflow_prompt_default = '---\nagent: agent\n---\n\n# Using CodeQL Development MCP Server Tools Queries\n\nThis guide helps you use the built-in "tools" queries (`PrintAST`, `PrintCFG`, `CallGraphFrom`, `CallGraphTo`) that ship with the CodeQL Development MCP Server to understand code structure before writing detection queries.\n\n## Why Use Tools Queries?\n\nTools queries provide essential insights into how CodeQL represents your source code:\n\n| Query | Purpose | Use When |\n| --------------- | ------------------------------------------------ | ----------------------------------------------- |\n| `PrintAST` | Visualize the Abstract Syntax Tree | Understanding code structure, finding AST nodes |\n| `PrintCFG` | Visualize Control Flow Graphs | Understanding execution paths, loop/branch flow |\n| `CallGraphFrom` | Find all functions called by a specific function | Tracing data flow through call chains |\n| `CallGraphTo` | Find all functions that call a specific function | Understanding function usage patterns |\n\n## Supported Languages\n\nTools queries are available for: `actions`, `cpp`, `csharp`, `go`, `java`, `javascript`, `python`, `ruby`, `rust`, `swift`\n\n## Prerequisites\n\nBefore using tools queries, you need:\n\n1. **A CodeQL database** - Either create one or use an existing database\n2. **Source files to analyze** - The tools queries filter output to specific files\n\n## Workflow Checklist\n\n### Step 1: Identify or Create Database\n\n- [ ] **Option A: Use existing database**\n - Tool: #codeql_resolve_database\n - Verify database is valid and note the language\n\n- [ ] **Option B: Create new database**\n - Tool: #codeql_database_create\n - Parameters: `database`, `language`, `source-root`\n\n### Step 2: Run PrintAST Query\n\nThe PrintAST query outputs a hierarchical tree of AST nodes with labels.\n\n- [ ] **Execute PrintAST**\n - Tool: #codeql_query_run\n - Parameters:\n - `database`: Path to your CodeQL database\n - `queryName`: `"PrintAST"`\n - `queryLanguage`: Your language (e.g., `"javascript"`, `"python"`, `"cpp"`)\n - `sourceFiles`: Comma-separated file names to analyze (e.g., `"main.js,utils.js"`)\n - `format`: `"graphtext"` (for human-readable output)\n\n- [ ] **Verify output contains AST nodes**\n - Look for hierarchical structure with indentation\n - Confirm nodes have `semmle.label` with class names\n - Identify relevant AST classes for your query\n\n**Example AST output structure:**\n\n```text\nTopLevelFunction\n\u251C\u2500\u2500 FunctionDeclarationEntry\n\u251C\u2500\u2500 Block\n\u2502 \u251C\u2500\u2500 DeclStmt\n\u2502 \u2502 \u2514\u2500\u2500 LocalVariable\n\u2502 \u251C\u2500\u2500 ExprStmt\n\u2502 \u2502 \u2514\u2500\u2500 FunctionCall\n\u2502 \u2514\u2500\u2500 ReturnStmt\n```\n\n### Step 3: Run PrintCFG Query (if needed)\n\nThe PrintCFG query outputs control flow nodes and edges.\n\n- [ ] **Execute PrintCFG**\n - Tool: #codeql_query_run\n - Parameters:\n - `database`: Path to your CodeQL database\n - `queryName`: `"PrintCFG"`\n - `queryLanguage`: Your language\n - `sourceFunction`: Function name to analyze (e.g., `"processData"`)\n - `format`: `"graphtext"`\n\n- [ ] **Verify output contains nodes and edges**\n - Look for `nodes` section with CFG nodes\n - Look for `edges` section with `\u2192` arrows showing flow\n - Identify control flow patterns (loops, branches)\n\n**Example CFG output structure:**\n\n```text\nnodes\n| node | semmle.label |\n| ... | entry: processData |\n| ... | if (...) |\n| ... | return |\n\nedges\n| from | to | semmle.label |\n| ... | ... | \u2192 |\n```\n\n### Step 4: Run CallGraph Queries (if needed)\n\nCall graph queries help trace function relationships.\n\n- [ ] **Execute CallGraphFrom** (to find what a function calls)\n - Tool: #codeql_query_run\n - Parameters:\n - `database`: Path to your CodeQL database\n - `queryName`: `"CallGraphFrom"`\n - `queryLanguage`: Your language\n - `sourceFunction`: Function name to trace from (e.g., `"main"`)\n - `format`: `"sarif-latest"` or `"csv"`\n\n- [ ] **Execute CallGraphTo** (to find what calls a function)\n - Tool: #codeql_query_run\n - Parameters:\n - `database`: Path to your CodeQL database\n - `queryName`: `"CallGraphTo"`\n - `queryLanguage`: Your language\n - `targetFunction`: Function name to find callers of (e.g., `"validate"`)\n - `format`: `"sarif-latest"` or `"csv"`\n\n- [ ] **Verify call relationships**\n - Confirm results show caller \u2192 callee relationships\n - Note function locations for further analysis\n\n### Step 5: Apply Insights to Query Development\n\nUse the gathered information to inform your query:\n\n- [ ] **From PrintAST**: Identify which AST classes to use in your `from` clause\n- [ ] **From PrintCFG**: Understand execution paths for control-flow-sensitive queries\n- [ ] **From CallGraph**: Map data flow paths through function boundaries\n\n## Common Patterns\n\n### Pattern 1: Finding All Function Calls\n\n```text\n1. Run PrintAST on your source file\n2. Look for FunctionCall, MethodAccess, or similar nodes\n3. Note the parent/child relationships\n4. Use those AST classes in your query\n```\n\n### Pattern 2: Tracing Data Through Functions\n\n```text\n1. Run CallGraphFrom on your entry point function\n2. Identify which functions are called\n3. Run CallGraphTo on sink functions\n4. Map the complete path from source to sink\n```\n\n### Pattern 3: Understanding Loop Structures\n\n```text\n1. Run PrintAST to find loop constructs (ForStmt, WhileStmt, etc.)\n2. Run PrintCFG on the containing function\n3. Identify back edges that represent loop iteration\n4. Use CFG analysis for loop-sensitive queries\n```\n\n## Troubleshooting\n\n| Issue | Likely Cause | Resolution |\n| --------------------- | ------------------------------------- | ------------------------------------------------------ |\n| Empty AST output | `sourceFiles` parameter not matching | Use just filenames, not full paths (e.g., `"test.js"`) |\n| Empty CFG output | `sourceFunction` not found | Check exact function name spelling |\n| Empty CallGraph | No calls exist or wrong function name | Verify function exists and has calls |\n| Query compilation err | Pack dependencies missing | Run #codeql_pack_install on the tools pack |\n\n## MCP Tools Reference\n\n| Tool | Purpose |\n| ------------------------ | ---------------------------------------------------- |\n| #codeql_query_run | Execute tools queries with parameters |\n| #codeql_resolve_database | Validate database before querying |\n| #codeql_database_create | Create database from source code |\n| #codeql_bqrs_interpret | Convert results to different formats |\n| #codeql_pack_install | Install pack dependencies if needed |\n| #codeql_lsp_completion | Explore available types after seeing AST class names |\n| #codeql_lsp_definition | Navigate to class definitions to see predicates |\n| #codeql_lsp_references | Find usage examples of a class or predicate |\n| #search_ql_code | Search QL source files for patterns (text or regex) |\n| #codeql_resolve_files | Find QL files by name, extension, or glob pattern |\n\n### Using LSP Tools After AST Analysis\n\nAfter running PrintAST and identifying relevant AST class names, use the LSP tools\nto explore those classes in your query file:\n\n1. **Write the class name** in your query\'s `from` clause and save the file\n2. **Run #codeql_lsp_completion** after the dot to see member predicates:\n - `file_path`: your query file, `line`/`character`: 0-based position after the dot\n - `workspace_uri`: the pack root directory (containing `codeql-pack.yml`)\n3. **Run #codeql_lsp_definition** on an AST class name to see its full API\n4. **Run #codeql_lsp_references** to find usage examples in the pack\n\n> **Note**: LSP tools use 0-based line/character positions. Run #codeql_pack_install\n> before using them \u2014 they require resolved dependencies. Set `workspace_uri` to\n> a plain directory path (not a `file://` URI).\n\nFor the full iterative LSP development workflow, see: `codeql://prompts/ql_lsp_iterative_development`\n';
|
|
@@ -191746,6 +192458,7 @@ var workshop_creation_workflow_prompt_default = '---\nagent: agent\n---\n\n# Cre
|
|
|
191746
192458
|
// src/prompts/prompt-loader.ts
|
|
191747
192459
|
var PROMPT_TEMPLATES = {
|
|
191748
192460
|
"check-for-duplicated-code.prompt.md": check_for_duplicated_code_prompt_default,
|
|
192461
|
+
"compare-overlapping-alerts.prompt.md": compare_overlapping_alerts_prompt_default,
|
|
191749
192462
|
"document-codeql-query.prompt.md": document_codeql_query_prompt_default,
|
|
191750
192463
|
"explain-codeql-query.prompt.md": explain_codeql_query_prompt_default,
|
|
191751
192464
|
"find-overlapping-queries.prompt.md": find_overlapping_queries_prompt_default,
|
|
@@ -191916,6 +192629,13 @@ var checkForDuplicatedCodeSchema = external_exports.object({
|
|
|
191916
192629
|
queryPath: external_exports.string().describe("Path to the .ql or .qll file to audit for duplicated definitions"),
|
|
191917
192630
|
workspaceUri: external_exports.string().optional().describe("Pack root directory containing codeql-pack.yml (for LSP resolution)")
|
|
191918
192631
|
});
|
|
192632
|
+
var compareOverlappingAlertsSchema = external_exports.object({
|
|
192633
|
+
databasePath: external_exports.string().optional().describe("Path to the CodeQL database for reading source code context"),
|
|
192634
|
+
ruleIdA: external_exports.string().optional().describe('First CodeQL query @id / SARIF rule ID (e.g. "js/sql-injection"). Omit to compare all rules.'),
|
|
192635
|
+
ruleIdB: external_exports.string().optional().describe('Second CodeQL query @id / SARIF rule ID (e.g. "js/cap-sql-injection"). Omit to compare all rules.'),
|
|
192636
|
+
sarifPathA: external_exports.string().describe("Path to the first SARIF file (or the only file when comparing within one file)"),
|
|
192637
|
+
sarifPathB: external_exports.string().optional().describe("Path to the second SARIF file (for cross-run or cross-database comparison)")
|
|
192638
|
+
});
|
|
191919
192639
|
var findOverlappingQueriesSchema = external_exports.object({
|
|
191920
192640
|
queryDescription: external_exports.string().describe(
|
|
191921
192641
|
`Description of the new query's purpose and target constructs (e.g. "detect placement-new on types with non-trivial destructors")`
|
|
@@ -192011,6 +192731,7 @@ Please check your inputs and try again.`
|
|
|
192011
192731
|
}
|
|
192012
192732
|
var WORKFLOW_PROMPT_NAMES = [
|
|
192013
192733
|
"check_for_duplicated_code",
|
|
192734
|
+
"compare_overlapping_alerts",
|
|
192014
192735
|
"document_codeql_query",
|
|
192015
192736
|
"explain_codeql_query",
|
|
192016
192737
|
"find_overlapping_queries",
|
|
@@ -192493,6 +193214,56 @@ ${workspaceUri ? `- **Workspace URI**: ${workspaceUri}
|
|
|
192493
193214
|
}
|
|
192494
193215
|
)
|
|
192495
193216
|
);
|
|
193217
|
+
server.prompt(
|
|
193218
|
+
"compare_overlapping_alerts",
|
|
193219
|
+
"Compare CodeQL SARIF alerts across rules, files, runs, databases, or CodeQL versions. Detect overlap, redundancy, and behavioral deviations.",
|
|
193220
|
+
toPermissiveShape(compareOverlappingAlertsSchema.shape),
|
|
193221
|
+
createSafePromptHandler(
|
|
193222
|
+
"compare_overlapping_alerts",
|
|
193223
|
+
compareOverlappingAlertsSchema,
|
|
193224
|
+
async ({ sarifPathA, sarifPathB, ruleIdA, ruleIdB, databasePath }) => {
|
|
193225
|
+
const template = loadPromptTemplate("compare-overlapping-alerts.prompt.md");
|
|
193226
|
+
const content = processPromptTemplate(template, {
|
|
193227
|
+
sarifPathA,
|
|
193228
|
+
sarifPathB: sarifPathB || sarifPathA,
|
|
193229
|
+
ruleIdA: ruleIdA || "<all-rules>",
|
|
193230
|
+
ruleIdB: ruleIdB || "<all-rules>",
|
|
193231
|
+
databasePath: databasePath || "<database-path>"
|
|
193232
|
+
});
|
|
193233
|
+
let contextSection = "## Alert Comparison Context\n\n";
|
|
193234
|
+
contextSection += `- **SARIF Path A**: ${sarifPathA}
|
|
193235
|
+
`;
|
|
193236
|
+
if (sarifPathB) {
|
|
193237
|
+
contextSection += `- **SARIF Path B**: ${sarifPathB}
|
|
193238
|
+
`;
|
|
193239
|
+
}
|
|
193240
|
+
if (ruleIdA) {
|
|
193241
|
+
contextSection += `- **Rule A**: ${ruleIdA}
|
|
193242
|
+
`;
|
|
193243
|
+
}
|
|
193244
|
+
if (ruleIdB) {
|
|
193245
|
+
contextSection += `- **Rule B**: ${ruleIdB}
|
|
193246
|
+
`;
|
|
193247
|
+
}
|
|
193248
|
+
if (databasePath) {
|
|
193249
|
+
contextSection += `- **Database Path**: ${databasePath}
|
|
193250
|
+
`;
|
|
193251
|
+
}
|
|
193252
|
+
contextSection += "\n";
|
|
193253
|
+
return {
|
|
193254
|
+
messages: [
|
|
193255
|
+
{
|
|
193256
|
+
role: "user",
|
|
193257
|
+
content: {
|
|
193258
|
+
type: "text",
|
|
193259
|
+
text: contextSection + content
|
|
193260
|
+
}
|
|
193261
|
+
}
|
|
193262
|
+
]
|
|
193263
|
+
};
|
|
193264
|
+
}
|
|
193265
|
+
)
|
|
193266
|
+
);
|
|
192496
193267
|
logger.info(`Registered ${WORKFLOW_PROMPT_NAMES.length} workflow prompts`);
|
|
192497
193268
|
}
|
|
192498
193269
|
function buildToolsQueryContext(language, database, sourceFiles, sourceFunction, targetFunction) {
|
|
@@ -193667,16 +194438,35 @@ function registerAuditListFindingsTool(server) {
|
|
|
193667
194438
|
function registerAuditAddNotesTool(server) {
|
|
193668
194439
|
server.tool(
|
|
193669
194440
|
"audit_add_notes",
|
|
193670
|
-
"Append notes to an existing audit finding.
|
|
194441
|
+
"Append notes to an existing audit finding. Identify the finding by findingId (preferred) or by owner+repo+sourceLocation+line.",
|
|
193671
194442
|
{
|
|
193672
|
-
|
|
193673
|
-
|
|
193674
|
-
|
|
193675
|
-
|
|
194443
|
+
findingId: external_exports.number().int().positive().optional().describe("Annotation ID of the finding (returned by audit_store_findings and audit_list_findings). Preferred lookup method."),
|
|
194444
|
+
owner: external_exports.string().optional().describe("Repository owner (required when findingId is not provided)."),
|
|
194445
|
+
repo: external_exports.string().optional().describe("Repository name (required when findingId is not provided)."),
|
|
194446
|
+
sourceLocation: external_exports.string().optional().describe("File path of the finding (required when findingId is not provided)."),
|
|
194447
|
+
line: external_exports.number().int().min(1).optional().describe("Line number of the finding (required when findingId is not provided)."),
|
|
193676
194448
|
notes: external_exports.string().describe("Notes to append.")
|
|
193677
194449
|
},
|
|
193678
|
-
async ({ owner, repo, sourceLocation, line, notes }) => {
|
|
194450
|
+
async ({ findingId, owner, repo, sourceLocation, line, notes }) => {
|
|
193679
194451
|
const store = sessionDataManager.getStore();
|
|
194452
|
+
if (findingId != null) {
|
|
194453
|
+
const annotation2 = store.getAnnotation(findingId);
|
|
194454
|
+
if (!annotation2 || annotation2.category !== AUDIT_CATEGORY) {
|
|
194455
|
+
return {
|
|
194456
|
+
content: [{ type: "text", text: `No audit finding found with id ${findingId}.` }]
|
|
194457
|
+
};
|
|
194458
|
+
}
|
|
194459
|
+
const updatedContent2 = (annotation2.content || "") + "\n" + notes;
|
|
194460
|
+
store.updateAnnotation(annotation2.id, { content: updatedContent2.trim() });
|
|
194461
|
+
return {
|
|
194462
|
+
content: [{ type: "text", text: `Updated notes for finding id ${findingId}.` }]
|
|
194463
|
+
};
|
|
194464
|
+
}
|
|
194465
|
+
if (!owner || !repo || !sourceLocation || !line) {
|
|
194466
|
+
return {
|
|
194467
|
+
content: [{ type: "text", text: "Either findingId or all of owner+repo+sourceLocation+line must be provided." }]
|
|
194468
|
+
};
|
|
194469
|
+
}
|
|
193680
194470
|
const entityKey = `${repoKey(owner, repo)}:${sourceLocation}:L${line}`;
|
|
193681
194471
|
const existing = store.listAnnotations({
|
|
193682
194472
|
category: AUDIT_CATEGORY,
|
|
@@ -193742,11 +194532,12 @@ function registerQueryResultsCacheLookupTool(server) {
|
|
|
193742
194532
|
{
|
|
193743
194533
|
cacheKey: external_exports.string().optional().describe("Look up by exact cache key (if known)."),
|
|
193744
194534
|
queryName: external_exports.string().optional().describe('Query name to search for (e.g. "PrintAST", "CallGraphFrom").'),
|
|
194535
|
+
ruleId: external_exports.string().optional().describe('Filter by CodeQL query @id (e.g. "js/sql-injection"). The @id is the most reliable identifier.'),
|
|
193745
194536
|
databasePath: external_exports.string().optional().describe("Database path to search for."),
|
|
193746
194537
|
language: external_exports.string().optional().describe('Filter by language (e.g. "cpp", "javascript").'),
|
|
193747
194538
|
limit: external_exports.number().int().positive().max(500).optional().describe("Maximum number of cache entries to return when listing by filter (default: 50, max: 500).")
|
|
193748
194539
|
},
|
|
193749
|
-
async ({ cacheKey: cacheKey2, queryName, databasePath, language, limit }) => {
|
|
194540
|
+
async ({ cacheKey: cacheKey2, queryName, ruleId, databasePath, language, limit }) => {
|
|
193750
194541
|
const store = sessionDataManager.getStore();
|
|
193751
194542
|
if (cacheKey2) {
|
|
193752
194543
|
const meta = store.getCacheEntryMeta(cacheKey2);
|
|
@@ -193755,9 +194546,9 @@ function registerQueryResultsCacheLookupTool(server) {
|
|
|
193755
194546
|
}
|
|
193756
194547
|
return { content: [{ type: "text", text: JSON.stringify({ cached: false, cacheKey: cacheKey2 }) }] };
|
|
193757
194548
|
}
|
|
193758
|
-
const entries = store.listCacheEntries({ queryName, databasePath, language, limit: limit ?? 50 });
|
|
194549
|
+
const entries = store.listCacheEntries({ queryName, ruleId, databasePath, language, limit: limit ?? 50 });
|
|
193759
194550
|
if (entries.length === 0) {
|
|
193760
|
-
return { content: [{ type: "text", text: JSON.stringify({ cached: false, queryName, databasePath, language }) }] };
|
|
194551
|
+
return { content: [{ type: "text", text: JSON.stringify({ cached: false, queryName, ruleId, databasePath, language }) }] };
|
|
193761
194552
|
}
|
|
193762
194553
|
return {
|
|
193763
194554
|
content: [{
|
|
@@ -193846,15 +194637,16 @@ function registerQueryResultsCacheClearTool(server) {
|
|
|
193846
194637
|
{
|
|
193847
194638
|
cacheKey: external_exports.string().optional().describe("Clear a specific cache entry."),
|
|
193848
194639
|
queryName: external_exports.string().optional().describe("Clear all entries for this query name."),
|
|
194640
|
+
ruleId: external_exports.string().optional().describe("Clear all entries for this CodeQL query @id."),
|
|
193849
194641
|
databasePath: external_exports.string().optional().describe("Clear all entries for this database."),
|
|
193850
194642
|
all: external_exports.boolean().optional().describe("Clear the entire query results cache.")
|
|
193851
194643
|
},
|
|
193852
|
-
async ({ cacheKey: cacheKey2, queryName, databasePath, all }) => {
|
|
193853
|
-
if (!cacheKey2 && !queryName && !databasePath && !all) {
|
|
193854
|
-
return { content: [{ type: "text", text: "At least one filter (cacheKey, queryName, databasePath, or all) is required." }] };
|
|
194644
|
+
async ({ cacheKey: cacheKey2, queryName, ruleId, databasePath, all }) => {
|
|
194645
|
+
if (!cacheKey2 && !queryName && !ruleId && !databasePath && !all) {
|
|
194646
|
+
return { content: [{ type: "text", text: "At least one filter (cacheKey, queryName, ruleId, databasePath, or all) is required." }] };
|
|
193855
194647
|
}
|
|
193856
194648
|
const store = sessionDataManager.getStore();
|
|
193857
|
-
const cleared = store.clearCacheEntries({ cacheKey: cacheKey2, queryName, databasePath, all: all ?? false });
|
|
194649
|
+
const cleared = store.clearCacheEntries({ cacheKey: cacheKey2, queryName, ruleId, databasePath, all: all ?? false });
|
|
193858
194650
|
return { content: [{ type: "text", text: `Cleared ${cleared} cached query result(s).` }] };
|
|
193859
194651
|
}
|
|
193860
194652
|
);
|
|
@@ -193864,14 +194656,19 @@ function registerQueryResultsCacheCompareTool(server) {
|
|
|
193864
194656
|
"query_results_cache_compare",
|
|
193865
194657
|
"Compare cached query results across multiple databases for the same query. Useful for MRVA-style cross-repository analysis.",
|
|
193866
194658
|
{
|
|
193867
|
-
queryName: external_exports.string().describe("The query name to compare across databases."),
|
|
194659
|
+
queryName: external_exports.string().optional().describe("The query name to compare across databases."),
|
|
194660
|
+
ruleId: external_exports.string().optional().describe("The CodeQL query @id to compare across databases (preferred over queryName)."),
|
|
193868
194661
|
language: external_exports.string().optional().describe("Filter by language.")
|
|
193869
194662
|
},
|
|
193870
|
-
async ({ queryName, language }) => {
|
|
194663
|
+
async ({ queryName, ruleId, language }) => {
|
|
194664
|
+
if (!queryName && !ruleId) {
|
|
194665
|
+
return { content: [{ type: "text", text: "Either queryName or ruleId is required." }] };
|
|
194666
|
+
}
|
|
193871
194667
|
const store = sessionDataManager.getStore();
|
|
193872
|
-
const entries = store.listCacheEntries({ queryName, language });
|
|
194668
|
+
const entries = store.listCacheEntries({ queryName, ruleId, language });
|
|
193873
194669
|
if (entries.length === 0) {
|
|
193874
|
-
|
|
194670
|
+
const identifier = ruleId ?? queryName;
|
|
194671
|
+
return { content: [{ type: "text", text: `No cached results found for "${identifier}".` }] };
|
|
193875
194672
|
}
|
|
193876
194673
|
const byDatabase = /* @__PURE__ */ new Map();
|
|
193877
194674
|
for (const entry of entries) {
|
|
@@ -193879,19 +194676,25 @@ function registerQueryResultsCacheCompareTool(server) {
|
|
|
193879
194676
|
if (!byDatabase.has(key)) byDatabase.set(key, []);
|
|
193880
194677
|
byDatabase.get(key).push(entry);
|
|
193881
194678
|
}
|
|
193882
|
-
const comparison = Array.from(byDatabase.entries()).map(([db, dbEntries]) =>
|
|
193883
|
-
|
|
193884
|
-
|
|
193885
|
-
|
|
193886
|
-
|
|
193887
|
-
|
|
193888
|
-
|
|
193889
|
-
|
|
194679
|
+
const comparison = Array.from(byDatabase.entries()).map(([db, dbEntries]) => {
|
|
194680
|
+
const latestEntry = dbEntries[0];
|
|
194681
|
+
const resultCount = latestEntry.resultCount ?? 0;
|
|
194682
|
+
return {
|
|
194683
|
+
database: db,
|
|
194684
|
+
languages: [...new Set(dbEntries.map((e) => e.language))],
|
|
194685
|
+
formats: [...new Set(dbEntries.map((e) => e.outputFormat))],
|
|
194686
|
+
resultCount,
|
|
194687
|
+
totalResultCount: resultCount,
|
|
194688
|
+
cachedRuns: dbEntries.length,
|
|
194689
|
+
latestCachedAt: dbEntries[0].createdAt
|
|
194690
|
+
};
|
|
194691
|
+
});
|
|
193890
194692
|
return {
|
|
193891
194693
|
content: [{
|
|
193892
194694
|
type: "text",
|
|
193893
194695
|
text: JSON.stringify({
|
|
193894
|
-
queryName,
|
|
194696
|
+
queryName: queryName ?? null,
|
|
194697
|
+
ruleId: ruleId ?? null,
|
|
193895
194698
|
databases: comparison.length,
|
|
193896
194699
|
comparison
|
|
193897
194700
|
}, null, 2)
|
|
@@ -193901,6 +194704,264 @@ function registerQueryResultsCacheCompareTool(server) {
|
|
|
193901
194704
|
);
|
|
193902
194705
|
}
|
|
193903
194706
|
|
|
194707
|
+
// src/tools/sarif-tools.ts
|
|
194708
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
194709
|
+
init_logger();
|
|
194710
|
+
function registerSarifTools(server) {
|
|
194711
|
+
const config2 = sessionDataManager.getConfig();
|
|
194712
|
+
if (!config2.enableAnnotationTools) {
|
|
194713
|
+
logger.info(
|
|
194714
|
+
"SARIF tools are disabled (opt-in). Set ENABLE_ANNOTATION_TOOLS=true to enable sarif_* tools."
|
|
194715
|
+
);
|
|
194716
|
+
return;
|
|
194717
|
+
}
|
|
194718
|
+
registerSarifExtractRuleTool(server);
|
|
194719
|
+
registerSarifListRulesTool(server);
|
|
194720
|
+
registerSarifRuleToMarkdownTool(server);
|
|
194721
|
+
registerSarifCompareAlertsTool(server);
|
|
194722
|
+
registerSarifDiffRunsTool(server);
|
|
194723
|
+
logger.info("Registered SARIF analysis tools");
|
|
194724
|
+
}
|
|
194725
|
+
function loadSarif(sarifPath, cacheKey2) {
|
|
194726
|
+
if (!sarifPath && !cacheKey2) {
|
|
194727
|
+
return { error: "Either sarifPath or cacheKey is required." };
|
|
194728
|
+
}
|
|
194729
|
+
let content;
|
|
194730
|
+
if (cacheKey2) {
|
|
194731
|
+
const store = sessionDataManager.getStore();
|
|
194732
|
+
const cached2 = store.getCacheContent(cacheKey2);
|
|
194733
|
+
if (!cached2) {
|
|
194734
|
+
return { error: `No cached content found for key: ${cacheKey2}` };
|
|
194735
|
+
}
|
|
194736
|
+
content = cached2;
|
|
194737
|
+
} else {
|
|
194738
|
+
try {
|
|
194739
|
+
content = readFileSync13(sarifPath, "utf8");
|
|
194740
|
+
} catch {
|
|
194741
|
+
return { error: `Failed to read SARIF file: ${sarifPath}` };
|
|
194742
|
+
}
|
|
194743
|
+
}
|
|
194744
|
+
try {
|
|
194745
|
+
const parsed = JSON.parse(content);
|
|
194746
|
+
if (!parsed || typeof parsed !== "object") {
|
|
194747
|
+
return { error: "Invalid SARIF: expected a JSON object." };
|
|
194748
|
+
}
|
|
194749
|
+
if (!Array.isArray(parsed.runs)) {
|
|
194750
|
+
return { error: 'Invalid SARIF: missing or invalid "runs" array.' };
|
|
194751
|
+
}
|
|
194752
|
+
if (parsed.runs.length === 0) {
|
|
194753
|
+
return { error: 'Invalid SARIF: "runs" array is empty.' };
|
|
194754
|
+
}
|
|
194755
|
+
const run = parsed.runs[0];
|
|
194756
|
+
if (!run.tool?.driver) {
|
|
194757
|
+
return { error: "Invalid SARIF: missing tool.driver in first run." };
|
|
194758
|
+
}
|
|
194759
|
+
return { sarif: parsed };
|
|
194760
|
+
} catch {
|
|
194761
|
+
return { error: "Failed to parse SARIF JSON." };
|
|
194762
|
+
}
|
|
194763
|
+
}
|
|
194764
|
+
function registerSarifExtractRuleTool(server) {
|
|
194765
|
+
server.tool(
|
|
194766
|
+
"sarif_extract_rule",
|
|
194767
|
+
"Extract all data for a specific rule/query from multi-rule SARIF. Returns a valid SARIF JSON subset with only the matching rule definition and results.",
|
|
194768
|
+
{
|
|
194769
|
+
cacheKey: external_exports.string().optional().describe("Cache key to read SARIF from (alternative to sarifPath)."),
|
|
194770
|
+
ruleId: external_exports.string().describe('The SARIF rule ID to extract (e.g. "js/sql-injection"). Corresponds to the CodeQL query @id.'),
|
|
194771
|
+
sarifPath: external_exports.string().optional().describe("Path to the SARIF file.")
|
|
194772
|
+
},
|
|
194773
|
+
async ({ sarifPath, cacheKey: cacheKey2, ruleId }) => {
|
|
194774
|
+
const loaded = loadSarif(sarifPath, cacheKey2);
|
|
194775
|
+
if (loaded.error) {
|
|
194776
|
+
return { content: [{ type: "text", text: loaded.error }] };
|
|
194777
|
+
}
|
|
194778
|
+
const extracted = extractRuleFromSarif(loaded.sarif, ruleId);
|
|
194779
|
+
const resultCount = extracted.runs[0]?.results?.length ?? 0;
|
|
194780
|
+
const ruleCount = extracted.runs[0]?.tool.driver.rules?.length ?? 0;
|
|
194781
|
+
if (resultCount === 0 && ruleCount === 0) {
|
|
194782
|
+
return {
|
|
194783
|
+
content: [{
|
|
194784
|
+
type: "text",
|
|
194785
|
+
text: `No results or rule definition found for ruleId "${ruleId}" in the SARIF data.`
|
|
194786
|
+
}]
|
|
194787
|
+
};
|
|
194788
|
+
}
|
|
194789
|
+
return {
|
|
194790
|
+
content: [{
|
|
194791
|
+
type: "text",
|
|
194792
|
+
text: JSON.stringify({
|
|
194793
|
+
ruleId,
|
|
194794
|
+
resultCount,
|
|
194795
|
+
extractedSarif: extracted
|
|
194796
|
+
}, null, 2)
|
|
194797
|
+
}]
|
|
194798
|
+
};
|
|
194799
|
+
}
|
|
194800
|
+
);
|
|
194801
|
+
}
|
|
194802
|
+
function registerSarifListRulesTool(server) {
|
|
194803
|
+
server.tool(
|
|
194804
|
+
"sarif_list_rules",
|
|
194805
|
+
"List all rules in a SARIF file with result counts, severity, precision, and tags. Essential for discovering available rules before extraction or comparison.",
|
|
194806
|
+
{
|
|
194807
|
+
cacheKey: external_exports.string().optional().describe("Cache key to read SARIF from (alternative to sarifPath)."),
|
|
194808
|
+
sarifPath: external_exports.string().optional().describe("Path to the SARIF file.")
|
|
194809
|
+
},
|
|
194810
|
+
async ({ sarifPath, cacheKey: cacheKey2 }) => {
|
|
194811
|
+
const loaded = loadSarif(sarifPath, cacheKey2);
|
|
194812
|
+
if (loaded.error) {
|
|
194813
|
+
return { content: [{ type: "text", text: loaded.error }] };
|
|
194814
|
+
}
|
|
194815
|
+
const rules = listSarifRules(loaded.sarif);
|
|
194816
|
+
return {
|
|
194817
|
+
content: [{
|
|
194818
|
+
type: "text",
|
|
194819
|
+
text: JSON.stringify({
|
|
194820
|
+
totalRules: rules.length,
|
|
194821
|
+
totalResults: rules.reduce((sum, r) => sum + r.resultCount, 0),
|
|
194822
|
+
rules
|
|
194823
|
+
}, null, 2)
|
|
194824
|
+
}]
|
|
194825
|
+
};
|
|
194826
|
+
}
|
|
194827
|
+
);
|
|
194828
|
+
}
|
|
194829
|
+
function registerSarifRuleToMarkdownTool(server) {
|
|
194830
|
+
server.tool(
|
|
194831
|
+
"sarif_rule_to_markdown",
|
|
194832
|
+
"Convert per-rule SARIF data to a structured markdown report with Mermaid dataflow diagrams. Renders dataflow paths as visual flowcharts.",
|
|
194833
|
+
{
|
|
194834
|
+
cacheKey: external_exports.string().optional().describe("Cache key to read SARIF from (alternative to sarifPath)."),
|
|
194835
|
+
ruleId: external_exports.string().describe('The rule ID to render (e.g. "js/sql-injection").'),
|
|
194836
|
+
sarifPath: external_exports.string().optional().describe("Path to the SARIF file.")
|
|
194837
|
+
},
|
|
194838
|
+
async ({ sarifPath, cacheKey: cacheKey2, ruleId }) => {
|
|
194839
|
+
const loaded = loadSarif(sarifPath, cacheKey2);
|
|
194840
|
+
if (loaded.error) {
|
|
194841
|
+
return { content: [{ type: "text", text: loaded.error }] };
|
|
194842
|
+
}
|
|
194843
|
+
const markdown = sarifRuleToMarkdown(loaded.sarif, ruleId);
|
|
194844
|
+
if (!markdown) {
|
|
194845
|
+
return {
|
|
194846
|
+
content: [{
|
|
194847
|
+
type: "text",
|
|
194848
|
+
text: `No results found for ruleId "${ruleId}" in the SARIF data.`
|
|
194849
|
+
}]
|
|
194850
|
+
};
|
|
194851
|
+
}
|
|
194852
|
+
return {
|
|
194853
|
+
content: [{
|
|
194854
|
+
type: "text",
|
|
194855
|
+
text: markdown
|
|
194856
|
+
}]
|
|
194857
|
+
};
|
|
194858
|
+
}
|
|
194859
|
+
);
|
|
194860
|
+
}
|
|
194861
|
+
function registerSarifCompareAlertsTool(server) {
|
|
194862
|
+
const alertSpecSchema = external_exports.object({
|
|
194863
|
+
cacheKey: external_exports.string().optional().describe("Cache key for the SARIF data."),
|
|
194864
|
+
resultIndex: external_exports.number().int().min(0).describe("0-based index of the result within the rule's results."),
|
|
194865
|
+
ruleId: external_exports.string().describe("The rule ID of the alert."),
|
|
194866
|
+
sarifPath: external_exports.string().optional().describe("Path to the SARIF file.")
|
|
194867
|
+
});
|
|
194868
|
+
server.tool(
|
|
194869
|
+
"sarif_compare_alerts",
|
|
194870
|
+
"Compare code locations of two SARIF alerts to detect overlap. Supports sink, source, any-location, and full-path comparison modes.",
|
|
194871
|
+
{
|
|
194872
|
+
alertA: alertSpecSchema.describe("First alert to compare."),
|
|
194873
|
+
alertB: alertSpecSchema.describe("Second alert to compare."),
|
|
194874
|
+
overlapMode: external_exports.enum(["sink", "source", "any-location", "full-path"]).optional().default("sink").describe('Comparison mode: "sink" (primary locations), "source" (first dataflow step), "any-location" (all locations), "full-path" (structural path similarity).')
|
|
194875
|
+
},
|
|
194876
|
+
async ({ alertA, alertB, overlapMode }) => {
|
|
194877
|
+
const loadedA = loadSarif(alertA.sarifPath, alertA.cacheKey);
|
|
194878
|
+
if (loadedA.error) {
|
|
194879
|
+
return { content: [{ type: "text", text: `Alert A: ${loadedA.error}` }] };
|
|
194880
|
+
}
|
|
194881
|
+
const loadedB = loadSarif(alertB.sarifPath, alertB.cacheKey);
|
|
194882
|
+
if (loadedB.error) {
|
|
194883
|
+
return { content: [{ type: "text", text: `Alert B: ${loadedB.error}` }] };
|
|
194884
|
+
}
|
|
194885
|
+
const extractedA = extractRuleFromSarif(loadedA.sarif, alertA.ruleId);
|
|
194886
|
+
const resultsA = extractedA.runs[0]?.results ?? [];
|
|
194887
|
+
if (alertA.resultIndex >= resultsA.length) {
|
|
194888
|
+
return { content: [{ type: "text", text: `Alert A: resultIndex ${alertA.resultIndex} out of range (${resultsA.length} results for rule "${alertA.ruleId}").` }] };
|
|
194889
|
+
}
|
|
194890
|
+
const extractedB = extractRuleFromSarif(loadedB.sarif, alertB.ruleId);
|
|
194891
|
+
const resultsB = extractedB.runs[0]?.results ?? [];
|
|
194892
|
+
if (alertB.resultIndex >= resultsB.length) {
|
|
194893
|
+
return { content: [{ type: "text", text: `Alert B: resultIndex ${alertB.resultIndex} out of range (${resultsB.length} results for rule "${alertB.ruleId}").` }] };
|
|
194894
|
+
}
|
|
194895
|
+
const resultA = resultsA[alertA.resultIndex];
|
|
194896
|
+
const resultB = resultsB[alertB.resultIndex];
|
|
194897
|
+
const overlap = computeLocationOverlap(resultA, resultB, overlapMode);
|
|
194898
|
+
const locA = resultA.locations?.[0]?.physicalLocation;
|
|
194899
|
+
const locB = resultB.locations?.[0]?.physicalLocation;
|
|
194900
|
+
const locStrA = locA ? `${locA.artifactLocation?.uri ?? "?"}:${locA.region?.startLine ?? "?"}` : "?";
|
|
194901
|
+
const locStrB = locB ? `${locB.artifactLocation?.uri ?? "?"}:${locB.region?.startLine ?? "?"}` : "?";
|
|
194902
|
+
const response = {
|
|
194903
|
+
overlaps: overlap.overlaps,
|
|
194904
|
+
overlapMode: overlap.overlapMode,
|
|
194905
|
+
alertA: {
|
|
194906
|
+
ruleId: alertA.ruleId,
|
|
194907
|
+
location: locStrA,
|
|
194908
|
+
message: resultA.message.text
|
|
194909
|
+
},
|
|
194910
|
+
alertB: {
|
|
194911
|
+
ruleId: alertB.ruleId,
|
|
194912
|
+
location: locStrB,
|
|
194913
|
+
message: resultB.message.text
|
|
194914
|
+
},
|
|
194915
|
+
sharedLocations: overlap.sharedLocations
|
|
194916
|
+
};
|
|
194917
|
+
if (overlap.pathSimilarity !== void 0) {
|
|
194918
|
+
response.pathSimilarity = overlap.pathSimilarity;
|
|
194919
|
+
}
|
|
194920
|
+
return {
|
|
194921
|
+
content: [{
|
|
194922
|
+
type: "text",
|
|
194923
|
+
text: JSON.stringify(response, null, 2)
|
|
194924
|
+
}]
|
|
194925
|
+
};
|
|
194926
|
+
}
|
|
194927
|
+
);
|
|
194928
|
+
}
|
|
194929
|
+
function registerSarifDiffRunsTool(server) {
|
|
194930
|
+
server.tool(
|
|
194931
|
+
"sarif_diff_runs",
|
|
194932
|
+
"Diff two SARIF files or cached results to find added, removed, and changed rules/results. Useful for comparing analysis across CodeQL versions, database updates, or query pack releases.",
|
|
194933
|
+
{
|
|
194934
|
+
cacheKeyA: external_exports.string().optional().describe("Cache key for the first (baseline) SARIF."),
|
|
194935
|
+
cacheKeyB: external_exports.string().optional().describe("Cache key for the second (comparison) SARIF."),
|
|
194936
|
+
labelA: external_exports.string().optional().describe('Label for the first run (e.g. "v2.20.3", "main-branch", "database-A").'),
|
|
194937
|
+
labelB: external_exports.string().optional().describe('Label for the second run (e.g. "v2.20.4", "feature-branch", "database-B").'),
|
|
194938
|
+
sarifPathA: external_exports.string().optional().describe("Path to the first (baseline) SARIF file."),
|
|
194939
|
+
sarifPathB: external_exports.string().optional().describe("Path to the second (comparison) SARIF file.")
|
|
194940
|
+
},
|
|
194941
|
+
async ({ sarifPathA, sarifPathB, cacheKeyA, cacheKeyB, labelA, labelB }) => {
|
|
194942
|
+
const loadedA = loadSarif(sarifPathA, cacheKeyA);
|
|
194943
|
+
if (loadedA.error) {
|
|
194944
|
+
return { content: [{ type: "text", text: `Run A: ${loadedA.error}` }] };
|
|
194945
|
+
}
|
|
194946
|
+
const loadedB = loadSarif(sarifPathB, cacheKeyB);
|
|
194947
|
+
if (loadedB.error) {
|
|
194948
|
+
return { content: [{ type: "text", text: `Run B: ${loadedB.error}` }] };
|
|
194949
|
+
}
|
|
194950
|
+
const diff = diffSarifRules(loadedA.sarif, loadedB.sarif);
|
|
194951
|
+
return {
|
|
194952
|
+
content: [{
|
|
194953
|
+
type: "text",
|
|
194954
|
+
text: JSON.stringify({
|
|
194955
|
+
labelA: labelA ?? "Run A",
|
|
194956
|
+
labelB: labelB ?? "Run B",
|
|
194957
|
+
...diff
|
|
194958
|
+
}, null, 2)
|
|
194959
|
+
}]
|
|
194960
|
+
};
|
|
194961
|
+
}
|
|
194962
|
+
);
|
|
194963
|
+
}
|
|
194964
|
+
|
|
193904
194965
|
// src/codeql-development-mcp-server.ts
|
|
193905
194966
|
init_cli_executor();
|
|
193906
194967
|
init_server_manager();
|
|
@@ -193908,7 +194969,7 @@ init_package_paths();
|
|
|
193908
194969
|
init_logger();
|
|
193909
194970
|
import_dotenv.default.config({ path: resolve14(packageRootDir, ".env"), quiet: true });
|
|
193910
194971
|
var PACKAGE_NAME = "codeql-development-mcp-server";
|
|
193911
|
-
var VERSION = "2.25.1-next.
|
|
194972
|
+
var VERSION = "2.25.1-next.2";
|
|
193912
194973
|
async function startServer(mode = "stdio") {
|
|
193913
194974
|
logger.info(`Starting CodeQL Development MCP McpServer v${VERSION} in ${mode} mode`);
|
|
193914
194975
|
const codeqlBinary = resolveCodeQLBinary();
|
|
@@ -193928,6 +194989,7 @@ async function startServer(mode = "stdio") {
|
|
|
193928
194989
|
registerAnnotationTools(server);
|
|
193929
194990
|
registerAuditTools(server);
|
|
193930
194991
|
registerCacheTools(server);
|
|
194992
|
+
registerSarifTools(server);
|
|
193931
194993
|
await sessionDataManager.initialize();
|
|
193932
194994
|
const manager = initServerManager();
|
|
193933
194995
|
Promise.all([
|
|
@@ -194005,7 +195067,7 @@ async function main() {
|
|
|
194005
195067
|
process.exit(1);
|
|
194006
195068
|
}
|
|
194007
195069
|
}
|
|
194008
|
-
var scriptPath = process.argv[1] ?
|
|
195070
|
+
var scriptPath = process.argv[1] ? realpathSync3(resolve14(process.argv[1])) : void 0;
|
|
194009
195071
|
if (scriptPath && import.meta.url === pathToFileURL5(scriptPath).href) {
|
|
194010
195072
|
main();
|
|
194011
195073
|
}
|