mcpField
S
sast-xss

sast-xss

github.com/utkusen安全1.3k78 次安装5 天前更新
代码仓库

关于 sast-xss

用于代码库安全评估的 XSS 检测技能,面向执行静态分析的开发者。它分阶段定位输出点、追踪污点并合并结果,同时区分漏洞与安全模式。

主要功能

  1. 01定位 HTML、DOM 与脚本执行输出点
  2. 02分批追踪输入与输出间的污点
  3. 03检查转义、清理等缓解措施
  4. 04汇总分析结果并排除误报

适用场景

  1. 01检查 Web 应用的反射型与存储型 XSS
  2. 02审查前端代码中的 DOM 型 XSS
  3. 03分析模板或框架中的不安全 HTML 输出

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/utkusen/sast-skills --skill sast-xss

安装后在 Claude Code 中输入/sast-xss确认已加载。

详细介绍

跨站脚本(XSS)检测

你正在进行有针对性的安全评估,以查找代码库中的跨站脚本漏洞。此技能采用包含子 Agent 的三阶段方法:侦察(查找危险调用点)、分批验证(对每批最多 3 个危险调用点进行污点追踪)和合并(汇总各批结果,生成一份报告)。

前提条件:sast/architecture.md 必须存在。如果不存在,请先运行分析技能。


什么是 XSS

当用户提供的输入未经适当转义或清理,便被插入网页的 HTML、JavaScript 或 DOM 中时,就会发生 XSS。这会使攻击者能够在受害者的浏览器中注入并执行任意脚本,进而导致会话劫持、凭据盗窃、网页篡改和恶意软件传播。

核心模式:未经转义和清理的用户输入到达 HTML/JS 输出接收点。

XSS 类型
  • 反射型 XSS:用户输入立即在 HTTP 响应中回显(例如,搜索词被直接渲染到页面 HTML 中)。
  • 存储型 XSS:用户输入被保存到持久化存储(数据库、文件)中,之后在面向其他用户的 HTML 中渲染。
  • 基于 DOM 的 XSS:客户端 JavaScript 从攻击者可控的来源(location.search、location.hash、document.cookie)读取数据,并在没有服务器参与的情况下将其写入危险的 DOM 接收点(innerHTML、eval、document.write)。
什么是 XSS

服务端 HTML 接收点 — 将用户数据渲染到 HTML 响应中,但未进行转义:

  • Python/Jinja2:{{ var | safe }}、{% autoescape off %}...{{ var }}...{% endautoescape %}
  • Python/Django:mark_safe(var)、使用 %s 且输入未转义的 format_html(...)、模板中的 {{ var | safe }}
  • Python/Flask:Markup(var)、render_template_string(f"...{var}...")
  • PHP:未使用 htmlspecialchars() 的 echo $var、print $var、<?= $var ?>
  • Ruby/Rails:raw(var)、var.html_safe、<%= raw var %>、与 .html_safe 搭配使用的 content_tag
  • Java/JSP:未使用 <c:out> 或 fn:escapeXml() 的 <%= var %>、${var}
  • Java/Thymeleaf:th:utext="${var}"(未转义)、[(${var})]
  • Go/html-template 误用:使用 template.HTML(var)、template.JS(var)、template.URL(var) 绕过自动转义
  • C#/Razor:@Html.Raw(var)、MvcHtmlString.Create(var)
  • Node.js/EJS:<%- var %>(未转义),而 <%= var %> 是安全的
  • Node.js/Handlebars:{{{ var }}}(三重花括号,未转义)
  • Node.js/Pug:!{var}(未转义)
  • Express:res.send("<html>..." + var + "...")、res.write("<p>" + var + "</p>")

客户端 DOM 接收点 — JavaScript 将用户可控数据不安全地写入 DOM:

  • element.innerHTML = var
  • element.outerHTML = var
  • document.write(var)、document.writeln(var)
  • element.insertAdjacentHTML('beforeend', var)
  • jQuery:$(element).html(var)、$(element).append(var)(当 var 包含 HTML 时)、$('<div>' + var + '</div>')
  • React:dangerouslySetInnerHTML={{ __html: var }}
  • Angular:[innerHTML]="var"、bypassSecurityTrustHtml(var)、bypassSecurityTrustScript(var)、bypassSecurityTrustUrl(var)
  • Vue:v-html="var"

