Last active
June 5, 2026 19:42
-
-
Save Patrick-Erichsen/7d6b3cae5e37e80e2c84fab0409f2cfd to your computer and use it in GitHub Desktop.
ClawHub scanner-triggered ban policy flow #pagedrop
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!doctype html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <title>ClawHub Scanner-Triggered Ban Flow</title> | |
| <style> | |
| :root { | |
| --bg: #f8faf8; | |
| --paper: #ffffff; | |
| --ink: #18211d; | |
| --muted: #5d6b63; | |
| --line: #d8e0da; | |
| --red: #b42318; | |
| --red-soft: #fff1f0; | |
| --amber: #a15c07; | |
| --amber-soft: #fff8e8; | |
| --green: #16703f; | |
| --green-soft: #eefaf2; | |
| --blue: #1b5d8f; | |
| --blue-soft: #edf6ff; | |
| --code: #0f172a; | |
| --shadow: 0 14px 32px rgba(30, 41, 59, 0.08); | |
| } | |
| * { | |
| box-sizing: border-box; | |
| } | |
| body { | |
| margin: 0; | |
| background: var(--bg); | |
| color: var(--ink); | |
| font: 15px/1.55 Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, | |
| "Segoe UI", sans-serif; | |
| } | |
| main { | |
| max-width: 1180px; | |
| margin: 0 auto; | |
| padding: 34px 20px 54px; | |
| } | |
| header { | |
| display: grid; | |
| grid-template-columns: minmax(0, 1fr) auto; | |
| gap: 22px; | |
| align-items: end; | |
| border-bottom: 1px solid var(--line); | |
| padding-bottom: 22px; | |
| margin-bottom: 24px; | |
| } | |
| h1, | |
| h2, | |
| h3, | |
| p { | |
| margin: 0; | |
| } | |
| h1 { | |
| font-size: clamp(30px, 5vw, 54px); | |
| line-height: 0.98; | |
| letter-spacing: 0; | |
| } | |
| h2 { | |
| font-size: 22px; | |
| line-height: 1.15; | |
| margin-bottom: 12px; | |
| } | |
| h3 { | |
| font-size: 14px; | |
| text-transform: uppercase; | |
| letter-spacing: 0.08em; | |
| color: var(--muted); | |
| margin-bottom: 10px; | |
| } | |
| .subtitle { | |
| max-width: 760px; | |
| margin-top: 14px; | |
| color: var(--muted); | |
| font-size: 17px; | |
| } | |
| .badge { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 8px; | |
| border: 1px solid var(--line); | |
| background: var(--paper); | |
| border-radius: 999px; | |
| padding: 8px 12px; | |
| color: var(--muted); | |
| white-space: nowrap; | |
| } | |
| .grid { | |
| display: grid; | |
| grid-template-columns: repeat(2, minmax(0, 1fr)); | |
| gap: 18px; | |
| } | |
| .card { | |
| background: var(--paper); | |
| border: 1px solid var(--line); | |
| border-radius: 8px; | |
| box-shadow: var(--shadow); | |
| padding: 18px; | |
| } | |
| .current { | |
| border-top: 5px solid var(--red); | |
| } | |
| .proposed { | |
| border-top: 5px solid var(--green); | |
| } | |
| .callout { | |
| margin: 20px 0; | |
| border: 1px solid var(--line); | |
| background: var(--paper); | |
| border-radius: 8px; | |
| padding: 16px 18px; | |
| display: grid; | |
| grid-template-columns: 180px minmax(0, 1fr); | |
| gap: 18px; | |
| box-shadow: var(--shadow); | |
| } | |
| .callout strong { | |
| display: block; | |
| font-size: 13px; | |
| text-transform: uppercase; | |
| letter-spacing: 0.08em; | |
| color: var(--green); | |
| } | |
| .flow { | |
| display: grid; | |
| gap: 10px; | |
| } | |
| .step { | |
| position: relative; | |
| border: 1px solid var(--line); | |
| background: #fbfdfb; | |
| border-radius: 8px; | |
| padding: 12px 12px 12px 42px; | |
| min-height: 64px; | |
| } | |
| .step::before { | |
| content: attr(data-step); | |
| position: absolute; | |
| left: 12px; | |
| top: 13px; | |
| width: 20px; | |
| height: 20px; | |
| border-radius: 999px; | |
| display: grid; | |
| place-items: center; | |
| font-size: 12px; | |
| font-weight: 800; | |
| color: #fff; | |
| background: var(--blue); | |
| } | |
| .current .step.hot::before, | |
| .current .step.danger::before { | |
| background: var(--red); | |
| } | |
| .proposed .step.safe::before { | |
| background: var(--green); | |
| } | |
| .step + .step::after { | |
| content: ""; | |
| position: absolute; | |
| left: 21px; | |
| top: -11px; | |
| width: 2px; | |
| height: 10px; | |
| background: var(--line); | |
| } | |
| .step b { | |
| display: block; | |
| margin-bottom: 2px; | |
| } | |
| .step span { | |
| display: block; | |
| color: var(--muted); | |
| font-size: 14px; | |
| } | |
| .pill { | |
| display: inline-block; | |
| margin-top: 8px; | |
| padding: 3px 8px; | |
| border-radius: 999px; | |
| font-size: 12px; | |
| font-weight: 700; | |
| } | |
| .pill.red { | |
| color: var(--red); | |
| background: var(--red-soft); | |
| } | |
| .pill.amber { | |
| color: var(--amber); | |
| background: var(--amber-soft); | |
| } | |
| .pill.green { | |
| color: var(--green); | |
| background: var(--green-soft); | |
| } | |
| .pill.blue { | |
| color: var(--blue); | |
| background: var(--blue-soft); | |
| } | |
| .split { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 18px; | |
| margin-top: 18px; | |
| } | |
| table { | |
| width: 100%; | |
| border-collapse: collapse; | |
| background: var(--paper); | |
| border: 1px solid var(--line); | |
| border-radius: 8px; | |
| overflow: hidden; | |
| box-shadow: var(--shadow); | |
| } | |
| th, | |
| td { | |
| text-align: left; | |
| vertical-align: top; | |
| padding: 11px 12px; | |
| border-bottom: 1px solid var(--line); | |
| } | |
| th { | |
| font-size: 12px; | |
| text-transform: uppercase; | |
| letter-spacing: 0.08em; | |
| color: var(--muted); | |
| background: #f1f6f2; | |
| } | |
| tr:last-child td { | |
| border-bottom: 0; | |
| } | |
| code { | |
| color: var(--code); | |
| background: #eef2f6; | |
| border: 1px solid #dde5ec; | |
| border-radius: 5px; | |
| padding: 1px 5px; | |
| font-size: 12px; | |
| } | |
| .repo { | |
| display: grid; | |
| grid-template-columns: repeat(3, minmax(0, 1fr)); | |
| gap: 12px; | |
| margin-top: 18px; | |
| } | |
| .repo-item { | |
| border: 1px solid var(--line); | |
| border-radius: 8px; | |
| background: var(--paper); | |
| padding: 12px; | |
| } | |
| .repo-item b { | |
| display: block; | |
| margin-bottom: 6px; | |
| } | |
| .repo-item p { | |
| color: var(--muted); | |
| font-size: 13px; | |
| } | |
| .mini-map { | |
| width: 100%; | |
| min-height: 210px; | |
| border: 1px solid var(--line); | |
| border-radius: 8px; | |
| background: linear-gradient(180deg, #ffffff, #f6faf7); | |
| margin: 12px 0 4px; | |
| } | |
| .legend { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 8px; | |
| color: var(--muted); | |
| font-size: 13px; | |
| margin-top: 8px; | |
| } | |
| .dot { | |
| width: 9px; | |
| height: 9px; | |
| display: inline-block; | |
| border-radius: 99px; | |
| margin-right: 5px; | |
| } | |
| .dot.red { | |
| background: var(--red); | |
| } | |
| .dot.green { | |
| background: var(--green); | |
| } | |
| .dot.blue { | |
| background: var(--blue); | |
| } | |
| .dot.amber { | |
| background: var(--amber); | |
| } | |
| footer { | |
| margin-top: 22px; | |
| color: var(--muted); | |
| font-size: 13px; | |
| } | |
| @media (max-width: 860px) { | |
| header, | |
| .grid, | |
| .split, | |
| .repo, | |
| .callout { | |
| grid-template-columns: 1fr; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <main> | |
| <header> | |
| <div> | |
| <h1>Move scanner enforcement from account bans to blocked attempts.</h1> | |
| <p class="subtitle"> | |
| Repo read: a ClawScan malicious skill result hides the skill, schedules | |
| <code>users.autobanMalwareAuthorInternal</code>, then bans the publisher and hides their | |
| other listings. The refined direction: block the malicious publish attempt, email local | |
| scan remediation guidance, and ban only after repeated malicious outcomes. | |
| </p> | |
| </div> | |
| <div class="badge">Planning artifact - no repo changes</div> | |
| </header> | |
| <section class="callout"> | |
| <strong>Verdict</strong> | |
| <p> | |
| We can make the system much less punitive without losing safety by splitting | |
| <b>publish-attempt risk</b> from <b>account risk</b>. A first malicious finding should block | |
| that submitted version and teach the publisher how to fix it locally. Appeals stay | |
| account-level only. | |
| </p> | |
| </section> | |
| <section class="grid" aria-label="Current and proposed flow diagrams"> | |
| <article class="card current"> | |
| <h2>Current Behavior</h2> | |
| <svg class="mini-map" viewBox="0 0 640 250" role="img" aria-label="Current flow"> | |
| <defs> | |
| <marker id="arrow-red" markerWidth="10" markerHeight="8" refX="8" refY="4" orient="auto"> | |
| <path d="M0,0 L10,4 L0,8 Z" fill="#b42318" /> | |
| </marker> | |
| <marker id="arrow-blue" markerWidth="10" markerHeight="8" refX="8" refY="4" orient="auto"> | |
| <path d="M0,0 L10,4 L0,8 Z" fill="#1b5d8f" /> | |
| </marker> | |
| </defs> | |
| <rect x="24" y="32" width="120" height="56" rx="8" fill="#edf6ff" stroke="#a6c9e8" /> | |
| <text x="84" y="58" text-anchor="middle" font-size="14" font-weight="700">Publish</text> | |
| <text x="84" y="76" text-anchor="middle" font-size="12">new version</text> | |
| <line x1="144" y1="60" x2="228" y2="60" stroke="#1b5d8f" stroke-width="2" marker-end="url(#arrow-blue)" /> | |
| <rect x="228" y="32" width="140" height="56" rx="8" fill="#fff8e8" stroke="#e9c783" /> | |
| <text x="298" y="57" text-anchor="middle" font-size="14" font-weight="700">Promote</text> | |
| <text x="298" y="76" text-anchor="middle" font-size="12">latest pointer</text> | |
| <line x1="368" y1="60" x2="452" y2="60" stroke="#1b5d8f" stroke-width="2" marker-end="url(#arrow-blue)" /> | |
| <rect x="452" y="32" width="140" height="56" rx="8" fill="#fff8e8" stroke="#e9c783" /> | |
| <text x="522" y="57" text-anchor="middle" font-size="14" font-weight="700">Scan async</text> | |
| <text x="522" y="76" text-anchor="middle" font-size="12">VT + ClawScan</text> | |
| <line x1="522" y1="88" x2="522" y2="128" stroke="#b42318" stroke-width="2" marker-end="url(#arrow-red)" /> | |
| <rect x="452" y="128" width="140" height="56" rx="8" fill="#fff1f0" stroke="#e7a6a0" /> | |
| <text x="522" y="152" text-anchor="middle" font-size="14" font-weight="700">Malicious</text> | |
| <text x="522" y="171" text-anchor="middle" font-size="12">hidden skill</text> | |
| <line x1="452" y1="156" x2="366" y2="156" stroke="#b42318" stroke-width="2" marker-end="url(#arrow-red)" /> | |
| <rect x="226" y="128" width="140" height="56" rx="8" fill="#fff1f0" stroke="#e7a6a0" /> | |
| <text x="296" y="152" text-anchor="middle" font-size="14" font-weight="700">Autoban</text> | |
| <text x="296" y="171" text-anchor="middle" font-size="12">owner account</text> | |
| <line x1="226" y1="156" x2="144" y2="156" stroke="#b42318" stroke-width="2" marker-end="url(#arrow-red)" /> | |
| <rect x="24" y="128" width="120" height="56" rx="8" fill="#fff1f0" stroke="#e7a6a0" /> | |
| <text x="84" y="152" text-anchor="middle" font-size="14" font-weight="700">Nuke blast</text> | |
| <text x="84" y="171" text-anchor="middle" font-size="12">tokens + all listings</text> | |
| </svg> | |
| <div class="flow"> | |
| <div class="step" data-step="1"> | |
| <b>Publish writes the version first.</b> | |
| <span> | |
| <code>publishVersionForUser</code> inserts the version, then schedules VT, ClawScan, | |
| and dependency scans. | |
| </span> | |
| <span class="pill amber">async scan after write</span> | |
| </div> | |
| <div class="step" data-step="2"> | |
| <b>If semver is newer, it becomes latest immediately.</b> | |
| <span> | |
| <code>insertVersion</code> updates <code>latestVersionId</code> and the | |
| <code>latest</code> tag before final scanner outcome. | |
| </span> | |
| <span class="pill amber">previous clean version can be displaced</span> | |
| </div> | |
| <div class="step hot" data-step="3"> | |
| <b>ClawScan malicious means skill hidden.</b> | |
| <span> | |
| Structured moderation sets <code>moderationVerdict="malicious"</code> and | |
| <code>moderationStatus="hidden"</code>. | |
| </span> | |
| <span class="pill red">artifact blocked</span> | |
| </div> | |
| <div class="step danger" data-step="4"> | |
| <b>Then it schedules account autoban.</b> | |
| <span> | |
| <code>scheduleClawScanAutobanForMalware</code> calls | |
| <code>users.autobanMalwareAuthorInternal</code>. | |
| </span> | |
| <span class="pill red">artifact risk becomes account risk</span> | |
| </div> | |
| <div class="step danger" data-step="5"> | |
| <b>Autoban hides owned content and blocks login.</b> | |
| <span> | |
| The mutation soft-deletes owned skills, revokes API tokens, applies package bans, | |
| clears telemetry, and sets <code>user.deletedAt</code>. | |
| </span> | |
| <span class="pill red">full-account recovery problem</span> | |
| </div> | |
| </div> | |
| </article> | |
| <article class="card proposed"> | |
| <h2>Proposed Behavior</h2> | |
| <svg class="mini-map" viewBox="0 0 640 250" role="img" aria-label="Proposed flow"> | |
| <defs> | |
| <marker id="arrow-green" markerWidth="10" markerHeight="8" refX="8" refY="4" orient="auto"> | |
| <path d="M0,0 L10,4 L0,8 Z" fill="#16703f" /> | |
| </marker> | |
| <marker id="arrow-amber" markerWidth="10" markerHeight="8" refX="8" refY="4" orient="auto"> | |
| <path d="M0,0 L10,4 L0,8 Z" fill="#a15c07" /> | |
| </marker> | |
| </defs> | |
| <rect x="24" y="32" width="120" height="56" rx="8" fill="#edf6ff" stroke="#a6c9e8" /> | |
| <text x="84" y="57" text-anchor="middle" font-size="14" font-weight="700">Publish</text> | |
| <text x="84" y="76" text-anchor="middle" font-size="12">candidate version</text> | |
| <line x1="144" y1="60" x2="228" y2="60" stroke="#16703f" stroke-width="2" marker-end="url(#arrow-green)" /> | |
| <rect x="228" y="32" width="140" height="56" rx="8" fill="#eefaf2" stroke="#a9dabd" /> | |
| <text x="298" y="57" text-anchor="middle" font-size="14" font-weight="700">Hold until clean</text> | |
| <text x="298" y="76" text-anchor="middle" font-size="12">or keep prior latest</text> | |
| <line x1="368" y1="60" x2="452" y2="60" stroke="#16703f" stroke-width="2" marker-end="url(#arrow-green)" /> | |
| <rect x="452" y="32" width="140" height="56" rx="8" fill="#fff8e8" stroke="#e9c783" /> | |
| <text x="522" y="57" text-anchor="middle" font-size="14" font-weight="700">Scan async</text> | |
| <text x="522" y="76" text-anchor="middle" font-size="12">final verdict</text> | |
| <line x1="522" y1="88" x2="522" y2="128" stroke="#a15c07" stroke-width="2" marker-end="url(#arrow-amber)" /> | |
| <rect x="452" y="128" width="140" height="56" rx="8" fill="#fff8e8" stroke="#e9c783" /> | |
| <text x="522" y="152" text-anchor="middle" font-size="14" font-weight="700">Malicious</text> | |
| <text x="522" y="171" text-anchor="middle" font-size="12">block attempt</text> | |
| <line x1="452" y1="156" x2="366" y2="156" stroke="#16703f" stroke-width="2" marker-end="url(#arrow-green)" /> | |
| <rect x="226" y="128" width="140" height="56" rx="8" fill="#eefaf2" stroke="#a9dabd" /> | |
| <text x="296" y="152" text-anchor="middle" font-size="14" font-weight="700">Owner active</text> | |
| <text x="296" y="171" text-anchor="middle" font-size="12">fix + resubmit</text> | |
| <line x1="226" y1="156" x2="144" y2="156" stroke="#16703f" stroke-width="2" marker-end="url(#arrow-green)" /> | |
| <rect x="24" y="128" width="120" height="56" rx="8" fill="#eefaf2" stroke="#a9dabd" /> | |
| <text x="84" y="152" text-anchor="middle" font-size="14" font-weight="700">Escalate only</text> | |
| <text x="84" y="171" text-anchor="middle" font-size="12">2 artifacts / 3 tries</text> | |
| </svg> | |
| <div class="flow"> | |
| <div class="step safe" data-step="1"> | |
| <b>Publish creates a candidate, not live-by-default latest.</b> | |
| <span> | |
| For updates, keep the previous clean version live until the new candidate passes | |
| policy. For a first version, nothing is public until the candidate is clean. | |
| </span> | |
| <span class="pill green">clean version stays live</span> | |
| </div> | |
| <div class="step safe" data-step="2"> | |
| <b>Malicious result blocks the attempt.</b> | |
| <span> | |
| Store verdict, evidence, hash, scanner labels, and blocked status on the submitted | |
| version. Do not promote it, index it, install it, or expose it as latest. | |
| </span> | |
| <span class="pill amber">candidate-only safety action</span> | |
| </div> | |
| <div class="step safe" data-step="3"> | |
| <b>Publisher gets email remediation, not appeal CTA.</b> | |
| <span> | |
| Email and dashboard explain the affected skill/version and tell them to run | |
| <code>clawhub scan</code> locally, fix the skill/plugin, and try uploading again. | |
| </span> | |
| <span class="pill blue">self-service recovery</span> | |
| </div> | |
| <div class="step safe" data-step="4"> | |
| <b>Retries are allowed, but counted silently.</b> | |
| <span> | |
| Let the publisher upload fixed versions of the same skill. Do not show the attempt | |
| count in UI, but track it server-side. | |
| </span> | |
| <span class="pill amber">no public counter</span> | |
| </div> | |
| <div class="step safe" data-step="5"> | |
| <b>Ban only when the pattern crosses threshold.</b> | |
| <span> | |
| Ban and send the account-level appeal email when the user has 2 distinct malicious | |
| skills/plugins, or the same artifact is still malicious after 3 attempts. | |
| </span> | |
| <span class="pill red">appeals stay account-level</span> | |
| </div> | |
| </div> | |
| </article> | |
| </section> | |
| <section class="split"> | |
| <article> | |
| <h2>Policy Split</h2> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Signal</th> | |
| <th>Current consequence</th> | |
| <th>Suggested consequence</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td>ClawScan says latest skill version is malicious</td> | |
| <td>Hide skill, then autoban publisher.</td> | |
| <td>Block that candidate version; keep prior clean version live when possible.</td> | |
| </tr> | |
| <tr> | |
| <td>Static or VirusTotal alone flags something</td> | |
| <td>Generally scanner evidence; should not be sole autoban trigger.</td> | |
| <td>Keep as evidence, not automatic account action.</td> | |
| </tr> | |
| <tr> | |
| <td>2 distinct malicious skills/plugins, same artifact still malicious after 3 attempts, evasion, spam, or rate-limit abuse</td> | |
| <td>Manual ban or autoban path can disable account.</td> | |
| <td>Account ban remains valid; email includes account appeal link.</td> | |
| </tr> | |
| <tr> | |
| <td>False-positive recovery</td> | |
| <td>External appeal can unban, then restore timestamp-matched content.</td> | |
| <td>Before a ban, owner fixes locally with <code>clawhub scan</code> and retries upload.</td> | |
| </tr> | |
| <tr> | |
| <td>Malicious first version</td> | |
| <td>Can become a full account-ban incident.</td> | |
| <td>Nothing is published; blocked candidate is retained as evidence/remediation context.</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </article> | |
| <article> | |
| <h2>Implementation Shape</h2> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Change</th> | |
| <th>Likely owner</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td>Stop scheduling autoban directly from ClawScan malicious skill results.</td> | |
| <td><code>convex/skills.ts</code></td> | |
| </tr> | |
| <tr> | |
| <td>Add candidate-version blocking/promotion semantics, especially for updates.</td> | |
| <td><code>insertVersion</code> and public skill resolution</td> | |
| </tr> | |
| <tr> | |
| <td>Email publisher when a submitted version is blocked as malicious.</td> | |
| <td>new artifact-blocked email action/template</td> | |
| </tr> | |
| <tr> | |
| <td>Expose publisher-visible remediation dashboard for blocked attempts.</td> | |
| <td><code>src/routes/dashboard.tsx</code> plus Convex query</td> | |
| </tr> | |
| <tr> | |
| <td>Track silent escalation counters: distinct malicious artifacts and attempts per artifact.</td> | |
| <td>new Convex table/fields keyed by owner + artifact/version hash</td> | |
| </tr> | |
| <tr> | |
| <td>Escalate to ban only through threshold or explicit account-risk policy.</td> | |
| <td><code>users.autoban...</code> becomes threshold/reviewer path</td> | |
| </tr> | |
| <tr> | |
| <td>Align skills with package/plugin release moderation vocabulary.</td> | |
| <td>reuse concepts from <code>packages.ts</code> / <code>packageSecurity.ts</code></td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </article> | |
| </section> | |
| <section class="callout" style="margin-top: 22px;"> | |
| <strong>Skill/plugin parity</strong> | |
| <p> | |
| Plugin/package releases already have a closer precedent for the desired model: resolve the | |
| release scan status, block malicious/quarantined releases from download, and keep the action | |
| scoped to the release. The skill flow should match that shape. If plugin publish attempts | |
| currently promote or expose a malicious candidate differently than this proposal, align them | |
| so skills and plugins both use candidate-version blocking, remediation email, silent | |
| thresholds, and account-level bans only after escalation. | |
| </p> | |
| </section> | |
| <section> | |
| <h2 style="margin-top: 22px;">Repo Anchors</h2> | |
| <div class="repo"> | |
| <div class="repo-item"> | |
| <b>Autoban trigger</b> | |
| <p> | |
| <code>convex/skills.ts:525-543</code> schedules | |
| <code>users.autobanMalwareAuthorInternal</code> for ClawScan malicious skill verdicts. | |
| </p> | |
| </div> | |
| <div class="repo-item"> | |
| <b>Account blast radius</b> | |
| <p> | |
| <code>convex/users.ts:2454-2542</code> hides owned skills, revokes tokens, sets | |
| <code>deletedAt</code>, applies package bans, audits, and emails. | |
| </p> | |
| </div> | |
| <div class="repo-item"> | |
| <b>Immediate latest promotion</b> | |
| <p> | |
| <code>convex/skills.ts:10582-10664</code> moves <code>latestVersionId</code> and tags | |
| on semver-greater publishes. | |
| </p> | |
| </div> | |
| <div class="repo-item"> | |
| <b>Async scan after publish</b> | |
| <p> | |
| <code>convex/lib/skillPublish.ts:313-366</code> inserts the version, then queues VT and | |
| ClawScan. | |
| </p> | |
| </div> | |
| <div class="repo-item"> | |
| <b>Existing package precedent</b> | |
| <p> | |
| <code>convex/lib/packageSecurity.ts:86-108</code> blocks only malicious/quarantined | |
| package releases. | |
| </p> | |
| </div> | |
| <div class="repo-item"> | |
| <b>Appeal service</b> | |
| <p> | |
| <code>convex/httpApiV1/usersV1.ts:658-718</code> supports service-token ban context | |
| lookup and accepted-appeal unban. | |
| </p> | |
| </div> | |
| </div> | |
| </section> | |
| <section class="callout" style="margin-top: 22px;"> | |
| <strong>Issue seed</strong> | |
| <p> | |
| Replace first-offense scanner-triggered skill autobans with candidate-version blocking: | |
| do not publish a malicious first version, retain the previous clean latest version for | |
| updates, email the publisher with <code>clawhub scan</code> remediation guidance, allow | |
| fixed re-upload attempts, and reserve account bans for 2 distinct malicious artifacts, the | |
| same artifact still malicious after 3 attempts, evasion, spam/rate-limit abuse, or | |
| human-confirmed malicious behavior. | |
| </p> | |
| </section> | |
| <footer> | |
| Generated from local repo inspection on 2026-06-05. This is a planning artifact, not a | |
| source change. | |
| </footer> | |
| </main> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment