Kibana CodeQL 安全扫描实战:自定义查询的本地开发、单元测试、远程 SARIF 获取与内联抑制
2026/9/17 8:22:51 网站建设 项目流程

Kibana CodeQL 安全扫描实战:自定义查询的本地开发、单元测试、远程 SARIF 获取与内联抑制

【免费下载链接】kibanaYour window into all of your data项目地址: https://gitcode.com/GitHub_Trending/ki/kibana

Kibana 仓库内置了一套完整的 CodeQL 安全扫描工作流:以 .github/codeql 目录承载自定义安全查询(qlpack),以 quick_check.sh 脚本在 Docker 中完成本地建库、分析与单元测试,以 fetch_sarif.mjs 脚本从 GitHub 拉取远端扫描结果。本文基于仓库中的 CodeQL 技能文档 与配套源码,系统讲解这套体系的目录布局、本地运行方式、单元测试结构、新查询编写规范、内联抑制(inline suppression)约定以及常见故障排查,读完后可直接在本地复现 Kibana 的 CodeQL 开发-测试-验证闭环。

整体目录布局

Kibana 的 CodeQL 相关资产分布在两个位置:.github/codeql/(查询与配置)和scripts/codeql/(本地运行工具):

.github/codeql/ ├── codeql-config.yml # Main config (paths-ignore, packs, query-filters) ├── custom-queries/ │ ├── qlpack.yml # QL pack definition (name: kibana-custom-queries) │ ├── codeql-pack.lock.yml │ ├── suppression/ # Alert suppression logic │ │ ├── AlertSuppression.ql │ │ └── AlertSuppression.qll │ └── <category>/ # e.g. dos/, xss/ │ ├── <RuleName>.ql # Query file │ ├── <RuleName>.qhelp # Help docs (XML) │ ├── <RuleName>.md # Human-readable docs │ ├── <category>-security.qls # Query suite │ └── <RuleName>/ # Unit test directory │ ├── <RuleName>.qlref # Points to the .ql file (relative to qlpack root) │ ├── <RuleName>.expected # Expected test output │ └── test.js # Test source code scripts/codeql/ ├── quick_check.sh # Local analysis via Docker └── codeql.dockerfile # Docker image (ubuntu + CodeQL CLI)

