Skip to content

Commit f1aa95a

Browse files
data-douserCopilot
andauthored
Prep for v2.24.1 release (#38)
* Fix release workflow: use setup-codeql-environment action instead of codeql-action/init Co-authored-by: data-douser <70299490+data-douser@users.noreply.github.com> * Add "fetch-tags" to release.yml workflow checkout * Upgrade codeql and repo version to 2.24.1 * Upgrade NodeJS dependencies to latest * Enhance MCP prompts with LSP tool guidance This commit: - updates 'server/src/prompts/*.prompt.md` files to provide better guidance to LLMs in relation to iterative and/or LSP-based tools; - improves unit tests of MCP ^ "workflow prompts"; - adds an example workshop, focused on using LSP-based MCP server tools, under the existing create-codeql-query-development-workshop agent skill; - updates .github/ instructions and prompts with lessons learned from tool validation via example workshop development. * improve release.yml workflow_dispatch compatibility * Improve prettier fix markdownlint required workflow * Fix tests flagged in PR review comments * Fix `release.yml` workflow for publishing CodeQL packs and npm package for `codeql-development-mcp-server` (#40) * Initial plan * Enable auto-creation of tags in release workflow for workflow_dispatch Co-authored-by: data-douser <70299490+data-douser@users.noreply.github.com> * More fixes for release workflow --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> * Add `advanced-security/ql-mcp-*-tools-src` query pack archives as uploaded release artifacts (#41) * Initial plan * Enable auto-creation of tags in release workflow for workflow_dispatch Co-authored-by: data-douser <70299490+data-douser@users.noreply.github.com> * More fixes for release workflow * Add CodeQL query packs as uploaded release artifacts --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> * Add version update script & fix packs for release (#42) --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
1 parent e21e634 commit f1aa95a

File tree

119 files changed

+3713
-498
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

119 files changed

+3713
-498
lines changed

.codeql-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v2.24.0
1+
v2.24.1

.github/instructions/prompts.instructions.md renamed to .github/instructions/github_prompts.instructions.md

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
---
2-
applyTo: '**/*.prompt.md'
2+
applyTo: '.github/prompts/*.prompt.md'
33
description: 'Instructions for managing .prompt.md files throughout the advanced-security/codeql-development-mcp-server repository.'
44
---
55

6-
# Instructions for managing `*.prompt.md` files
6+
# Instructions for managing `.github/prompts/*.prompt.md` files
77

88
## PURPOSE
99

@@ -27,13 +27,11 @@ That higher-level component should link (i.e. point) to at least one `.github/pr
2727
- ALWAYS follow best practices for writing markdown files, including proper use of headings, lists, links, and code blocks. This explicitly includes inserting a newline before and after code blocks, lists, and headings.
2828
- ALWAYS check formatting with `npm run lint && npm run format:check` from the repo root directory to ensure consistent formatting after making changes.
2929
- ALWAYS fix linting and formatting errors by running `npm run lint:fix && npm run format` from the repo root directory before committing changes.
30-
- ALWAYS start each `*.prompt.md` file with the following YAML block of frontmatter:
31-
32-
```yaml
33-
---
34-
mode: agent
35-
---
36-
```
30+
- ALWAYS start each `*.prompt.md` file with a YAML front-matter block containing, at minimum, values for fields such as:
31+
- `agent` -> pointing to the name of the agent this prompt is intended for (e.g. `agent: ql-mcp-tool-tester`)
32+
- `name` -> a unique name for the prompt (e.g. `name: validate-ql-mcp-tools-via-workshop`)
33+
- `description` -> a concise description of the prompt's purpose and functionality
34+
- `argument-hint` -> a brief hint about the expected arguments for the prompt
3735

3836
## PREFERENCES
3937

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
applyTo: 'server/src/prompts/*.prompt.md'
3+
description: 'Instructions for MCP-server-hosted workflow prompt files.'
4+
---
5+
6+
# Copilot Instructions for `server/src/prompts/*.prompt.md` prompt files
7+
8+
## PURPOSE
9+
10+
This file contains instructions for working with workflow prompt files in the `server/src/prompts/` directory. These prompts are registered by the MCP server (via `workflow-prompts.ts`) and exposed as slash commands in VS Code Copilot Chat.
11+
12+
## REQUIREMENTS
13+
14+
- ALWAYS start each prompt file with the following YAML frontmatter block:
15+
16+
```yaml
17+
---
18+
agent: agent
19+
---
20+
```
21+
22+
Note: VS Code has deprecated `mode: agent` in favor of `agent: agent`.
23+
24+
- ALWAYS ensure each prompt has a corresponding registration in `server/src/prompts/workflow-prompts.ts`, including a Zod parameter schema and a `server.prompt()` call.
25+
- ALWAYS ensure the prompt name in `WORKFLOW_PROMPT_NAMES` matches the registration.
26+
- ALWAYS write prompts that work in **any environment** where `codeql` and the MCP server tools are available, including terminal-only environments without an IDE.
27+
- ALWAYS use explicit, numeric tool parameters (e.g., `file_path`, `line`, `character`) instead of IDE-centric language like "position the cursor" or "click on".
28+
- ALWAYS document when MCP tools use **0-based** positions (all `codeql_lsp_*` tools) versus **1-based** positions (`find_predicate_position`, `find_class_position`, `read_file`).
29+
- ALWAYS note the `workspace_uri` requirement for LSP tools: it must be a **plain directory path** to the pack root containing `codeql-pack.yml`, not a `file://` URI.
30+
- **ALWAYS run `npm run tidy` from the repo root directory to apply (markdown) linting for all prompt files.**
31+
32+
## PREFERENCES
33+
34+
- PREFER referencing other prompts by their registered MCP name (e.g., `codeql://prompts/ql_lsp_iterative_development`) to enable cross-prompt navigation.
35+
- PREFER including a "Worked Example" section showing concrete tool invocations with realistic parameter values.
36+
- PREFER a validation tools comparison table when multiple tools can validate queries at different fidelity levels (e.g., `validate_codeql_query` vs `codeql_lsp_diagnostics` vs `codeql_query_compile`).
37+
- PREFER documenting tool limitations discovered through actual usage (e.g., `codeql_lsp_diagnostics` cannot resolve imports; `find_class_position` finds `class` only, not `module`).
38+
39+
## CONSTRAINTS
40+
41+
- NEVER leave any trailing whitespace on any line.
42+
- NEVER use four backticks to nest one code block inside another.
43+
- NEVER assume the calling LLM has access to an IDE, cursor, or editor UI.
44+
- NEVER reference `file://` URIs for the `workspace_uri` parameter — use plain directory paths.

.github/prompts/validate-ql-mcp-server-tools-via-workshop.prompt.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ agent: mcp-enabled-ql-workshop-developer
33
name: validate-ql-mcp-server-tools-via-workshop
44
description: 'A prompt for validating the real-world functionality of the CodeQL Development MCP Server tools by creating a CodeQL query development workshop from scratch using an existing, production-grade CodeQL query as the workshop "solution".'
55
argument-hint: 'Provide the absolute or relative path to a local ".ql" or ".qlref" file associated with a production-grade CodeQL query to be used as the "solution" for the last stage of the to-be-created workshop.'
6-
model: Claude Opus 4.5 (copilot)
6+
model: Claude Opus 4.6 (copilot)
77
---
88

99
# `validate-ql-mcp-server-tools-via-workshop` Prompt
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
# JavaScript XSS Taint-Tracking Workshop
2+
3+
A CodeQL query development workshop that teaches you to find client-side cross-site scripting (XSS) vulnerabilities using taint-tracking analysis. Inspired by a production SAP UI5 XSS query pattern.
4+
5+
## Background
6+
7+
This workshop is modeled after a real-world production query (`UI5Xss.ql`) that detects XSS vulnerabilities in SAP UI5 applications. The production query uses the `DataFlow::ConfigSig` pattern with:
8+
9+
- **Sources**: `RemoteFlowSource` and DOM-based XSS sources
10+
- **Sinks**: HTML injection sinks (`innerHTML`, `document.write`, etc.)
11+
- **Barriers**: Sanitizer functions (`encodeHTML`, `encodeJS`, etc.)
12+
- **Path-problem**: Full taint path visualization
13+
14+
Since the production query depends on custom SAP libraries, this workshop uses **only** `codeql/javascript-all` (version `2.6.19`) to teach the same core concepts with standard JavaScript APIs.
15+
16+
## Prerequisites
17+
18+
- CodeQL CLI v2.23.9 or later
19+
- `codeql/javascript-all` pack (installed automatically via `codeql pack install`)
20+
21+
## Setup
22+
23+
```bash
24+
# Navigate to the workshop directory
25+
cd .github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss
26+
27+
# Install dependencies for all packs
28+
codeql pack install solutions
29+
codeql pack install solutions-tests
30+
codeql pack install exercises
31+
codeql pack install exercises-tests
32+
33+
# Build test databases (optional — tests do this automatically)
34+
chmod +x build-databases.sh
35+
./build-databases.sh
36+
```
37+
38+
## Workshop Structure
39+
40+
```text
41+
exercises/ — Your workspace (incomplete queries to complete)
42+
exercises-tests/ — Tests that validate your solutions
43+
solutions/ — Reference solutions (don't peek!)
44+
solutions-tests/ — Tests for the solutions
45+
tests-common/ — Shared test source code
46+
```
47+
48+
## Test Code Overview
49+
50+
The test file (`tests-common/test.js`) contains carefully crafted cases:
51+
52+
| Case | Function | Source | Sink | Expected |
53+
| ---------- | ------------------------- | ------------------------------- | -------------------------------------- | ------------------------ |
54+
| POSITIVE 1 | `positiveDirectInnerHTML` | `document.location.search` | `innerHTML` | Detected |
55+
| POSITIVE 2 | `positiveDocumentWrite` | `window.location.hash` | `document.write()` | Detected |
56+
| POSITIVE 3 | `positiveOuterHTML` | URL param via `URLSearchParams` | `outerHTML` | Detected |
57+
| NEGATIVE 1 | `negativeSanitized` | `document.location.search` | `innerHTML` (via `DOMPurify.sanitize`) | NOT detected (barrier) |
58+
| NEGATIVE 2 | `negativeHardcoded` | hardcoded string | `innerHTML` | NOT detected (no source) |
59+
| EDGE CASE | `edgeCaseEval` | `window.location.hash` | `eval()` | Detected |
60+
| NEGATIVE 3 | `negativeEncoded` | `document.location.search` | `innerHTML` (via `encodeURIComponent`) | NOT detected (barrier) |
61+
62+
## Exercises
63+
64+
Each exercise builds on the previous one, progressively teaching more advanced CodeQL concepts.
65+
66+
### Exercise 1: Find XSS Sinks
67+
68+
**Goal**: Write a query that identifies dangerous DOM operations that could introduce XSS.
69+
70+
**Concepts**: `DataFlow::Node`, `DataFlow::PropWrite`, `DataFlow::CallNode`, `DataFlow::globalVarRef`
71+
72+
**What you'll find**:
73+
74+
- Property writes to `innerHTML` and `outerHTML`
75+
- Calls to `document.write()`
76+
- Calls to `eval()`
77+
78+
**Validate**:
79+
80+
```bash
81+
codeql test run exercises-tests/Exercise1
82+
```
83+
84+
### Exercise 2: Find Remote Flow Sources
85+
86+
**Goal**: Identify all user-controlled inputs that could be XSS sources.
87+
88+
**Concepts**: `RemoteFlowSource`, `getSourceType()`
89+
90+
**What you'll find**:
91+
92+
- `document.location.search`, `window.location.hash`
93+
- URL parameters via `URLSearchParams`
94+
- Any other browser API that returns user-controlled data
95+
96+
**Validate**:
97+
98+
```bash
99+
codeql test run exercises-tests/Exercise2
100+
```
101+
102+
### Exercise 3: Basic Taint-Tracking Configuration
103+
104+
**Goal**: Connect sources to sinks using CodeQL's taint-tracking framework.
105+
106+
**Concepts**: `DataFlow::ConfigSig`, `TaintTracking::Global`, `isSource`, `isSink`, `flow()`
107+
108+
**Key pattern**:
109+
110+
```ql
111+
module MyConfig implements DataFlow::ConfigSig {
112+
predicate isSource(DataFlow::Node source) { ... }
113+
predicate isSink(DataFlow::Node sink) { ... }
114+
}
115+
module MyFlow = TaintTracking::Global<MyConfig>;
116+
```
117+
118+
This exercise will find ALL XSS flows including the sanitized ones (no barriers yet).
119+
120+
**Validate**:
121+
122+
```bash
123+
codeql test run exercises-tests/Exercise3
124+
```
125+
126+
### Exercise 4: Add Sanitizer Barriers
127+
128+
**Goal**: Reduce false positives by adding barriers for sanitizer functions.
129+
130+
**Concepts**: `isBarrier` predicate, `DataFlow::CallNode`, `getCalleeName()`
131+
132+
**What changes**: Flows through `DOMPurify.sanitize()` and `encodeURIComponent()` are blocked.
133+
134+
**Validate**:
135+
136+
```bash
137+
codeql test run exercises-tests/Exercise4
138+
```
139+
140+
### Exercise 5: Full Path-Problem Query
141+
142+
**Goal**: Convert the query to a production-style `path-problem` query with full taint paths.
143+
144+
**Concepts**: `@kind path-problem`, `PathGraph`, `PathNode`, `flowPath()`
145+
146+
**What changes**:
147+
148+
- Query metadata includes `@kind path-problem`, `@security-severity`, CWE tags
149+
- Uses `PathNode` instead of `Node` for source/sink
150+
- Imports `PathGraph` for visualization
151+
- Shows complete taint propagation paths in results
152+
153+
**Validate**:
154+
155+
```bash
156+
codeql test run exercises-tests/Exercise5
157+
```
158+
159+
## Validating Solutions
160+
161+
```bash
162+
# Run all solution tests (should pass after accepting results)
163+
codeql test run solutions-tests
164+
165+
# Run a specific exercise test
166+
codeql test run solutions-tests/Exercise3
167+
168+
# Accept current results as expected baseline
169+
codeql test run solutions-tests --learn
170+
```
171+
172+
## Learning Path
173+
174+
```
175+
Exercise 1 (Sinks)
176+
177+
Exercise 2 (Sources)
178+
179+
Exercise 3 (Taint Tracking) ← Core concept
180+
181+
Exercise 4 (Barriers) ← Reducing false positives
182+
183+
Exercise 5 (Path Problem) ← Production-ready query
184+
```
185+
186+
## Relation to Production Query
187+
188+
| Workshop Concept | Production Query (`UI5Xss.ql`) |
189+
| ---------------------------------------- | -------------------------------------------- |
190+
| `RemoteFlowSource` | `RemoteFlowSource` + SAP-specific sources |
191+
| `innerHTML`/`document.write` sinks | SAP UI5 HTML injection sinks |
192+
| `sanitize`/`encodeURIComponent` barriers | `encodeHTML`/`encodeJS`/`encodeCSS` barriers |
193+
| `DataFlow::ConfigSig` | Same pattern |
194+
| `path-problem` | Same query kind |
195+
196+
## Tips
197+
198+
- Start with Exercise 1 and work sequentially — each builds on the previous
199+
- Use `codeql test run --learn` to accept current output as expected results
200+
- If stuck, look at the solution for the PREVIOUS exercise as a starting point
201+
- The `tests-common/test.js` file has comments explaining each test case
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/bin/bash
2+
set -e
3+
4+
WORKSHOP_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5+
6+
echo "Building test databases for SAP UI5 XSS workshop..."
7+
8+
# Build solution test databases
9+
for exercise_dir in "${WORKSHOP_ROOT}"/solutions-tests/Exercise*/; do
10+
exercise_name="$(basename "$exercise_dir")"
11+
DB_PATH="${exercise_dir}/${exercise_name}.testproj"
12+
13+
echo " Creating database: ${exercise_name} (solutions)"
14+
rm -rf "${DB_PATH}"
15+
16+
codeql test extract --search-path="${WORKSHOP_ROOT}/solutions" "$exercise_dir"
17+
done
18+
19+
# Build exercise test databases
20+
for exercise_dir in "${WORKSHOP_ROOT}"/exercises-tests/Exercise*/; do
21+
exercise_name="$(basename "$exercise_dir")"
22+
DB_PATH="${exercise_dir}/${exercise_name}.testproj"
23+
24+
echo " Creating database: ${exercise_name} (exercises)"
25+
rm -rf "${DB_PATH}"
26+
27+
codeql test extract --search-path="${WORKSHOP_ROOT}/exercises" "$exercise_dir"
28+
done
29+
30+
echo "Database creation complete!"
31+
echo ""
32+
echo "To run tests:"
33+
echo " codeql test run solutions-tests # Validate solutions"
34+
echo " codeql test run exercises-tests # Test your exercises"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
provide:
2+
- '*/codeql-pack.yml'

.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise1/Exercise1.expected

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Exercise1.ql
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Test cases for XSS taint-tracking workshop
2+
// Inspired by SAP UI5 XSS production query patterns
3+
4+
// ============================================================
5+
// POSITIVE CASE 1: Direct XSS via document.location.search → innerHTML
6+
// ============================================================
7+
function positiveDirectInnerHTML() {
8+
var userInput = document.location.search; // source: URL query string
9+
var container = document.getElementById("output");
10+
container.innerHTML = userInput; // sink: innerHTML assignment — XSS!
11+
}
12+
13+
// ============================================================
14+
// POSITIVE CASE 2: XSS via window.location.hash → document.write()
15+
// ============================================================
16+
function positiveDocumentWrite() {
17+
var hashValue = window.location.hash; // source: URL hash fragment
18+
document.write(hashValue); // sink: document.write — XSS!
19+
}
20+
21+
// ============================================================
22+
// POSITIVE CASE 3: XSS via URL param parsed → outerHTML
23+
// ============================================================
24+
function positiveOuterHTML() {
25+
var params = new URLSearchParams(window.location.search);
26+
var name = params.get("name"); // source: user-controlled URL parameter
27+
var el = document.getElementById("profile");
28+
el.outerHTML = "<div>" + name + "</div>"; // sink: outerHTML assignment — XSS!
29+
}
30+
31+
// ============================================================
32+
// NEGATIVE CASE 1: Sanitized value via DOMPurify.sanitize() → innerHTML
33+
// Should NOT be flagged because the input is sanitized
34+
// ============================================================
35+
function negativeSanitized() {
36+
var userInput = document.location.search; // source
37+
var clean = DOMPurify.sanitize(userInput); // barrier: sanitization
38+
var container = document.getElementById("safe-output");
39+
container.innerHTML = clean; // safe: sanitized input
40+
}
41+
42+
// ============================================================
43+
// NEGATIVE CASE 2: Hardcoded string → innerHTML
44+
// Should NOT be flagged because there is no user-controlled source
45+
// ============================================================
46+
function negativeHardcoded() {
47+
var container = document.getElementById("static-content");
48+
container.innerHTML = "<p>Welcome to the application</p>"; // safe: hardcoded
49+
}
50+
51+
// ============================================================
52+
// EDGE CASE: XSS via eval() with URL input
53+
// ============================================================
54+
function edgeCaseEval() {
55+
var code = window.location.hash.substring(1); // source: URL hash
56+
eval(code); // sink: eval — code injection / XSS!
57+
}
58+
59+
// ============================================================
60+
// NEGATIVE CASE 3: encodeURIComponent barrier
61+
// Should NOT be flagged because input is encoded
62+
// ============================================================
63+
function negativeEncoded() {
64+
var userInput = document.location.search;
65+
var encoded = encodeURIComponent(userInput); // barrier: encoding
66+
var container = document.getElementById("encoded-output");
67+
container.innerHTML = encoded; // safe: encoded input
68+
}

0 commit comments

Comments
 (0)