JavaScript 执行接收点 — 将用户可控数据作为代码求值:

  • eval(var)
  • setTimeout(var, delay) / setInterval(var, delay)(当 var 是字符串时)
  • new Function(var)()
  • element.setAttribute('onclick', var)、element.setAttribute('href', 'javascript:' + var)
  • location.href = var、location.replace(var)、location.assign(var)(当 var 由用户控制且可以是 javascript:... 时)
  • element.src = var、element.action = var(通过 javascript: URI 注入脚本)
  • scriptElement.text = var、scriptElement.textContent = var

基于 DOM 的来源 — 客户端 JavaScript 读取的攻击者可控输入:

  • location.search(URL 查询字符串)
  • location.hash(URL 片段)
  • location.href
  • document.referrer
  • document.URL、document.documentURI
  • document.cookie
  • postMessage 事件数据(event.data)
  • window.name
  • localStorage.getItem(...)、sessionStorage.getItem(...)(若数据来自 URL 或 postMessage)
什么不是 XSS

不要将以下情况报告为 XSS:

  • CSRF:伪造请求并冒充用户发起操作——这是另一类漏洞
  • 通过 XSS 实施 SQLi:通过 XSS 向量注入 SQL——主要问题是 SQL 注入本身
  • 点击劫持:将页面嵌入 iframe——这是另一类漏洞
  • 标头注入:向 HTTP 响应标头注入换行符——这是另一类问题(HTTP 响应拆分)
  • 安全的模板输出:启用了自动转义的 Jinja2/Django/Twig/Blade/Handlebars 双花括号语法中的自动转义 {{ var }}——这些是安全的
  • textContent / innerText:这些属性只写入纯文本,不会解析 HTML——是安全的
可防止 XSS 的模式

看到以下模式时,代码很可能不存在漏洞:

1. 上下文感知的自动转义(大多数模板引擎的默认行为)

# Jinja2 / Django (auto-escape on by default)
{{ var }}          # HTML-escaped → safe

# EJS
<%= var %>         # HTML-escaped → safe

# Handlebars
{{ var }}          # HTML-escaped → safe

# Pug
= var              # HTML-escaped → safe

# Thymeleaf
th:text="${var}"   # HTML-escaped → safe

# Razor (C#)
@var               # HTML-encoded → safe

2. 输出前显式转义

// PHP
echo htmlspecialchars($var, ENT_QUOTES, 'UTF-8');
# Rails
<%= h(var) %>
<%= ERB::Util.html_escape(var) %>
// JSP with JSTL
<c:out value="${var}"/>
// or fn:escapeXml()
${fn:escapeXml(var)}
// html/template — auto-escapes by context (HTML, JS, URL, CSS)
{{.Var}}   // safe inside html/template

3. 使用安全属性操作 DOM

element.textContent = userInput;   // plain text, no HTML parsing — safe
element.innerText = userInput;     // plain text — safe

4. 使用允许列表 HTML 库进行净化

// DOMPurify
element.innerHTML = DOMPurify.sanitize(userInput);

// sanitize-html with strict config
const clean = sanitizeHtml(userInput, { allowedTags: [], allowedAttributes: {} });

5. React / Angular / Vue 自动转义

// React JSX — auto-escaped
return <div>{userInput}</div>;

<div>{{ userInput }}</div>

<div>{{ userInput }}</div>

易受攻击与安全的示例

Python — Flask / Jinja2
# VULNERABLE: Markup() bypasses Jinja2 auto-escaping
@app.route('/greet')
def greet():
    name = request.args.get('name', '')
    return render_template_string(f"<h1>Hello, {name}!</h1>")   # raw f-string, no template escaping

# VULNERABLE: mark_safe equivalent
@app.route('/profile')
def profile():
    bio = request.args.get('bio', '')
    return render_template('profile.html', bio=Markup(bio))      # Markup() marks it as safe, bypassing escaping

# SECURE: use template with auto-escaping (never pass Markup around user input)
@app.route('/greet')
def greet():
    name = request.args.get('name', '')
    return render_template('greet.html', name=name)              # template: {{ name }} — auto-escaped
