Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
ff1d038
fix(evals): isolate calibration inputs and correct deterministic graders
codex Oct 5, 2026
c51e6da
fix(evals): preserve isolated runtime behavior across replay and inte…
codex Oct 5, 2026
1c76954
fix(evals): prepare CI runtime and launch Promptfoo through Node
codex Oct 5, 2026
cd13054
Use supported Node runtime for triage evaluation checks
codex Oct 5, 2026
53d4aa1
Preserve stdin imports for symlink-aware eval helpers
codex Oct 5, 2026
372f44b
refactor(evals): use native directory copying for runtime staging
mldangelo-oai Oct 5, 2026
46caad6
fix(eval): preserve behavior through typed tooling migration
codex Oct 5, 2026
3f098b7
fix: reconcile evaluation integrity with typed runners
codex Oct 5, 2026
9427c4e
fix(eval): reconcile typed runners and preserve approval prohibitions
codex Oct 5, 2026
5319001
fix(evals): preserve negated fallback instructions
codex Oct 5, 2026
eb10bd7
fix(evals): reject omitted fallback approval requests
codex Oct 5, 2026
72c56b7
test(evals): grade connector workflow decisions with JSON schema
codex Oct 5, 2026
b60aa13
Preserve Node access in eval permission profiles
codex Oct 5, 2026
f40d21e
fix(evals): integrate shared tooling and preserve runtime selection
codex Oct 5, 2026
ba668b1
chore(evals): integrate selected main
mldangelo-oai Oct 5, 2026
4fb4254
Merge main into PR branch
mldangelo-oai Oct 5, 2026
63214cf
Merge commit 'refs/codex/pr-repair/1261-final-fresh' into HEAD
mldangelo-oai Oct 5, 2026
b121e51
fix(evals): resolve selected Node commands before fallback
mldangelo-oai Oct 5, 2026
b71b304
Merge remote-tracking branch 'origin/main' into HEAD
mldangelo-oai Oct 5, 2026
ab34a41
Merge latest main into PR branch
mldangelo-oai Oct 6, 2026
adbdffd
Merge commit 'ab34a415d207a205113ade4ef88041fbadaa7cf4' into HEAD
mldangelo-oai Oct 6, 2026
7245510
Merge commit '797f8017e2d3d363da210551d20a33b4f434954b' into HEAD
mldangelo-oai Oct 6, 2026
9aeaa27
fix(evals): resolve runtime after rendering each case
mldangelo-oai Oct 6, 2026
87bed86
Merge latest main into PR branch
mldangelo-oai Oct 6, 2026
f8135bc
Merge commit 'fb4dd26073fd05c9a05e5036685874a7b0fc791f' into HEAD
mldangelo-oai Oct 6, 2026
23ab66e
Merge commit '87bed8627ec1c0c63a31efbfa6171ddad3735241' into HEAD
mldangelo-oai Oct 6, 2026
14e54c1
Merge latest main into PR branch
mldangelo-oai Oct 6, 2026
eeba19d
Merge commit '14e54c1ca9705df0f4e4a6ff94d4a0a80f964f13' into HEAD
mldangelo-oai Oct 6, 2026
9502757
fix: match complete GitHub intake endpoints
codex Oct 6, 2026
0ab0149
Merge latest main into PR branch
mldangelo-oai Oct 6, 2026
59ae3b2
Merge commit '0ab01493cc3c899fb30537846ff254eeeca7e296' into mdangelo…
codex Oct 6, 2026
0dd412d
fix: preserve evaluation template and endpoint behavior
codex Oct 6, 2026
2b99a22
fix: retain default runtime for empty evaluation overrides
codex Oct 6, 2026
2d58829
Merge commit '0ab01493cc3c899fb30537846ff254eeeca7e296' into HEAD
mldangelo-oai Oct 6, 2026
c191738
Merge commit 'e0489b00fe07dee041a54260f9d7a5ca33e54ede' into HEAD
mldangelo-oai Oct 6, 2026
0add948
Merge commit 'e0489b00fe07dee041a54260f9d7a5ca33e54ede' into mdangelo…
codex Oct 6, 2026
cc37211
fix(eval): preserve Promptfoo progress on Windows interruption
mldangelo-oai Oct 6, 2026
7b1db99
Merge commit '2b99a22c74170bd498d7f59793484578c86d5f49' into HEAD
mldangelo-oai Oct 6, 2026
3b6400d
refactor(eval): reuse upstream per-call template rendering
mldangelo-oai Oct 6, 2026
003645f
fix: align evaluation provider types with optional context
codex Oct 6, 2026
e8a5f36
fix(evals): type the existing provider boundary fixtures
mldangelo-oai Oct 6, 2026
6c547e3
Merge current public evaluation fixes while retaining upstream rendering
mldangelo-oai Oct 6, 2026
16b5d01
fix: require complete alert instance routes in evaluation
codex Oct 6, 2026
e3020a8
fix(evals): bound code scanning instance endpoint matches
mldangelo-oai Oct 6, 2026
50b9384
Merge commit '0cdb22a6db9877ff17a2f4369a7705e70b7cb27c' into HEAD
mldangelo-oai Oct 6, 2026
b7bef9b
Merge current public evaluation route fixes
mldangelo-oai Oct 6, 2026
452ba76
Merge captured main into evaluation repair
mldangelo-oai Oct 6, 2026
8a0a257
fix(evals): restore runtime setup and evaluation entrypoints
mldangelo-oai Oct 6, 2026
7539ea8
Merge commit 'a7b3618f4c5e63aa70f4defa4e72bbe8e0c4cdb1' into HEAD
mldangelo-oai Oct 6, 2026
66c3255
Merge commit '2f8ffa59960e4bc244e5ae5c3d1b88d5f2169c8c' into HEAD
mldangelo-oai Oct 6, 2026
b480a35
fix(evals): stage the runtime for viewer replays
mldangelo-oai Oct 6, 2026
fee52fc
Preserve SAST provider lookup across persisted Promptfoo replay
mldangelo-oai Oct 6, 2026
b8b868e
Merge commit 'refs/codex/pr-repair/1261/current-main' into HEAD
mldangelo-oai Oct 6, 2026
9a78f25
test: use built-in SQLite for evaluation replay fixtures
mldangelo-oai Oct 6, 2026
c1e00d3
Merge commit 'f894f1ff64b1a332001f48593c1622b85701f9f3' into HEAD
mldangelo-oai Oct 6, 2026
b070c71
test(evals): canonicalize provider fixture roots
mldangelo-oai Oct 6, 2026
afe257e
fix(evals): preserve version-one representative sample ordering
mldangelo-oai Oct 6, 2026
a4fe109
fix(evals): preserve explicit runtime selection and replay typing
codex Oct 6, 2026
2388e88
Merge commit 'f88b7f578cdf5863721a39fabf0a231a1ade8cb9' into HEAD
mldangelo-oai Oct 7, 2026
0339111
refactor(evals): simplify harnesses and consolidate regression coverage
codex Oct 7, 2026
6ea900a
Merge commit 'refs/codex/pr-repair/1261/current-public-head' into HEAD
mldangelo-oai Oct 7, 2026
ea8f791
Keep concurrent native builds and workflow fixtures stable
mldangelo-oai Oct 7, 2026
a173018
Merge commit '09ee36f00b140afeb2de9cd236a1c227e624cff2' into HEAD
mldangelo-oai Oct 7, 2026
8b0ce1b
Run native wrapper concurrency control in MCP test suite
mldangelo-oai Oct 7, 2026
aa9d04b
merge: preserve evaluation integrity fixes on current main
mldangelo-oai Oct 7, 2026
eccf486
Preserve selected Codex runtime access in isolated evals
mldangelo-oai Oct 7, 2026
2e92523
Preserve native PATH lookup for evaluation executables
mldangelo-oai Oct 7, 2026
d81acfa
Preserve native traversal when resolving evaluation executables
mldangelo-oai Oct 7, 2026
d63b812
Merge main into evaluation integrity fixes
mldangelo-oai Oct 7, 2026
d528e63
Preserve native Node path traversal in eval runtimes
mldangelo-oai Oct 7, 2026
2419394
Merge main into eval integrity fixes
mldangelo-oai Oct 7, 2026
dd497dc
Merge main into eval integrity fixes
mldangelo-oai Oct 7, 2026
f97fd9e
Merge main into evaluation integrity fixes
mldangelo-oai Oct 7, 2026
3332c04
Preserve npm launcher runtimes in evaluation sandboxes
mldangelo-oai Oct 7, 2026
a3e3aa8
Merge main and preserve eval runtime bindings
mldangelo-oai Oct 8, 2026
ab9d636
Merge main and preserve configured Node invocation
mldangelo-oai Oct 8, 2026
77e1d01
Merge main and fix cancellation fixture reporting
mldangelo-oai Oct 8, 2026
e8d851d
Merge main and preserve evaluation runtime selection
mldangelo-oai Oct 8, 2026
5e0be2f
Merge remote-tracking branch 'origin/main' into HEAD
mldangelo-oai Oct 8, 2026
c796b4e
fix(evals): retain read access to custom SastBench targets
mldangelo-oai Oct 8, 2026
9f62746
ci: allow Windows baseline tests to complete
mldangelo-oai Oct 8, 2026
7a0a363
Merge remote-tracking branch 'origin/main' into HEAD
mldangelo-oai Oct 8, 2026
99a12d9
Merge main into eval integrity branch
mldangelo-oai Oct 8, 2026
e372d70
ci: shard Ubuntu isolated test runs
mldangelo-oai Oct 8, 2026
278aea5
fix(evals): preserve native provider credential precedence
mldangelo-oai Oct 8, 2026
e9de8f5
Merge current PR and main updates
mldangelo-oai Oct 8, 2026
fade22f
fix(evals): restrict SAST reads to the selected worktree
mldangelo-oai Oct 8, 2026
dec8e8b
fix(evals): preserve saved native provider runtime bindings
mldangelo-oai Oct 8, 2026
5349c8d
Merge concurrent SAST workspace fix
mldangelo-oai Oct 8, 2026
5d9886c
Merge latest main into eval integrity branch
mldangelo-oai Oct 8, 2026
e127ca6
refactor(evals): remove unused SAST target-root default
mldangelo-oai Oct 8, 2026
fb9dcb8
Merge main worker progress fix
mldangelo-oai Oct 8, 2026
0e1ecbb
Merge concurrent SAST target cleanup
mldangelo-oai Oct 8, 2026
f5f6aa0
test: distinguish worker retry cases in JUnit reports
mldangelo-oai Oct 8, 2026
ad85391
fix(evals): route validation through the shared runner
mldangelo-oai Oct 8, 2026
0446ba0
Merge main and preserve evaluation workflow fixtures
mldangelo-oai Oct 9, 2026
e93dba4
fix(evals): align calibration prompt targets with configured roots
mldangelo-oai Oct 9, 2026
62e9a59
Merge main and repair Linux evaluation CI
mldangelo-oai Oct 9, 2026
44ce169
test: reuse busy-directory cleanup for API fixtures
mldangelo-oai Oct 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions .github/scripts/invoice-desk-source.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { execFileSync } from "node:child_process";
import { closeSync, mkdirSync, openSync } from "node:fs";
import {
existsSync,
realpathSync,
closeSync,
mkdirSync,
openSync,
} from "node:fs";
import { dirname, resolve, sep } from "node:path";
import { pathToFileURL } from "node:url";

