Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .codeql-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v2.24.0
v2.24.1
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
applyTo: '**/*.prompt.md'
applyTo: '.github/prompts/*.prompt.md'
description: 'Instructions for managing .prompt.md files throughout the advanced-security/codeql-development-mcp-server repository.'
---

# Instructions for managing `*.prompt.md` files
# Instructions for managing `.github/prompts/*.prompt.md` files

## PURPOSE

Expand All @@ -27,13 +27,11 @@ That higher-level component should link (i.e. point) to at least one `.github/pr
- 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.
- ALWAYS check formatting with `npm run lint && npm run format:check` from the repo root directory to ensure consistent formatting after making changes.
- ALWAYS fix linting and formatting errors by running `npm run lint:fix && npm run format` from the repo root directory before committing changes.
- ALWAYS start each `*.prompt.md` file with the following YAML block of frontmatter:

```yaml
---
mode: agent
---
```
- ALWAYS start each `*.prompt.md` file with a YAML front-matter block containing, at minimum, values for fields such as:
- `agent` -> pointing to the name of the agent this prompt is intended for (e.g. `agent: ql-mcp-tool-tester`)
- `name` -> a unique name for the prompt (e.g. `name: validate-ql-mcp-tools-via-workshop`)
- `description` -> a concise description of the prompt's purpose and functionality
- `argument-hint` -> a brief hint about the expected arguments for the prompt

## PREFERENCES

Expand Down
44 changes: 44 additions & 0 deletions .github/instructions/server_src_prompts_md.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
applyTo: 'server/src/prompts/*.prompt.md'
description: 'Instructions for MCP-server-hosted workflow prompt files.'
---

# Copilot Instructions for `server/src/prompts/*.prompt.md` prompt files

## PURPOSE

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.

## REQUIREMENTS

- ALWAYS start each prompt file with the following YAML frontmatter block:

```yaml
---
agent: agent
---
```

Note: VS Code has deprecated `mode: agent` in favor of `agent: agent`.

- 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.
- ALWAYS ensure the prompt name in `WORKFLOW_PROMPT_NAMES` matches the registration.
- ALWAYS write prompts that work in **any environment** where `codeql` and the MCP server tools are available, including terminal-only environments without an IDE.
- ALWAYS use explicit, numeric tool parameters (e.g., `file_path`, `line`, `character`) instead of IDE-centric language like "position the cursor" or "click on".
- 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`).
- 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.
- **ALWAYS run `npm run tidy` from the repo root directory to apply (markdown) linting for all prompt files.**

## PREFERENCES

- PREFER referencing other prompts by their registered MCP name (e.g., `codeql://prompts/ql_lsp_iterative_development`) to enable cross-prompt navigation.
- PREFER including a "Worked Example" section showing concrete tool invocations with realistic parameter values.
- 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`).
- PREFER documenting tool limitations discovered through actual usage (e.g., `codeql_lsp_diagnostics` cannot resolve imports; `find_class_position` finds `class` only, not `module`).

## CONSTRAINTS

- NEVER leave any trailing whitespace on any line.
- NEVER use four backticks to nest one code block inside another.
- NEVER assume the calling LLM has access to an IDE, cursor, or editor UI.
- NEVER reference `file://` URIs for the `workspace_uri` parameter — use plain directory paths.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ agent: mcp-enabled-ql-workshop-developer
name: validate-ql-mcp-server-tools-via-workshop
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".'
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.'
model: Claude Opus 4.5 (copilot)
model: Claude Opus 4.6 (copilot)
---

# `validate-ql-mcp-server-tools-via-workshop` Prompt
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
# JavaScript XSS Taint-Tracking Workshop

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.

## Background

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:

- **Sources**: `RemoteFlowSource` and DOM-based XSS sources
- **Sinks**: HTML injection sinks (`innerHTML`, `document.write`, etc.)
- **Barriers**: Sanitizer functions (`encodeHTML`, `encodeJS`, etc.)
- **Path-problem**: Full taint path visualization

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.

## Prerequisites

- CodeQL CLI v2.23.9 or later
- `codeql/javascript-all` pack (installed automatically via `codeql pack install`)

## Setup

```bash
# Navigate to the workshop directory
cd .github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss

# Install dependencies for all packs
codeql pack install solutions
codeql pack install solutions-tests
codeql pack install exercises
codeql pack install exercises-tests

# Build test databases (optional — tests do this automatically)
chmod +x build-databases.sh
./build-databases.sh
```

## Workshop Structure

```text
exercises/ — Your workspace (incomplete queries to complete)
exercises-tests/ — Tests that validate your solutions
solutions/ — Reference solutions (don't peek!)
solutions-tests/ — Tests for the solutions
tests-common/ — Shared test source code
```

## Test Code Overview

The test file (`tests-common/test.js`) contains carefully crafted cases:

| Case | Function | Source | Sink | Expected |
| ---------- | ------------------------- | ------------------------------- | -------------------------------------- | ------------------------ |
| POSITIVE 1 | `positiveDirectInnerHTML` | `document.location.search` | `innerHTML` | Detected |
| POSITIVE 2 | `positiveDocumentWrite` | `window.location.hash` | `document.write()` | Detected |
| POSITIVE 3 | `positiveOuterHTML` | URL param via `URLSearchParams` | `outerHTML` | Detected |
| NEGATIVE 1 | `negativeSanitized` | `document.location.search` | `innerHTML` (via `DOMPurify.sanitize`) | NOT detected (barrier) |
| NEGATIVE 2 | `negativeHardcoded` | hardcoded string | `innerHTML` | NOT detected (no source) |
| EDGE CASE | `edgeCaseEval` | `window.location.hash` | `eval()` | Detected |
| NEGATIVE 3 | `negativeEncoded` | `document.location.search` | `innerHTML` (via `encodeURIComponent`) | NOT detected (barrier) |

## Exercises

Each exercise builds on the previous one, progressively teaching more advanced CodeQL concepts.

### Exercise 1: Find XSS Sinks

**Goal**: Write a query that identifies dangerous DOM operations that could introduce XSS.

**Concepts**: `DataFlow::Node`, `DataFlow::PropWrite`, `DataFlow::CallNode`, `DataFlow::globalVarRef`

**What you'll find**:

- Property writes to `innerHTML` and `outerHTML`
- Calls to `document.write()`
- Calls to `eval()`

**Validate**:

```bash
codeql test run exercises-tests/Exercise1
```

### Exercise 2: Find Remote Flow Sources

**Goal**: Identify all user-controlled inputs that could be XSS sources.

**Concepts**: `RemoteFlowSource`, `getSourceType()`

**What you'll find**:

- `document.location.search`, `window.location.hash`
- URL parameters via `URLSearchParams`
- Any other browser API that returns user-controlled data

**Validate**:

```bash
codeql test run exercises-tests/Exercise2
```

### Exercise 3: Basic Taint-Tracking Configuration

**Goal**: Connect sources to sinks using CodeQL's taint-tracking framework.

**Concepts**: `DataFlow::ConfigSig`, `TaintTracking::Global`, `isSource`, `isSink`, `flow()`

**Key pattern**:

```ql
module MyConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { ... }
predicate isSink(DataFlow::Node sink) { ... }
}
module MyFlow = TaintTracking::Global<MyConfig>;
```

This exercise will find ALL XSS flows including the sanitized ones (no barriers yet).

**Validate**:

```bash
codeql test run exercises-tests/Exercise3
```

### Exercise 4: Add Sanitizer Barriers

**Goal**: Reduce false positives by adding barriers for sanitizer functions.

**Concepts**: `isBarrier` predicate, `DataFlow::CallNode`, `getCalleeName()`

**What changes**: Flows through `DOMPurify.sanitize()` and `encodeURIComponent()` are blocked.

**Validate**:

```bash
codeql test run exercises-tests/Exercise4
```

### Exercise 5: Full Path-Problem Query

**Goal**: Convert the query to a production-style `path-problem` query with full taint paths.

**Concepts**: `@kind path-problem`, `PathGraph`, `PathNode`, `flowPath()`

**What changes**:

- Query metadata includes `@kind path-problem`, `@security-severity`, CWE tags
- Uses `PathNode` instead of `Node` for source/sink
- Imports `PathGraph` for visualization
- Shows complete taint propagation paths in results

**Validate**:

```bash
codeql test run exercises-tests/Exercise5
```

## Validating Solutions

```bash
# Run all solution tests (should pass after accepting results)
codeql test run solutions-tests

# Run a specific exercise test
codeql test run solutions-tests/Exercise3

# Accept current results as expected baseline
codeql test run solutions-tests --learn
```

## Learning Path

```
Exercise 1 (Sinks)
Exercise 2 (Sources)
Exercise 3 (Taint Tracking) ← Core concept
Exercise 4 (Barriers) ← Reducing false positives
Exercise 5 (Path Problem) ← Production-ready query
```

## Relation to Production Query

| Workshop Concept | Production Query (`UI5Xss.ql`) |
| ---------------------------------------- | -------------------------------------------- |
| `RemoteFlowSource` | `RemoteFlowSource` + SAP-specific sources |
| `innerHTML`/`document.write` sinks | SAP UI5 HTML injection sinks |
| `sanitize`/`encodeURIComponent` barriers | `encodeHTML`/`encodeJS`/`encodeCSS` barriers |
| `DataFlow::ConfigSig` | Same pattern |
| `path-problem` | Same query kind |

## Tips

- Start with Exercise 1 and work sequentially — each builds on the previous
- Use `codeql test run --learn` to accept current output as expected results
- If stuck, look at the solution for the PREVIOUS exercise as a starting point
- The `tests-common/test.js` file has comments explaining each test case
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/bin/bash
set -e

WORKSHOP_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

echo "Building test databases for SAP UI5 XSS workshop..."

# Build solution test databases
for exercise_dir in "${WORKSHOP_ROOT}"/solutions-tests/Exercise*/; do
exercise_name="$(basename "$exercise_dir")"
DB_PATH="${exercise_dir}/${exercise_name}.testproj"