Python — Django
# VULNERABLE: mark_safe() with user input
def user_bio(request):
    bio = request.GET.get('bio', '')
    safe_bio = mark_safe(bio)   # user input bypasses Django's auto-escaping
    return render(request, 'bio.html', {'bio': safe_bio})

# SECURE: pass raw string; template handles escaping
def user_bio(request):
    bio = request.GET.get('bio', '')
    return render(request, 'bio.html', {'bio': bio})   # template: {{ bio }} — auto-escaped
PHP
// VULNERABLE: echo without escaping
function showUsername($username) {
    echo "<p>Welcome, " . $username . "</p>";
}

// SECURE: htmlspecialchars
function showUsername($username) {
    echo "<p>Welcome, " . htmlspecialchars($username, ENT_QUOTES, 'UTF-8') . "</p>";
}
Node.js — Express(字符串拼接)
// VULNERABLE: user input concatenated into HTML response
app.get('/search', (req, res) => {
  const query = req.query.q;
  res.send(`<h1>Results for: ${query}</h1>`);
});

// SECURE: use a template engine with auto-escaping, or escape manually
const escapeHtml = require('escape-html');
app.get('/search', (req, res) => {
  const query = req.query.q;
  res.send(`<h1>Results for: ${escapeHtml(query)}</h1>`);
});
Node.js / EJS

<div><%- userInput %></div>


<div><%= userInput %></div>
Node.js / Handlebars

<div>{{{ userInput }}}</div>


<div>{{ userInput }}</div>
JavaScript — DOM 接收点
// VULNERABLE: innerHTML with URL fragment
const name = location.hash.substring(1);
document.getElementById('greeting').innerHTML = 'Hello, ' + name;

// SECURE: textContent
const name = location.hash.substring(1);
document.getElementById('greeting').textContent = 'Hello, ' + name;
// VULNERABLE: eval with postMessage data
window.addEventListener('message', (event) => {
  eval(event.data);
});

// SECURE: parse and validate; never eval postMessage data
window.addEventListener('message', (event) => {
  const data = JSON.parse(event.data);
  // handle data safely
});
React
// VULNERABLE: dangerouslySetInnerHTML with user input
function Comment({ content }) {
  return <div dangerouslySetInnerHTML={{ __html: content }} />;
}

// SECURE: render as text (auto-escaped by React)
function Comment({ content }) {
  return <div>{content}</div>;
}
Angular
// VULNERABLE: bypassing Angular's DomSanitizer
constructor(private sanitizer: DomSanitizer) {}
getUserHtml(input: string): SafeHtml {
  return this.sanitizer.bypassSecurityTrustHtml(input);  // unsafe if input is user-controlled
}

<div [innerHTML]="userInput"></div>


<div>{{ userInput }}</div>
Ruby on Rails
<%# VULNERABLE: raw() or html_safe with user input %>
<%= raw(@user.bio) %>
<%= @user.bio.html_safe %>

<%# SECURE: default ERB escaping %>
<%= @user.bio %>
Java — JSP
<%-- VULNERABLE: scriptlet echo --%>
<p>Hello, <%= request.getParameter("name") %></p>

<%-- VULNERABLE: EL without c:out --%>
<p>Hello, ${param.name}</p>

<%-- SECURE: c:out escaping --%>
<p>Hello, <c:out value="${param.name}"/></p>
Go — html/template 与 text/template
// VULNERABLE: using text/template (no HTML escaping)
import "text/template"
tmpl := template.Must(template.New("").Parse("<h1>Hello, {{.Name}}!</h1>"))
tmpl.Execute(w, data)

// VULNERABLE: using template.HTML() cast to bypass escaping
import "html/template"
name := template.HTML(r.URL.Query().Get("name"))   // bypasses auto-escaping

// SECURE: html/template with plain string value
import "html/template"
tmpl := template.Must(template.New("").Parse("<h1>Hello, {{.Name}}!</h1>"))
tmpl.Execute(w, data)   // .Name is a plain string — auto-escaped

执行

此技能使用子 Agent 分三个阶段运行。将 sast/architecture.md 的内容作为上下文传递给所有子 Agent。

阶段 1:查找 XSS 接收点

启动一个子 Agent,并向其发送以下指令:

目标:找出代码库中所有将数据渲染到 HTML、JavaScript 或 DOM 中,并可能导致脚本注入的位置——包括任何未转义或显式标记为安全的输出、任何危险的 DOM 属性赋值,以及任何 JavaScript 执行汇点。将结果写入 sast/xss-recon.md。

