Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ ql/rust/ql/src/queries/diagnostics/UnextractedElements.ql
ql/rust/ql/src/queries/diagnostics/UnresolvedMacroCalls.ql
ql/rust/ql/src/queries/security/CWE-020/RegexInjection.ql
ql/rust/ql/src/queries/security/CWE-022/TaintedPath.ql
ql/rust/ql/src/queries/security/CWE-079/XSS.ql
ql/rust/ql/src/queries/security/CWE-089/SqlInjection.ql
ql/rust/ql/src/queries/security/CWE-311/CleartextTransmission.ql
ql/rust/ql/src/queries/security/CWE-312/CleartextLogging.ql
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ ql/rust/ql/src/queries/diagnostics/UnextractedElements.ql
ql/rust/ql/src/queries/diagnostics/UnresolvedMacroCalls.ql
ql/rust/ql/src/queries/security/CWE-020/RegexInjection.ql
ql/rust/ql/src/queries/security/CWE-022/TaintedPath.ql
ql/rust/ql/src/queries/security/CWE-079/XSS.ql
ql/rust/ql/src/queries/security/CWE-089/SqlInjection.ql
ql/rust/ql/src/queries/security/CWE-117/LogInjection.ql
ql/rust/ql/src/queries/security/CWE-311/CleartextTransmission.ql
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ ql/rust/ql/src/queries/diagnostics/UnextractedElements.ql
ql/rust/ql/src/queries/diagnostics/UnresolvedMacroCalls.ql
ql/rust/ql/src/queries/security/CWE-020/RegexInjection.ql
ql/rust/ql/src/queries/security/CWE-022/TaintedPath.ql
ql/rust/ql/src/queries/security/CWE-079/XSS.ql
ql/rust/ql/src/queries/security/CWE-089/SqlInjection.ql
ql/rust/ql/src/queries/security/CWE-117/LogInjection.ql
ql/rust/ql/src/queries/security/CWE-311/CleartextTransmission.ql
Expand Down
5 changes: 5 additions & 0 deletions rust/ql/lib/codeql/rust/frameworks/actix-web.model.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ extensions:
- ["<actix_web::route::Route>::to", "Argument[0].Parameter[0..7]", "remote", "manual"]
# Actix attributes such as `get` expand to this `to` call on the handler.
- ["<actix_web::resource::Resource>::to", "Argument[0].Parameter[0..7]", "remote", "manual"]
- addsTo:
pack: codeql/rust-all
extensible: sinkModel
data:
- ["<actix_web::types::html::Html>::new", "Argument[0]", "html-injection", "manual"]
- addsTo:
pack: codeql/rust-all
extensible: summaryModel
Expand Down
7 changes: 6 additions & 1 deletion rust/ql/lib/codeql/rust/frameworks/warp.model.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,9 @@ extensions:
data:
- ["<_ as warp::filter::Filter>::then", "Argument[0].Parameter[0..7]", "remote", "manual"]
- ["<_ as warp::filter::Filter>::map", "Argument[0].Parameter[0..7]", "remote", "manual"]
- ["<_ as warp::filter::Filter>::and_then", "Argument[0].Parameter[0..7]", "remote", "manual"]
- ["<_ as warp::filter::Filter>::and_then", "Argument[0].Parameter[0..7]", "remote", "manual"]
- addsTo:
pack: codeql/rust-all
extensible: sinkModel
data:
- ["warp::reply::html", "Argument[0]", "html-injection", "manual"]
62 changes: 62 additions & 0 deletions rust/ql/lib/codeql/rust/security/XssExtensions.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* Provides classes and predicates for reasoning about cross-site scripting (XSS)
* vulnerabilities.
*/

import rust
private import codeql.rust.dataflow.DataFlow
private import codeql.rust.dataflow.FlowSink
private import codeql.rust.Concepts
private import codeql.util.Unit
private import codeql.rust.security.Barriers as Barriers