echo " Creating database: ${exercise_name} (solutions)"
rm -rf "${DB_PATH}"

codeql test extract --search-path="${WORKSHOP_ROOT}/solutions" "$exercise_dir"
done

# Build exercise test databases
for exercise_dir in "${WORKSHOP_ROOT}"/exercises-tests/Exercise*/; do
exercise_name="$(basename "$exercise_dir")"
DB_PATH="${exercise_dir}/${exercise_name}.testproj"

echo " Creating database: ${exercise_name} (exercises)"
rm -rf "${DB_PATH}"

codeql test extract --search-path="${WORKSHOP_ROOT}/exercises" "$exercise_dir"
done

echo "Database creation complete!"
echo ""
echo "To run tests:"
echo " codeql test run solutions-tests # Validate solutions"
echo " codeql test run exercises-tests # Test your exercises"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
provide:
- '*/codeql-pack.yml'
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Exercise1.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Test cases for XSS taint-tracking workshop
// Inspired by SAP UI5 XSS production query patterns

// ============================================================
// POSITIVE CASE 1: Direct XSS via document.location.search → innerHTML
// ============================================================
function positiveDirectInnerHTML() {
var userInput = document.location.search; // source: URL query string
var container = document.getElementById("output");
container.innerHTML = userInput; // sink: innerHTML assignment — XSS!

Check failure

Code scanning / CodeQL

Client-side cross-site scripting High test

Cross-site scripting vulnerability due to
user-provided value
.
}