背景:你将获得项目的架构摘要。利用它了解前端技术栈、模板引擎、服务端渲染框架以及客户端 JavaScript 模式。

搜索目标——易受攻击的汇点模式:

标记传递给危险输出汇点的任何动态变量。此阶段暂不检查变量是否由用户控制——这是第 2 阶段的任务。

1. 服务端模板未转义输出:

  • Jinja2/Django:{{ var | safe }}、{% autoescape off %}、Markup(var)、mark_safe(var)、带有直接用户控制格式参数的 format_html(...)
  • EJS:<%- var %>
  • Handlebars/Mustache:{{{ var }}}
  • Pug:!{var}
  • Thymeleaf:th:utext="${var}"、[(${var})]
  • Twig:{{ var | raw }}
  • Blade(Laravel):{!! $var !!}
  • Rails ERB:raw(var)、var.html_safe、<%= raw var %>
  • PHP:未经 htmlspecialchars() 处理的 echo $var、print $var、<?= $var ?>
  • Go:template.HTML(var)、template.JS(var)、template.URL(var),以及使用 text/template 输出 HTML
  • C#/Razor:@Html.Raw(var)、MvcHtmlString.Create(var)

2. 服务端代码中直接构造 HTML 字符串:

  • 拼接或插值字符串以构建 HTML 响应:res.send("<p>" + var + "</p>")、f"<h1>{var}</h1>"、"<div>" + var + "</div>"
  • Flask 中的 render_template_string(f"...{var}...")

3. 客户端 DOM 汇点:

  • element.innerHTML = var
  • element.outerHTML = var
  • document.write(var)、document.writeln(var)
  • element.insertAdjacentHTML(position, var)
  • jQuery:$(el).html(var)、$(el).append(var)、$('<tag>' + var + '</tag>')、传入 DOM 的 $.parseHTML(var)
  • React:dangerouslySetInnerHTML={{ __html: var }}
  • Angular:[innerHTML]="var"、bypassSecurityTrustHtml(var)、bypassSecurityTrustScript(var)、bypassSecurityTrustUrl(var)、bypassSecurityTrustStyle(var)、bypassSecurityTrustResourceUrl(var)
  • Vue:v-html="var"

4. JavaScript 执行汇点:

  • eval(var)
  • setTimeout(var, ...) / setInterval(var, ...),其中 var 是字符串变量(而非函数引用)
  • new Function(var)()
  • scriptElement.text = var、scriptElement.textContent = var
  • element.setAttribute('onclick', var)、element.setAttribute('href', 'javascript:' + var) 以及类似的事件处理程序属性赋值
  • 可能执行 javascript: URI 的基于 URL 的汇点:location.href = var、location.replace(var)、element.src = var、element.action = var

5. 基于 DOM 的 XSS 模式——客户端代码从攻击者可控的来源读取数据,并将其传递给上述任一汇点:

  • 读取来源:location.search、location.hash、location.href、document.referrer、document.URL、document.cookie、window.name、postMessage 处理程序(event.data)、URLSearchParams
  • 然后未经转义就传递给 HTML 或 JS 汇点

跳过项(这些是安全的输出模式——不要标记):

  • 自动转义的模板输出:Jinja2 中的 {{ var }}(启用自动转义时)、EJS 中的 <%= var %>、Handlebars 双花括号中的 {{ var }}、Razor 中的 @var、Thymeleaf 中的 th:text
  • element.textContent = var 和 element.innerText = var——不会解析 HTML,安全
  • React JSX {var}——自动转义
  • Angular {{ var }} 插值——自动转义
  • Vue {{ var }} 插值——自动转义
  • 包裹 innerHTML 赋值的 DOMPurify.sanitize(var)——通常安全(请核实配置)
  • 包裹输出的 sanitize-html、xss 或类似白名单清理库

输出格式——写入 sast/xss-recon.md:

# XSS 侦察:[Project Name]

## 摘要
发现 [N] 处将数据渲染到 HTML/JS/DOM 且未保证转义的位置。

## 汇点位置

