diff --git a/.gitignore b/.gitignore index 0ad853f..c387275 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,14 @@ models-output.json *.testproj trap +# Java test artifacts +.gradle/ +build/ +db/ +*.bqrs + +# Gradle wrapper (should be committed in real projects, but not needed for tests) +gradle/ +gradlew +gradlew.bat + diff --git a/UNDERTOW_IMPLEMENTATION_SUMMARY.md b/UNDERTOW_IMPLEMENTATION_SUMMARY.md new file mode 100644 index 0000000..156d44f --- /dev/null +++ b/UNDERTOW_IMPLEMENTATION_SUMMARY.md @@ -0,0 +1,174 @@ +# Undertow CodeQL Data Extension - Implementation Summary + +## Overview + +Successfully developed and tested a CodeQL data extension for the Undertow HTTP library in Java. The extension enables detection of Cross-Site Scripting (XSS) vulnerabilities in applications using the Undertow web server. + +## Implementation Location + +- **Model File**: `/languages/java/custom/src/undertow.model.yml` +- **Test Directory**: `/languages/java/custom/test/UndertowXSS/` + +## Models Implemented + +### 1. Remote Flow Source +- **Method**: `io.undertow.server.HttpServerExchange.getQueryParameters()` +- **Return Type**: `Map>` +- **Description**: Captures query parameters from HTTP requests +- **Classification**: Remote user input (untrusted data source) + +### 2. XSS Sinks (8 overloads) +All `io.undertow.io.Sender.send()` method overloads that write HTTP response content: +- `send(String)` +- `send(String, IoCallback)` +- `send(String, Charset)` +- `send(String, Charset, IoCallback)` +- `send(ByteBuffer)` +- `send(ByteBuffer, IoCallback)` +- `send(ByteBuffer[])` +- `send(ByteBuffer[], IoCallback)` + +## Key Fixes Applied + +### 1. Signature Format +**Problem**: Used JVM bytecode format `(Ljava/lang/String;)V` +**Solution**: Use CodeQL string signature format `(String)` + +### 2. Boolean Capitalization +**Problem**: Used Java-style `false` +**Solution**: Use YAML/Python-style `False` + +### 3. Sink Kind +**Problem**: Used generic `"xss"` +**Solution**: Use CodeQL-specific `"html-injection"` (required by `XssSink` class) + +### 4. Source Access Path +**Problem**: Used overly specific `"ReturnValue.MapValue.Element"` +**Solution**: Use simplified `"ReturnValue"` (CodeQL automatically handles Map values and Collection elements) + +## Test Results + +✅ **All tests passing** + +### Debug Sources Query +``` +| source | col1 | ++-------------------------+---------------------+ +| getQueryParameters(...) | Remote source found | +``` + +### Debug Sinks Query +``` +| sink | col1 | ++-----------+----------------+ +| ... + ... | XSS sink found | +``` + +### Main XSS Detection Query +``` +Detected 1 XSS vulnerability: +- Source: Line 17 - getQueryParameters() +- Sink: Line 22 - String concatenation in send() +- Path: Query parameter → local variable → string concatenation → HTTP response +``` + +## Vulnerability Detected in Test Code + +The test successfully identifies this XSS vulnerability: + +```java +// Line 17: Source - User input from query parameter +Deque res = exchange.getQueryParameters().get("namex"); +if (res != null) { + name = res.getFirst(); +} + +// Line 22: Sink - Unsanitized data in HTTP response +exchange.getResponseSender().send("Hello " + name + ""); +``` + +**Attack Vector**: An attacker can inject malicious JavaScript via the `namex` parameter: +``` +http://localhost:8080/?namex= +``` + +## Files Modified + +1. **languages/java/custom/src/undertow.model.yml** + - Fixed source model (1 entry) + - Fixed sink models (8 entries) + +2. **languages/java/custom/test/UndertowXSS/UndertowXSS.ql** + - Updated to use modern CodeQL module-based syntax + - Uses `RemoteFlowSource` and `XssSink` from data extensions + +3. **languages/java/custom/test/UndertowXSS/UndertowXSS.expected** + - Updated to match actual CodeQL output format + +## Files Created + +1. **languages/java/custom/test/UndertowXSS/README.md** + - Comprehensive documentation of the models and tests + +2. **languages/java/custom/test/UndertowXSS/DebugSources.ql** + - Debug query to verify source detection + +3. **languages/java/custom/test/UndertowXSS/DebugSinks.ql** + - Debug query to verify sink detection + +## Verification Commands + +```bash +# Navigate to test directory +cd languages/java/custom/test/UndertowXSS + +# Run main XSS detection query +codeql query run UndertowXSS.ql -d db/ --output=results.bqrs +codeql bqrs decode results.bqrs --format=text + +# Run debug queries +codeql query run DebugSources.ql -d db/ --output=sources.bqrs +codeql bqrs decode sources.bqrs --format=text + +codeql query run DebugSinks.ql -d db/ --output=sinks.bqrs +codeql bqrs decode sinks.bqrs --format=text +``` + +## Development Approach + +Followed Test-Driven Development (TDD) principles: + +1. **Red Phase**: Identified that existing models weren't working (no sources/sinks detected) +2. **Debug Phase**: Created debug queries to understand the problem +3. **Analysis Phase**: Examined method signatures and CodeQL internals +4. **Fix Phase**: Applied corrections to model format and parameters +5. **Green Phase**: Verified all queries return expected results +6. **Refactor Phase**: Cleaned up temporary debug files and added documentation + +## CodeQL Version + +- **CodeQL CLI**: 2.23.5 +- **Java Library**: codeql/java-all@7.7.3 + +## Security Impact + +This data extension enables CodeQL to automatically detect XSS vulnerabilities in Undertow-based applications, helping developers: +- Identify unsafe handling of user input +- Detect missing output encoding/sanitization +- Prevent injection attacks in HTTP responses +- Secure web applications at development time + +## Next Steps (Optional Enhancements) + +1. Add summary models for common sanitization methods +2. Add models for additional sources (cookies, headers, path parameters) +3. Add models for other security-sensitive sinks (redirects, SQL injection, etc.) +4. Create additional test cases covering edge cases +5. Submit models to CodeQL community repository + +## References + +- [Undertow Documentation](https://undertow.io/) +- [CodeQL Data Extensions](https://codeql.github.com/docs/codeql-language-guides/data-extensions/) +- [CodeQL XSS Query](https://github.com/github/codeql/blob/main/java/ql/src/Security/CWE/CWE-079/XSS.ql) +- [Model Format Specification](https://codeql.github.com/docs/codeql-cli/creating-codeql-databases/#model-format) diff --git a/languages/java/custom/src/qlpack.yml b/languages/java/custom/src/qlpack.yml index 04a1481..a69461e 100644 --- a/languages/java/custom/src/qlpack.yml +++ b/languages/java/custom/src/qlpack.yml @@ -3,3 +3,4 @@ version: 0.0.1 library: false dependencies: codeql/java-all: "*" +dataExtensions: "*.model.yml" diff --git a/languages/java/custom/src/undertow.model.yml b/languages/java/custom/src/undertow.model.yml new file mode 100644 index 0000000..8245429 --- /dev/null +++ b/languages/java/custom/src/undertow.model.yml @@ -0,0 +1,38 @@ +extensions: + - addsTo: + pack: codeql/java-all + extensible: sourceModel + data: + # HttpServerExchange.getQueryParameters() returns Map> + # The map values contain user-controlled query parameter values + # CodeQL automatically handles Map values and Collection elements + - ["io.undertow.server", "HttpServerExchange", False, "getQueryParameters", "()", "", "ReturnValue", "remote", "manual"] + + - addsTo: + pack: codeql/java-all + extensible: sinkModel + data: + # Sender.send(String) - sends response content, vulnerable to XSS + - ["io.undertow.io", "Sender", False, "send", "(String)", "", "Argument[0]", "html-injection", "manual"] + + # Sender.send(String, IoCallback) - sends response content, vulnerable to XSS + - ["io.undertow.io", "Sender", False, "send", "(String,IoCallback)", "", "Argument[0]", "html-injection", "manual"] + + # Sender.send(String, Charset) - sends response content, vulnerable to XSS + - ["io.undertow.io", "Sender", False, "send", "(String,Charset)", "", "Argument[0]", "html-injection", "manual"] + + # Sender.send(String, Charset, IoCallback) - sends response content, vulnerable to XSS + - ["io.undertow.io", "Sender", False, "send", "(String,Charset,IoCallback)", "", "Argument[0]", "html-injection", "manual"] + + # Sender.send(ByteBuffer) - sends response content, could be vulnerable to XSS + - ["io.undertow.io", "Sender", False, "send", "(ByteBuffer)", "", "Argument[0]", "html-injection", "manual"] + + # Sender.send(ByteBuffer, IoCallback) - sends response content, could be vulnerable to XSS + - ["io.undertow.io", "Sender", False, "send", "(ByteBuffer,IoCallback)", "", "Argument[0]", "html-injection", "manual"] + + # Sender.send(ByteBuffer[]) - sends response content, could be vulnerable to XSS + - ["io.undertow.io", "Sender", False, "send", "(ByteBuffer[])", "", "Argument[0]", "html-injection", "manual"] + + # Sender.send(ByteBuffer[], IoCallback) - sends response content, could be vulnerable to XSS + - ["io.undertow.io", "Sender", False, "send", "(ByteBuffer[],IoCallback)", "", "Argument[0]", "html-injection", "manual"] + diff --git a/languages/java/custom/test/UndertowXSS/DebugSinks.ql b/languages/java/custom/test/UndertowXSS/DebugSinks.ql new file mode 100644 index 0000000..8eddda3 --- /dev/null +++ b/languages/java/custom/test/UndertowXSS/DebugSinks.ql @@ -0,0 +1,13 @@ +/** + * @name Debug Undertow sinks + * @description Check if Undertow sinks are recognized + * @kind problem + * @id java/test/undertow-sinks-debug + */ + +import java +import semmle.code.java.security.XSS + +from XssSink sink +where sink.getLocation().getFile().getBaseName() = "UndertowExample.java" +select sink, "XSS sink found" diff --git a/languages/java/custom/test/UndertowXSS/DebugSources.ql b/languages/java/custom/test/UndertowXSS/DebugSources.ql new file mode 100644 index 0000000..6fa3281 --- /dev/null +++ b/languages/java/custom/test/UndertowXSS/DebugSources.ql @@ -0,0 +1,13 @@ +/** + * @name Debug Undertow sources + * @description Check if Undertow sources are recognized + * @kind problem + * @id java/test/undertow-sources-debug + */ + +import java +import semmle.code.java.dataflow.FlowSources + +from RemoteFlowSource source +where source.getLocation().getFile().getBaseName() = "UndertowExample.java" +select source, "Remote source found" diff --git a/languages/java/custom/test/UndertowXSS/README.md b/languages/java/custom/test/UndertowXSS/README.md new file mode 100644 index 0000000..0e53f1b --- /dev/null +++ b/languages/java/custom/test/UndertowXSS/README.md @@ -0,0 +1,101 @@ +# Undertow XSS Data Extension Tests + +This directory contains tests for the Undertow HTTP library data extension models. + +## Overview + +The Undertow data extension models enable CodeQL to detect security vulnerabilities in applications using the [Undertow](https://undertow.io/) HTTP server library. The models define sources and sinks for taint tracking analysis. + +## Models Defined + +### Sources +- **`io.undertow.server.HttpServerExchange.getQueryParameters()`** + - Returns: `Map>` + - Description: Query parameters from HTTP request (user-controlled data) + - Kind: `remote` (remote flow source) + +### Sinks +- **`io.undertow.io.Sender.send(String)`** +- **`io.undertow.io.Sender.send(String, IoCallback)`** +- **`io.undertow.io.Sender.send(String, Charset)`** +- **`io.undertow.io.Sender.send(String, Charset, IoCallback)`** +- **`io.undertow.io.Sender.send(ByteBuffer)`** +- **`io.undertow.io.Sender.send(ByteBuffer, IoCallback)`** +- **`io.undertow.io.Sender.send(ByteBuffer[])`** +- **`io.undertow.io.Sender.send(ByteBuffer[], IoCallback)`** + - Description: Sends HTTP response content + - Kind: `html-injection` (XSS sink) + +## Test Files + +### UndertowExample.java +Example vulnerable code that demonstrates an XSS vulnerability: +- **Line 17**: User input from query parameter `namex` via `getQueryParameters()` +- **Line 22**: Unsanitized data sent in HTTP response via `send()` + +### UndertowXSS.ql +Main test query that detects XSS vulnerabilities using the data extension models. + +### UndertowXSS.expected +Expected test output showing the detected vulnerability. + +### DebugSources.ql +Debug query to verify that sources are properly recognized. + +### DebugSinks.ql +Debug query to verify that sinks are properly recognized. + +## Running the Tests + +### Using existing database: +```bash +# Run the main XSS detection query +codeql query run UndertowXSS.ql -d db/ --output=results.bqrs +codeql bqrs decode results.bqrs --format=text + +# Run debug queries +codeql query run DebugSources.ql -d db/ --output=sources.bqrs +codeql bqrs decode sources.bqrs --format=text + +codeql query run DebugSinks.ql -d db/ --output=sinks.bqrs +codeql bqrs decode sinks.bqrs --format=text +``` + +### Creating a new test database: +```bash +# Create database (this will also build the code) +codeql database create db-new --language=java --command="./gradlew clean build" + +# Run queries against new database +codeql query run UndertowXSS.ql -d db-new/ +``` + +## Expected Results + +The main query should detect **1 XSS vulnerability**: +- **Source**: `getQueryParameters()` call on line 17 +- **Sink**: String concatenation expression on line 22 +- **Path**: Query parameter → local variable → string concatenation → HTTP response + +## Key Implementation Details + +### Model Format Fixes Applied + +1. **Signature Format**: Use CodeQL format `(String)` not JVM format `(Ljava/lang/String;)V` +2. **Boolean Values**: Use `False` (YAML/Python style) not `false` (Java style) +3. **Sink Kind**: Use `html-injection` (CodeQL XSS library expects this) not `xss` +4. **Source Access Path**: Use `ReturnValue` (CodeQL handles Map/Collection elements automatically) not `ReturnValue.MapValue.Element` + +### Query Implementation + +The test query uses: +- `RemoteFlowSource` class to match sources defined in the model +- `XssSink` class to match sinks defined in the model +- Modern CodeQL module-based taint tracking configuration +- `TaintTracking::Global` for global taint analysis + +## References + +- [Undertow Documentation](https://undertow.io/) +- [CodeQL Data Extensions Documentation](https://codeql.github.com/docs/codeql-language-guides/data-extensions/) +- [CodeQL Java Security Queries](https://github.com/github/codeql/tree/main/java/ql/src/Security) diff --git a/languages/java/custom/test/UndertowXSS/UndertowExample.java b/languages/java/custom/test/UndertowXSS/UndertowExample.java new file mode 100644 index 0000000..ba31666 --- /dev/null +++ b/languages/java/custom/test/UndertowXSS/UndertowExample.java @@ -0,0 +1,27 @@ +package org.example; + +import io.undertow.Undertow; +import io.undertow.server.HttpHandler; +import io.undertow.server.HttpServerExchange; +import io.undertow.util.Headers; +import java.util.Deque; + +public class UndertowExample { + public static void main(String[] args) { + Undertow server = Undertow.builder() + .addHttpListener(8080, "localhost") + .setHandler(new HttpHandler() { + @Override + public void handleRequest(final HttpServerExchange exchange) throws Exception { + String name = "world"; + Deque res = exchange.getQueryParameters().get("namex"); // source + if (res != null) { + name = res.getFirst(); + } + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/html"); + exchange.getResponseSender().send("Hello " + name + ""); // sink: XSS + } + }).build(); + server.start(); + } +} diff --git a/languages/java/custom/test/UndertowXSS/UndertowXSS.expected b/languages/java/custom/test/UndertowXSS/UndertowXSS.expected new file mode 100644 index 0000000..1f0d1c7 --- /dev/null +++ b/languages/java/custom/test/UndertowXSS/UndertowXSS.expected @@ -0,0 +1,2 @@ +#select +| UndertowExample.java:22:59:22:104 | ... + ... | UndertowExample.java:17:45:17:73 | getQueryParameters(...) : Map | UndertowExample.java:22:59:22:104 | ... + ... | XSS vulnerability: user input from $@ flows to HTTP response. | UndertowExample.java:17:45:17:73 | getQueryParameters(...) | remote source | diff --git a/languages/java/custom/test/UndertowXSS/UndertowXSS.ql b/languages/java/custom/test/UndertowXSS/UndertowXSS.ql new file mode 100644 index 0000000..5dc8130 --- /dev/null +++ b/languages/java/custom/test/UndertowXSS/UndertowXSS.ql @@ -0,0 +1,32 @@ +/** + * @name Test Undertow XSS detection + * @description Test query to validate Undertow source and sink models + * @kind path-problem + * @problem.severity warning + * @id java/test/undertow-xss-test + * @tags security + */ + +import java +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.dataflow.TaintTracking +import semmle.code.java.security.XSS + +/** + * A taint-tracking configuration for testing Undertow XSS. + */ +module UndertowXSSConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + predicate isSink(DataFlow::Node sink) { sink instanceof XssSink } +} + +/** Tracks flow from remote sources to XSS sinks. */ +module UndertowXSSFlow = TaintTracking::Global; + +import UndertowXSSFlow::PathGraph + +from UndertowXSSFlow::PathNode source, UndertowXSSFlow::PathNode sink +where UndertowXSSFlow::flowPath(source, sink) +select sink.getNode(), source, sink, "XSS vulnerability: user input from $@ flows to HTTP response.", + source.getNode(), "remote source" diff --git a/languages/java/custom/test/UndertowXSS/build.gradle b/languages/java/custom/test/UndertowXSS/build.gradle new file mode 100644 index 0000000..b192d14 --- /dev/null +++ b/languages/java/custom/test/UndertowXSS/build.gradle @@ -0,0 +1,24 @@ +plugins { + id 'java' +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'io.undertow:undertow-core:2.3.21.Final' +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +sourceSets { + main { + java { + srcDirs = ['.'] + } + } +}