各组成部分在仓库中均有真实落地,可以逐一对照:

  • qlpack.yml 定义查询包元信息:包名为kibana-custom-queries,版本1.0.0,依赖官方的codeql/javascript-all库,提取器(extractor)为javascript

    name: kibana-custom-queries version: 1.0.0 dependencies: codeql/javascript-all: "*" extractor: javascript
  • codeql-config.yml 是 GitHub CodeQL 分析的主配置,包含三部分:

    • paths-ignore:大规模排除测试夹具(**/__fixtures__/**)、mock/stub 目录、*.test.*/*.spec.*文件、dev_docsdocsexamplesscripts等非产品代码路径,保证扫描聚焦于真实运行代码;
    • query-filters:排除官方库中与 Kibana 技术栈无关的规则目录(codeql/javascript-queries/AngularJScodeql/javascript-queries/Electron);
    • packsqueries:声明官方githubsecuritylab/codeql-javascript-queries查询包,并通过uses: ./.github/codeql/custom-queries把自定义查询纳入分析。
  • 自定义查询目前已按风险类别组织为dos/(拒绝服务)与xss/(跨站脚本)两类,另有suppression/承载抑制逻辑。例如 custom-queries/README.md 记录了UnboundedArrayInRoute规则:ID 为js/kibana/unbounded-array-in-route,用于检测缺少maxSize约束的schema.arrayOf()调用,修复方式是添加{ maxSize: N }第二个参数。

本地运行分析(quick_check.sh)

quick_check.sh 是本地 CodeQL 全流程入口:它通过 Docker 构建 CodeQL 数据库,并对真实源码运行查询。三种典型用法:

# 用整个自定义查询目录分析指定源码目录 bash scripts/codeql/quick_check.sh -s <source_dir> -q .github/codeql/custom-queries # 用单个查询文件分析 bash scripts/codeql/quick_check.sh -s <source_dir> -q .github/codeql/custom-queries/dos/UnboundedArrayInRoute.ql # 指定结果目录 bash scripts/codeql/quick_check.sh -s <source_dir> -r .codeql-results -q .github/codeql/custom-queries

选项说明:

选项说明
-s <source_dir>(分析模式必填)要扫描的源码目录
-q <query_dir\|query_file>自定义查询目录,或单个.ql文件
-r <results_dir>数据库与 SARIF 的存放位置(默认.codeql/
-t改为运行单元测试而非分析(配合-q使用,不需要-s

输出:SARIF 文件位于<results_dir>/database/results.sarif;若系统安装了jq,脚本会自动打印彩色摘要(Rule / Message / File / Line / Security Severity)。

从源码可以看到脚本的完整执行链路:

  1. 架构适配(quick_check.sh#L47-L54):先通过uname -m检测本机架构,由于 CodeQL CLI 二进制不支持 arm64,在 Apple Silicon(arm64)机器上会自动附加--platform linux/amd64,以模拟运行方式执行。
  2. 镜像构建(quick_check.sh#L56-L63):首次运行时本地构建名为codeql-env的镜像,构建上下文为scripts/codeql/,Dockerfile 见下节。
  3. 建库(quick_check.sh#L112-L122):把源码目录挂载进容器,执行codeql database create /workspace/shared/codeql-db --language=javascript --source-root=/workspace/source-code --overwrite
  4. 分析(quick_check.sh#L124-L170):分三种分支——
    • -q指向单个.ql文件:脚本从该文件所在目录逐级向上查找qlpack.yml/codeql-pack.yml(quick_check.sh#L130-L149),找到 qlpack 根后以相对路径挂载执行codeql database analyze
    • -q指向目录:直接挂载该目录分析;
    • 未指定-q:回退到官方查询集,运行codeql database analyze ... javascript-security-and-quality.qls githubsecuritylab/codeql-javascript-queries ... --download(quick_check.sh#L165-L170)。
  5. 摘要打印(quick_check.sh#L178-L219):用jq解析 SARIF 的runs[].results[],按ruleId关联tool.driver.rules取出security-severity并着色输出;无jq时提示安装并指引用 SARIF 查看器查看完整结果。

quick_check.sh#L5 附近还定义了固定常量:语言固定为javascript,输出格式sarif-latest,默认结果目录.codeql,镜像名codeql-env

配套镜像 codeql.dockerfile 基于ubuntu:latest,安装gitnodejsjq等基础工具后,下载并解压 CodeQL CLIv2.23.2官方 bundle 到/usr/local/codeql-home,并以非 root 用户codeql运行(USER codeql),入口为ENTRYPOINT ["/bin/bash", "-c"],便于docker run直接传入codeql子命令。

CodeQL 单元测试

单元测试用于验证“查询是否精准命中目标行”。每个测试位于以查询命名的子目录中,结构如下:

<category>/<RuleName>/ ├── <RuleName>.qlref # Reference: "category/RuleName.ql" ├── test.js # Source code with `// $ Alert` annotations └── <RuleName>.expected # Expected output (auto-generated or hand-written)

标注约定:

  • 行尾// $ Alert表示查询应当标记该行;
  • 未标注// $ Alert的行不应被标记;
  • .expected文件为管道分隔格式:| <location> | <message> |

以仓库中现成的 dos/UnboundedArrayInRoute 测试为例:

  • UnboundedArrayInRoute.qlref 内容只有一行dos/UnboundedArrayInRoute.ql,即相对 qlpack 根(custom-queries/)的查询路径;

  • test.js 是一组带标注的模拟路由校验代码,覆盖多种正负用例,例如:

    // BAD: Direct router pattern without maxSize router.post( { path: '/api/bad/direct-array', validate: { body: schema.arrayOf(schema.string()), // $ Alert }, }, handler );

    测试还包含嵌套在schema.object内的数组、只有minSize没有maxSize的空 options 等边界场景,以及测试所需的__fixtures__/桩模块;

  • UnboundedArrayInRoute.expected 记录预期的告警行与消息。

通过 Docker 运行测试(复用codeql-env镜像,首次运行自动构建):

# 运行某个具体测试目录 bash scripts/codeql/quick_check.sh -t -q .github/codeql/custom-queries/dos/UnboundedArrayInRoute # 运行 qlpack 下全部测试 bash scripts/codeql/quick_check.sh -t -q .github/codeql/custom-queries

从 quick_check.sh#L68-L98 可以看到测试模式的实现:-t-q组合使用时,脚本先向上定位 qlpack 根目录(找不到则报错退出),再把测试路径换算为相对 qlpack 根的路径,最后在容器内执行:

codeql test run /workspace/queries/<TEST_REL_PATH> --additional-packs /workspace/queries

--additional-packs保证查询内import dos.KibanaDoSExclusions这类 qlpack 内部导入可以解析。

CI 中的单元测试:.github/workflows/codeql-pr.yml 工作流在 PR 触发(目标分支main,且改动涉及 JS/TS 源码或.github/codeql/**时)自动执行三件事:

  1. 权限检查(codeql-pr.yml#L18-L50):先确认 PR 作者具有admin/maintain/write权限,避免外部 PR 触发耗时的分析任务;
  2. 单元测试(codeql-pr.yml#L73-L87):find .github/codeql/custom-queries -name "*.qlref"收集所有测试目录并去重,逐个执行codeql test run "$testdir" --additional-packs .github/codeql/custom-queries;若一个测试目录都找不到会直接失败,防止测试被静默跳过;
  3. 正式分析(codeql-pr.yml#L89-L123):codeql-action/init指定config-file: ./.github/codeql/codeql-config.yml,分析时设置环境变量CODEQL_EXTRACTOR_JAVASCRIPT_OPTION_SKIP_TYPES: true以跳过类型信息提取,加快大型仓库的扫描速度。

编写新查询

编写新查询遵循五步流程(对应技能文档 “Writing a New Query” 一节):

  1. 创建.ql文件,放在.github/codeql/custom-queries/<category>/

    • 使用@id js/kibana/<descriptive-id>(必须全局唯一);
    • 包含@kind problem(污点跟踪类查询用path-problem);
    • 设置@problem.severity@security-severity
    • import javascript引入官方 JavaScript 库;
    • 可参考 UnboundedArrayInRoute.ql 的既有写法。

    以现有 DoS 查询为例,它的核心思路是:先通过schemaVariable()谓词定位从@kbn/config-schema导入的schema绑定(同时兼容具名导入与命名空间导入),再在SchemaArrayOfCall类中匹配arrayOf属性调用,并用hasMaxSize()排除已提供maxSize选项的调用:

    /** * Gets the local variable bound to 'schema' imported from '@kbn/config-schema' */ LocalVariable schemaVariable() { exists(ImportDeclaration decl, ImportSpecifier spec | decl.getImportedPathExpr().getStringValue() = "@kbn/config-schema" and spec = decl.getASpecifier() and ( spec.getImportedName() = "schema" or spec instanceof ImportNamespaceSpecifier ) and result = spec.getLocal().getVariable() ) }
    from SchemaArrayOfCall arrayCall where not arrayCall.hasMaxSize() and not shouldExcludeFileFromDoSRules(arrayCall) select arrayCall, "This schema.arrayOf() call does not specify a maxSize. Unbounded input can cause Denial of Service (DoS) vulnerabilities. Consider adding { maxSize: N } as the second argument."

    注意查询头部还引用了共享的排除逻辑import dos.KibanaDoSExclusions,通过shouldExcludeFileFromDoSRules过滤已知误报文件,这是 Kibana 自定义查询控制精确率(@precision medium)的一种手段。

  2. 创建单元测试目录<category>/<RuleName>/

    • <RuleName>.qlref内容为<category>/<RuleName>.ql
    • test.js内含// $ Alert标注的测试用例;
    • 运行测试生成.expected,并人工核对是否符合预期。
  3. 添加.qhelp(XML)与/或.md作为规则文档。

  4. 可选:若需将多条查询成组运行,添加.qls查询套件(现有示例如 dos-security.qls)。

  5. 本地实测:用quick_check.sh对真实 Kibana 源码跑一遍,确认查询在生产代码上的行为。

获取远程 SARIF / 扫描结果

fetch_sarif.mjs 脚本用于从 GitHub 拉取某个 PR 或分支的 CodeQL 结果:

# 按 PR 号 GITHUB_TOKEN=ghp_xxx node .agents/skills/codeql/scripts/fetch_sarif.mjs 252121 # 按完整 ref GITHUB_TOKEN=ghp_xxx node .agents/skills/codeql/scripts/fetch_sarif.mjs refs/heads/main

要求:环境变量GITHUB_TOKEN需具备security_events权限(scope);脚本依赖@octokit/rest(已在 Kibana 依赖中)。

从 fetch_sarif.mjs 源码可以看到它固定针对elastic/kibana仓库执行四个步骤:

  1. 解析 ref:输入为纯数字时自动转换为refs/pull/<N>/merge(fetch_sarif.mjs#L44),否则原样作为分支 ref 使用;
  2. 列出分析记录:调用codeScanning.listRecentAnalyses获取该 ref 最近的 CodeQL 分析(取前 10 条),无结果直接退出;
  3. 下载 SARIF:取最新一条分析,经getSarifanalyses_url→ 分析详情 URL 三级跳转,以Accept: application/sarif+json拉取完整 SARIF,然后遍历runs[].results[],按ruleId关联规则表打印Rule / Severity / Message / File:Line摘要(fetch_sarif.mjs#L94-L116);
  4. 拉取告警列表:调用codeScanning.listAlertsForRepo获取同一 ref 的 code scanning alerts(每页 100 条),打印编号、规则、严重级别、状态与最近实例位置。

这使开发者无需打开 Web 界面即可在终端核对 CI 扫描结果,适合在本地修复误报或确认新告警后与远端数据比对。

内联抑制(Inline Suppressions)

对于确实安全的告警,Kibana 采用行内抑制注释,格式为// codeql[rule-id] justification text,且每条抑制必须附带具体理由,说明为什么该处是安全的。

有效示例:

// codeql[js/path-injection] User input is validated against an allowlist before use return fs.readFileSync(`/etc/${validatedPath}`, 'utf8');

无效示例(应被标记出来):

  • 缺少理由// codeql[js/path-injection]后跟任何解释;
  • 泛化理由"false positive""safe""not a vulnerability"—— 没有说明实际缓解措施;
  • 不完整理由:如仅写"sanitized"—— 没有说明以何种机制完成净化。

好的理由应描述具体的安全机制,例如白名单校验、DOMPurify 转义、shell-quote 库、仅限测试代码等。

支撑这套机制的是 suppression/AlertSuppression.ql:它是一个@kind alert-suppression查询(IDjs/alert-suppression),定义了单行注释节点SingleLineComment(要求注释内不含换行符)并复用官方AS::Make<AstNode, SingleLineComment>机制,使 GitHub 能识别并解析// codeql[...]注释中的规则 ID 与理由文本,从而在平台上展示抑制理由。

故障排查

问题解决方案
Docker 在 ARM 上构建失败确认设置了--platform linux/amd64(脚本会自动处理,见 quick_check.sh#L51-L54)
qlpack.ymlnot found脚本从.ql文件所在目录逐级向上查找qlpack.yml—— 确保custom-queries/根目录下存在该文件
测试产生.actual文件对比.actual.expected的 diff ——.actual文件已被 gitignore
查询什么都没命中检查 codeql-config.yml 的paths-ignore—— 测试/mock 目录被整体排除
摘要因找不到jq而无法打印安装 jq(如brew install jq

小结

Kibana 的 CodeQL 体系形成了“查询包 + 本地工具 + CI 门禁 + 远端结果”的完整闭环:

  • 查询包kibana-custom-queries 与 codeql-config.yml 决定扫描范围与规则集;
  • quick_check.shcodeql-env容器(CodeQL v2.23.2)中统一完成建库、分析与codeql test run,并在 arm64 上自动降级为 amd64 模拟执行;
  • 单元测试.qlref+test.js+.expected)保证每条规则在改动前后行为可控,并由 codeql-pr.yml 在 PR 上强制执行;
  • fetch_sarif.mjs补齐了远端结果核对环节;
  • 内联抑制规范+ AlertSuppression.ql 让每一次告警豁免都有据可查。

更多查询写法细节可参考 CodeQL 官方“编写 CodeQL 查询”文档(随 CodeQL CLI 发布),仓库内现成的 dos/ 与 xss/ 查询目录则是最直接的可运行范例。

【免费下载链接】kibanaYour window into all of your data项目地址: https://gitcode.com/GitHub_Trending/ki/kibana

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询