### 1. [描述性名称——例如,“搜索结果处理程序中的 innerHTML 赋值”]
- **文件**:`path/to/file.ext`(第 X-Y 行)
- **函数 / 端点 / 组件**:[函数名、路由或组件]
- **汇点类型**:[服务端模板 / HTML 字符串拼接 / DOM innerHTML / eval / JS 执行汇点 / 基于 DOM 的源到汇点]
- **汇点调用**:[使用的确切 API 或属性——例如,`innerHTML`、`mark_safe()`、`<%- %>`]
- **插值变量**:`var_name` — [简要说明,例如,“来源未知”或“看起来是用户个人资料字段”]
- **XSS 类型**:[反射型 / 存储型 / 基于 DOM——现阶段的最佳判断]
- **代码片段**:

[存在漏洞的汇点代码]


[每个位置重复此格式]
阶段 1 之后:继续前先检查候选项

阶段 1 完成后,读取 sast/xss-recon.md。如果侦察发现 零个 sink 站点(摘要报告“Found 0”,或者“Sink Sites”部分为空或不存在),则完全跳过阶段 2 和阶段 3。请改为将以下内容写入 sast/xss-results.md,然后停止(写入后可以删除 sast/xss-recon.md):

# XSS Analysis Results

No vulnerabilities found.

只有当阶段 1 发现至少一个 sink 站点时,才继续阶段 2。

阶段 2:验证——追踪用户输入至 sink(分批处理)

阶段 1 完成后,读取 sast/xss-recon.md,并将 sink 站点拆分为每批最多 3 个 sink 站点。为每批并行启动一个子 Agent。每个子 Agent 仅追踪分配给自己的 sink 的污点,并将结果写入自己的批次文件。

