From 590a474a6670a8c67e954ee846ffd77f3f17ceeb Mon Sep 17 00:00:00 2001 From: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com> Date: Mon, 4 May 2026 02:30:39 +1000 Subject: [PATCH] test: guard raw HTTP2 with OpenGrep --- docs/network.md | 1 + package.json | 1 - scripts/check-no-raw-http2-connect.mjs | 49 ------------------- security/opengrep/precise.yml | 40 +++++++++++++-- .../openclaw-policy/no-raw-http2-connect.yml | 32 ++++++++++++ .../check-no-raw-http2-connect.test.ts | 35 ------------- 6 files changed, 70 insertions(+), 88 deletions(-) delete mode 100644 scripts/check-no-raw-http2-connect.mjs create mode 100644 security/opengrep/rules/openclaw-policy/no-raw-http2-connect.yml delete mode 100644 test/scripts/check-no-raw-http2-connect.test.ts diff --git a/docs/network.md b/docs/network.md index 50f09b9dfac..c0166e21888 100644 --- a/docs/network.md +++ b/docs/network.md @@ -63,6 +63,7 @@ Local trust: ## Security +- Direct APNs delivery uses HTTP/2. OpenClaw code should use `connectApnsHttp2Session()` from `src/infra/push-apns-http2.ts` instead of raw `http2.connect()` so managed proxy mode can route APNs through the configured CONNECT proxy. - [Security overview](/gateway/security) - [Gateway config reference](/gateway/configuration) - [Troubleshooting](/gateway/troubleshooting) diff --git a/package.json b/package.json index 54d43a21e6f..db57bd378b0 100644 --- a/package.json +++ b/package.json @@ -1426,7 +1426,6 @@ "lint:tmp:dynamic-import-warts": "node scripts/check-dynamic-import-warts.mjs", "lint:tmp:no-random-messaging": "node scripts/check-no-random-messaging-tmp.mjs", "lint:tmp:no-raw-channel-fetch": "node scripts/check-no-raw-channel-fetch.mjs", - "lint:tmp:no-raw-http2-connect": "node scripts/check-no-raw-http2-connect.mjs", "lint:tmp:tsgo-core-boundary": "node scripts/check-tsgo-core-boundary.mjs", "lint:ui:no-raw-window-open": "node scripts/check-no-raw-window-open.mjs", "lint:web-fetch-provider-boundaries": "node scripts/check-web-fetch-provider-boundaries.mjs", diff --git a/scripts/check-no-raw-http2-connect.mjs b/scripts/check-no-raw-http2-connect.mjs deleted file mode 100644 index ea94797f216..00000000000 --- a/scripts/check-no-raw-http2-connect.mjs +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env node - -import path from "node:path"; -import ts from "typescript"; -import { runCallsiteGuard } from "./lib/callsite-guard.mjs"; -import { - collectCallExpressionLines, - runAsScript, - unwrapExpression, -} from "./lib/ts-guard-utils.mjs"; - -const sourceRoots = ["src", "extensions"]; -const allowedRawHttp2ConnectCallsites = new Set([ - "src/infra/push-apns-http2.ts:39", - "src/infra/push-apns-http2.ts:55", -]); - -function isHttp2ConnectCall(expression) { - const callee = unwrapExpression(expression); - if (!ts.isPropertyAccessExpression(callee) || callee.name.text !== "connect") { - return false; - } - const receiver = unwrapExpression(callee.expression); - return ts.isIdentifier(receiver) && receiver.text === "http2"; -} - -export function findRawHttp2ConnectCallLines(content, fileName = "source.ts") { - const sourceFile = ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true); - return collectCallExpressionLines(ts, sourceFile, (node) => - isHttp2ConnectCall(node.expression) ? node.expression : null, - ); -} - -export async function main() { - await runCallsiteGuard({ - importMetaUrl: import.meta.url, - sourceRoots, - extraTestSuffixes: [".browser.test.ts", ".node.test.ts"], - findCallLines: findRawHttp2ConnectCallLines, - allowCallsite: (callsite) => allowedRawHttp2ConnectCallsites.has(callsite), - skipRelativePath: (relPath) => - relPath === path.posix.join("src", "infra", "push-apns-http2.test.ts"), - header: "Found raw http2.connect usage outside APNs proxy wrapper:", - footer: - "Use connectApnsHttp2Session() from src/infra/push-apns-http2.ts so APNs HTTP/2 honors managed proxy policy.", - }); -} - -runAsScript(import.meta.url, main); diff --git a/security/opengrep/precise.yml b/security/opengrep/precise.yml index 836cbe0a9ae..99f874c9053 100644 --- a/security/opengrep/precise.yml +++ b/security/opengrep/precise.yml @@ -3,9 +3,9 @@ # Auto-generated by security/opengrep/compile-rules.mjs. # DO NOT EDIT BY HAND. Re-run the compile script after editing source rules. # -# Source rules dir: -# Generated at : 2026-04-29T07:10:35.427Z -# Rule count : 147 +# Source rules dir: security/opengrep/rules/openclaw-policy +# Generated at : 2026-04-30T09:09:41.198Z +# Rule count : 148 rules: - id: ghsa-25gx-x37c-7pph.openclaw-novnc-x11vnc-missing-auth message: x11vnc starts without VNC authentication; avoid -nopw and require password auth when exposing noVNC observer access. @@ -4976,3 +4976,37 @@ rules: - pattern-not-inside: | import { resolvePathWithinRoot, ... } from "$X"; ... + - id: openclaw-policy-raw-http2-connect.no-raw-http2-connect + languages: + - typescript + - javascript + severity: ERROR + message: Use connectApnsHttp2Session() from src/infra/push-apns-http2.ts instead of raw http2.connect() so APNs HTTP/2 honors managed proxy policy. + metadata: + advisory-id: OPENCLAW-POLICY-RAW-HTTP2-CONNECT + advisory-url: https://github.com/openclaw/openclaw/pull/74905 + cwe: + - CWE-441 + category: security + confidence: HIGH + detector-bucket: precise + source-rule-id: no-raw-http2-connect + source-file: security/opengrep/rules/openclaw-policy/no-raw-http2-connect.yml + paths: + include: + - src/**/*.ts + - src/**/*.mts + - src/**/*.js + - src/**/*.mjs + - extensions/**/*.ts + - extensions/**/*.mts + - extensions/**/*.js + - extensions/**/*.mjs + exclude: + - src/infra/push-apns-http2.ts + - "**/*.test.ts" + - "**/*.test.mts" + - "**/*.test.js" + - "**/*.test.mjs" + patterns: + - pattern: http2.connect(...) diff --git a/security/opengrep/rules/openclaw-policy/no-raw-http2-connect.yml b/security/opengrep/rules/openclaw-policy/no-raw-http2-connect.yml new file mode 100644 index 00000000000..531f876f2ac --- /dev/null +++ b/security/opengrep/rules/openclaw-policy/no-raw-http2-connect.yml @@ -0,0 +1,32 @@ +rules: + - id: no-raw-http2-connect + languages: + - typescript + - javascript + severity: ERROR + message: Use connectApnsHttp2Session() from src/infra/push-apns-http2.ts instead of raw http2.connect() so APNs HTTP/2 honors managed proxy policy. + metadata: + advisory-id: OPENCLAW-POLICY-RAW-HTTP2-CONNECT + advisory-url: https://github.com/openclaw/openclaw/pull/74905 + cwe: + - "CWE-441" + category: security + confidence: HIGH + paths: + include: + - "src/**/*.ts" + - "src/**/*.mts" + - "src/**/*.js" + - "src/**/*.mjs" + - "extensions/**/*.ts" + - "extensions/**/*.mts" + - "extensions/**/*.js" + - "extensions/**/*.mjs" + exclude: + - "src/infra/push-apns-http2.ts" + - "**/*.test.ts" + - "**/*.test.mts" + - "**/*.test.js" + - "**/*.test.mjs" + patterns: + - pattern: http2.connect(...) diff --git a/test/scripts/check-no-raw-http2-connect.test.ts b/test/scripts/check-no-raw-http2-connect.test.ts deleted file mode 100644 index 49ae8962c7c..00000000000 --- a/test/scripts/check-no-raw-http2-connect.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { findRawHttp2ConnectCallLines } from "../../scripts/check-no-raw-http2-connect.mjs"; - -describe("check-no-raw-http2-connect", () => { - it("finds direct http2.connect calls", () => { - const source = ` - import http2 from "node:http2"; - export function connect() { - return http2.connect("https://api.push.apple.com"); - } - `; - - expect(findRawHttp2ConnectCallLines(source)).toEqual([4]); - }); - - it("finds parenthesized or asserted http2 references", () => { - const source = ` - import http2 from "node:http2"; - export function connect() { - return (http2 as typeof import("node:http2")).connect("https://api.push.apple.com"); - } - `; - - expect(findRawHttp2ConnectCallLines(source)).toEqual([4]); - }); - - it("ignores mentions in strings and comments", () => { - const source = ` - // http2.connect("https://api.push.apple.com") - const text = "http2.connect('https://api.push.apple.com')"; - `; - - expect(findRawHttp2ConnectCallLines(source)).toEqual([]); - }); -});