Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
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