From 49d159bc2d08d6e833bbf5044546c66b027b9c7a Mon Sep 17 00:00:00 2001 From: Julian Ahlmark Date: Fri, 29 May 2026 15:33:16 +0300 Subject: [PATCH] Add bounty submission quarantine guard --- bounty-submission-quarantine-guard/demo.js | 56 +++++++++++++++ bounty-submission-quarantine-guard/index.js | 38 ++++++++++ bounty-submission-quarantine-guard/readme.md | 33 +++++++++ .../render-video.js | 38 ++++++++++ .../reports/demo.mp4 | Bin 0 -> 7227 bytes .../reports/submission-quarantine-report.json | 67 ++++++++++++++++++ .../reports/submission-quarantine-report.md | 45 ++++++++++++ .../reports/summary.svg | 21 ++++++ .../sample-data.js | 52 ++++++++++++++ bounty-submission-quarantine-guard/test.js | 20 ++++++ 10 files changed, 370 insertions(+) create mode 100644 bounty-submission-quarantine-guard/demo.js create mode 100644 bounty-submission-quarantine-guard/index.js create mode 100644 bounty-submission-quarantine-guard/readme.md create mode 100644 bounty-submission-quarantine-guard/render-video.js create mode 100644 bounty-submission-quarantine-guard/reports/demo.mp4 create mode 100644 bounty-submission-quarantine-guard/reports/submission-quarantine-report.json create mode 100644 bounty-submission-quarantine-guard/reports/submission-quarantine-report.md create mode 100644 bounty-submission-quarantine-guard/reports/summary.svg create mode 100644 bounty-submission-quarantine-guard/sample-data.js create mode 100644 bounty-submission-quarantine-guard/test.js diff --git a/bounty-submission-quarantine-guard/demo.js b/bounty-submission-quarantine-guard/demo.js new file mode 100644 index 00000000..124052cb --- /dev/null +++ b/bounty-submission-quarantine-guard/demo.js @@ -0,0 +1,56 @@ +const fs = require("fs"); +const path = require("path"); +const { submissionBatch } = require("./sample-data"); +const { evaluateBatch } = require("./index"); + +const reportsDir = path.join(__dirname, "reports"); +fs.mkdirSync(reportsDir, { recursive: true }); + +const report = evaluateBatch(submissionBatch); +fs.writeFileSync(path.join(reportsDir, "submission-quarantine-report.json"), `${JSON.stringify(report, null, 2)}\n`); + +const markdown = [ + "# Scientific Bounty Submission Quarantine Report", + "", + `Batch: ${report.batchId}`, + `Generated: ${report.generatedAt}`, + "", + "## Summary", + "", + `- Submissions evaluated: ${report.summary.total}`, + `- Released to review: ${report.summary["release-to-review"] || 0}`, + `- Quarantined: ${report.summary["quarantine-submission"] || 0}`, + `- Findings: ${report.summary.findingCount}`, + "", + "## Decisions", + "", + ...report.submissions.flatMap((item) => [ + `### ${item.id}`, + "", + `- Challenge: ${item.challengeId}`, + `- Team: ${item.teamId}`, + `- Decision: ${item.decision}`, + `- Findings: ${item.findings.length ? item.findings.join("; ") : "none"}`, + `- Actions: ${item.actions.join("; ")}`, + "" + ]) +].join("\n").trim(); + +fs.writeFileSync(path.join(reportsDir, "submission-quarantine-report.md"), `${markdown}\n`); + +const svg = ` + + Submission Quarantine Guard + Scientific bounty package safety before sponsor access + ${report.submissions.map((item, index) => { + const y = 150 + index * 82; + const color = item.decision === "release-to-review" ? "#047857" : "#b45309"; + return ` + ${item.id} + ${item.decision} + ${item.teamId}, ${item.findings.length} finding(s)`; + }).join("\n ")} + +`; +fs.writeFileSync(path.join(reportsDir, "summary.svg"), svg); +console.log(JSON.stringify(report.summary, null, 2)); diff --git a/bounty-submission-quarantine-guard/index.js b/bounty-submission-quarantine-guard/index.js new file mode 100644 index 00000000..4154166a --- /dev/null +++ b/bounty-submission-quarantine-guard/index.js @@ -0,0 +1,38 @@ +function evaluateSubmission(submission) { + const findings = []; + + if (!submission.manifestComplete) findings.push("submission package manifest is incomplete"); + if (submission.artifactHashes.length === 0) findings.push("submission artifacts are missing hashes"); + if (submission.unsafeFlags.length > 0) findings.push(`unsafe artifact flags: ${submission.unsafeFlags.join(", ")}`); + if (submission.sponsorVisible && findings.length > 0) findings.push("sponsor visibility is enabled before quarantine clearance"); + if (!submission.reviewerPreview) findings.push("reviewer-safe preview is missing"); + if (submission.ipStatement === "missing") findings.push("IP handoff statement is missing"); + + const decision = findings.length === 0 ? "release-to-review" : "quarantine-submission"; + const actions = decision === "release-to-review" + ? ["release package to reviewer queue", "keep sponsor access gated until award decision"] + : ["quarantine package", "disable sponsor visibility", "request manifest and safety remediation"]; + + return { + id: submission.id, + challengeId: submission.challengeId, + teamId: submission.teamId, + decision, + findings, + actions + }; +} + +function evaluateBatch(batch) { + const submissions = batch.submissions.map(evaluateSubmission); + const summary = submissions.reduce((acc, submission) => { + acc.total += 1; + acc[submission.decision] = (acc[submission.decision] || 0) + 1; + acc.findingCount += submission.findings.length; + return acc; + }, { total: 0, findingCount: 0 }); + + return { batchId: batch.id, generatedAt: batch.generatedAt, summary, submissions }; +} + +module.exports = { evaluateSubmission, evaluateBatch }; diff --git a/bounty-submission-quarantine-guard/readme.md b/bounty-submission-quarantine-guard/readme.md new file mode 100644 index 00000000..679059aa --- /dev/null +++ b/bounty-submission-quarantine-guard/readme.md @@ -0,0 +1,33 @@ +# Scientific Bounty Submission Quarantine Guard + +This module adds a focused quarantine layer for the Scientific Bounty System. It decides whether solver submissions are safe and complete enough to reach reviewers or sponsors. + +It is intentionally separate from challenge intake, scoring and arbitration, payout routing, evidence freeze, anonymous review packets, prequalification, data-use agreements, closeout, reviewer workload, deadline fairness, and package security scanning. This guard handles submission quarantine and sponsor-visibility gating. + +## What It Checks + +- Package manifest completeness +- Artifact hash coverage +- Unsafe artifact flags such as executable binaries or macro-enabled documents +- Sponsor visibility before clearance +- Reviewer-safe preview availability +- IP handoff statement presence + +## Demo + +Run: + +```bash +node bounty-submission-quarantine-guard/test.js +node bounty-submission-quarantine-guard/demo.js +node bounty-submission-quarantine-guard/render-video.js +``` + +Generated artifacts: + +- `reports/submission-quarantine-report.json` +- `reports/submission-quarantine-report.md` +- `reports/summary.svg` +- `reports/demo.mp4` + +All data is synthetic. The module does not execute solver files, call storage providers, expose sponsor data, process payment details, use credentials, or contact SCIBASE production services. diff --git a/bounty-submission-quarantine-guard/render-video.js b/bounty-submission-quarantine-guard/render-video.js new file mode 100644 index 00000000..64b46c8f --- /dev/null +++ b/bounty-submission-quarantine-guard/render-video.js @@ -0,0 +1,38 @@ +const fs = require("fs"); +const path = require("path"); +const { spawnSync } = require("child_process"); + +const reportsDir = path.join(__dirname, "reports"); +fs.mkdirSync(reportsDir, { recursive: true }); +const width = 960; +const height = 540; +const ppm = path.join(reportsDir, "demo-frame.ppm"); +const mp4 = path.join(reportsDir, "demo.mp4"); + +function pixel(x, y) { + if (y < 112) return [23, 32, 51]; + if (x > 46 && x < 914 && y > 126 && y < 188) return [255, 255, 255]; + if (x > 46 && x < 914 && y > 210 && y < 272) return [255, 255, 255]; + if (x > 46 && x < 914 && y > 294 && y < 356) return [255, 255, 255]; + if (x > 46 && x < 914 && y > 378 && y < 440) return [255, 255, 255]; + if (x > 64 && x < 238 && y > 140 && y < 174) return [4, 120, 87]; + if (x > 64 && x < 238 && y > 224 && y < 258) return [180, 83, 9]; + if (x > 64 && x < 238 && y > 308 && y < 342) return [180, 83, 9]; + if (x > 64 && x < 238 && y > 392 && y < 426) return [4, 120, 87]; + return [248, 250, 252]; +} +const header = `P6\n${width} ${height}\n255\n`; +const body = Buffer.alloc(width * height * 3); +for (let y = 0; y < height; y += 1) { + for (let x = 0; x < width; x += 1) { + const offset = (y * width + x) * 3; + const [r, g, b] = pixel(x, y); + body[offset] = r; + body[offset + 1] = g; + body[offset + 2] = b; + } +} +fs.writeFileSync(ppm, Buffer.concat([Buffer.from(header), body])); +const result = spawnSync("ffmpeg", ["-y", "-loop", "1", "-framerate", "24", "-i", ppm, "-t", "5", "-vf", "format=yuv420p", "-movflags", "+faststart", mp4], { stdio: "inherit" }); +if (result.status !== 0) throw new Error("ffmpeg failed to render demo video"); +console.log(mp4); diff --git a/bounty-submission-quarantine-guard/reports/demo.mp4 b/bounty-submission-quarantine-guard/reports/demo.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..638c1cc12665c957f301e04019bbc20eca2afa14 GIT binary patch literal 7227 zcmeHKeNYtV8GnzH4~{s?hLpxa$0nvt?Zlc!kqikj(V!+lr8C$XuFt!B$a#0% zOglIIi+6VS{dm59@AG~<&mx2fm(t|pWzmCB62djSCZ>U!oTR<)@QV+?5nyOAZrPAt3n zKA7bqRWOHhDl)Vdae}ObRDlMpcSZR@9>+f7LBNWvw}gW zt-w{LbE53v5c+Bj)cgBON9H{?gpeLv(Iawh7PUi%Qrs8MzP+sP%ds&moas=ckW-iR ztJ)CCk`>kAxQp@m@rHwv3G=`UfeRkTTHj(Pc^kDcSQM2uD^ySPaTm*|x}ct>YyOz_ zkLCxwJ*M}^|L=UHVSmG6RL|ogINK~9fD$^}_}t^RZWL(EDn4W!G{cLp=!K!z zG~NHN{9%mnS7iT=2zsLX%OdoL_u;N*MWEvXdm!y9)Q)#ykEk$;-#zvJ^SaQGt(1S?7$L0Jn4$!s#22#2}QN)|Fes4xOUL1k6>y#@j=R0&x^5{#D-nq-b9{pJ$VPf=#V!_jWePssHS zXcZAYxd}w!YnPlfWh6leK7@z&b51+fNP&c1V!SmRO<4)nEr}k+4x$vHNSq+>GPH{Q z#ZFd%23zN$Nr1pOn?)~2o2Yz>B3z8D*nM&>@53CLf;yjFbh%_sp$#TNaZ4ZpiV6uq z6l)nb^z0LZCPEf?7Qjplkc3xKjj+6jQ7{nRt8kLQ027QmgnEf-va_Pc$0*Qe!AFn~ z@?KB@j1q%+ToU8qWbl^5?rVZJ@1$X|?2MD~;b}PR4xW**5nkfdi8XS3jazX5P4sbI zdyVJ=sy65Y(ORwvbkn9nGSsv~nns%}gv@eYj;&W{3yEP$4ECwS$!-vrSo@9XV!FiA ztmGm*ko|CYTF{|QW+O?|;mOdX(F#o;R;<>HpSFGj24scv(H4UD!79OyfJFpdOdV_h zyqu^7&dLb)diLOXqvzrF)VV#sNXg$6 z=o#6{WTrlP0ZV<~MQ7apOG7ZX^NsBFZRS(_zJImv>aXi~rUL1%z4?c47uBru9$mc} zv(9g|{UCkdkD@UIw?>GJfA?irKp~ySxed4W*k^||cGtXB~z|-qe-n+hh z<$>SqocT~&<<8+WQUnUzwdgeDb5#Q-yPS4wYiT+wSdN z`t-}t(wA=h_z;@S>#U#UY#V%GUZ1LVQ?~b+O9x&Vy}U%%eXy$e%CF7Gj`{bZ6hCut zcy!>)nr~LGJU?1;+vR)2-5qW$k^E9-v7VUMm0P)fh4Id-$3JIVf8Xfpg`^iU+tt^u ztZm5Kc$!rHdHZX@BgUsTU0nOE%OAC%f&-U=vwn1a!G*ntn$O&~@u|_@sn*$(KNTK8 z#3N-0C8zyrWCKcmB{)|ovE*I(B&StAcY6Mtf87r2s7GlR*0il&Hu$%noNDV(2VgAv zS#Sa&RA6h*5z2bUU)%waaftld^AEIi9o&DuCuxhj^~j@lw+{~F-5M;ijp@(zL(jt@G&69++EGy=^B zo${0j{4o*4^TCc-2>P^{9*#y3@s3?U1GAMf|mGX#pBU=T~FoqyGW*! + + Submission Quarantine Guard + Scientific bounty package safety before sponsor access + + sub-1 + release-to-review + team-alpha, 0 finding(s) + + sub-2 + quarantine-submission + team-beta, 5 finding(s) + + sub-3 + quarantine-submission + team-gamma, 2 finding(s) + + sub-4 + release-to-review + team-delta, 0 finding(s) + diff --git a/bounty-submission-quarantine-guard/sample-data.js b/bounty-submission-quarantine-guard/sample-data.js new file mode 100644 index 00000000..c0603258 --- /dev/null +++ b/bounty-submission-quarantine-guard/sample-data.js @@ -0,0 +1,52 @@ +const submissionBatch = { + id: "bounty-submission-quarantine-2026-05-29", + generatedAt: "2026-05-29T14:34:00.000Z", + submissions: [ + { + id: "sub-1", + challengeId: "challenge-bio-17", + teamId: "team-alpha", + manifestComplete: true, + artifactHashes: ["sha256:aa01", "sha256:bb02"], + unsafeFlags: [], + sponsorVisible: false, + reviewerPreview: true, + ipStatement: "solver-retains-until-paid" + }, + { + id: "sub-2", + challengeId: "challenge-ml-04", + teamId: "team-beta", + manifestComplete: false, + artifactHashes: ["sha256:cc03"], + unsafeFlags: ["macro-enabled-document"], + sponsorVisible: true, + reviewerPreview: false, + ipStatement: "missing" + }, + { + id: "sub-3", + challengeId: "challenge-climate-09", + teamId: "team-gamma", + manifestComplete: true, + artifactHashes: [], + unsafeFlags: ["executable-binary"], + sponsorVisible: false, + reviewerPreview: true, + ipStatement: "open-license-after-award" + }, + { + id: "sub-4", + challengeId: "challenge-quantum-02", + teamId: "team-delta", + manifestComplete: true, + artifactHashes: ["sha256:dd04"], + unsafeFlags: [], + sponsorVisible: false, + reviewerPreview: true, + ipStatement: "sponsor-transfer-on-payout" + } + ] +}; + +module.exports = { submissionBatch }; diff --git a/bounty-submission-quarantine-guard/test.js b/bounty-submission-quarantine-guard/test.js new file mode 100644 index 00000000..78c328b0 --- /dev/null +++ b/bounty-submission-quarantine-guard/test.js @@ -0,0 +1,20 @@ +const assert = require("assert"); +const { submissionBatch } = require("./sample-data"); +const { evaluateBatch } = require("./index"); + +const report = evaluateBatch(submissionBatch); + +assert.strictEqual(report.summary.total, 4); +assert.strictEqual(report.summary["release-to-review"], 2); +assert.strictEqual(report.summary["quarantine-submission"], 2); + +const beta = report.submissions.find((item) => item.id === "sub-2"); +assert(beta.findings.some((finding) => finding.includes("manifest"))); +assert(beta.findings.some((finding) => finding.includes("sponsor visibility"))); +assert(beta.findings.some((finding) => finding.includes("IP handoff"))); + +const gamma = report.submissions.find((item) => item.id === "sub-3"); +assert(gamma.findings.some((finding) => finding.includes("missing hashes"))); +assert(gamma.findings.some((finding) => finding.includes("executable-binary"))); + +console.log("bounty-submission-quarantine-guard tests passed");