Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
100 changes: 26 additions & 74 deletions plugins/codex-security/mcp-app/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,6 @@ const diffTargetSchema = z.discriminatedUnion("kind", [
headRevision: requiredText.max(512),
}),
]);
const currentScanPreflightCheckSchema = z.strictObject({
capability: requiredText.max(128),
reason: requiredText.max(1200),
severity: z.enum(["block", "warn", "suggest"]),
status: z.enum(["pass", "fail", "unknown"]),
});
const openSchema = {
diffTarget: diffTargetSchema
.optional()
Expand Down Expand Up @@ -350,13 +344,6 @@ const progressSchema = {
.describe(
"What phaseItemsTotal and phaseItemsCompleted count for the current phase.",
),
preflightChecks: z
.array(currentScanPreflightCheckSchema)
.max(32)
.optional()
.describe(
"Current standard or diff scan preflight results. Project every helper results entry to capability, reason, severity, and status only. The server derives the item counts and visible block/warn attention items.",
),
reportableFindingsCount: nonnegativeInteger.optional(),
reviewItemsCompleted: nonnegativeInteger
.optional()
Expand Down Expand Up @@ -645,7 +632,7 @@ export function createCodexSecurityServer(): McpServer {
scanDir,
handoffClaimToken,
},
`${started.startDisposition === "created" ? "Started" : "Rejoined"} Standard scan ${scanId}. When the scan is in preflight, complete security_scan preflight before reviewing the target or creating a goal. Preserve the returned handoffClaimToken for scan progress, the semantic draft, and completion.`,
`${started.startDisposition === "created" ? "Started" : "Rejoined"} Standard scan ${scanId}. When the scan is in preflight, follow references/scan-runtime.md, then advance to threat_model before reviewing the target. Use available workers or parent fallback. Preserve the returned handoffClaimToken for scan progress, the semantic draft, and completion.`,
);
},
);
Expand Down Expand Up @@ -1395,7 +1382,6 @@ export function createCodexSecurityServer(): McpServer {
phaseItemsTotal,
phaseItemsCompleted,
phaseProgressUnit,
preflightChecks,
reviewItemsTotal,
reviewItemsCompleted,
reportableFindingsCount,
Expand Down Expand Up @@ -1427,66 +1413,32 @@ export function createCodexSecurityServer(): McpServer {
"Deep Scan is still preparing. Discovery progress begins after its setup worker succeeds.",
);
}
if (
preflightChecks !== undefined &&
(phaseItemsTotal !== undefined ||
phaseItemsCompleted !== undefined ||
phaseProgressUnit !== undefined)
) {
throw new Error(
"preflightChecks derives phaseItemsTotal, phaseItemsCompleted, and phaseProgressUnit; omit those fields.",
);
}
const preflightIssues = preflightChecks?.filter(
(check) =>
(check.severity === "block" || check.severity === "warn") &&
(check.status === "fail" || check.status === "unknown"),
);
const derivedPhaseItemsTotal = preflightChecks?.length ?? phaseItemsTotal;
const derivedPhaseItemsCompleted = preflightChecks
? preflightChecks.filter((check) => check.status !== "unknown").length
: phaseItemsCompleted;
const derivedPhaseProgressUnit = preflightChecks
? "checks"
: phaseProgressUnit;
const serializedPreflightIssues = preflightIssues
? JSON.stringify(preflightIssues)
: undefined;
return scanActionResult(
await runWorkbench(
[
"update-progress",
"--scan-id",
scanId,
...(scan?.mode === "deep"
? deepScanStore.coordinatorLeaseArgs(scanId)
: []),
...optionalArg("--model", modelSettings.model),
...optionalArg("--reasoning-effort", modelSettings.reasoningEffort),
...optionalArg("--claim-token", handoffClaimToken),
...optionalNumberArg("--deep-review-pass", deepReviewPass),
...optionalArg("--phase", phase),
...optionalNumberArg("--phase-items-total", derivedPhaseItemsTotal),
...optionalNumberArg(
"--phase-items-completed",
derivedPhaseItemsCompleted,
),
...optionalArg("--phase-progress-unit", derivedPhaseProgressUnit),
...(serializedPreflightIssues
? ["--preflight-issues-json-stdin"]
: []),
...optionalNumberArg("--review-items-total", reviewItemsTotal),
...optionalNumberArg(
"--review-items-completed",
reviewItemsCompleted,
),
...optionalNumberArg(
"--reportable-findings-count",
reportableFindingsCount,
),
],
serializedPreflightIssues,
),
await runWorkbench([
"update-progress",
"--scan-id",
scanId,
...(scan?.mode === "deep"
? deepScanStore.coordinatorLeaseArgs(scanId)
: []),
...optionalArg("--model", modelSettings.model),
...optionalArg("--reasoning-effort", modelSettings.reasoningEffort),
...optionalArg("--claim-token", handoffClaimToken),
...optionalNumberArg("--deep-review-pass", deepReviewPass),
...optionalArg("--phase", phase),
...optionalNumberArg("--phase-items-total", phaseItemsTotal),
...optionalNumberArg("--phase-items-completed", phaseItemsCompleted),
...optionalArg("--phase-progress-unit", phaseProgressUnit),
...optionalNumberArg("--review-items-total", reviewItemsTotal),
...optionalNumberArg(
"--review-items-completed",
reviewItemsCompleted,
),
...optionalNumberArg(
"--reportable-findings-count",
reportableFindingsCount,
),
]),
"Updated Codex Security scan progress.",
);
},
Expand Down
151 changes: 13 additions & 138 deletions plugins/codex-security/mcp-app/tests/test_mcp_app_smoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,16 @@ async function assertHeadlessStandardScanWorksWithoutUiCapability(
assertNoError(joined);
assert.equal(joined.result.structuredContent.startDisposition, "joined");
assert.equal(joined.result.structuredContent.scanId, result.scanId);
for (const response of [started, joined]) {
assert.match(
response.result.content[0].text,
/references\/scan-runtime\.md/,
);
assert.doesNotMatch(
response.result.content[0].text,
/security_scan preflight/,
);
}
assert.equal(
joined.result.structuredContent.handoffClaimToken,
result.handoffClaimToken,
Expand Down Expand Up @@ -642,17 +652,6 @@ async function assertHeadlessStandardScanWorksWithoutUiCapability(
/only available for Deep or diff scans/,
);

const progressed = await headlessServer.callTool(8, {
name: "update_codex_security_scan_progress",
arguments: {
scanId: result.scanId,
handoffClaimToken: result.handoffClaimToken,
preflightChecks: [],
},
_meta: { "openai/threadId": ownerThread },
});
assertNoError(progressed);

const advanced = await headlessServer.callTool(81, {
name: "update_codex_security_scan_progress",
arguments: {
Expand Down Expand Up @@ -1814,16 +1813,6 @@ try {
progress.inputSchema.properties.phaseItemsTotal.description,
"Expected authoritative coverage, receipts, or artifacts for the current phase. Increase it before newly discovered work begins.",
);
assert.deepEqual(
progress.inputSchema.properties.preflightChecks.items.properties.severity
.enum,
["block", "warn", "suggest"],
);
assert.deepEqual(
progress.inputSchema.properties.preflightChecks.items.properties.status
.enum,
["pass", "fail", "unknown"],
);
assert.equal(
progress.inputSchema.properties.deepReviewPass.description,
"Current Deep Scan discovery pass. Send it when starting each pass together with that pass's total and zero completed items.",
Expand Down Expand Up @@ -2651,104 +2640,6 @@ try {
urlContextUpdate,
);

const conflictingPreflightCounts = await testServer.callTool(90161, {
name: "update_codex_security_scan_progress",
arguments: {
scanId,
handoffClaimToken,
phaseItemsTotal: 1,
preflightChecks: [
{
capability: "delegated_workers",
reason: "Delegated workers are available.",
severity: "warn",
status: "pass",
},
],
},
});
assert.equal(conflictingPreflightCounts.result.isError, true);
assert.match(
conflictingPreflightCounts.result.content[0].text,
/preflightChecks derives/,
);

const successfulPreflightChecks = [
{
capability: "delegated_workers",
reason: "Delegated workers are available.",
severity: "warn",
status: "pass",
},
{
capability: "goal_tools",
reason: "Goal tools help long scans preserve completion criteria.",
severity: "suggest",
status: "pass",
},
{
capability: "goals_enabled",
reason: "Goals are enabled.",
severity: "suggest",
status: "pass",
},
];
const incompletePreflightProgress = await testServer.callTool(90162, {
name: "update_codex_security_scan_progress",
arguments: {
scanId,
handoffClaimToken,
preflightChecks: [
{
capability: "usable_worker_slots_6",
reason: "The runtime did not report worker capacity.",
severity: "block",
status: "unknown",
},
...successfulPreflightChecks,
],
},
});
assertNoError(incompletePreflightProgress);
assert.equal(
incompletePreflightProgress.result.structuredContent.scan.progress
.preflightIssues[0].status,
"unknown",
);
assert.deepEqual(
incompletePreflightProgress.result.structuredContent.scan.progress
.preflightProgress,
{ completed: 3, total: 4 },
);

const blockedPreflightProgress = await testServer.callTool(90163, {
name: "update_codex_security_scan_progress",
arguments: {
scanId,
handoffClaimToken,
preflightChecks: [
{
capability: "usable_worker_slots_6",
reason: "Only three usable worker slots are available.",
severity: "block",
status: "fail",
},
...successfulPreflightChecks,
],
},
});
assertNoError(blockedPreflightProgress);
assert.equal(
blockedPreflightProgress.result.structuredContent.scan.progress
.preflightIssues[0].capability,
"usable_worker_slots_6",
);
assert.deepEqual(
blockedPreflightProgress.result.structuredContent.scan.progress
.preflightProgress,
{ completed: 4, total: 4 },
);

const readyPreflightProgress = await testServer.callTool(90164, {
name: "update_codex_security_scan_progress",
_meta: {
Expand All @@ -2761,15 +2652,9 @@ try {
arguments: {
scanId,
handoffClaimToken,
preflightChecks: [
{
capability: "usable_worker_slots_6",
reason: "The scan will continue with reduced parallelism.",
severity: "warn",
status: "fail",
},
...successfulPreflightChecks,
],
phaseItemsTotal: 4,
phaseItemsCompleted: 4,
phaseProgressUnit: "checks",
},
});
assertNoError(readyPreflightProgress);
Expand All @@ -2781,11 +2666,6 @@ try {
readyPreflightProgress.result.structuredContent.scan.reasoningEffort,
"low",
);
assert.equal(
readyPreflightProgress.result.structuredContent.scan.progress
.preflightIssues[0].severity,
"warn",
);
assert.deepEqual(
readyPreflightProgress.result.structuredContent.scan.progress
.preflightProgress,
Expand Down Expand Up @@ -2840,11 +2720,6 @@ try {
unit: "candidate_findings",
},
);
assert.equal(
updated.result.structuredContent.scan.progress.preflightIssues[0]
.capability,
"usable_worker_slots_6",
);
assert.deepEqual(
updated.result.structuredContent.scan.progress.preflightProgress,
{
Expand Down
5 changes: 1 addition & 4 deletions plugins/codex-security/plugin-files.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,15 @@
"mcp/server.mjs",
"mcp/server.mjs.br.part-000",
"mcp/server.mjs.br.part-001",
"preflight/capability-profiles.toml",
"references/artifact-storage.md",
"references/config-preflight.md",
"references/core-scan.md",
"references/desktop-config-preflight.md",
"references/final-report.md",
"references/finding-detail-fields.md",
"references/sarif-adapter.md",
"references/scan-artifacts.md",
"references/scan-contract.md",
"references/scan-prologue.md",
"references/scan-runtime.md",
"references/security-guidance.md",
"references/standalone-validation.md",
"references/static-finding-assessment.md",
Expand All @@ -58,7 +56,6 @@
"schemas/tools/discovery-candidates.schema.json",
"schemas/tools/review-items.schema.json",
"schemas/tools/scan-draft.schema.json",
"scripts/config_preflight.py",
"scripts/codex_profile.mjs",
"scripts/codex_session.mjs",
"scripts/deep_scan_config.py",
Expand Down
Loading
Loading