ThinkGrid-Labs/oxide-ci

GitHub: ThinkGrid-Labs/oxide-ci

一款 Rust 编写的零依赖 DevOps CLI,集密钥扫描、AST SAST、K8s/Docker 规范检查、依赖审计、覆盖率门禁于一体的 CI 质量门禁工具。

Stars: 0 | Forks: 0

# OxideCI — Rust DevOps CLI:Secret Scanner、AST SAST、Kubernetes Linter 与 CI Quality Gates [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![Build](https://img.shields.io/github/actions/workflow/status/ThinkGrid-Labs/oxide-ci/ci.yml?branch=main)](https://github.com/ThinkGrid-Labs/oxide-ci/actions) [![GitHub release](https://img.shields.io/github/v/release/ThinkGrid-Labs/oxide-ci)](https://github.com/ThinkGrid-Labs/oxide-ci/releases/latest) [![Crates.io](https://img.shields.io/crates/v/oxide-ci)](https://crates.io/crates/oxide-ci) [![Downloads](https://img.shields.io/crates/d/oxide-ci)](https://crates.io/crates/oxide-ci) [![MSRV](https://img.shields.io/badge/MSRV-1.85-orange)](https://www.rust-lang.org) [![GitHub Stars](https://img.shields.io/github/stars/ThinkGrid-Labs/oxide-ci?style=social)](https://github.com/ThinkGrid-Labs/oxide-ci/stargazers) ## 目录 - [为什么选择 OxideCI?](#why-oxideci) - [功能](#features) - [安装](#installation) - [快速开始](#quick-start) - [命令](#commands) - [scan](#scan--secret--pii-scanning) - [JS/TS/Python/Go 的 SAST](#sast-static-analysis-for-jstspythongo) - [GitHub Annotations](#github-check-run-annotations---annotate) - [Baseline 模式](#baseline-mode---update-baseline--since-baseline) - [lint](#lint--kubernetes-manifest-linting) - [docker-lint](#docker-lint--dockerfile-linting) - [coverage](#coverage--coverage-threshold-gate) - [audit](#audit--dependency-vulnerability-audit) - [install-hooks](#install-hooks--git-pre-commit-hook) - [lighthouse](#lighthouse--web-performance-audit) - [reassure](#reassure--react-component-performance-gate) - [sbom](#sbom--sbom-generation) - [check-config](#check-config--validate-configuration) - [init](#init--interactive-setup-wizard) - [watch](#watch--live-file-watcher) - [run](#run--pipeline-runner) - [Secret 检测模式](#secret-detection-patterns) - [SAST 规则参考](#sast-rules-reference) - [抑制发现项](#suppressing-findings) - [配置文件](#configuration-file-oxidecitorml) - [配置 Profiles](#config-profiles) - [输出格式](#output-formats) - [退出码](#exit-codes) - [CI/CD 集成](#cicd-integration) - [架构](#architecture) - [React Native](#react-native) - [故障排除](#troubleshooting) - [贡献](#contributing) ## 为什么选择 OxideCI? 大多数 DevOps 质量工具要么速度慢,要么需要运行时环境(Node、Python、Java),或者只能解决单一问题。OxideCI 将必要的 CI 门禁打包成一个单独的 Rust 编译二进制文件: | 问题 | OxideCI 命令 | |---|---| | 硬编码的 secrets 被推送到 git | `oxide-ci scan` | | JS/TS/Python/Go 中的 XSS、eval、命令注入 | `oxide-ci scan` (SAST) | | Kubernetes manifests 缺少资源限制 | `oxide-ci lint` | | Dockerfile 最佳实践违规 | `oxide-ci docker-lint` | | 测试覆盖率静默下降 | `oxide-ci coverage` | | 易受攻击的依赖项被发布到生产环境 | `oxide-ci audit` | | 在任何人注意到之前 secrets 已被提交 | `oxide-ci install-hooks` | | Web Lighthouse 分数在部署之间出现倒退 | `oxide-ci lighthouse` | | React 组件渲染性能出现倒退 | `oxide-ci reassure` | | 现有的发现项淹没 CI 噪音 | `oxide-ci scan --since-baseline` | | 需要知道谁引入了一个 secret | `oxide-ci scan --blame` | | 需要 SBOM 以满足合规要求 | `oxide-ci sbom` | | 配置文件有语法错误 | `oxide-ci check-config` | | 使用一个命令运行所有门禁 | `oxide-ci run` | | 无需阅读文档即可开始 | `oxide-ci init` | **核心优势:** - **零运行时依赖** — 将单个二进制文件放入任何 CI pipeline、Docker 镜像或开发人员机器。无需 Node、Python 或 JVM。 - **极快** — 通过 `rayon` 在所有 CPU 核心上并行扫描文件。典型的仓库扫描不到一秒钟。 - **基于 AST 的 SAST** — tree-sitter 在模式匹配之前将 JS/TS/TSX/JSX/Python/Go 解析为真正的 AST。Secrets 仅在它们出现在字符串字面量中时才会被标记,从而消除注释噪音和误报。 - **云服务商无关** — 检测 AWS、Azure、GCP、DigitalOcean、Alibaba Cloud、Stripe、GitHub、Twilio、Expo、Sentry、Mapbox 等平台的 secrets。 - **gitignore 感知** — 使用 `ignore` crate 自动跳过 `.gitignore` 中的文件,因此您绝不会意外扫描 `node_modules/` 或 `target/`。 - **CI 原生输出** — `--format sarif` 生成 SARIF 2.1.0 输出,GitHub Advanced Security 无需额外配置即可显示为内联 PR annotations。 - **可配置** — 单个 `.oxideci.toml` 文件为所有命令设置默认值;CLI flags 始终覆盖它。 ## 功能 | 功能 | 状态 | |---|---| | Secret & PII 扫描(26 种内置模式) | ✅ | | 每个发现项的严重性级别(critical / high / medium / low) | ✅ | | JS/TS/TSX/JSX 的基于 AST 的 SAST | ✅ | | Python 的基于 AST 的 SAST(eval、exec、pickle、subprocess、yaml.load) | ✅ | | Go 的基于 AST 的 SAST(unsafe、exec.Command、panic) | ✅ | | 字符串字面量范围界定(无注释 / JSX 文本噪音) | ✅ | | 危险模式检测(XSS、eval、命令注入) | ✅ | | 代码异味 / 复杂度规则(长函数、深层嵌套、参数过多) | ✅ | | 通过配置自定义 SAST 规则(tree-sitter S-expression 查询) | ✅ | | 通过配置自定义额外模式 | ✅ | | 通过 glob 模式排除路径 | ✅ | | 内联抑制(`// oxide-ci: ignore`) | ✅ | | Git diff / 仅暂存 / 完整历史扫描 | ✅ | | Git blame 增强(`--blame`) | ✅ | | JSON、SARIF 2.1.0、JUnit XML、GitLab SAST 输出格式 | ✅ | | GitHub Check Run annotations + 富 PR 摘要评论(`--annotate`) | ✅ | | Kubernetes manifest linting(7 条规则) | ✅ | | Dockerfile linting(8 条规则) | ✅ | | LCOV & Cobertura XML 覆盖率阈值门禁 | ✅ | | 通过 OSV API 进行依赖审计(6 个生态系统) | ✅ | | 离线 OSV 审计缓存(24 小时 TTL,适用于离线环境) | ✅ | | CycloneDX 1.5 SBOM 生成(`oxide-ci sbom`) | ✅ | | Git pre-commit hook 安装程序 | ✅ | | 通过 PageSpeed Insights 进行 Web 性能审计 | ✅ | | React 组件性能门禁(Reassure) | ✅ | | 扫描基线(在 CI 中抑制已知发现项) | ✅ | | 配置 profiles(`--profile strict\|relaxed\|ci`) | ✅ | | 配置验证(`oxide-ci check-config`) | ✅ | | Pipeline runner(`oxide-ci run`) | ✅ | | 交互式设置向导(`oxide-ci init`) | ✅ | | 实时文件监视器(`oxide-ci watch`) | ✅ | | `.oxideci.toml` 配置文件 | ✅ | | 遵循 `.gitignore` | ✅ | ## 安装 ### 推荐:预编译二进制文件 **macOS (Apple Silicon / M1+):** ``` curl -sL https://github.com/ThinkGrid-Labs/oxide-ci/releases/latest/download/oxide-ci-macos-arm64 \ -o /usr/local/bin/oxide-ci && chmod +x /usr/local/bin/oxide-ci ``` **macOS (Intel):** ``` curl -sL https://github.com/ThinkGrid-Labs/oxide-ci/releases/latest/download/oxide-ci-macos-amd64 \ -o /usr/local/bin/oxide-ci && chmod +x /usr/local/bin/oxide-ci ``` **Linux (x64):** ``` curl -sL https://github.com/ThinkGrid-Labs/oxide-ci/releases/latest/download/oxide-ci-linux-amd64 \ -o /usr/local/bin/oxide-ci && chmod +x /usr/local/bin/oxide-ci ``` **Windows (x64) — PowerShell:** ``` Invoke-WebRequest -Uri "https://github.com/ThinkGrid-Labs/oxide-ci/releases/latest/download/oxide-ci-windows-amd64.exe" ` -OutFile "$env:USERPROFILE\.local\bin\oxide-ci.exe" # 将 $env:USERPROFILE\.local\bin 添加到 PATH(如果尚未存在) ``` ### 从源码构建(需要 Rust 1.85+) ``` cargo install --git https://github.com/ThinkGrid-Labs/oxide-ci ``` ### 验证安装 ``` $ oxide-ci --version oxide-ci 0.2.6 $ oxide-ci --help A high-performance DevOps CLI tool in Rust Usage: oxide-ci [--profile ] Commands: scan Scans the current directory for hardcoded secrets and PII lint Validates Kubernetes YAML manifests for resource limits and security issues docker-lint Lints a Dockerfile for best-practice violations coverage Parses an LCOV or Cobertura XML coverage file and fails if below threshold audit Audits project dependencies for known vulnerabilities via the OSV database install-hooks Installs oxide-ci as a git pre-commit hook lighthouse Audits web performance via Google PageSpeed Insights (Lighthouse) reassure Parses a Reassure performance report and gates on regressions sbom Generates a CycloneDX 1.5 SBOM from the project's lock file check-config Validates .oxideci.toml and prints all resolved configuration values init Interactive wizard that generates a .oxideci.toml config file watch Re-runs scan automatically whenever source files change run Runs all pipeline steps defined in .oxideci.toml in order help Print this message or the help of the given subcommand(s) ``` ## 快速开始 ``` # 扫描当前仓库中的 secrets(包含针对 JS/TS 文件的 SAST) oxide-ci scan # Lint 所有 Kubernetes YAML 文件 oxide-ci lint --dir ./k8s # 强制执行最低 80% 覆盖率 oxide-ci coverage --file coverage/lcov.info --min 80 # 审计依赖项中的 CVE oxide-ci audit # 作为 git hook 安装(在每次提交时运行) oxide-ci install-hooks # 基于 Lighthouse web 性能分数进行把关 oxide-ci lighthouse --url https://yourapp.com # 基于 React 组件性能回归进行把关(在 `reassure measure` 之后) oxide-ci reassure ``` ## 命令 ### `scan` — Secret & PII 扫描 递归扫描当前目录中的每个文件,使用 26 种内置正则表达式模式查找硬编码的 secrets、凭证和 PII。自动遵循 `.gitignore`。 对于 JavaScript、TypeScript、Python 和 Go 文件,扫描器自动使用基于 AST 的传递(SAST)而不是普通正则表达式。这消除了来自注释和非字符串内容的误报,并额外标记危险的 API 模式。请参阅下面的 [SAST for JS/TS/Python/Go](#sast-static-analysis-for-jstspythongo)。 ``` oxide-ci scan [OPTIONS] Options: --format Output format: text (default), json, sarif, junit, gitlab --staged Only scan git-staged files (git diff --cached) --since Only scan files changed since the given commit --history Scan the entire git commit history (slow on large repos) --annotate Post findings as a GitHub Check Run with per-line annotations and a rich PR summary comment. Requires GITHUB_TOKEN, GITHUB_REPOSITORY, and GITHUB_SHA env vars. No-op when absent. --update-baseline Save current findings as the baseline (.oxide-baseline.json) --since-baseline Only fail on findings not present in the saved baseline --blame Enrich each finding with git blame info (author + commit) -h, --help Print help ``` **示例:** ``` # 全量扫描,人类可读输出 oxide-ci scan # 仅扫描您即将提交的内容(快速,非常适合 pre-commit) oxide-ci scan --staged # 仅扫描上次提交中更改的文件 oxide-ci scan --since HEAD~1 # 输出 SARIF 用于 GitHub Advanced Security PR 批注 oxide-ci scan --format sarif > results.sarif # 用于 Jenkins / Azure DevOps 的 JUnit XML oxide-ci scan --format junit > results.xml # GitLab SAST Security Scanner JSON oxide-ci scan --format gitlab > gl-sast-report.json # 输出 JSON 用于自定义工具 oxide-ci scan --format json | jq '.findings[].rule' # 利用 git blame 信息(作者 + 提交)丰富发现结果 oxide-ci scan --blame # 将发现结果直接发布到 GitHub Checks 选项卡和详细的 PR 摘要评论 GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} \ GITHUB_REPOSITORY=owner/repo \ GITHUB_SHA=${{ github.sha }} \ oxide-ci scan --annotate ``` **示例输出(文本):** ``` ℹ️ Starting secret and PII scan... ℹ️ Running SAST checks... ℹ️ SAST: scanning 14 file(s)... ⚠️ Found 3 potential issue(s): - [CRITICAL] [AWS Access Key] src/config.ts:14 - [CRITICAL] [SAST/EvalUsage] src/utils/parser.js:42 - [HIGH] [SAST/InnerHTMLAssignment] src/components/Widget.tsx:88 Error: Scan failed: 3 secret(s)/PII found. Review the findings above. ``` **示例输出(`--format json`):** ``` { "total": 3, "findings": [ { "rule": "AWS Access Key", "file": "src/config.ts", "line": 14, "severity": "critical" }, { "rule": "SAST/EvalUsage", "file": "src/utils/parser.js", "line": 42, "severity": "critical" }, { "rule": "SAST/InnerHTMLAssignment", "file": "src/components/Widget.tsx", "line": 88, "severity": "high" } ] } ``` #### SAST:JS/TS/Python/Go 的静态分析 当扫描 `.js`、`.jsx`、`.ts`、`.tsx`、`.py` 或 `.go` 文件时,oxide-ci 使用 [tree-sitter](https://tree-sitter.github.io/tree-sitter/) 在运行任何检查之前将每个文件解析为 AST。与普通正则表达式相比,这带来了两个重要的改进: **1. 用于 secrets 的字符串字面量范围界定** Secret 和 PII 正则表达式模式仅应用于字符串字面量值——而不是注释、导入声明或 JSX 文本内容。这意味着: ``` // AKIAIOSFODNN7EXAMPLE123 ← comment: NOT flagged ✅ const key = "AKIAIOSFODNN7EXAMPLE123"; // ← string literal: flagged ⚠️ const msg = `Contact us at admin@example.com`; // ← template literal: flagged ⚠️ ``` ``` // JSX text content is never flagged — only string attributes are:

contact@example.com

// ← JSX text: NOT flagged ✅ // ← string attribute: flagged ⚠️ ``` **2. 危险模式检测** SAST 还运行结构性规则,检测危险的 API 调用,无论其参数是否为字符串字面量。这些规则捕获正则表达式无法可靠发现的 XSS sinks、代码注入、命令注入和不安全的反序列化模式。 *JavaScript / TypeScript:* | Rule ID | 标记内容 | |---|---| | `SAST/DangerouslySetInnerHTML` | TSX/JSX 中的 `
` | | `SAST/InnerHTMLAssignment` | `element.innerHTML = expr` | | `SAST/OuterHTMLAssignment` | `element.outerHTML = expr` | | `SAST/EvalUsage` | `eval(expr)` | | `SAST/FunctionConstructor` | `new Function(...)` | | `SAST/SetTimeoutString` | `setTimeout("code", delay)` — 字符串作为第一个参数 | | `SAST/SetIntervalString` | `setInterval("code", delay)` — 字符串作为第一个参数 | | `SAST/ChildProcessExec` | `child_process.exec(cmd)` | | `SAST/ChildProcessExecSync` | `child_process.execSync(cmd)` | | `SAST/ChildProcessSpawn` | `child_process.spawn(cmd, args)` | | `SAST/ChildProcessExecFile` | `child_process.execFile(cmd)` | | `SAST/DocumentWrite` | `document.write(expr)` | | `SAST/DocumentWriteln` | `document.writeln(expr)` | *Python:* | Rule ID | 标记内容 | |---|---| | `SAST/PythonEval` | `eval(expr)` | | `SAST/PythonExec` | `exec(code)` | | `SAST/PythonPickle` | `pickle.load(f)` / `pickle.loads(data)` — 不安全的反序列化 | | `SAST/PythonSubprocessShell` | 任何带有 `shell=True` 的调用 — 命令注入风险 | | `SAST/PythonYamlLoad` | `yaml.load(data)` — 改用 `yaml.safe_load` | *Go:* | Rule ID | 标记内容 | |---|---| | `SAST/GoUnsafe` | `import "unsafe"` — 直接内存操作 | | `SAST/GoExecCommand` | `exec.Command(cmd, ...)` — 可能的命令注入 | | `SAST/GoPanic` | `panic(...)` — 意外的进程终止 | **3. 代码异味 / 复杂度规则**(仅限 JS/TS) 三个额外的规则在函数级别标记可维护性问题: | Rule ID | 触发条件 | 默认阈值 | |---|---|---| | `SMELL/LongFunction` | 函数体超过 `max_function_lines` 行 | 50 行 | | `SMELL/TooManyParameters` | 函数有超过 `max_parameters` 个参数 | 5 个参数 | | `SMELL/DeepNesting` | 函数内的控制流深度超过 `max_nesting_depth` | 4 层 | Rule IDs 包含用于提供即时上下文的测量值 — 例如 `SMELL/LongFunction (63 lines, max 50)`。阈值可在 `.oxidecil` 中配置: ``` [sast] max_function_lines = 60 # default 50 max_parameters = 6 # default 5 max_nesting_depth = 5 # default 4 ``` **4. 自定义 SAST 规则** 使用 [tree-sitter S-expression 查询](https://tree-sitter.github.io/tree-sitter/using-parsers/queries/) 定义项目特定的规则。每个规则必须包含一个标记要报告的最外层节点的 `@match` 捕获: ``` [sast] custom_rules = [ # Flag any direct call to eval() { id = "CUSTOM/EvalCall", query = "(call_expression function: (identifier) @_fn (#eq? @_fn \"eval\") arguments: (_) @match)" }, # Flag fetch() calls (useful for auditing outbound requests) { id = "CUSTOM/FetchCall", query = "(call_expression function: (identifier) @_fn (#eq? @_fn \"fetch\") @match)" }, ] ``` 自定义规则在启动时进行验证 — 无效的查询会被跳过并发出警告,绝不会导致 `oxide-ci scan` 崩溃。自定义 rule IDs 可以像任何内置规则一样使用 `disabled_rules` 禁用。 **禁用 SAST 或单个规则:** ``` # .oxideci.toml [sast] # 设置为 false 以完全禁用 SAST 并对所有文件回退到 regex enabled = true # 抑制在您的代码库中产生噪音的特定规则 disabled_rules = [ "SAST/ChildProcessExec", # if you intentionally shell out in a Node script "SAST/DocumentWrite", # if you have a legacy codebase that uses it "SAST/PythonSubprocessShell", # if subprocess with shell=True is intentional "SMELL/LongFunction", # if you have intentionally large generated files ] ``` **GitHub Check Run annotations + PR 摘要(`--annotate`):** 在 GitHub Actions 中运行时,传递 `--annotate` 让 oxide-ci 将发现项直接发布到 Checks 选项卡,附带逐行 annotations 和 PR 上的富 markdown 摘要评论。该评论包括严重性细分和逐项发现表(最多 20 行)。不需要 SARIF 上传步骤。 ``` - name: Secret, PII & SAST Scan env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: oxide-ci scan --annotate ``` 必需的环境变量(在 GitHub Actions 中自动设置):`GITHUB_TOKEN`、`GITHUB_REPOSITORY`、`GITHUB_SHA`。当任何环境变量缺失时,`--annotate` 无操作,扫描正常运行。干净的扫描(无发现项)会发布“No issues found”通过评论。 ### `lint` — Kubernetes Manifest Linting 根据安全性和可靠性最佳实践验证 Kubernetes workload YAML 文件(`Deployment`、`DaemonSet`、`StatefulSet`、`Job`、`CronJob`)。支持多文档 YAML 文件(`---` 分隔符)。 ``` oxide-ci lint [OPTIONS] Options: -d, --dir Directory to scan for Kubernetes manifests [default: . or lint.target_dir from config] -h, --help Print help ``` **执行的规则:** | Rule ID | 描述 | 应用于 | |---|---|---| | `no-latest-image` | Container image 使用 `:latest` 标签或根本没有标签 | 所有 workloads | | `no-resource-limits` | `resources.limits` 块完全缺失 | 所有 workloads | | `no-cpu-limit` | `resources.limits.cpu` 未设置 | 所有 workloads | | `no-memory-limit` | `resources.limits.memory` 未设置 | 所有 workloads | | `run-as-root` | `securityContext.runAsUser` 为 `0` | 所有 workloads | | `no-readiness-probe` | `readinessProbe` 未定义 | Deployment, DaemonSet, StatefulSet | | `no-liveness-probe` | `livenessProbe` 未定义 | Deployment, DaemonSet, StatefulSet | **示例:** ``` # Lint 当前目录中的 manifests oxide-ci lint # Lint 特定目录 oxide-ci lint --dir ./infrastructure/k8s ``` **示例输出:** ``` ℹ️ Linting Kubernetes manifests in './k8s'... ⚠️ Found 3 issue(s) across 2 file(s): [no-latest-image] k8s/api.yaml (container: api) — Image 'myapp:latest' uses an unpinned or :latest tag [no-memory-limit] k8s/api.yaml (container: api) — resources.limits.memory is not set [no-readiness-probe] k8s/worker.yaml (container: worker) — readinessProbe is not defined Error: K8s lint failed: 3 issue(s) found. ``` **完全合规的 manifest 示例:** ``` apiVersion: apps/v1 kind: Deployment metadata: name: api spec: template: spec: containers: - name: api image: myapp:1.4.2 # pinned tag resources: limits: cpu: "500m" memory: "256Mi" readinessProbe: httpGet: path: /health port: 8080 livenessProbe: httpGet: path: /health port: 8080 ``` ### `coverage` — 覆盖率阈值门禁 解析覆盖率报告,如果总行覆盖率低于指定的最小值,则以退出码 1 失败。显示低于阈值的文件的逐文件细分。 支持两种格式,并通过文件扩展名自动检测(或通过查看文件内容以识别模糊扩展名): | 格式 | 扩展名 | 使用者 | |---|---|---| | **LCOV** | `.info` 或无扩展名 | Rust (cargo-llvm-cov), Jest, pytest-cov, Go | | **Cobertura XML** | `.xml` | Python (coverage.py), Java (JaCoCo), .NET | ``` oxide-ci coverage [OPTIONS] Options: -f, --file Path to the coverage file [default: coverage/lcov.info or coverage.file from config] -m, --min Minimum coverage threshold percentage [default: 80 or coverage.min from config] -h, --help Print help ``` **示例:** ``` # LCOV (Rust, Jest, pytest) oxide-ci coverage --file coverage/lcov.info --min 80 # Cobertura XML (Python coverage.py, JaCoCo) oxide-ci coverage --file coverage.xml --min 80 # 强制执行更严格的 90% 关卡 oxide-ci coverage --file coverage/lcov.info --min 90 # 从 .oxideci.toml 读取默认值 oxide-ci coverage ``` **按语言生成覆盖率报告:** ``` # Rust (cargo-llvm-cov) → LCOV cargo llvm-cov --lcov --output-path coverage/lcov.info # JavaScript / TypeScript (Jest) → LCOV jest --coverage --coverageReporters=lcov # Python (pytest-cov) → LCOV or Cobertura pytest --cov=. --cov-report=lcov:coverage/lcov.info pytest --cov=. --cov-report=xml:coverage.xml # Go (go test) → LCOV go test ./... -coverprofile=coverage/lcov.info # Java (JaCoCo) → Cobertura XML # 配置您的构建工具以输出 Cobertura 格式并传递 .xml 文件 ``` **示例输出:** ``` ℹ️ Analyzing coverage file: coverage/lcov.info (threshold: 80.0%) Files below threshold (80.0%): 61.2% src/handlers/auth.rs 72.4% src/utils/parser.rs ⚠️ Coverage 74.8% is below threshold 80.0% (12 files, 748/1000 lines covered) Error: Coverage gate failed: 74.8% < 80.0% ``` ### `audit` — 依赖漏洞审计 自动检测项目的 lock 文件,解析所有固定的依赖项,并在单个批处理请求中查询 [OSV (Open Source Vulnerabilities)](https://osv.dev) 数据库。跨六个生态系统工作。 ``` oxide-ci audit Options: -h, --help Print help ``` **支持的 lock 文件(按顺序检查):** | Lock file | Ecosystem | 备注 | |---|---|---| | `Cargo.lock` | `crates.io` | 所有 registry 包 | | `package-lock.json` | `npm` | v2/v3 格式(`packages` map) | | `yarn.lock` | `npm` | v1 (classic) 和 v2/Berry | | `pnpm-lock.yaml` | `npm` | v5–v9(斜杠和无斜杠格式) | | `requirements.txt` | `PyPI` | 仅 `==` 固定版本 | | `go.sum` | `Go` | 所有模块校验和 | **示例:** ``` # Rust 项目 oxide-ci audit # Node.js 项目(npm, yarn, 或 pnpm — 自动检测) oxide-ci audit # Python 项目(自动检测) oxide-ci audit # Go 项目(自动检测) oxide-ci audit ``` **示例输出:** ``` ℹ️ Auditing 312 packages from Cargo.lock (crates.io) via OSV... ⚠️ Found 2 vulnerability/-ies in 312 packages: [GHSA-jfh8-c2jp-hdmh] openssl@0.10.55 — Use-after-free in X.509 certificate verification [CVE-2023-26964] h2@0.3.15 — Denial of Service via CONTINUATION frames Error: Audit failed: 2 known vulnerability/-ies found. ``` ### `install-hooks` — Git Pre-commit Hook 将 oxide-ci 安装为 git pre-commit hook,在每次 `git commit` 之前自动运行 `scan --staged`,在 secrets 到达远程之前将其捕获。 ``` oxide-ci install-hooks [OPTIONS] Options: --force Overwrite an existing hook without prompting -h, --help Print help ``` **它安装的内容**(写入 `.git/hooks/pre-commit`): ``` #!/bin/sh # oxide-ci pre-commit hook(自动安装) # 在每次提交前仅扫描已暂存文件中的 secrets 和 PII。 oxide-ci scan --staged ``` **示例:** ``` # 安装(安全 — 不会覆盖现有的 hook) oxide-ci install-hooks # 覆盖现有的 hook oxide-ci install-hooks --force ``` **示例输出:** ``` ✅ Pre-commit hook installed at /your/repo/.git/hooks/pre-commit ℹ️ oxide-ci scan --staged will now run before every commit. ``` ### `lighthouse` — Web 性能审计 从 [Google PageSpeed Insights API v5](https://developers.google.com/speed/docs/insights/v5/get-started)(在服务端运行真实的 Lighthouse 审计)获取您部署的 URL,并对四个类别的分数进行门禁:Performance、Accessibility、Best Practices 和 SEO。不需要 Node.js — 这是一个通过 `audit` 使用的相同 HTTP 客户端进行的纯 HTTPS 调用。 ``` oxide-ci lighthouse [OPTIONS] Options: --url URL to audit (overrides config) --strategy Device strategy: mobile (default) or desktop (overrides config) --min-performance Minimum Performance score 0–100 [default: 80] --min-accessibility Minimum Accessibility score 0–100 [default: 90] --min-best-practices Minimum Best Practices score 0–100 [default: 80] --min-seo Minimum SEO score 0–100 [default: 80] --key Google PageSpeed Insights API key (overrides PAGESPEED_API_KEY env var) -h, --help Print help ``` **示例:** ``` # 使用默认阈值进行审计(移动端策略) oxide-ci lighthouse --url https://yourapp.com # 使用更严格性能阈值的桌面端审计 oxide-ci lighthouse --url https://yourapp.com --strategy desktop --min-performance 90 # 使用 API key 获取更高配额(未认证配额:少量请求/天) oxide-ci lighthouse --url https://yourapp.com --key AIza... # 从 .oxideci.toml 读取 URL 和阈值 oxide-ci lighthouse ``` **API key:** PageSpeed Insights API 偶尔运行(开发、不频繁的 CI)无需 key 即可工作。对于在每个 PR 上运行的生产 CI pipelines,请在 [Google Cloud Console](https://console.cloud.google.com/apis/library/pagespeedonline.googleapis.com) 中创建一个免费 key 并通过 `PAGESPEED_API_KEY` 环境变量传递它: ``` export PAGESPEED_API_KEY=AIza... oxide-ci lighthouse --url https://yourapp.com ``` **示例输出:** ``` ℹ️ Running Lighthouse audit: https://yourapp.com (mobile) Performance: 87 ✅ (min: 80) Accessibility: 95 ✅ (min: 90) Best Practices: 75 ❌ (min: 80) SEO: 98 ✅ (min: 80) Error: Lighthouse failed: 1 category/-ies below threshold. ``` **配置(`.oxideci.toml`):** ``` [lighthouse] url = "https://yourapp.com" strategy = "mobile" # mobile | desktop min_performance = 80 min_accessibility = 90 min_best_practices = 80 min_seo = 80 # api_key = "" # 首选 PAGESPEED_API_KEY env var ``` ### `reassure` — React 组件性能门禁 解析由 [Reassure](https://github.com/callstack/reassure)(`reassure measure`)生成的 JSON 性能报告,如果任何组件的平均渲染时间超过可配置的阈值倒退,或者渲染次数增加,则使 CI 失败。 Reassure 通过多次运行每个测试场景(默认 10 次迭代 × 5 次运行)来测量真实的组件性能。输出是一个 `.perf` JSON 文件,oxide-ci 直接读取该文件 — 门禁时不需要 Node.js。 ``` oxide-ci reassure [OPTIONS] Options: --current Path to current.perf file [default: output/current.perf] --baseline Path to baseline.perf file [default: output/baseline.perf] --threshold % mean-time increase allowed before failure [default: 15] -h, --help Print help ``` **典型的 CI 工作流:** ``` # 1. 在您的前端项目中,运行 Reassure 以生成 current.perf npx reassure measure # 2. 使用 oxide-ci 对回归进行把关 oxide-ci reassure # 3. 要与保存的 baseline 进行比较,首先保存它: cp output/current.perf output/baseline.perf # after a known-good run # 然后在后续运行中,oxide-ci 会自动比较 current 与 baseline ``` **倒退规则:** | 条件 | 结果 | |---|---| | `meanTime` 增加超过 `threshold`% | ❌ 失败 | | `renders` 计数相对于 baseline 增加 | ❌ 失败 | | 未找到 `baseline.perf` | 仅报告(信息性,无失败) | | 新组件无 baseline 条目 | 列为 `new`,不标记 | **示例输出(带 baseline):** ``` ℹ️ Parsing Reassure report: output/current.perf ℹ️ Baseline found: output/baseline.perf Component Mean (ms) Renders Δ Mean Δ Renders ──────────────────────────────────────────────────────────────────────────────────── ProductList render 15.4 2.3 +2.1% ─ HeavyList render 42.1 3.0 +21.3% ❌ ─ SearchBox render 8.9 1.0 -5.2% ─ NewComponent render 6.3 1.0 new ─ Error: Reassure failed: 1 component(s) exceed the 15.0% regression threshold. ``` **示例输出(无 baseline — 仅报告模式):** ``` ℹ️ Parsing Reassure report: output/current.perf ⚠️ Baseline file not found — running in report-only mode. Component Mean (ms) Renders ───────────────────────────────────────────────────────────── ProductList render 15.4 2.3 HeavyList render 42.1 3.0 SearchBox render 8.9 1.0 ℹ️ No baseline provided — metrics reported above (no gating applied). ``` **配置(`.oxideci.toml`):** ``` [reassure] current = "output/current.perf" baseline = "output/baseline.perf" threshold = 15.0 # % mean-time regression allowed ``` **在 React 项目中设置 Reassure:** ``` # 安装 npm install --save-dev reassure # 编写性能测试(例如 __perf__/ProductList.perf.tsx) import { measureRenders } from 'reassure'; import { ProductList } from '../ProductList'; test('ProductList render', async () => { await measureRenders(); }); # 运行测量(生成 output/current.perf) npx reassure measure # 然后使用 oxide-ci 进行把关 oxide-ci reassure --threshold 10 ``` ### `sbom` — SBOM 生成 从项目的 lock 文件生成 [CycloneDX 1.5](https://cyclonedx.org/specification/overview/) 软件物料清单。不需要互联网访问。 ``` oxide-ci sbom [OPTIONS] Options: -o, --output Write SBOM to a file instead of stdout ``` **支持的 lock 文件**(按顺序检查):`Cargo.lock`、`package-lock.json`、`requirements.txt`、`go.sum` ``` # 打印到 stdout oxide-ci sbom # 写入文件 oxide-ci sbom --output sbom.json ``` 每个组件包括 `name`、`version`、`purl` (Package URL) 和 `scope: required`。CycloneDX SBOMs 被 Dependency-Track、FOSSA、Grype、Trivy 和大多数企业合规平台接受。 ### `check-config` — 验证配置 读取 `.oxideci.toml`,验证它,并打印所有解析后的配置值。在运行 CI 之前确认覆盖已正确应用非常有用。 ``` oxide-ci check-config # 应用 profile 预览合并后的值 oxide-ci --profile strict check-config ``` 配置有效时(或文件不存在时,打印内置默认值)退出码为 0。如果文件有解析错误则退出码为 1。 ## `docker-lint` — Dockerfile Linting 检查 `Dockerfile` 是否违反 8 条规则的最佳实践。 ``` oxide-ci docker-lint [--file ] ``` | 规则 | 捕获内容 | |---|---| | `no-latest-image` | `FROM node:latest` 或未标记的 `FROM node` | | `prefer-copy-over-add` | `ADD` 在 `COPY` 足够的地方使用 | | `no-user-directive` | 没有 `USER` 指令 — 默认以 root 运行 | | `no-root-user` | 显式的 `USER root` 或 `USER 0` | | `no-healthcheck` | 缺少 `HEALTHCHECK` 指令 | | `secret-in-env` | `ENV API_KEY=…`、`ENV PASSWORD=…` 烘焙到镜像层中 | | `apt-no-recommends` | `apt-get install` 没有 `--no-install-recommends` | | `apt-stale-cache` | `apt-get update` 和 `apt-get install` 在单独的 `RUN` 层中 | 在 `.oxideci.toml` 中配置默认路径: ``` [docker] dockerfile = "Dockerfile" ``` ## Baseline 模式 — `--update-baseline` / `--since-baseline` 在有现有发现项的仓库中,baseline 模式允许 CI 仅对 PR 引入的**新**发现项进行门禁 — 而不会因团队尚未修复的先前存在的问题而失败。 **步骤 1:保存 baseline**(在您的主分支上运行一次,提交输出) ``` oxide-ci scan --update-baseline # 写入 .oxide-baseline.json git add .oxide-baseline.json && git commit -m "chore: add oxide-ci scan baseline" ``` **步骤 2:在 CI 中使用 baseline**(PR 仅因新发现项而失败) ``` oxide-ci scan --since-baseline ``` Baseline 存储 `(file, rule, line)` 指纹。移动到不同行的 secret 被视为新发现项并重新审查。 ## `init` — 交互式设置向导 通过询问几个问题生成定制的 `.oxideci.toml`。最快的入门方式。 ``` oxide-ci init # guided setup oxide-ci init --force # overwrite existing config ``` 示例会话: ``` oxide-ci init — generating .oxideci.toml [ Secret & SAST Scanning ] Enable entropy-based secret detection? [Y/n]: Entropy threshold [4.5]: [ Coverage Gate ] LCOV file path [coverage/lcov.info]: Minimum coverage % [80]: [ Docker Lint ] Dockerfile path (leave blank to skip) []: [ Pipeline Runner ] Generate a default pipeline (oxide-ci run)? [Y/n]: ✅ .oxideci.toml written successfully. ``` ## `watch` — 实时文件监视器 每次源文件更改时自动重新运行扫描。在主动开发期间很有用。 ``` oxide-ci watch # watch full working tree oxide-ci watch --staged # only scan staged files on change oxide-ci watch --interval 500 # poll every 500ms ``` 与 CI 模式不同,`watch` **不会因发现项而退出** — 它报告它们并继续监视,以便您可以就地修复。 ## `run` — Pipeline Runner 使用 `.oxideci.toml` 中定义的步骤按顺序运行所有质量门禁。第一次失败会停止 pipeline。 ``` oxide-ci run ``` 定义您的 pipeline: ``` [pipeline] steps = [ "scan", "audit", "coverage --min 80", "lint --dir ./k8s", "docker-lint", "lighthouse", ] ``` 每个步骤直接映射到一个 `oxide-ci` 子命令。内联 flags 覆盖该步骤的配置值。使用 `oxide-ci init` 自动生成启动 pipeline。 ## Secret 检测模式 OxideCI 附带 26 种内置模式,涵盖最常见的云提供商和服务。所有模式对于非 JS/TS 文件按行应用,对于 JS/TS/TSX/JSX 文件则范围限定为字符串字面量(无注释噪音)。 ### AWS | Rule ID | 检测内容 | |---|---| | `AWS Access Key` | IAM access key IDs(`AKIA…16 chars`) | | `AWS Secret Key` | 配置文件中的 `aws_secret_access_key = …40 chars` | ### Azure | Rule ID | 检测内容 | |---|---| | `Azure Storage Connection String` | 包含 `DefaultEndpointsProtocol` + `AccountKey` 的完整连接字符串 | | `Azure SAS Token` | 包含 `sv=20XX-XX-XX` + `&sig=` 的 Shared Access Signature URLs | ### GCP / Google Cloud | Rule ID | 检测内容 | |---|---| | `Google API Key` | Browser/server API keys(`AIza…35 chars`);也捕获 Firebase API keys | | `GCP Service Account Key` | Service account JSON 文件(`"type": "service_account"`);也捕获 `google-services.json` | | `GCP OAuth2 Token` | 短期访问令牌(`ya29.…`) | ### DigitalOcean | Rule ID | 检测内容 | |---|---| | `DigitalOcean PAT` | 个人访问令牌(`dop_v1_…64 chars`) | ### Alibaba Cloud | Rule ID | 检测内容 | |---|---| | `Alibaba Cloud Access Key ID` | Access key IDs(`LTAI…14-20 chars`) | ### GitHub | Rule ID | 检测内容 | |---|---| | `GitHub PAT (classic)` | 经典个人访问令牌(`ghp_…36 chars`) | | `GitHub PAT (fine-grained)` | 细粒度个人访问令牌(`github_pat_…82 chars`) | ### 通信与支付 | Rule ID | 检测内容 | |---|---| | `Slack Webhook` | Incoming webhook URLs(`hooks.slack.com/services/…`) | | `Stripe Secret Key` | Live secret keys(`sk_live_…24 chars`) | | `Stripe Publishable Key` | Live publishable keys(`pk_live_…24`) | | `SendGrid API Key` | API keys(`SG.22chars.43chars`) | | `Mailgun API Key` | API keys(`key-…32 chars`) | | `Twilio Account SID` | Account SIDs(`AC` + 32 个小写十六进制字符) | ### 基础设施 | Rule ID | 检测内容 | |---|---| | `HashiCorp Vault Token` | Service tokens(`hvs.…90+ chars`) | | `PEM Private Key` | RSA、EC、DSA、OPENSSH 私钥头 | | `JWT Token` | 三部分 base64url 令牌(`eyJ…`) | ### React Native / Mobile | Rule ID | 检测内容 | |---|---| | `Expo Access Token` | EAS CLI robot/personal tokens(`expa_…40+ chars`) | | `Sentry DSN` | 错误报告 DSN(`https://key@o123.ingest.sentry.io/project`) | | `Mapbox Secret Token` | Secret tokens(`sk.eyJ…`);public tokens(`pk.eyJ…`)不被标记 | ### PII | Rule ID | 检测内容 | |---|---| | `Generic PII (SSN)` | 美国社会安全号码(`XXX-XX-XXXX`) | | `Generic PII (Email)` | 电子邮件地址 | ### Shannon 熵检测 除了命名模式外,oxide-ci 还标记不匹配任何已知模式的高熵令牌 — 捕获无法识别的 API keys、随机 secrets 和不透明凭证: ``` [scan] entropy = true # enable/disable (default: true) entropy_threshold = 4.5 # Shannon entropy score; lower = more sensitive (default: 4.5) entropy_min_length = 20 # minimum token length before entropy is checked (default: 20) ``` ### 添加自定义模式 使用 `.oxideci.toml` 中的 `extra_patterns` 添加您自己的模式而无需 fork: ``` [scan] extra_patterns = [ { name = "Internal API Token", regex = "myapp_[a-z0-9]{32}" }, { name = "Database URL", regex = "postgres://[^@]+@[^/]+" }, ] ``` ## SAST 规则参考 规则使用 tree-sitter AST 查询在 `.js`、`.jsx`、`.ts`、`.tsx`、`.py` 和 `.go` 文件上检查。它们基于代码结构触发,而不是字符串内容。 ### XSS sinks (JS/TS) | Rule ID | 触发条件 | 风险 | |---|---|---| | `SAST/DangerouslySetInnerHTML` | `` | 通过 React 绕过 XSS | | `SAST/InnerHTMLAssignment` | `el.innerHTML = expr` | DOM XSS | | `SAST/OuterHTMLAssignment` | `el.outerHTML = expr` | DOM XSS | | `SAST/DocumentWrite` | `document.write(expr)` | 旧版 DOM XSS | | `SAST/DocumentWriteln` | `document.writeln(expr)` | 旧版 DOM XSS | ### 代码注入 (JS/TS) | Rule ID | 触发条件 | 风险 | |---|---|---| | `SAST/EvalUsage` | `eval(expr)` | 任意代码执行 | | `SAST/FunctionConstructor` | `new Function(...)` | 任意代码执行 | | `SAST/SetTimeoutString` | `setTimeout("string", delay)` | eval 等效 | | `SAST/SetIntervalString` | `setInterval("string", delay)` | eval 等效 | ### 命令注入 (Node.js) | Rule ID | 触发条件 | 风险 | |---|---|---| | `SAST/ChildProcessExec` | `cp.exec(cmd)` | OS 命令注入 | | `SAST/ChildProcessExecSync` | `cp.execSync(cmd)` | OS 命令注入 | | `SAST/ChildProcessSpawn` | `cp.spawn(cmd, args)` | OS 命令注入 | | `SAST/ChildProcessExecFile` | `cp.execFile(path)` | OS 命令注入 | ### Python 规则 | Rule ID | 触发条件 | 严重性 | |---|---|---| | `SAST/PythonEval` | `eval(expr)` | critical | | `SAST/PythonExec` | `exec(code)` | critical | | `SAST/PythonPickle` | `pickle.load(f)` / `pickle.loads(data)` | high | | `SAST/PythonSubprocessShell` | 任何带有 `shell=True` 关键字参数的调用 | high | | `SAST/PythonYamlLoad` | `yaml.load(data)` — 改用 `yaml.safe_load` | high | ### Go 规则 | Rule ID | 触发条件 | 严重性 | |---|---|---| | `SAST/GoUnsafe` | `import "unsafe"` | high | | `SAST/GoExecCommand` | `exec.Command(cmd, ...)` | high | | `SAST/GoPanic` | `panic(...)` | medium | ### 代码异味 / 复杂度(仅限 JS/TS) | Rule ID | 触发条件 | 默认值 | |---|---|---| | `SMELL/LongFunction` | 函数体超过 `max_function_lines` 行 | 50 行 | | `SMELL/TooManyParameters` | 函数有超过 `max_parameters` 个参数 | 5 个参数 | | `SMELL/DeepNesting` | 控制流嵌套深度超过 `max_nesting_depth` | 4 层 | Rule IDs 嵌入测量值 — 例如 `SMELL/LongFunction (63 lines, max 50)` — 因此发现项无需额外上下文即可自我解释。阈值可在 `.oxideci.toml` 中按项目配置。详情请参阅 [配置文件](#configuration-file-oxidecitorml)。 ## 抑制发现项 在发现项的同一行添加 `// oxide-ci: ignore` 以抑制它: ``` const legacyKey = "AKIAIOSFODNN7EXAMPLE123"; // oxide-ci: ignore el.innerHTML = sanitizedHtml; // oxide-ci: ignore eval(trustedAdminScript); // oxide-ci: ignore ``` 抑制适用于 secret/PII 发现项和 SAST 危险模式发现项。它按行应用 — 仅抑制该确切行的发现项。 ## 配置文件(`.oxideci.toml`) 将 `.oxideci.toml` 放在仓库的根目录中。CLI flags 始终覆盖配置文件值。 ``` [scan] # 扫描期间要跳过的路径 Glob 模式 exclude_patterns = [ "tests/**", "*.test.ts", "fixtures/**", "vendor/**", ] # 在 26 个内置模式之上的额外模式 extra_patterns = [ { name = "Internal Service Token", regex = "svc_[a-z0-9]{40}" }, ] # Shannon entropy 检测(标记高熵令牌,如无法识别的 API keys) entropy = true entropy_threshold = 4.5 # lower = more sensitive entropy_min_length = 20 # ignore tokens shorter than this [sast] # 设置为 false 以对 JS/TS 文件完全禁用 SAST(回退到纯 regex) enabled = true # 抑制在您的代码库中产生噪音的特定 SAST rule ID disabled_rules = [ # "SAST/ChildProcessExec", # "SAST/EvalUsage", # "SMELL/LongFunction", ] # 代码异味阈值(显示默认值) max_function_lines = 50 # flag functions longer than this many lines max_parameters = 5 # flag functions with more than this many parameters max_nesting_depth = 4 # flag control-flow nesting deeper than this inside a function # 使用 tree-sitter S-expression 查询的自定义规则 # 每条规则必须包含一个 @match capture,标记要报告的最外层节点。 custom_rules = [ # { id = "CUSTOM/FetchCall", query = "(call_expression function: (identifier) @_fn (#eq? @_fn \"fetch\") @match)" }, ] [coverage] # 默认 LCOV 文件路径(被 --file 覆盖) file = "coverage/lcov.info" # 默认最低阈值(被 --min 覆盖) min = 85.0 [lint] # 扫描 Kubernetes manifests 的默认目录(被 --dir 覆盖) target_dir = "./infrastructure/k8s" [lighthouse] # 要审计的已部署应用的 URL(被 --url 覆盖) url = "https://yourapp.com" # 设备策略:mobile(默认)或 desktop(被 --strategy 覆盖) strategy = "mobile" # 每类最低分数 0–100(被 --min-* flags 覆盖) min_performance = 80 min_accessibility = 90 min_best_practices = 80 min_seo = 80 # API key(可选;首选 PAGESPEED_API_KEY env var) # api_key = "" [reassure] # Reassure current 测量文件的路径(被 --current 覆盖) current = "output/current.perf" # Reassure baseline 文件的路径(被 --baseline 覆盖) # 如果不存在,oxide-ci 将以仅报告模式运行(不会失败) baseline = "output/baseline.perf" # 失败前的最大平均耗时回归百分比(被 --threshold 覆盖) threshold = 15.0 ``` 所有字段都是可选的。省略的值回退到安全默认值。CLI flags 始终优先于配置文件值。 ## 输出格式 `scan` 命令通过 `--format` 支持五种输出格式: ### `text`(默认) 人类可读的输出到 stderr,每个发现项都有严重性前缀。 ``` - [CRITICAL] [AWS Access Key] src/config.ts:14 - [HIGH] [SAST/InnerHTMLAssignment] src/components/Widget.tsx:88 ``` ### `json` 机器可读的 JSON 写入 stdout。每个发现项包含 `severity` 字段。 ``` { "total": 1, "findings": [ { "rule": "SAST/EvalUsage", "file": "./src/utils.js", "line": 42, "severity": "critical" } ] } ``` ### `sarif` SARIF 2.1.0 JSON 写入 stdout。直接上传到 GitHub Advanced Security 以获取内联 PR annotations。 ``` oxide-ci scan --format sarif > results.sarif ``` ### `junit` JUnit XML 写入 stdout。与 Jenkins、Azure DevOps 和 CircleCI 测试报告解析器兼容。 ``` oxide-ci scan --format junit > results.xml ``` ### `gitlab` GitLab SAST Security Scanner JSON(schema v15.0.6)写入 stdout。用作 GitLab 安全 artifact 以在 Merge Request 安全报告中显示发现项。 ``` # .gitlab-ci.yml secret-scan: script: oxide-ci scan --format gitlab > gl-sast-report.json artifacts: reports: sast: gl-sast-report.json ``` ## 配置 Profiles 在加载的配置之上应用命名质量 profile,而无需编辑 `.oxideci.toml`。在子命令之前传递 `--profile` 作为全局 flag: ``` oxide-ci --profile strict scan oxide-ci --profile ci scan --staged oxide-ci --profile relaxed coverage ``` | Profile | 效果 | |---|---| | `strict` | Coverage ≥ 90%,熵阈值 3.5(更敏感),Lighthouse performance ≥ 90 & accessibility ≥ 95,SAST 启用 | | `relaxed` | Coverage ≥ 70%,熵阈值 5.0(更少误报) | | `ci` | Coverage ≥ 80%,SAST 启用,`SMELL/*` 规则禁用以减少 CI 中的噪音 | Profiles 仅修改内存中的配置 — 它们从不写入 `.oxideci.toml`。 ## 退出码 | 代码 | 含义 | |---|---| | `0` | 所有检查通过 — 可以安全继续 | | `1` | 检查失败(发现 secrets、SAST 问题、lint 问题、覆盖率低于阈值、检测到漏洞)或工具错误 | ## CI/CD 集成 ### GitHub Actions — 完整 pipeline ``` name: OxideCI Quality Gate on: [push, pull_request] permissions: contents: read security-events: write # required for SARIF upload and Check Runs checks: write # required for --annotate (GitHub Check Runs) pull-requests: write # required for --annotate (PR review comment) jobs: # ── Security & code-quality gates ────────────────────────────────────────── oxide-ci: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install OxideCI run: | curl -sL https://github.com/ThinkGrid-Labs/oxide-ci/releases/latest/download/oxide-ci-linux-amd64 \ -o /usr/local/bin/oxide-ci chmod +x /usr/local/bin/oxide-ci # Option A: Native GitHub Check Run annotations (no SARIF upload step needed) - name: Secret, PII & SAST Scan env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: oxide-ci scan --annotate # Option B: SARIF upload to GitHub Advanced Security (alternative to --annotate) # - name: Scan (SARIF for PR annotations) # run: oxide-ci scan --format sarif > results.sarif # if: always() # continue-on-error: true # - name: Upload SARIF # uses: github/codeql-action/upload-sarif@v4 # if: always() # with: # sarif_file: results.sarif # continue-on-error: true - name: Kubernetes Lint run: oxide-ci lint --dir ./k8s - name: Coverage Gate run: oxide-ci coverage --file coverage/lcov.info --min 80 - name: Dependency Audit run: oxide-ci audit # ── Performance gates (post-deploy) ──────────────────────────────────────── perf: runs-on: ubuntu-latest # needs: [deploy] # uncomment and set your deploy job name steps: - uses: actions/checkout@v4 - name: Install OxideCI run: | curl -sL https://github.com/ThinkGrid-Labs/oxide-ci/releases/latest/download/oxide-ci-linux-amd64 \ -o /usr/local/bin/oxide-ci chmod +x /usr/local/bin/oxide-ci # Lighthouse — gates on PageSpeed scores for the deployed URL. # Set LIGHTHOUSE_URL as a repository variable (Settings → Variables). # Set PAGESPEED_API_KEY as a repository secret for higher quota. - name: Lighthouse audit if: ${{ vars.LIGHTHOUSE_URL != '' }} env: PAGESPEED_API_KEY: ${{ secrets.PAGESPEED_API_KEY }} run: | oxide-ci lighthouse \ --url "${{ vars.LIGHTHOUSE_URL }}" \ --strategy mobile \ --min-performance 80 \ --min-accessibility 90 # Reassure — gates on React component render regressions. # Your frontend test job should run `reassure measure` and upload # output/current.perf as an artifact, then download it here. - name: Download Reassure report uses: actions/download-artifact@v4 with: name: reassure-report path: output/ continue-on-error: true # skip gracefully if artifact not found - name: Reassure performance gate if: hashFiles('output/current.perf') != '' run: oxide-ci reassure --threshold 15 ``` ### GitLab CI ``` stages: - security - quality variables: OXIDE_CI_URL: https://github.com/ThinkGrid-Labs/oxide-ci/releases/latest/download/oxide-ci-linux-amd64 .install_oxide: &install_oxide before_script: - curl -sL $OXIDE_CI_URL -o /usr/local/bin/oxide-ci - chmod +x /usr/local/bin/oxide-ci secret-and-sast-scan: stage: security <<: *install_oxide script: - oxide-ci scan k8s-lint: stage: security <<: *install_oxide script: - oxide-ci lint --dir ./k8s coverage-gate: stage: quality <<: *install_oxide script: - oxide-ci coverage --file coverage/lcov.info --min 80 dependency-audit: stage: security <<: *install_oxide script: - oxide-ci audit allow_failure: true # optional: don't block pipeline on network issues ``` ### Bitbucket Pipelines ``` image: ubuntu:22.04 pipelines: default: - step: name: OxideCI Security & Quality Gates script: - apt-get update -qq && apt-get install -y curl - curl -sL https://github.com/ThinkGrid-Labs/oxide-ci/releases/latest/download/oxide-ci-linux-amd64 -o /usr/local/bin/oxide-ci - chmod +x /usr/local/bin/oxide-ci - oxide-ci scan - oxide-ci lint --dir ./k8s - oxide-ci coverage --file coverage/lcov.info --min 80 - oxide-ci audit ``` ### CircleCI ``` version: 2.1 jobs: oxide-ci: docker: - image: cimg/base:stable steps: - checkout - run: name: Install OxideCI command: | curl -sL https://github.com/ThinkGrid-Labs/oxide-ci/releases/latest/download/oxide-ci-linux-amd64 \ -o /usr/local/bin/oxide-ci chmod +x /usr/local/bin/oxide-ci - run: name: Secret, PII & SAST Scan command: oxide-ci scan - run: name: Kubernetes Lint command: oxide-ci lint --dir ./k8s - run: name: Coverage Gate command: oxide-ci coverage --file coverage/lcov.info --min 80 - run: name: Dependency Audit command: oxide-ci audit workflows: quality: jobs: - oxide-ci ``` ### Pre-commit(本地强制执行) 在本地强制执行 secrets 扫描的最快方法 — 在每次 `git commit` 时自动运行: ``` oxide-ci install-hooks ``` 要移除 hook: ``` rm .git/hooks/pre-commit ``` ## 架构 ``` oxide-ci/ ├── src/ │ ├── main.rs # CLI entry point (clap) │ ├── modules/ │ │ ├── scanner.rs # Secret/PII scanning (rayon parallel) + SAST orchestration │ │ ├── sast.rs # AST-based SAST for JS/TS/TSX/JSX (tree-sitter) │ │ ├── github.rs # GitHub Check Runs + PR review comment (--annotate) │ │ ├── k8s_lint.rs # Kubernetes manifest linter (serde_yaml) │ │ ├── coverage.rs # LCOV & Cobertura XML parser and threshold gate │ │ ├── audit.rs # OSV dependency audit (ureq) │ │ ├── hooks.rs # Git hook installer │ │ ├── perf_lighthouse.rs # PageSpeed Insights Lighthouse gate (ureq) │ │ └── reassure.rs # Reassure .perf report parser and gate │ └── utils/ │ ├── config.rs # .oxideci.toml loader (toml + serde) │ ├── files.rs # File walker (ignore crate) │ └── terminal.rs # Styled output + progress bars (indicatif) └── tests/ └── integration_test.rs # End-to-end binary tests ``` **JS/TS 文件的扫描 pipeline:** ``` collect_findings() │ ├── non-JS/TS files ──→ run_regex_scan() (rayon parallel, per-line regex + entropy) │ └── JS/TS files ──────→ run_sast_scan() (rayon parallel, tree-sitter per file) │ ├── parse AST (tree-sitter) ├── scan_string_literals() ← secrets/PII scoped to string nodes ├── scan_dangerous_patterns() ← 13 structural rules + custom rules └── scan_complexity() ← 3 code smell rules (long function, params, nesting) emit_findings() ← text / JSON / SARIF │ └── --annotate ──→ github::annotate() ← GitHub Check Run + PR comment (no-op if env absent) ``` **依赖项:** | Crate | 用途 | |---|---| | `clap` | CLI 参数解析 | | `rayon` | CPU 密集型并行(文件扫描) | | `ignore` | gitignore 感知的文件遍历 | | `regex` | Secret 模式匹配 | | `tree-sitter` | AST 解析引擎 | | `tree-sitter-javascript` | JS/JSX 语法 | | `tree-sitter-typescript` | TS/TSX 语法 | | `streaming-iterator` | tree-sitter 0.24 `QueryMatches` API 必需 | | `serde` + `serde_json` | JSON 输出(SARIF、audit、GitHub API bodies) | | `serde_yaml` | Kubernetes YAML 解析 | | `roxmltree` | 零拷贝 Cobertura XML 解析 | | `toml` | 配置文件解析 | | `ureq` | HTTP 客户端(OSV audit、PageSpeed、GitHub APIs) | | `indicatif` | 进度条 | | `anyhow` | 错误处理和传播 | ## React Native oxide-ci 开箱即用地支持 React Native 和 Expo 项目。npm、Yarn 和 pnpm lock 文件都支持依赖审计,SAST 自动在所有 JS/TS/TSX 文件上运行,而最初由 Callstack *为* React Native 构建的 Reassure 是一等公民。 ### 命令兼容性 | 命令 | React Native 支持 | 备注 | |---|---|---| | `scan` | ✅ 完全 | Secrets + PII + JS/TS/TSX 文件上的 SAST;gitignore 感知(自动跳过 `node_modules/`) | | `audit` | ✅ 完全 | 读取 `package-lock.json`、`yarn.lock` 和 `pnpm-lock.yaml`;查询 OSV 获取 npm CVEs | | `coverage` | ✅ 完全 | 读取 Jest LCOV 输出(`--coverageReporters=lcov`) | | `install-hooks` | ✅ 完全 | Pre-commit hook 适用于任何 git repo | | `reassure` | ✅ 完全 | 为 React Native 构建;解析 `reassure measure` 的 `output/current.perf` | | `lint` | ⚠️ 可选 | 仅在项目有 Kubernetes 后端时有用 | | `lighthouse` | ⚠️ 仅限 Web | 审计公共 URL;如果您发布 React Native Web build 或营销网站则适用 | ### React Native 特定的 secrets 检测 除了 23 种内置云模式外,oxide-ci 还检测 RN 项目中常见的这些模式(范围限定为 JS/TS 文件中的字符串字面量): | 规则 | 检测内容 | |---|---| | `Expo Access Token` | EAS CLI robot/personal tokens(`expa_…`) | | `Sentry DSN` | Sentry 错误报告 DSN(配额耗尽 + 事件读取风险) | | `Mapbox Secret Token` | Mapbox secret tokens(`sk.eyJ…`);public tokens(`pk.eyJ…`)不被标记 | | `Google API Key` | Firebase API keys(`AIza…`) — 与 GCP 前缀相同,已内置 | | `GCP Service Account Key` | `google-services.json` 泄露 — 已内置 | ### React Native 项目中的 SAST SAST 在 RN/Expo 代码库中特别有价值,因为: - **`dangerouslySetInnerHTML`** — `SAST/DangerouslySetInnerHTML` 在 TSX/JSX 文件中捕获此问题。React Native 本身不渲染 HTML,但 React Native Web builds 会。 - **`eval`** — `SAST/EvalUsage` 标记 JS bundles 中的动态代码评估。Metro bundler 在最终 bundle 中包含所有 JS,使 eval 成为供应链风险。 - **Node.js build scripts** — `SAST/ChildProcessExec` / `SAST/ChildProcessSpawn` 标记自定义 Metro 配置、build scripts 和 Expo plugins 中的 shell 命令。 ### React Native 的推荐 `.oxideci.toml` ``` [scan] exclude_patterns = [ # Build artifacts "android/build/**", "android/.gradle/**", "ios/build/**", "ios/Pods/**", # Expo cache and generated files ".expo/**", ".expo-shared/**", # Metro bundler cache ".metro-cache/**", # Test fixtures that intentionally contain fake patterns "__tests__/**", "__mocks__/**", # React Native generated bundle "android/app/src/main/assets/index.android.bundle", ] # Shannon entropy 捕获未被显式规则匹配的随机 API keys。 entropy = true entropy_threshold = 4.5 entropy_min_length = 20 [sast] enabled = true # disabled_rules = [] # 取消注释以抑制特定规则 [coverage] file = "coverage/lcov.info" min = 80.0 [reassure] current = "output/current.perf" baseline = "output/baseline.perf" threshold = 15.0 ``` ### GitHub Actions — React Native pipeline ``` name: React Native CI on: [push, pull_request] jobs: quality: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install oxide-ci run: | curl -sL https://github.com/ThinkGrid-Labs/oxide-ci/releases/latest/download/oxide-ci-linux-amd64 \ -o /usr/local/bin/oxide-ci chmod +x /usr/local/bin/oxide-ci - name: Secret, PII & SAST scan run: oxide-ci scan - name: Dependency audit (npm CVEs via OSV) run: oxide-ci audit # reads package-lock.json / yarn.lock / pnpm-lock.yaml - name: Coverage gate run: oxide-ci coverage # reads coverage/lcov.info (generated by jest --coverage) perf: runs-on: ubuntu-latest needs: quality steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - name: Install dependencies run: yarn install --frozen-lockfile - name: Run Reassure measurement run: npx reassure measure # Produces output/current.perf - name: Install oxide-ci run: | curl -sL https://github.com/ThinkGrid-Labs/oxide-ci/releases/latest/download/oxide-ci-linux-amd64 \ -o /usr/local/bin/oxide-ci chmod +x /usr/local/bin/oxide-ci - name: Reassure performance gate run: oxide-ci reassure --threshold 15 # Fails if any component regresses > 15% vs baseline.perf ``` ## 故障排除 ### SARIF 上传失败并提示“Resource not accessible by integration” ``` Error: Resource not accessible by integration ``` 这是**私有仓库的预期行为**。将 SARIF 结果上传到 GitHub Security 选项卡需要 [GitHub Advanced Security (GHAS)](),即: - 对于公共仓库**免费** - 对于私有仓库是**付费附加组件**(GitHub Enterprise 的一部分) `oxide-ci scan` 步骤本身仍会运行,如果发现真正的 secrets 或 SAST 问题将使作业失败 — 您的安全门禁完好无损。SARIF 上传仅用于 Security 选项卡中的内联 PR annotations。 **选项:** | 选项 | 何时使用 | |---|---| | 保留 `continue-on-error: true`(默认) | 私有 repo,无 GHAS — 扫描仍会因发现项而阻止 | | 启用 GitHub Advanced Security | 您拥有 GitHub Enterprise 或想要内联 PR annotations | | 移除 SARIF 上传步骤 | 您不需要 Security 选项卡集成 | ### 来自 lockfiles 的太多误报 包管理器在 lockfiles(`pnpm-lock.yaml`、`yarn.lock`、`package-lock.json`)中存储 sha512 完整性哈希。这些看起来像高熵 secrets 并会产生数百个误报。 **修复:** 在 `.oxideci.toml` 中排除 lockfiles: ``` [scan] exclude_patterns = [ "pnpm-lock.yaml", "yarn.lock", "package-lock.json", "Cargo.lock", # sha256 checksums ] ``` ### 来自 SVG 和压缩文件的高熵误报 编译或生成的文件通常包含 base64 编码的数据和非 secrets 的高熵字符串: | 文件类型 | 标记原因 | |---|---| | `favicon.svg` | 嵌入的 base64 图像数据 | | `pdf.worker.min.mjs` / `*.min.js` | 压缩的第三方库代码 | | `*.chunk.js` | Webpack/Next.js 构建输出 | **修复:** 排除公共资源和压缩文件: ``` [scan] exclude_patterns = [ "**/public/**", # static assets directory "**/*.min.js", "**/*.min.mjs", ".next/**", # Next.js build output "dist/**", "build/**", ] ``` ### Sentry 配置文件因 PII (Email) 被标记 Sentry DSN URLs 包含 `@` 符号(例如 `https://abc123@o123.ingest.sentry.io/456`),这会触发 `Generic PII (Email)` 模式。在正确配置的项目中,DSN 从环境变量(`NEXT_PUBLIC_SENTRY_DSN`)加载,因此源文件不包含硬编码的 secret。 **修复:** 排除 Sentry 和 instrumentation 配置文件: ``` [scan] exclude_patterns = [ "**/sentry.client.config.ts", "**/sentry.server.config.ts", "**/sentry.edge.config.ts", "**/instrumentation.ts", ] ``` ### Test fixtures 因 PII (Email) 被标记 测试文件通常使用像 `user@example.com` 这样的占位符地址作为输入 fixtures。这些不是真正的 PII。 **修复:** 排除测试文件和目录: ``` [scan] exclude_patterns = [ "**/*.test.ts", "**/*.test.tsx", "**/*.spec.ts", "**/*.spec.tsx", "**/__tests__/**", ] ``` ### 文档或 UI 组件中的 Emails 被标记为 PII `Generic PII (Email)` 模式捕获任何电子邮件形状的字符串。页脚、文档、表单占位符或帮助页面中的联系地址是有意的 — 不是 secrets。 **修复:** 排除特定文件或目录: ``` [scan] exclude_patterns = [ "docs/**", "*.md", "**/components/footer/**", "**/components/forms/**", # input placeholder emails "**/app/**/help/**", # contact emails on help pages ] ``` ### `.oxideci.toml` 本身被标记 如果您的配置文件在注释中包含示例电子邮件(例如 `user@example.com`),oxide-ci 将标记配置文件本身。排除它: ``` [scan] exclude_patterns = [ ".oxideci.toml", ] ``` ### SAST 规则因故意使用而触发 某些 SAST 规则(例如 `SAST/ChildProcessExec`)在任何模式使用时都会触发,即使在 build script 或 CLI 工具中是故意的。有两种处理方法: **选项 1 — 内联抑制**(适用于孤立情况): ``` child_process.exec(buildCmd, callback); // oxide-ci: ignore ``` **选项 2 — 全局禁用规则**(当模式广泛存在时首选): ``` [sast] disabled_rules = ["SAST/ChildProcessExec"] ``` ### Next.js / React monorepo 的参考 `.oxideci.toml` 复制此作为起点,并移除不适用于您项目的排除项: ``` [scan] exclude_patterns = [ # Package manager lockfiles — sha512 integrity hashes, not secrets "pnpm-lock.yaml", "yarn.lock", "package-lock.json", # Public assets and minified files — base64 image data and bundled libraries "**/public/**", "**/*.min.js", "**/*.min.mjs", # Sentry / OpenTelemetry config — DSN URLs contain @ but are loaded from env vars "**/sentry.client.config.ts", "**/sentry.server.config.ts", "**/sentry.edge.config.ts", "**/instrumentation.ts", # Test fixtures — placeholder emails used as test input, not real PII "**/*.test.ts", "**/*.test.tsx", "**/*.spec.ts", "**/*.spec.tsx", "**/__tests__/**", # The config file itself — comments may contain example emails like user@example.com ".oxideci.toml", # Docs and markdown — example snippets and editorial emails "docs/**", "*.md", # Build output ".next/**", "dist/**", "build/**", ".git/**", ] entropy = true entropy_threshold = 4.5 entropy_min_length = 20 [sast] enabled = true # disabled_rules = ["SAST/ChildProcessExec"] ``` ### CodeQL Action v3 弃用警告 ``` Warning: CodeQL Action v3 will be deprecated in December 2026. ``` 将您工作流中的 SARIF 上传 action 从 `@v3` 更新到 `@v4`: ``` # 之前 uses: github/codeql-action/upload-sarif@v3 # 之后 uses: github/codeql-action/upload-sarif@v4 ``` ## 贡献 OxideCI 是 [MIT License](LICENSE) 下的开源项目。欢迎贡献。 **添加新的 secret 模式:** 1. 在 [src/modules/scanner.rs](src/modules/scanner.rs) 的 `BUILTIN_PATTERNS` 中添加一个 `(&str, &str)` 元组到适当的云提供商部分 2. 为正向匹配和误报检查添加一个匹配的 `#[test]` 3. 运行 `cargo test` 验证 **添加新的 SAST 规则:** 1. 在 [src/modules/sast.rs](src/modules/sast.rs) 的 `RULES` 中添加一个 `SastRule { id, query }` 条目 2. 编写 tree-sitter S-expression 查询 — `@match` 捕获标记用于位置报告的最外层节点 3. 在 `#[cfg(test)]` 块中添加一个单元测试,断言规则触发,并且(如果适用)安全变体不触发 4. 运行 `cargo test` 验证 **添加新的 lint 规则:** 1. 在 [src/modules/k8s_lint.rs](src/modules/k8s_lint.rs) 的 `check_manifest()` 中添加检查 2. 在 `#[cfg(test)]` 块中添加单元测试 **运行测试:** ``` cargo test # all unit + integration tests cargo test scanner # only scanner tests cargo test sast # only SAST tests cargo clippy # lint ``` **问题 & 功能请求:** [github.com/ThinkGrid-Labs/oxide-ci/issues](https://github.com/ThinkGrid-Labs/oxide-ci/issues)
标签:CMS安全, DevSecOps, DNS 反向解析, Docker, GitHooks, Go, IP 地址批量处理, JavaScript, Linting, Pre-commit, Python, Ruby工具, Rust, SAST, SBOM, StruQ, TypeScript, Web性能, Web截图, 上游代理, 二进制工具, 云安全监控, 依赖审计, 可视化界面, 安全扫描, 安全插件, 安全防御评估, 容器安全, 文档安全, 无后门, 时序注入, 瑞士军刀, 盲注攻击, 硬件无关, 网络流量审计, 覆盖率, 质量门禁, 跌倒检测, 软件物料清单, 通知系统, 配置检查, 零依赖, 静态分析