分批流程(由你,即编排器执行,而非子 Agent):

  1. 读取 sast/xss-recon.md,并统计带编号的 sink 部分(### 1.、### 2. 等)。
  2. 将它们拆分为每批最多 3 个。例如,8 个 sink → 3 批(1-3、4-6、7-8)。
  3. 从侦察文件中提取各批 sink 部分的完整文本。
  4. 并行启动所有批次子 Agent,并且只向每个子 Agent传递分配给它的 sink。
  5. 每个子 Agent 将结果写入 sast/xss-batch-N.md,其中 N 是从 1 开始的批次编号。
  6. 从 sast/architecture.md 中确定项目的主要语言/框架,并从上文“易受攻击与安全示例”部分中仅选择匹配的示例。例如,如果项目使用 React 和 Express API,则包含相关的 Node.js 和 React 示例。在下方 [TECH-STACK EXAMPLES] 所示的位置,将这些选出的示例加入每个子 Agent 的指令。

向每个批次子 Agent 提供以下指令(替换为对应批次的值):

目标:针对每个分配的 XSS sink 位置,判断用户提供的值是否会流入输出变量。将结果写入 sast/xss-batch-[N].md。

分配给你的 sink 位置(来自侦察阶段):

[在此粘贴分配给你的 sink 部分的完整文本,并保留原始编号]

上下文:你将获得项目的架构摘要。利用它了解请求入口、数据流、中间件和客户端数据源。

对于每个 sink 位置,向后追踪插值变量的来源:

需要查找的用户可控来源:

  1. HTTP 请求来源(服务端):
    • 查询参数:request.GET.get(...)、req.query.x、params[:x]、$_GET['x']、c.Query("x")、r.URL.Query().Get("x")
    • 路径参数:request.path_params['id']、req.params.id、params[:id]、$_GET['id']
    • 请求体/表单字段:request.POST.get(...)、req.body.x、request.form.get(...)、$_POST['x']
    • HTTP 标头:request.headers.get(...)、req.headers['x']、$_SERVER['HTTP_X_CUSTOM']
    • Cookie:request.COOKIES.get(...)、req.cookies.x、$_COOKIE['x']
    • 文件上传的文件名或内容:request.files['x'].filename
  2. 攻击者可控的 DOM 来源(客户端/基于 DOM 的 XSS):
    • location.search、location.hash、location.href、document.referrer、document.URL
    • window.name、document.cookie
    • postMessage 事件:window.addEventListener('message', (e) => { ... e.data ... })
    • 从 location.search 派生的 URLSearchParams 值
    • 从 URL 或 postMessage 写入的 localStorage/sessionStorage 值
  3. 已存储输入(二阶)——变量从持久化存储(数据库、文件、缓存)中读取,但存储的值最初来自用户输入:
    • 找出写入路径:此字段存储在何处?写入时是否由用户提供?
    • 写入时是否应用了转义或净化?(注意:写入时进行 HTML 转义并不可靠——之后可能会被重复编码或移除)
    • 即使已验证或安全存储,存储型 XSS 仍然是漏洞;应追踪回读路径在渲染前是否进行了转义
  4. 服务端/硬编码值——变量来自配置、环境、硬编码常量,或不受用户影响的服务端逻辑——此位置不可被利用。

对于每个 sink 位置,还要检查是否存在可阻止利用的缓解措施:

  • 输出是否在进入 sink 前立即使用安全函数显式转义?(htmlspecialchars()、escapeHtml()、h()、fn:escapeXml())
  • 是否使用了带严格允许列表配置的净化库?(DOMPurify.sanitize(input)——检查配置是否会移除脚本)
  • HTTP 响应的 Content-Type 是否设置为 application/json 或 text/plain(不进行 HTML 渲染)?
  • 是否存在可阻止内联脚本的内容安全策略标头?(CSP 可降低影响,但不能彻底修复问题)
  • 是否存在 WAF 或严格限定预期格式(例如数字 ID)的输入验证?

适用于本项目技术栈的易受攻击与安全示例:

[TECH-STACK EXAMPLES]

分类:

  • 易受攻击:有明确证据表明用户输入会到达 sink,且没有有效的转义或净化。
  • 很可能易受攻击:用户输入很可能会到达 sink(间接/存储型数据流),或者仅存在较弱的缓解措施(仅 CSP、仅 WAF、部分净化、不完整的允许列表)。
  • 不易受攻击:变量仅来自服务端且不受用户影响,或在进入 sink 前立即应用了适用于相应上下文的正确转义。
  • 需要人工审查:无法有把握地确定变量来源(不透明的辅助函数、复杂的条件流程、外部库或跨服务数据流)。

输出格式——写入 sast/xss-batch-[N].md:

# XSS 批次 [N] 结果

## 发现

### [易受攻击] 描述性名称
- **文件**:`path/to/file.ext`(第 X-Y 行)
- **端点/函数/组件**:[路由、函数或组件名称]
- **XSS 类型**:[反射型/存储型/基于 DOM]
- **问题**:[例如,“HTTP 查询参数 `q` 未经转义,直接流入 innerHTML”]
- **污点追踪**:[从源到 sink 的逐步追踪——例如,“req.query.q → query → `<h1>${query}</h1>` → res.send()”]
- **影响**:[攻击者可以实施的操作——会话劫持、凭据窃取、键盘记录、篡改页面、重定向至恶意网站等]
- **修复建议**:[具体修复方法——使用正确的函数进行转义、改用 textContent、使用自动转义的模板语法、应用 DOMPurify]
- **动态测试**:

[用于确认该发现的 curl 命令或浏览器负载。 展示确切的参数、负载以及需要观察的内容。 示例:curl "https://app.example.com/search?q=" 或:访问 https://app.example.com/#README 配图 并观察是否出现警告框]


### [很可能易受攻击] 描述性名称
- **文件**:`path/to/file.ext`(第 X-Y 行)
- **端点/函数/组件**:[路由、函数或组件名称]
- **XSS 类型**:[反射型/存储型/基于 DOM]
- **问题**:[例如,“已确认写入路径接收用户输入;存储的用户简介很可能通过 innerHTML 渲染”]
- **污点追踪**:[尽力追踪,并指出不确定的步骤]
- **风险说明**:[此处仍存在风险的原因——例如,“存在净化库,但其配置允许可执行脚本的标签”]
- **修复建议**:[具体修复方法]
- **动态测试**:

[尝试使用的负载]


### [不易受攻击] 描述性名称
- **文件**:`path/to/file.ext`(第 X-Y 行)
- **端点/函数/组件**:[路由、函数或组件名称]
- **原因**:[例如,“输出在 echo 前经过 htmlspecialchars() 处理”或“变量是硬编码的服务端常量”]

### [需要人工审查] 描述性名称
- **文件**:`path/to/file.ext`(第 X-Y 行)
- **端点/函数/组件**:[路由、函数或组件名称]
- **不确定性**:[无法确定变量来源或转义状态的原因]
- **建议**:[需要手动追踪的内容——例如,“跟进 utils.js 中的 `buildProfileHtml()`,检查其返回值的来源”]
阶段 3:合并 — 汇总批次结果

在 所有 阶段 2 批次子 Agent 完成后,读取每个 sast/xss-batch-*.md 文件,并将它们合并到单个 sast/xss-results.md 中。由你(编排器)直接完成此操作——无需子 Agent。

合并流程:

  1. 读取所有 sast/xss-batch-1.md、sast/xss-batch-2.md……文件。
  2. 收集各批次文件中的所有发现并合并为一个列表,同时保留原始分类和所有详细字段。
  3. 汇总所有批次的总数,供执行摘要使用(分析的 sink 位置总数应等于侦察阶段的数量;各分类的数量应跨批次累加)。
  4. 使用以下格式将合并后的报告写入 sast/xss-results.md:
# XSS Analysis Results: [Project Name]

## Executive Summary
- Sink sites analyzed: [total across all batches]
- Vulnerable: [N]
- Likely Vulnerable: [N]
- Not Vulnerable: [N]
- Needs Manual Review: [N]

## Findings

[All findings from all batches, grouped by classification:
 VULNERABLE first, then LIKELY VULNERABLE, then NEEDS MANUAL REVIEW, then NOT VULNERABLE.
 Preserve every field from the batch results exactly as written.]
  1. 写入 sast/xss-results.md 后,删除所有中间批次文件(sast/xss-batch-*.md)。

重要提醒

  • 读取 sast/architecture.md,并将其内容作为上下文传递给所有子 Agent。
  • 阶段 2 必须在阶段 1 完成后运行——它依赖侦察阶段的输出。
  • 阶段 3 必须在所有阶段 2 批次完成后运行——它依赖所有批次的输出。
  • 每个子 Agent 的批次大小为 3 个 sink 位置。如果总共只有 1–3 个 sink,则使用单个子 Agent。如果有 10 个,则使用 4 个子 Agent(3+3+3+1)。
  • 并行启动所有批次子 Agent——不要按顺序运行。
  • 每个批次子 Agent 只接收侦察文件中分配给它的 sink 文本,而非整个侦察文件。这样可以让每个子 Agent 的上下文更小、更聚焦。
  • 阶段 1 纯粹是结构分析:标记传入 HTML/JS/DOM sink 的任何动态变量,无论其来源如何。阶段 1 不要尝试追踪用户输入——这是阶段 2 的工作。
  • 阶段 2 纯粹是污点分析:对于阶段 1 中发现的每个 sink,将变量追溯到其来源。如果它来自用户可控的源,且没有经过有效转义,则该位置属于真实漏洞。
  • 上下文很重要:同一变量在一种输出上下文中可能安全(经转义的 HTML 正文),在另一种上下文中却很危险(JavaScript 字符串字面量、URL 属性或事件处理程序属性)。检查确切的渲染上下文。
  • 自定义清理(自行编写正则表达式剥离、将 <script> 列入黑名单等)并不充分——应标记为“可能存在漏洞”。只有采用严格配置的 DOMPurify 或等效的允许列表库才可接受。
  • 存储型 XSS 很容易被漏掉:追踪写入路径以确认字段由用户提供,然后单独验证读取/渲染路径是否缺少转义。两个条件都成立,漏洞才可利用。
  • 基于 DOM 的 XSS 完全存在于客户端 JavaScript 中:查找 location.*、document.referrer、event.data 及其他攻击者可控属性是否流入 DOM sink,且未经过服务器。
  • CSP 标头可以降低 XSS 的可利用性,但不能修复问题——仍应标记底层注入点。
  • 如有疑问,应归类为“需要人工审查”,而非“无漏洞”。在安全评估中,漏报比误报更糟。
  • Angular 的 DomSanitizer.bypassSecurityTrust* 方法始终可疑——只要参数不是硬编码常量,就应标记。
  • 对于 JavaScript 执行 sink(eval、使用字符串参数的 setTimeout),即使看似无害的数据(错误消息、ID),只要攻击者能够影响它们,也可能造成危险。
  • 清理中间文件:写入最终的 sast/xss-results.md 后,删除 sast/xss-recon.md 和所有 sast/xss-batch-*.md 文件。

评论

支持 Markdown