// ============================================================
// POSITIVE CASE 2: XSS via window.location.hash → document.write()
// ============================================================
function positiveDocumentWrite() {
var hashValue = window.location.hash; // source: URL hash fragment
document.write(hashValue); // sink: document.write — XSS!

Check failure

Code scanning / CodeQL

Client-side cross-site scripting High test

Cross-site scripting vulnerability due to
user-provided value
.
}

// ============================================================
// POSITIVE CASE 3: XSS via URL param parsed → outerHTML
// ============================================================
function positiveOuterHTML() {
var params = new URLSearchParams(window.location.search);
var name = params.get("name"); // source: user-controlled URL parameter
var el = document.getElementById("profile");
el.outerHTML = "<div>" + name + "</div>"; // sink: outerHTML assignment — XSS!

Check failure

Code scanning / CodeQL

Client-side cross-site scripting High test

Cross-site scripting vulnerability due to
user-provided value
.
}

// ============================================================
// NEGATIVE CASE 1: Sanitized value via DOMPurify.sanitize() → innerHTML
// Should NOT be flagged because the input is sanitized
// ============================================================
function negativeSanitized() {
var userInput = document.location.search; // source
var clean = DOMPurify.sanitize(userInput); // barrier: sanitization
var container = document.getElementById("safe-output");
container.innerHTML = clean; // safe: sanitized input
}

// ============================================================
// NEGATIVE CASE 2: Hardcoded string → innerHTML
// Should NOT be flagged because there is no user-controlled source
// ============================================================
function negativeHardcoded() {
var container = document.getElementById("static-content");
container.innerHTML = "<p>Welcome to the application</p>"; // safe: hardcoded
}

// ============================================================
// EDGE CASE: XSS via eval() with URL input
// ============================================================
function edgeCaseEval() {
var code = window.location.hash.substring(1); // source: URL hash
eval(code); // sink: eval — code injection / XSS!

Check failure

Code scanning / CodeQL

Code injection Critical test

This code execution depends on a
user-provided value
.
}

// ============================================================
// NEGATIVE CASE 3: encodeURIComponent barrier
// Should NOT be flagged because input is encoded
// ============================================================
function negativeEncoded() {
var userInput = document.location.search;
var encoded = encodeURIComponent(userInput); // barrier: encoding
var container = document.getElementById("encoded-output");
container.innerHTML = encoded; // safe: encoded input
}
Loading
Loading