-
Notifications
You must be signed in to change notification settings - Fork 2
Prep for v2.24.1 release #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
55c767b
4187359
893217f
73e0d19
5cc92ef
fee78b9
00cf29b
41b3a77
314261e
d5789a1
d89dce7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 |
|---|---|---|
| @@ -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 |
|---|---|---|
| @@ -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() { | ||
data-douser marked this conversation as resolved.
Dismissed
Show dismissed
Hide dismissed
data-douser marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| var userInput = document.location.search; // source: URL query string | ||
| var container = document.getElementById("output"); | ||
| container.innerHTML = userInput; // sink: innerHTML assignment — XSS! | ||
Check failureCode scanning / CodeQL Client-side cross-site scripting High test
Cross-site scripting vulnerability due to
user-provided value Error loading related location Loading |
||
| } | ||
|
|
||
| // ============================================================ | ||
| // POSITIVE CASE 2: XSS via window.location.hash → document.write() | ||
| // ============================================================ | ||
| function positiveDocumentWrite() { | ||
data-douser marked this conversation as resolved.
Dismissed
Show dismissed
Hide dismissed
data-douser marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| var hashValue = window.location.hash; // source: URL hash fragment | ||
| document.write(hashValue); // sink: document.write — XSS! | ||
Check failureCode scanning / CodeQL Client-side cross-site scripting High test
Cross-site scripting vulnerability due to
user-provided value Error loading related location Loading |
||
data-douser marked this conversation as resolved.
Show resolved
Hide resolved
data-douser marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| // ============================================================ | ||
| // POSITIVE CASE 3: XSS via URL param parsed → outerHTML | ||
| // ============================================================ | ||
| function positiveOuterHTML() { | ||
data-douser marked this conversation as resolved.
Show resolved
Hide resolved
data-douser marked this conversation as resolved.
Dismissed
Show dismissed
Hide dismissed
|
||
| 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 failureCode scanning / CodeQL Client-side cross-site scripting High test
Cross-site scripting vulnerability due to
user-provided value Error loading related location Loading |
||
| } | ||
|
|
||
| // ============================================================ | ||
| // NEGATIVE CASE 1: Sanitized value via DOMPurify.sanitize() → innerHTML | ||
| // Should NOT be flagged because the input is sanitized | ||
| // ============================================================ | ||
| function negativeSanitized() { | ||
data-douser marked this conversation as resolved.
Show resolved
Hide resolved
github-code-quality[bot] marked this conversation as resolved.
Fixed
Show fixed
Hide fixed
|
||
| 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() { | ||
data-douser marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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 failureCode scanning / CodeQL Code injection Critical test
This code execution depends on a
user-provided value Error loading related location Loading |
||
| } | ||
|
|
||
| // ============================================================ | ||
| // NEGATIVE CASE 3: encodeURIComponent barrier | ||
| // Should NOT be flagged because input is encoded | ||
| // ============================================================ | ||
| function negativeEncoded() { | ||
data-douser marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| var userInput = document.location.search; | ||
| var encoded = encodeURIComponent(userInput); // barrier: encoding | ||
| var container = document.getElementById("encoded-output"); | ||
| container.innerHTML = encoded; // safe: encoded input | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.