/**
* Provides default sources, sinks and barriers for detecting XSS
* vulnerabilities, as well as extension points for adding your own.
*/
module Xss {
/**
* A data flow source for XSS vulnerabilities.
*/
abstract class Source extends DataFlow::Node { }

/**
* A data flow sink for XSS vulnerabilities.
*/
abstract class Sink extends QuerySink::Range {
override string getSinkType() { result = "Xss" }
}

/**
* A barrier for XSS vulnerabilities.
*/
abstract class Barrier extends DataFlow::Node { }

/**
* An active threat-model source, considered as a flow source.
*/
private class ActiveThreatModelSourceAsSource extends Source, ActiveThreatModelSource { }

/**
* A sink for XSS from model data.
*/
private class ModelsAsDataSink extends Sink {
ModelsAsDataSink() { sinkNode(this, "html-injection") }
}

/**
* A barrier for XSS vulnerabilities for nodes whose type is a
* numeric or boolean type, which is unlikely to expose any vulnerability.
*/
private class NumericTypeBarrier extends Barrier instanceof Barriers::NumericTypeBarrier { }

/** A call to a function with "escape" or "encode" in its name. */
private class HeuristicHtmlEncodingBarrier extends Barrier {
HeuristicHtmlEncodingBarrier() {
exists(Call fc |
fc.getStaticTarget().(Function).getName().getText().regexpMatch(".*(escape|encode).*") and
fc.getArgument(_) = this.asExpr()
)
}
}
}
4 changes: 4 additions & 0 deletions rust/ql/src/change-notes/2025-11-24-xss-query.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: newQuery
---
* Added new a query `rust/xss`, to detect XSS security vulnerabilities.
46 changes: 46 additions & 0 deletions rust/ql/src/queries/security/CWE-079/XSS.qhelp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>

<overview>
<p>Directly writing user input (for example, an HTTP request parameter) to a web
page, without properly sanitizing the input first, allows for a cross-site
scripting vulnerability.</p>
</overview>

<recommendation>
<p>To guard against cross-site scripting, consider encoding/escaping the unstrusted
input before including it in the HTML.</p>
</recommendation>

<example>

<p>The following example shows a simple web handler that writes a path of the
URL parameter directly to an HTML response, leaving the website vulnerable to
cross-site scripting:</p>

<sample src="XSSBad.rs" />

<p>To fix this vulnerability, the user input should be HTML-encoded before being
included in the response:</p>

<sample src="XSSGood.rs" />

</example>

<references>
<li>
OWASP:
<a href="https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html">XSS
(Cross Site Scripting) Prevention Cheat Sheet</a>.
</li>
<li>
WiMISSING: Alert[rust/xss]kipedia: <a href="http://en.wikipedia.org/wiki/Cross-site_scripting">Cross-site scripting</a>.
</li>
<li>
OWASP:
<a href="https://owasp.org/www-community/attacks/xss/">Cross-site Scripting (XSS)</a>.
</li>
</references>
</qhelp>
42 changes: 42 additions & 0 deletions rust/ql/src/queries/security/CWE-079/XSS.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* @name Cross-site scripting
* @description Writing user input directly to a web page
* allows for a cross-site scripting vulnerability.
* @kind path-problem
* @problem.severity error
* @security-severity 6.1
* @precision high
* @id rust/xss
* @tags security
* external/cwe/cwe-079
* external/cwe/cwe-116
*/

import rust
import codeql.rust.dataflow.DataFlow
import codeql.rust.dataflow.TaintTracking
import codeql.rust.security.XssExtensions

/**
* A taint configuration for tainted data that reaches an XSS sink.
*/
module XssConfig implements DataFlow::ConfigSig {
import Xss

predicate isSource(DataFlow::Node node) { node instanceof Source }

predicate isSink(DataFlow::Node node) { node instanceof Sink }

predicate isBarrier(DataFlow::Node barrier) { barrier instanceof Barrier }

predicate observeDiffInformedIncrementalMode() { any() }
}

module XssFlow = TaintTracking::Global<XssConfig>;

import XssFlow::PathGraph

from XssFlow::PathNode sourceNode, XssFlow::PathNode sinkNode
where XssFlow::flowPath(sourceNode, sinkNode)
select sinkNode.getNode(), sourceNode, sinkNode, "Cross-site scripting vulnerability due to a $@.",
sourceNode.getNode(), "user-provided value"
21 changes: 21 additions & 0 deletions rust/ql/src/queries/security/CWE-079/XSSBad.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use actix_web::{web, HttpResponse, Result};

// BAD: User input is directly included in HTML response without sanitization
async fn vulnerable_handler(path: web::Path<String>) -> impl Responder {
let user_input = path.into_inner();

let html = format!(
r#"
<!DOCTYPE html>
<html>
<head><title>Welcome</title></head>
<body>
<h1>Hello, {}!</h1>
</body>
</html>
"#,
user_input
);

Html::new(html) // Unsafe: User input included directly in the response
}
23 changes: 23 additions & 0 deletions rust/ql/src/queries/security/CWE-079/XSSGood.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use actix_web::{web, HttpResponse, Result};
use askama::Template;

// GOOD: Manual HTML encoding using an `html_escape` function
async fn safe_handler_with_encoding(path: web::Path<String>) -> impl Responder {
let user_input = path.into_inner();
let escaped_input = html_escape(&user_input);

let html = format!(
r#"
<!DOCTYPE html>
<html>
<head><title>Welcome</title></head>
<body>
<h1>Hello, {}!</h1>
</body>
</html>
"#,
escaped_input
);

Html::new(html) // Safe: user input is HTML-encoded
}
Loading