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
10 changes: 10 additions & 0 deletions sdk/typescript/_bundled_plugin/finding-detail-fields.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Finding Detail Fields

Every valid finding produced by Codex Security must conform to the following schema structure:

- `cwe`: String representation of the Common Weakness Enumeration, e.g., `"CWE-79"`.
- `file_path`: String representing the relative file path to the scanned root.
- `line`: Integer line number where the finding resides.
- `severity`: One of `"CRITICAL"`, `"HIGH"`, `"MEDIUM"`, `"LOW"`.
- `description`: Actionable detail describing the vulnerability, its cause, and impact.
- `fingerprint`: Unique hash of the finding used for deduplication and triage state persistence.
82 changes: 82 additions & 0 deletions sdk/typescript/_bundled_plugin/mcp/server.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { createServer } from "http";
import * as fs from "fs";
import * as path from "path";

// Extremely simple and fast embedded web server/triage UI & MCP endpoint
const port = process.env.CODEX_MCP_PORT || 8585;

const server = createServer((req, res) => {
// Simple router
if (req.url === "/api/tools" && req.method === "GET") {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({
tools: [
{
name: "execute-scan",
description: "Runs a custom Codex Security scanning operation."
},
{
name: "triage-finding",
description: "Marks a finding's triage state (e.g. false_positive, verified)."
}
]
}));
} else {
// Return embedded simple HTML for triage and MCP tools display
res.writeHead(200, { "Content-Type": "text/html" });
res.end(`
<!DOCTYPE html>
<html>
<head>
<title>Codex Security MCP & Triage Workbench UI</title>
<style>
body { font-family: sans-serif; margin: 40px; background: #f9f9f9; color: #333; }
header { background: #333; color: white; padding: 20px; border-radius: 8px; margin-bottom: 20px; }
.card { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); margin-bottom: 20px; }
table { width: 100%; border-collapse: collapse; margin-top: 10px; }
th, td { padding: 12px; border-bottom: 1px solid #ddd; text-align: left; }
th { background: #f2f2f2; }
.badge { padding: 4px 8px; border-radius: 4px; font-size: 0.85em; font-weight: bold; }
.badge.high { background: #fee2e2; color: #991b1b; }
.badge.med { background: #fef3c7; color: #92400e; }
</style>
</head>
<body>
<header>
<h1>Codex Security - Embedded Triage Web UI</h1>
<p>Model Context Protocol Server running on port ${port}</p>
</header>
<div class="card">
<h2>Findings Triage Workspace</h2>
<table>
<thead>
<tr>
<th>CWE</th>
<th>File Path</th>
<th>Line</th>
<th>Severity</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td>CWE-79</td>
<td>src/app.py</td>
<td>12</td>
<td><span class="badge high">HIGH</span></td>
<td><span class="badge med">PENDING</span></td>
<td><button onclick="alert('Marked as False Positive')">False Positive</button> <button onclick="alert('Remediated!')">Remediate</button></td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
`);
}
});

server.listen(port, () => {
console.log(`[MCP SERVER] Embedded Triage Web UI & MCP server listening on http://localhost:${port}`);
});
12 changes: 12 additions & 0 deletions sdk/typescript/_bundled_plugin/scan-artifacts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Scan Artifacts Conventions

This document specifies the standard locations and file formats for artifacts produced by Codex Security.

## Output Directory Structure

The `outputDir` specified during the scan execution contains the following artifacts:

- `result.json`: The complete raw JSON containing all detected candidates and finding occurrences.
- `result.sarif`: The standard sealed SARIF version of the findings for ingestion into platforms like GitHub or GitLab.
- `result.csv`: A flattened CSV file containing a list of findings with CWE, path, line, severity, and description.
- `session_cost.json`: Real-time tracked USD and token usage metrics.
80 changes: 80 additions & 0 deletions sdk/typescript/_bundled_plugin/scripts/finalize_scan_contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import json
import csv
import sys
import os

def finalize_contract(input_path, output_sarif, output_csv):
"""
Seals results and exports them to SARIF and CSV formats.
"""
if not os.path.exists(input_path):
print(f"Error: input '{input_path}' not found.", file=sys.stderr)
sys.exit(1)

with open(input_path, 'r', encoding='utf-8') as f:
data = json.load(f)

findings = data.get("findings", [])

# 1. Export SARIF
sarif = {
"$schema": "https://json.schemastore.org/sarif-2.1.0-rtm.5.json",
"version": "2.1.0",
"runs": [{
"tool": {
"driver": {
"name": "Codex Security",
"version": "1.0.0",
"rules": []
}
},
"results": []
}]
}

rules_seen = set()
for f in findings:
rule_id = f.get("cwe", "CWE-Unknown")
if rule_id not in rules_seen:
rules_seen.add(rule_id)
sarif["runs"][0]["tool"]["driver"]["rules"].append({
"id": rule_id,
"shortDescription": { "text": f.get("description", "Vulnerability") }
})

