
深入解析 oh-my-pi 安全扫描发布工具 security_publish一次发布、规范存储与 SARIF 落盘全流程【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi导读oh-my-pi 的 OMP 原生安全扫描流水线在完成仓库审查后需要把模型整理出的结构化结论转成不可伪造、可审计、可被外部工具消费的规范产物。security_publish工具正是这条流水线的收口环节它在扫描协调器内部被创建、由审查会话在“所有在作用域内文件与候选对象都到达最终处置”后恰好调用一次负责校验输入、生成指纹与 OMP 自有 ID、写入规范安全存储并同时产出 SARIF 2.1.0 结果文件。读完本文你将掌握security_publish的输入契约findings / coverage / report 三大结构、路径与作用域校验规则、指纹与 ID 派生方式、单次发布守卫的实现原理以及规范存储目录与 SARIF 输出的完整落盘布局并能在自己的扫描集成中正确使用或扩展这套发布机制。一、工具定位扫描流水线的唯一收口security_publish是 OMP 安全扫描流程的发布工具其行为契约写在 security-publish.md 中原文只有五条约束但每条都对应一条硬性实现Publish current OMP-native security scans canonical result. Call exactly once after every in-scope file and candidate reaches final disposition. Evidence: only repository files inspected during this scan. Tool: validates, fingerprints, assigns OMP-owned IDs, writes canonical security store, creates SARIF. NEVER invent IDs or edit store directly.翻译成实现语言就是只发布一次——工具内部有一个published布尔守卫第二次执行会直接抛错输入必须通过校验——位置路径必须为仓库相对路径且落在扫描作用域内ID 一律由程序派生——finding / occurrence / evidence / scan / plan 的 ID 全部由 SHA-256 指纹计算得出模型无权编造写入规范存储并生成 SARIF——一次调用同时产出存储记录与可移植的静态分析交换格式。该工具的定义位于 publication.ts通过createSecurityPublicationTool(options)工厂创建并被 coordinator.ts 注入到审查会话的customTools中审查会话允许使用的工具白名单里明确包含security_publish见 coordinator.ts 的SECURITY_SESSION_TOOLS。也就是说模型在扫描会话中只能通过这个工具“交卷”没有其他写入口。1.1 与扫描协调器的配合方式在 coordinator.ts 中协调器为每次扫描构造发布工具const publicationTool createSecurityPublicationTool({ plan, // 扫描计划含 target、output、account 等 scanId: record.snapshot.scanId, // 如 secscan_uuid store, // SecurityStore 实例 startedAt, sessionId: security:${record.snapshot.scanId}, operationId: record.snapshot.operationId, onPublished: async bundle { // 发布成功回调 publishedBundle bundle; record.snapshot.findingCount bundle.findings.length; this.#update(record, publishing); }, });这里有一个值得注意的细节onPublished只是把“已发布”状态回传给协调器真正的落盘在工具内部同步完成先写输出目录再写SecurityStore。协调器在会话结束后检查publishedBundle若已发布则补充metrics运行时长、token 用量、成本后再次写回目录与存储使最终产物包含完整统计若会话结束仍未发布则记录为partial状态并给出错误信息 “The scan session ended without publishing a canonical result”见 coordinator.ts。这正体现了绝不提前成功返回的流程纪律。二、单次发布守卫exactly once 的工程实现“恰好调用一次”不是口头约束而是代码级强制。在 publication.ts 中export function createSecurityPublicationTool( options: SecurityPublicationOptions, ): ToolDefinitiontypeof securityPublishSchema, SecurityPublishDetails { let published false; return { name: security_publish, label: Publish Security Scan, description: securityPublishDescription.trim(), parameters: securityPublishSchema, approval: write, strict: true, async execute(_toolCallId, params) { if (published) throw new Error(Security scan ${options.scanId} has already been published); published true; let persisted false; try { // ...构建 bundle 并落盘... } catch (error) { if (!persisted) published false; // 落盘失败则回滚守卫允许重试 throw error; } }, }; }几个关键工程决策先置位再执行published true在业务逻辑之前设置因此即使持久化仍在进行中异步写盘未完成并发/串行的第二次调用也会被立即拒绝失败可重试如果写盘尚未完成就抛出异常persisted仍为false守卫会回滚为published false允许模型修正参数后重试成功即终态一旦persisted true任何后续调用都直接报错already been published。该行为有专门的并发测试佐证publication.test.ts 通过一个“延迟 putBundle”的假 Store 验证当第一次发布仍在写盘时发起第二次调用会抛出already been published且底层putBundle只被调用 1 次expect(putCalls).toBe(1)。三、输入契约findings / coverage / reportsecurity_publish的参数由securityPublishSchema严格定义publication.ts全部使用oh-my-pi/omptype的类型 DSL 声明。完整结构如下3.1 findings发现项数组必填可为空字段类型说明rule_idstring 0规则标识如command-injectiontitlestring 0发现项标题summarystring摘要描述severitycritical \| high \| medium \| low \| informational严重级别confidencehigh \| medium \| low置信度categorystring 0分类参与指纹计算anchorstring?锚点可选参与指纹计算cwestring[]?CWE 编号列表locationsarray.atLeastLength(1)至少一个位置evidencearray?证据列表见下remediationstring?修复建议validationunvalidated \| validated \| partial?校验状态默认unvalidatedlocation 对象包含path仓库相对源码路径、start_line1 起始的首行、可选end_line/start_column/end_column以及可选的role取值为entrypoint、root_control、sink或supporting。evidence 对象包含label、explanation、可选excerpt代码摘录与可选location。3.2 coverage覆盖率必填字段类型说明completenesscomplete \| partial \| unknown本轮审查的完整性声明surfacesarray?每个检查面的最终处置explicit_exclusionsarray?显式排除{ pattern, reason }deferredarray?推迟项{ reason, paths?, surface_ids? }open_questionsarray?待确认问题{ question, follow_up_prompt? }其中surface检查面是“作用域内每个文件或候选对象”的处置记录labeldispositionreported/no_issue_found/rejected/not_applicable/needs_follow_up 可选risk_area、notes、receipt_refs。这正是文档中 every in-scope file and candidate reaches final disposition 的数据载体——扫描协调器提示词scan-coordinator.md要求“审查每一个文件或在 coverage 中如实交代”发布时这些处置被逐条固化。3.3 report报告正文必填一段 Markdown 文本作为人工可读的扫描报告写入report.md。3.4 完整调用示例{ findings: [ { rule_id: shell-command-injection, title: 命令拼接导致注入, summary: 用户输入未经转义直接拼入 shell 命令, severity: high, confidence: high, category: command-injection, cwe: [CWE-78], anchor: run_script, locations: [ { path: src/tasks/run.ts, start_line: 42, end_line: 46, role: sink } ], evidence: [ { label: sink-call, explanation: child_process.exec 接收了拼接后的命令字符串, excerpt: exec(ls ${userInput}), location: { path: src/tasks/run.ts, start_line: 44 } } ], remediation: 使用参数化执行接口并做白名单校验, validation: unvalidated } ], coverage: { completeness: complete, surfaces: [ { label: src/tasks/run.ts, disposition: reported, risk_area: sink }, { label: src/lib/safe.ts, disposition: no_issue_found } ], deferred: [] }, report: # 安全扫描报告\n\n本次扫描共发现 1 个问题。\n }四、路径校验只接受仓库相对路径与作用域内文件发布工具对位置路径执行两层强校验任何违规都会直接抛错拒绝落盘第一层格式校验normalizePublishedPath。路径必须满足反斜杠统一转为正斜杠、去掉./前缀不允许以/开头绝对路径不允许盘符前缀如C:/...任何段不允许出现..目录穿越。第二层作用域校验toLocation。规范化后的路径必须通过pathMatchesSecurityScope实现在 preflight.tsexport function pathMatchesSecurityScope( relativePath: string, includePaths: readonly string[], excludePaths: readonly string[], ): boolean { const normalized normalizeRelativePath(relativePath); const included includePaths.length 0 || includePaths.some(candidate scopeContainsPath(candidate, normalized)); const excluded excludePaths.some(candidate scopeContainsPath(candidate, normalized)); return included !excluded; }即includePaths为空则全部纳入否则必须落在某个 include 前缀之下同时不得命中任何excludePaths。这个作用域是在扫描计划SecurityScanPlan创建时就冻结的plan.target.includePaths / excludePaths发布阶段不可更改——这就杜绝了模型“在计划外文件上报告漏洞”的越权行为。测试用例 publication.test.ts 明确覆盖了三种非法路径../outside.ts穿越、/etc/passwd绝对路径、C:/Windows/System32/config盘符均断言抛出包含repository-relative的错误。五、指纹与 ID一切 ID 均由 SHA-256 派生security_publish的一个核心纪律是NEVER invent IDs——模型提交的参数里根本没有 ID 字段所有 ID 在发布阶段由 ids.ts 计算生成。派生链路如下5.1 规范化与指纹首先对所有参与指纹的字段做规范化 JSON 序列化canonicalSecurityJson对象键按字母序排序、递归展开、剔除undefined值保证语义相同即序列化相同。位置列表还会按path → startLine → endLine → startColumn → endColumn → role排序normalizedLocations确保相同位置的顺序抖动不会改变指纹。finding 指纹createSecurityFindingFingerprintomp-security/v1:sha256:${SHA256(ruleId小写 category小写 anchor小写 规范化位置)}指纹使用ruleId、category、可选anchor与全部位置计算但刻意排除了 summary、severity 等描述性字段——这保证了同一位置同一规则的问题无论描述措辞如何变化指纹始终稳定天然具备去重能力。5.2 ID 派生表ID前缀派生输入finding IDsecf_指纹的 SHA-256 前 24 位 hexoccurrence IDseco_{ fingerprint, 规范化位置 }的 SHA-256evidence IDsece_{ fingerprint, label, ordinal }的 SHA-256scan IDsecscan_randomUUIDv7()去连字符plan IDsecplan_计划指纹的 SHA-256project key可读名-hash仓库根路径规范化 SHA-256 前 12 位由于 ID 完全由内容派生同一扫描内的重复 finding 会被findingsByFingerprintMap 自动合并publication.ts从源头避免重复上报。存储层还会对 scan/plan ID 做格式校验/^secscan_[a-zA-Z0-9]$/、/^secplan_[a-zA-Z0-9]$/见 store.ts非法 ID 直接拒绝读写。六、规范存储bundle 如何原子化落盘发布成功后一次调用会同时写两处输出目录plan.output.root与SecurityStore状态目录。6.1 输出目录布局writeSecurityBundleToDirectorystore.ts在输出目录生成 5 个文件文件内容findings.json全部 finding 的规范 JSON2 空格缩进report.md人工可读报告results.sarifSARIF 2.1.0 导出provenance.json经过脱敏的 provenance见下scan.json公开版 scan 清单脱敏 不含 plan作为提交标记目录权限强制为0o700文件权限0o600并且每个文件都通过临时文件 原子 rename写入writeSecurityFileAtomic临时文件名带 PID 与 UUIDv7wx独占标志防止并发写冲突。扫描清单scan.json特意最后写——读取方永远看不到有清单、无数据的半成品状态。6.2 SecurityStore带文件锁的事务化索引SecurityStorestore.ts按仓库投影键projectKey隔离状态stateRoot/projectKey/ ├── index.json # schemaVersion、scanIds、planIds、updatedAt ├── plans/planId.json └── scans/scanId/ ├── findings.json ├── report.md ├── results.sarif └── scan.json所有写操作通过withSecurityStoreWrite串行化进程内用 Promise 链排队跨进程用index.json上的文件锁withFileLock重试 200 次、间隔 50ms——这是 store.ts 中明确的双层互斥设计。putBundle与putPlan都会先写入数据文件最后才更新index.json保证索引永远指向已完整落盘的记录。6.3 隐私脱敏凭证永不落盘发布时写入的provenance.json和scan.json都经过redactPrivateSecurityMetadata处理provenance.ts凡是键名归一化后命中account、email、token、secret、sessionid、credentialid等 16 个敏感词集合的字段都会被剔除。账户信息则替换为凭证亲和度指纹omp-security-credential/v1:sha256:...既能追踪哪条凭证发起了扫描又不泄露任何凭证明文。测试 publication.test.ts 断言序列化后的scan.json不含fixture-workspace与credentialId且不含plan对象。七、SARIF 2.1.0 导出与外部工具互通的桥梁exportSecurityBundleToSarifsarif.ts把规范 bundle 转换为标准 SARIF 2.1.0 JSON工具驱动tool.driver.name/version取自scan.producerOMP 原生生产者OMP Native Security版本1.0.0每个ruleId去重生成一条 rule携带标题、摘要、CWE/标签以及security-severity分数结果映射每条 finding 映射为一个 resultruleId、message、locations含%SRCROOT%基址 URI 与行列区间、fingerprints写入omp-security/v1指纹一应俱全严重级映射critical/high → error、medium → warning、low → note、informational → none可追溯扩展每个 result 的properties额外写入 finding ID、confidence、validation、disposition 与 category外部工具可据此回链到 OMP 存储中的规范记录源码根 URIoriginalUriBaseIds[%SRCROOT%]使用pathToFileURL(repositoryRoot)生成让 SARIF 消费方如 CI 面板、IDE 插件能直接解析为绝对源码位置。results.sarif同时出现在输出目录与存储的 scan 目录中后续若对 finding 做处置更新updateDisposition或校验更新updateValidationSARIF 会被重新导出以保持同步store.ts。八、典型工作流从协调器到发布完成把以上机制串起来一次 OMP 原生安全扫描的发布流程是规划协调器preflight创建SecurityScanPlan冻结 target 作用域、知识库、输出目录、模型与账户存入plans/启动start生成secop_操作 ID 与secscan_扫描 ID校验计划新鲜度assertSecurityScanPlanFresh并写入初始running状态 bundle执行会话使用受限工具集read、grep、glob、lsp、ast_grep、task、security_publish审查作用域内文件task派生子审查代理security-reviewer并回收其结构化发现发布所有文件与候选对象到达最终处置后会话恰好一次调用security_publish工具内部完成校验 → 指纹 → ID 派生 → 输出目录原子写入 → Store 事务化写入 → SARIF 生成 → 触发onPublished回调收尾协调器补充metrics耗时与 token 成本后再次写回 bundle操作状态置为completed若未发布则置为partial若进程中断则由恢复逻辑recoverInterruptedOperations把遗留的running/planned记录标记为failed。九、最佳实践与注意事项不要手工编造 IDfindings 中不要带任何id、fingerprint字段它们由secf_/seco_/sece_派生器生成手工编造会被忽略或破坏去重语义不要直接改存储所有写入必须经security_publish→SecurityStore链路完成绕过工具直接改index.json或 scan 目录会破坏原子性与文件锁约束严格使用仓库相对路径任何../、绝对路径、盘符路径都会在发布阶段被拒绝路径必须同时落在计划冻结的 include/exclude 作用域内覆盖率如实申报未审查的文件要么给出 surface 处置rejected/not_applicable等要么列入explicit_exclusions或deferred否则completeness与真实情况不符发布是终态操作确认所有内容齐备后再调用一旦成功落盘persisted true同一次扫描内无法二次发布如需更新请通过 Store 的updateDisposition/updateValidation接口它们会自动重建 SARIF敏感信息自动脱敏落盘前的 provenance 已剔除账户、令牌、会话 ID 等敏感字段无需也不应在 report 中重复强调凭证细节。十、源码导航工具提示词与契约security-publish.md工具实现与单次发布守卫publication.ts参数 Schema 定义publication.ts指纹与 ID 派生ids.ts全量契约 Schemaschemas.ts规范存储与原子写入store.tsSARIF 导出sarif.ts路径作用域校验preflight.ts隐私脱敏与 provenanceprovenance.ts协调器接线与生命周期coordinator.ts发布行为测试publication.test.ts扫描协调器提示词约束每个文件都要有处置scan-coordinator.md结语security_publish看似只是一个把结果存下来的工具实际承载了 OMP 安全扫描流水线最核心的工程承诺单次发布并发安全的一次性守卫、不可伪造所有 ID 由内容指纹派生、路径严格受作用域约束、规范持久化原子写入 文件锁 索引事务与生态互通SARIF 2.1.0 导出 隐私脱敏。理解这条发布链路也就理解了 oh-my-pi 如何把模型的安全审查输出变成可信、可审计、可继续处置的工程资产。【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考