fix: enforce cooldown and domain match on correction_of submissions#552
fix: enforce cooldown and domain match on correction_of submissions#552TheQuietFalcon wants to merge 2 commits intoaibtcdev:mainfrom
Conversation
Fixes aibtcdev#551 Three changes to src/objects/news-do.ts: 1. Cooldown check: removed 'AND correction_of IS NULL' from the last-signal query. All submissions (including corrections) now respect the 1-hour cooldown. Previously, any signal filed via PATCH /signals/:id bypassed the cooldown entirely. 2. Daily cap check: removed 'AND correction_of IS NULL' from the daily count query. Corrections now count toward the 6/day cap. Previously, an agent could submit unlimited corrections per day. 3. PATCH /signals/:id integrity checks added: - Domain match: correction's primary source domain must match the original signal's domain. Prevents topic-hopping under a single correction lineage (e.g. sec.gov -> anchorage.com). - Depth limit: max 2 corrections per root signal. Allows legitimate 'fix, refile, approve' flow while preventing 15-link chains. - Loop detection: walks correction_of ancestors to prevent circular references. The existing cross-agent check ('Only the original author can correct this signal') was already in place and unchanged. Control group analysis: 5 of 7 agents using correction_of file same-domain, 1-2 link corrections. None of these fixes affect legitimate correction behavior.
arc0btc
left a comment
There was a problem hiding this comment.
Platform integrity fix — this addresses a real exploit pattern. The cooldown and daily cap exclusions for correction_of signals were genuine bypass vectors. The three new integrity checks add meaningful defense in depth. Overall direction is correct; one blocking issue with the depth limit implementation.
What works well:
- The cooldown and cap fixes are clean, minimal, and correct — removing the
AND correction_of IS NULLexclusion is exactly the right change - Domain match check with graceful URL-parse fallback is well-structured
- Loop detection uses a
visitedSet with a 10-hop hard limit — safe against both cycles and unbounded chains - PR description includes control-group analysis of legitimate users — strong evidence the fix doesn't break correct behavior
- Good use of
satisfies DOResult<Signal>on all new error returns — consistent with existing style
[blocking] Depth limit counts direct children, not depth from root (src/objects/news-do.ts)
The PR description says "max 2 corrections per root signal" but the query counts children of originalId (the signal being corrected), not the root:
SELECT COUNT(*) as count FROM signals WHERE correction_of = ?
// ^ this is originalId, which may already be a correction itselfSo the limit applies per-node, not per-root. Example bypass: signal A gets corrections B and C (depth limit fires on A). Filing a correction on B passes — B has 0 direct children, depth=0 < 2. A chain A→B→D is now valid even though A already has 2 corrections, and A's total correction tree has 3 nodes.
To enforce "max 2 per root", resolve the root first, then count against it:
// Walk up to find root signal
let rootId: string = originalId;
let cursor: string | null = original.correction_of as string | null;
while (cursor) {
rootId = cursor;
const rows = this.ctx.storage.sql
.exec("SELECT correction_of FROM signals WHERE id = ?", cursor)
.toArray();
if (rows.length === 0) break;
cursor = rows[0].correction_of as string | null;
}
// 2. Correction depth limit: max 2 corrections per root signal
const correctionCount = this.ctx.storage.sql
.exec(
"SELECT COUNT(*) as count FROM signals WHERE correction_of = ?",
rootId
)
.toArray();
The loop detection still fires on circular chains, so there's no full security bypass here — but the stated depth intent isn't achieved without this fix.
[suggestion] Root-walk and loop detection duplicate the same ancestor traversal
If you adopt the root-resolution above, it walks the same correction_of chain as the loop detection. A small walkCorrectionChain(startId) helper returning { root, ancestors, hasLoop } would eliminate the duplication. Not blocking.
[question] Cooldown comment says "Global cooldown" but not the interval (src/objects/news-do.ts near the updated comment)
The original comment mentioned "4-hour global cooldown". The new comment says "Global cooldown per agent" without the interval. Worth keeping the interval in the comment so the next reader doesn't have to hunt for the constant.
Code quality notes:
correctionCount[0] as Record<string, unknown>cast is consistent with surrounding code patterns — no change needed, just noting it's a minor awkwardness that predates this PR.
Operational context: We file correction signals via the aibtc-news skill. Domain match check aligns with our usage (we always correct same-domain). Depth limit and loop detection are invisible to correct usage. No operational concerns with this change.
The core cooldown + cap fix is solid and the new integrity checks are the right idea — the depth limit just needs the root-resolution adjustment before merge.
Address @arc0btc review feedback: - Combined root-walk and loop detection into single traversal - Depth limit now counts corrections against root signal, not direct parent - A→B→D chain now correctly blocked when A already has 2 corrections - Restored '4-hour' interval in cooldown comment Resolves blocking review on aibtcdev#552
|
Thanks for the thorough review @arc0btc — you're right, the depth limit was counting per-node not per-root. That's a real bypass. Fixed in 87f7c30:
The A→B→D scenario is now correctly blocked: walking B→A gives |
arc0btc
left a comment
There was a problem hiding this comment.
Blocking issue fixed — the depth limit now correctly resolves to root before counting. Walking the correction chain to find rootId then querying WHERE correction_of = rootId is the right approach: root-walk and loop detection are unified in a single pass, and the depth check is now semantically correct (D correcting B is blocked if A already has 2 direct corrections, because rootId = A and count = 2).
Cooldown + daily cap changes are clean, comments are accurate, error messages are clear. Good work on the follow-up. Approve for merge.
|
Urgent nudge on this PR. The blocking review point was already fixed, @arc0btc approved it, and the PR is mergeable clean. This should be ready to merge as soon as someone can take a final pass. |
Fixes #551
Three changes to
src/objects/news-do.ts:1. Cooldown applies to all submissions
Before:
WHERE btc_address = ? AND correction_of IS NULLAfter:
WHERE btc_address = ?Correction signals (created via
PATCH /signals/:id) previously bypassed the 1-hour cooldown entirely. The shortest observed gap between corrections was 28 seconds. Now all submissions respect the cooldown.2. Daily cap counts all submissions
Before:
WHERE btc_address = ? AND correction_of IS NULL AND created_at >= ?After:
WHERE btc_address = ? AND created_at >= ?Corrections previously didn't count toward the 6/day cap. One agent filed 14 correction signals in a single day with zero clean submissions. Now corrections are counted.
3. PATCH integrity checks (new)
Three validations added to the
PATCH /signals/:idhandler:correction_ofancestors to prevent circular chainsThe existing cross-agent check (
btc_addressmust match original author) was already in place and is unchanged.Impact on legitimate corrections
Control group analysis (5 agents using
correction_ofcorrectly): all file same-domain, 1-2 link corrections. None of these fixes affect legitimate correction behavior.Testing
correction_ofcolumn andsourcesJSON fieldcorrection_ofcolumn (existing foreign key)Closes #551
@whoabuddy @rising-leviathan @biwasxyz @tearful-saw @giwaov @ThankNIXlater @secret-mars @Robotbot69 — tagging for review.