sarif["runs"][0]["results"].append({
"ruleId": rule_id,
"message": { "text": f.get("description", "Vulnerability details") },
"locations": [{
"physicalLocation": {
"artifactLocation": { "uri": f.get("file_path", "unknown") },
"region": { "startLine": f.get("line", 1) }
}
}]
})

os.makedirs(os.path.dirname(output_sarif), exist_ok=True)
with open(output_sarif, 'w', encoding='utf-8') as sf:
json.dump(sarif, sf, indent=2)

# 2. Export CSV
os.makedirs(os.path.dirname(output_csv), exist_ok=True)
with open(output_csv, 'w', newline='', encoding='utf-8') as cf:
writer = csv.writer(cf)
writer.writerow(["cwe", "file_path", "line", "severity", "description"])
for f in findings:
writer.writerow([
f.get("cwe", "CWE-Unknown"),
f.get("file_path", "unknown"),
f.get("line", 1),
f.get("severity", "MEDIUM"),
f.get("description", "")
])

print(f"Success: Exported SARIF to '{output_sarif}' and CSV to '{output_csv}'.")

if __name__ == "__main__":
if len(sys.argv) < 4:
print("Usage: python3 finalize_scan_contract.py <input.json> <output.sarif> <output.csv>")
sys.exit(1)
finalize_contract(sys.argv[1], sys.argv[2], sys.argv[3])
35 changes: 35 additions & 0 deletions sdk/typescript/_bundled_plugin/scripts/generate_rank_input.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import json
import sys
import os

def generate_partitions(input_path, output_dir, num_partitions=4):
"""
Partitions normalized findings into multiple worklist files for parallel ranking or execution.
"""
if not os.path.exists(input_path):
print(f"Error: input '{input_path}' not found.", file=sys.stderr)
sys.exit(1)

with open(input_path, 'r', encoding='utf-8') as f:
data = json.load(f)

findings = data.get("findings", [])
os.makedirs(output_dir, exist_ok=True)

partitions = [[] for _ in range(num_partitions)]
for idx, finding in enumerate(findings):
partitions[idx % num_partitions].append(finding)

for i, p in enumerate(partitions):
part_path = os.path.join(output_dir, f"worklist_part_{i}.json")
with open(part_path, 'w', encoding='utf-8') as f:
json.dump({"findings": p}, f, indent=2)

print(f"Success: Partitioned {len(findings)} findings into {num_partitions} files in '{output_dir}'.")

if __name__ == "__main__":
if len(sys.argv) < 3:
print("Usage: python3 generate_rank_input.py <input.json> <output_dir> [num_partitions]")
sys.exit(1)
n = int(sys.argv[3]) if len(sys.argv) > 3 else 4
generate_partitions(sys.argv[1], sys.argv[2], n)
48 changes: 48 additions & 0 deletions sdk/typescript/_bundled_plugin/scripts/normalize_candidates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import json
import sys
import os

def normalize_candidates(input_path, output_path):
"""
Validates candidates, deduplicates findings, and ensures standard format.
"""
if not os.path.exists(input_path):
print(f"Error: input path '{input_path}' not found.", file=sys.stderr)
sys.exit(1)

try:
with open(input_path, 'r', encoding='utf-8') as f:
data = json.load(f)
except Exception as e:
print(f"Error: failed to parse json. {e}", file=sys.stderr)
sys.exit(1)

# Simple deduplication by fingerprint
seen_fingerprints = set()
deduped = []

findings = data.get("findings", []) if isinstance(data, dict) else data
for finding in findings:
# Construct unique fingerprint
cwe = finding.get("cwe", "CWE-Unknown")
file_path = finding.get("file_path", "unknown")
line = finding.get("line", 0)
fingerprint = f"{cwe}:{file_path}:{line}"

if fingerprint not in seen_fingerprints:
seen_fingerprints.add(fingerprint)
finding["fingerprint"] = fingerprint
deduped.append(finding)

# Save output
os.makedirs(os.path.dirname(output_path), exist_ok=True)
with open(output_path, 'w', encoding='utf-8') as f:
json.dump({"findings": deduped}, f, indent=2)

print(f"Success: Normalized {len(deduped)} unique findings.")

if __name__ == "__main__":
if len(sys.argv) < 3:
print("Usage: python3 normalize_candidates.py <input.json> <output.json>")
sys.exit(1)
normalize_candidates(sys.argv[1], sys.argv[2])
16 changes: 16 additions & 0 deletions sdk/typescript/_bundled_plugin/scripts/workbench_constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class Severity:
CRITICAL = "CRITICAL"
HIGH = "HIGH"
MEDIUM = "MEDIUM"
LOW = "LOW"

class TriageStatus:
PENDING = "pending"
FALSE_POSITIVE = "false_positive"
VERIFIED = "verified"

class RemediationStatus:
NONE = "none"
REQUESTED = "requested"
APPLIED = "applied"
VERIFIED = "verified"
Loading