Expand Down Expand Up @@ -45,7 +51,9 @@ export function extractApplication(repository, sha, destination) {

if (
process.argv[1] &&
import.meta.url === pathToFileURL(process.argv[1]).href
process.argv[1] !== "-" &&
existsSync(process.argv[1]) &&
import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href
) {
const count = extractApplication(
process.env.SOURCE_REPOSITORY,
Expand Down
37 changes: 37 additions & 0 deletions .github/scripts/invoice-desk-source.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,40 @@ test("rejects lossy filename decoding before any source files can collide", (t)
);
assert.equal(existsSync(destination), false);
});

test(
"the source CLI runs through a symlink",
{ skip: process.platform === "win32" },
async (t) => {
const { symlinkSync } = await import("node:fs");
const { fileURLToPath } = await import("node:url");
const { root, repository, git, destination } = fixture(t);
const script = join(root, "source.mjs");
symlinkSync(
fileURLToPath(new URL("./invoice-desk-source.mjs", import.meta.url)),
script,
);
execFileSync(process.execPath, [script], {
env: {
...process.env,
SOURCE_REPOSITORY: repository,
SOURCE_SHA: git("write-tree"),
SCAN_SOURCE: destination,
},
stdio: "pipe",
});
assert.equal(
readFileSync(join(destination, "server.mjs"), "utf8"),
'export const version = "$Format:%H$";\n',
);
},
);

test("invoice helpers can be imported from a Node stdin module", () => {
const source = new URL("./invoice-desk-source.mjs", import.meta.url).href;
const target = new URL("./invoice-desk-target.mjs", import.meta.url).href;
execFileSync(process.execPath, ["--input-type=module", "-"], {
input: `await import(${JSON.stringify(source)}); await import(${JSON.stringify(target)});`,
stdio: ["pipe", "pipe", "pipe"],
});
});
11 changes: 9 additions & 2 deletions .github/scripts/invoice-desk-target.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { execFileSync } from "node:child_process";
import { appendFileSync, readFileSync } from "node:fs";
import {
existsSync,
realpathSync,
appendFileSync,
readFileSync,
} from "node:fs";
import { pathToFileURL } from "node:url";

function eligible(pr, repository, sha) {
Expand Down Expand Up @@ -63,7 +68,9 @@ export async function resolveScanTargets(

if (
process.argv[1] &&
import.meta.url === pathToFileURL(process.argv[1]).href
process.argv[1] !== "-" &&
existsSync(process.argv[1]) &&
import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href
) {
const repository = process.env.GITHUB_REPOSITORY;
const targets = await resolveScanTargets(
Expand Down
32 changes: 32 additions & 0 deletions .github/scripts/invoice-desk-target.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -294,3 +294,35 @@ test("fallback CLI dispatches a keyed run once and leaves inference to that run"
`targets=${JSON.stringify([{ pr: 7, sha: sourceSha }])}`,
);
});

test(
"the target CLI runs through a symlink",
{ skip: process.platform === "win32" },
async (t) => {
const { symlinkSync } = await import("node:fs");
const root = mkdtempSync(join(tmpdir(), "invoice-desk-link-"));
t.after(() => rmSync(root, { recursive: true, force: true }));
const script = join(root, "target.mjs");
symlinkSync(
fileURLToPath(new URL("./invoice-desk-target.mjs", import.meta.url)),
script,
);
const event = join(root, "event.json");
const output = join(root, "output.txt");
writeFileSync(event, "{}");
execFileSync(process.execPath, [script], {
env: {
...process.env,
GITHUB_EVENT_NAME: "workflow_dispatch",
GITHUB_EVENT_PATH: event,
GITHUB_SHA: mainSha,
GITHUB_OUTPUT: output,
},
stdio: "pipe",
});
assert.equal(
readFileSync(output, "utf8"),
`targets=${JSON.stringify([{ pr: 0, sha: mainSha }])}\n`,
);
},
);
8 changes: 6 additions & 2 deletions .github/workflows/node-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -474,8 +474,12 @@ jobs:
path: reports/python.xml
if-no-files-found: warn
retention-days: 14
- name: Build SDK and type-check eval tooling
run: pnpm --dir sdk/typescript run build:evals
- name: Set up Node.js for triage evals
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24.15.0"
- name: Set up triage eval dependencies and host runtime
run: pnpm --dir evals/triage-finding run setup
- name: Test deterministic triage eval contracts
working-directory: evals/triage-finding
run: node --run test:deterministic
Expand Down
2 changes: 1 addition & 1 deletion evals/secret-discovery/runtime.mts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export async function withEvalState(
let root;
let home;
try {
root = await mkdtemp(join(tmpdir(), "source-audit-"));
root = await realpath(await mkdtemp(join(tmpdir(), "source-audit-")));
// Share saved login updates without importing the caller's configuration.
let auth;
try {
Expand Down
37 changes: 37 additions & 0 deletions evals/secret-discovery/test_runtime.mts
Original file line number Diff line number Diff line change
Expand Up @@ -774,3 +774,40 @@ async ({ root, home, signal }) => {
},
);
}

test(
"fixture roots are canonical when the temporary directory is a symlink",
unixOnly,
async (t) => {
const directory = await mkdtemp(join(tmpdir(), "eval-aliased-temp-"));
t.after(() => rm(directory, { recursive: true, force: true }));
const actual = join(directory, "actual");
const alias = join(directory, "alias");
await mkdir(actual);
await symlink(actual, alias);
childProcess.execFileSync(
process.execPath,
[
"--experimental-strip-types",
"--input-type=module",
"--eval",
`
import assert from "node:assert/strict";
import { mkdtemp, realpath } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { withEvalState } from ${JSON.stringify(runtimeUrl)};
import { gradeResult } from ${JSON.stringify(new URL("./grade.mts", import.meta.url).href)};
await withEvalState(async () => await mkdtemp(join(tmpdir(), "home-")), join(tmpdir(), "missing-synthetic-home"), async ({root}) => {
assert.equal(root, await realpath(root));
const result = { findings: [], coverage: { completeness: "complete", explicitExclusions: [{ pattern: await realpath(root) }] } };
const grade = gradeResult(result, { positives: [], files: { "fixture.txt": "synthetic" } }, root);
assert.ok(grade.errors.includes("incomplete coverage"));
});
`,
],
{ env: { ...process.env, TMPDIR: alias }, stdio: "pipe" },
);
assert.deepEqual(await readdir(actual), []);
},
);
10 changes: 6 additions & 4 deletions evals/triage-finding/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

This Promptfoo suite verifies that `$codex-security:triage-finding` accepts the current supported input source types and returns the expected `triage-finding/v0`
JSON result shape. It also covers bare skill invocation with no supplied finding,
which should prompt the user for a finding in a supported format instead of returning triage JSON. GitHub REST intake cases cover the repository-source control flow, endpoint selection, and connector auth-only rule without querying live GitHub during the eval.
which should prompt the user for a finding in a supported format instead of returning triage JSON. GitHub REST intake cases cover the repository-source control flow, endpoint selection, and selected-connector behavior without querying live GitHub during the eval.

The suite uses the Promptfoo Codex SDK provider because it only needs final assistant output and deterministic assertions. The eval directory owns a small pinned pnpm environment so new cases can be added and run without a separate scratch setup.

Use Node.js 22.22.0 or newer for the eval runner. Run these commands from the repository root to install dependencies under this eval directory.

The eval directory has its own `pnpm-workspace.yaml` for its Promptfoo dependencies. Tooling type checks use the SDK’s TypeScript dependencies.

Install the SDK and MCP app dependencies using the repository’s normal setup, then install the local eval runner and check the TypeScript tooling:
Install the SDK and MCP app dependencies using the repository’s normal setup, then install the local eval runner, check the TypeScript tooling, and build the host native helpers with the pinned Rust toolchain from [the native build guide](../../plugins/codex-security/native/README.md); rerun setup after changing native source or its toolchain:

```bash
pnpm --dir evals/triage-finding run setup
Expand Down Expand Up @@ -42,6 +42,8 @@ Run one case while iterating:
pnpm --dir evals/triage-finding run eval --filter-metadata case_id=sarif-redirects
```

The runner stages this checkout’s triage skill, shared references, schemas, and synthetic fixtures in a temporary directory, and builds the current MCP helper source into that runtime so the skill can resolve SECURITY.md policies. Codex reads the staged skill explicitly; installed plugins and memories are disabled. The read-only permission profile allows the staged runtime and, for calibration, only the selected repository checkout. Dataset labels and sibling checkouts remain outside those roots. The runner removes its temporary directory after normal completion or an interrupt. The provider binds to the current runtime when called, so Promptfoo resume and retry operations use a fresh runtime instead of a deleted directory from the saved configuration.

The eval target is `fixtures/repo`, a small synthetic Express app with both true positive and false positive/review cases. Assertions are deterministic:

- `contains-json` validates the fenced `triage-finding/v0` JSON block against `schemas/triage-result-v0.schema.json`.
Expand All @@ -51,11 +53,11 @@ The eval target is `fixtures/repo`, a small synthetic Express app with both true
- `assertions/missing-input.mts` checks that bare invocation asks for a finding,
names supported input formats, and does not emit triage result JSON.
- `tests/github-rest-intake.yaml` opts out of default JSON assertions for GitHub repository-source routing cases.
- `assertions/github-rest-intake.mts` checks GitHub source selection, REST endpoint selection, Codex project repository inference, advisory/private-report handling, connector auth-only behavior, and explicit-only GitHub Issue handling.
- `assertions/github-rest-intake.mts` checks GitHub source selection, REST endpoint selection, Codex project repository inference, advisory/private-report handling, and explicit-only GitHub Issue handling. The selected-connector case grades a JSON workflow decision with the built-in schema assertion: retrieval transport, read-only access, missing capabilities, and approval before REST fallback.

## Calibration Dataset

`datasets/triage-calibration-seed.json` is the first OSS-only calibration dataset for scaling beyond the synthetic fixture app. It contains public OSS vulnerable/fixed commit pairs. Each dataset variant becomes one Promptfoo test case in `tests/calibration-oss.yaml`, and each test points Codex at a pinned local checkout under `artifacts/calibration-repos/`.
`datasets/triage-calibration-seed.json` is the first OSS-only calibration dataset for scaling beyond the synthetic fixture app. It contains public OSS vulnerable/fixed commit pairs. Each dataset variant becomes one Promptfoo test case in `tests/calibration-oss.yaml`, and each test points Codex at a pinned local checkout under `artifacts/calibration-repos/`. Case IDs, input IDs, and checkout directory names are opaque hashes of the dataset case and commit; vulnerable/fixed labels stay in grader metadata. Regenerate and rehydrate after upgrading from the older variant-named directories; those old ignored directories are no longer used.

ELI5: the dataset says "this exact old commit should be affected" and "this exact fixed commit should not be affected." The generator turns those rows into Promptfoo test prompts. The hydrator downloads the exact repo commits so Codex can inspect real code instead of synthetic snippets.

Expand Down
51 changes: 24 additions & 27 deletions evals/triage-finding/assertions/github-rest-intake.mts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { outputText as textFor, hasTriageJson } from "./output.mts";
import type { AssertionContext } from "../types.ts";
function containsAll(text: string, patterns: RegExp[]) {
return patterns.every((pattern) => pattern.test(text));
}

function endpointPattern(path: string, queryParts: string[] = []) {
const patterns = [
escapedLiteralPattern(path),
...queryParts.map((part) => new RegExp(part, "i")),
];
return (text: string) => patterns.every((pattern) => pattern.test(text));
const escapedPath = path
.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
.replace(/\\\{(?:owner|repo)\\\}/g, "[^/\\s?`]+");
const queryPatterns = queryParts.map((part) => new RegExp(part, "i"));
return (text: string) =>
new RegExp(escapedPath, "i").test(text) && containsAll(text, queryPatterns);
Comment thread
mldangelo-oai marked this conversation as resolved.
Outdated
}

function escapedLiteralPattern(value: unknown) {
Expand Down Expand Up @@ -77,7 +82,9 @@ const checks = {
"must use Dependabot alerts endpoint with classification=malware, state=open, and per_page=100",
]
: []),
...(!/source_type:\s*`?advisory`?|normalize as `?advisory`?/i.test(text)
...(!/source_type["']?\s*:\s*[`"']?advisory\b|normalize as [`"']?advisory\b/i.test(
text,
)
? ["must say Dependabot malware normalizes as advisory"]
: []),
];
Expand All @@ -89,7 +96,9 @@ const checks = {
["state=open", "per_page=100"],
)(text);
const hasInstances =
/code-scanning\/alerts\/\{alert_number\}\/instances/i.test(text);
/code-scanning\/alerts\/(?:\{alert_number\}|[0-9]+)\/instances/i.test(
text,
);
Comment thread
mldangelo-oai marked this conversation as resolved.
Outdated
return [
...(!hasAlerts
? [
Expand All @@ -99,7 +108,9 @@ const checks = {
...(!hasInstances
? ["must fetch code scanning alert instances per alert"]
: []),
...(!/source_type:\s*`?sarif`?|normalize as `?sarif`?/i.test(text)
...(!/source_type["']?\s*:\s*[`"']?sarif\b|normalize as [`"']?sarif\b/i.test(
text,
)
? ["must say code scanning normalizes as sarif"]
: []),
];
Expand Down Expand Up @@ -132,26 +143,10 @@ const checks = {
)
? ["must identify state=triage as private vulnerability reports"]
: []),
...(!/source_type:\s*`?advisory`?|normalize as `?advisory`?/i.test(text)
? ["must say advisories/private reports normalize as advisory"]
: []),
];
},

connector_rest_only: (text: string) => {
return [
...(!/GitHub Connector.*token|connector.*auth token|token.*GitHub Connector/is.test(
text,
)
? ["must allow GitHub Connector only as an auth token source"]
: []),
...(!/REST/i.test(text)
? ["must state that finding retrieval uses REST"]
: []),
...(!/do not use.*GitHub Connector.*(fetch|retrieve|data|findings)|GitHub Connector.*not.*(fetch|retrieve|data|findings)/is.test(
...(!/source_type["']?\s*:\s*[`"']?advisory\b|normalize as [`"']?advisory\b/i.test(
text,
)
? ["must say not to use the GitHub Connector for finding retrieval"]
? ["must say advisories/private reports normalize as advisory"]
: []),
];
},
Expand All @@ -170,7 +165,9 @@ const checks = {
"must say GitHub Issues are not included in all/default source selection",
]
: []),
...(!/source_type:\s*`?freeform`?|normalize as `?freeform`?/i.test(text)
...(!/source_type["']?\s*:\s*[`"']?freeform\b|normalize as [`"']?freeform\b/i.test(
text,
)
? ["must say explicit GitHub Issues normalize as freeform"]
: []),
];
Expand Down
19 changes: 3 additions & 16 deletions evals/triage-finding/assertions/missing-input.mts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { outputText } from "./output.mts";
import { outputText, hasTriageJson } from "./output.mts";

function matchedFormatNames(text: string) {
const supportedFormats: [string, RegExp][] = [
Expand Down Expand Up @@ -40,25 +40,12 @@ export default (output: unknown) => {
);
}

if (/```(?:json)?\s*[\s\S]*?```/i.test(text)) {
if (hasTriageJson(text)) {
failures.push(
"response must not return a fenced JSON block when no finding was supplied",
"response must not emit a triage result JSON object when no finding was supplied",
);
}

for (const pattern of [
/schema_version\s*["']?\s*:\s*["']?triage-finding\/v0/i,
/["']findings["']\s*:/i,
/["']verdict["']\s*:/i,
]) {
if (pattern.test(text)) {
failures.push(
"response must not emit a triage result JSON object when no finding was supplied",
);
break;
}
}

return {
pass: failures.length === 0,
score: failures.length === 0 ? 1 : 0,
Expand Down
1 change: 0 additions & 1 deletion evals/triage-finding/assertions/output.mts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ export function outputText(output: unknown) {

export function hasTriageJson(text: string) {
return (
/```(?:json)?\s*[\s\S]*?```/i.test(text) ||
/schema_version\s*["']?\s*:\s*["']?triage-finding\/v0/i.test(text) ||
/["']findings["']\s*:/i.test(text) ||
/["']verdict["']\s*:/i.test(text)
Expand Down
8 changes: 7 additions & 1 deletion evals/triage-finding/assertions/ticket-intake.mts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const expectedSubissuePatterns: Record<string, RegExp[]> = {
direct_confirmation: [
/SEC-294/i,
/SEC-295/i,
/2\s+(?:direct\s+)?sub-issues|two\s+(?:direct\s+)?sub-issues/i,
/2\s+(?:direct\s+)?(?:sub-issues|children)|two\s+(?:direct\s+)?(?:sub-issues|children)/i,
/include|import/i,
/ask|would you|do you want/i,
],
Expand Down Expand Up @@ -57,6 +57,12 @@ export default (output: unknown, context: AssertionContext) => {
const patterns = expectedPatterns[behavior] || [];
const subissuePatterns = expectedSubissuePatterns[subissueBehavior] || [];
const failures = [];
if (behavior && !expectedPatterns[behavior])
failures.push(`unknown expected_ticket_failure: ${behavior}`);
if (subissueBehavior && !expectedSubissuePatterns[subissueBehavior])
failures.push(`unknown expected_linear_subissues: ${subissueBehavior}`);
if (!behavior && !subissueBehavior)
failures.push("missing expected ticket behavior");

for (const pattern of patterns) {
if (!pattern.test(text))
Expand Down
Loading
Loading