Files
rengine/shared/services/bounty_programs.py
T
Yogesh OjhaandClaude Opus 5 0b853e04b6 chore: production-readiness review across the tree
A full static review of the 3.0 tree, run as two fan-out passes with every
finding verified against the running stack rather than taken on inspection.

Correctness and security
- instance settings encrypted the mask over the stored AI key: `!= MASK` never
  matched `••••••••abcd`, so saving settings destroyed the key and `test_ai`
  posted the mask as a bearer token
- the MFA token was replayable for its full five-minute life; the gate now
  checks revocation and consumes the token after a successful exchange
- logout revoked nothing for a bearer client while answering "Logged out"
- `POST /api-keys/{id}/test` returned the plaintext key inside httpx's error
  string, which embeds the whole URL
- an interest rule could be edited or deleted through another project's id
- redis ran the celery broker under `allkeys-lru`, free to evict a queued task
  or a chord counter; now `noeviction`
- nuclei's stored command carried the OAST token and every auth header
- a finalize step could poison the session and leave a scan RUNNING for ever
- `is:issue` raised TypeError on every search
- offset paging repeated and skipped rows in four dimensions
- a cancelled run kept a stale `scans.*_found` rollup
- every PDF report raised `KeyError: 'software'`
- `version_key` truncated a wide numeric run and inverted release ordering
- psycopg raises a NUL as a bare ValueError, above StatementError, so every
  savepoint guard was blind to the case it was written for

Performance
- endpoints.host had no trigram index: 35.9ms seq scan -> 0.389ms
- free text compiled to one OR, not the prescribed UNION ALL: 132ms -> 41ms
- `/http-assets/summary` and `/ips/summary` materialised whole tables
- the cross-link `ip` kind ran an estate query per page and drew nothing

Frontend
- `$effect` loaders that read the state they write, an unbounded request loop
  on a failing endpoint
- error states that rendered as empty states
- 2FA backup codes were unusable while the copy said they worked
- a "Scan history" dialog that always reported none
- ~400 duplicated lines across five filter bars, now one component

Build and tooling
- every scanner installed at @latest; all 13 pinned to the measured versions
- CI built `web/`, gone since 2.x, with tags that cannot be valid
- the prettier pre-commit hook discarded its filenames and always passed
- the test suite destroyed itself when run twice

Also: subdomains from public code, via a pinned github-subdomains source and a
GitHub API key that subfinder receives too; sublist3r and oneforall removed
after measuring one unique host in 89 across four targets.

Verified: 853 tests, ruff clean over eleven packages, svelte-check 0 errors,
the count-is-a-promise gate at 131 checks and 0 mismatches, and 143 GET routes
with no 5xx. Four migrations, each with an exercised downgrade.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DVkdx5ynZ6aof5kLFczmhX
2026-09-13 18:03:29 +05:30

269 lines
8.5 KiB
Python

"""Program libraries from the platforms with a researcher API: sync, events and settings."""
from __future__ import annotations
import time
from datetime import datetime, timedelta
from sqlalchemy import delete, select, text
from sqlalchemy.orm import Session
from shared.definitions.bounty_programs import (
DEFAULT_SYNC_INTERVAL,
MAX_EVENT_DETAIL,
SYNC_INTERVAL_HOURS,
BountyEvent,
ProgramState,
ScopeAccess,
ScopeState,
SubmissionState,
asset_type_spec,
)
from shared.logging import get_logger
from shared.models.bounty_program import BountyEventRow, BountyProgram, BountyScope
from shared.services.bounty_providers import AccessDeniedError, BountyProvider
from shared.utils.datetime import utc_now
logger = get_logger(__name__)
def event_row(program: BountyProgram, kind: str, **extra) -> BountyEventRow:
return BountyEventRow(
platform=program.platform,
program_id=program.id,
handle=program.handle,
program_name=program.name,
kind=kind,
**extra,
)
def _program_changes(current: BountyProgram, row: dict) -> list[str]:
"""What moved on a program the library already knew about."""
kinds: list[str] = []
if current.submission_state != row["submission_state"]:
kinds.append(
BountyEvent.SUBMISSIONS_OPENED.value
if row["submission_state"] == SubmissionState.OPEN.value
else BountyEvent.SUBMISSIONS_CLOSED.value
)
if (
current.program_state != row["program_state"]
and row["program_state"] == ProgramState.PUBLIC.value
):
kinds.append(BountyEvent.PROGRAM_WENT_PUBLIC.value)
if row["offers_bounties"] and not current.offers_bounties:
kinds.append(BountyEvent.BOUNTIES_STARTED.value)
return kinds
def sync_programs(session: Session, provider: BountyProvider) -> dict[str, int]:
"""Refresh one platform's program list."""
started = time.monotonic()
rows = provider.programs()
existing = {
p.handle: p
for p in session.execute(
select(BountyProgram).where(BountyProgram.platform == provider.platform)
).scalars()
}
baseline = bool(existing)
created = updated = 0
seen: set[str] = set()
events: list[BountyEventRow] = []
for row in rows:
handle = row.get("handle")
if not handle or handle in seen:
continue
seen.add(handle)
current = existing.get(handle)
row["sources"] = sorted({*(current.sources if current else []), row["source"]})
if current is None:
program = BountyProgram(**row)
session.add(program)
session.flush()
created += 1
if baseline:
events.append(event_row(program, BountyEvent.PROGRAM_ADDED.value))
continue
for kind in _program_changes(current, row):
events.append(event_row(current, kind))
if current.source != row["source"]:
current.scopes_synced_at = None
current.scope_access = None
for key, value in row.items():
setattr(current, key, value)
updated += 1
session.add_all(events)
session.commit()
return {
"platform": provider.platform,
"programs": len(seen),
"created": created,
"updated": updated,
"duration_ms": int((time.monotonic() - started) * 1000),
"events": len(events),
}
_SCOPE_TRANSITIONS = {
(ScopeState.IN_SCOPE.value, ScopeState.OUT_OF_SCOPE.value): (
BountyEvent.WENT_OUT_OF_SCOPE.value
),
(ScopeState.OUT_OF_SCOPE.value, ScopeState.IN_SCOPE.value): (
BountyEvent.CAME_INTO_SCOPE.value
),
}
def scope_changes(
program: BountyProgram, before: dict[tuple[str, str], str], after: dict
) -> list[BountyEventRow]:
"""Added, removed and flipped assets, never on a program's first read."""
events: list[BountyEventRow] = []
for key, row in after.items():
asset_type, identifier = key
was = before.get(key)
if was is None:
kind = BountyEvent.SCOPE_ADDED.value
else:
kind = _SCOPE_TRANSITIONS.get((was, row["scope_state"]))
if kind is None:
continue
events.append(
event_row(
program,
kind,
asset_type=asset_type,
asset_identifier=identifier,
detail=(row.get("instruction") or None)
and row["instruction"][:MAX_EVENT_DETAIL],
)
)
for key in before.keys() - after.keys():
events.append(
event_row(
program,
BountyEvent.SCOPE_REMOVED.value,
asset_type=key[0],
asset_identifier=key[1],
)
)
return events
def sync_scopes(
session: Session, program: BountyProgram, provider: BountyProvider
) -> int:
"""Replace one program's scope rows with what the platform reports now."""
try:
fetched = provider.scopes(program)
except AccessDeniedError:
logger.info(
"bounty scope refused",
platform=provider.platform,
handle=program.handle,
)
program.scope_access = ScopeAccess.DENIED.value
program.joined = False
session.commit()
return 0
deduped: dict[tuple[str, str], dict] = {}
for row in fetched.entries:
deduped[(row["asset_type"], row["asset_identifier"])] = {
**row,
"program_id": program.id,
}
before: dict[tuple[str, str], str] = {}
if program.scopes_synced_at is not None:
before = {
(s.asset_type, s.asset_identifier): s.scope_state
for s in session.execute(
select(BountyScope).where(BountyScope.program_id == program.id)
).scalars()
}
session.add_all(scope_changes(program, before, deduped))
session.execute(delete(BountyScope).where(BountyScope.program_id == program.id))
session.add_all([BountyScope(**row) for row in deduped.values()])
for key, value in fetched.program_updates.items():
setattr(program, key, value)
program.scope_access = None
program.scopes_synced_at = utc_now()
session.commit()
return len(deduped)
def scopes_to_refresh(
session: Session, provider: BountyProvider
) -> list[BountyProgram]:
"""Every program whose scope this sync must read, narrowed by the platform's own feed."""
programs = list(
session.execute(
select(BountyProgram).where(BountyProgram.platform == provider.platform)
)
.scalars()
.all()
)
denied = ScopeAccess.DENIED.value
read_at = [p.scopes_synced_at for p in programs if p.scopes_synced_at is not None]
unread = [
p for p in programs if p.scopes_synced_at is None and p.scope_access != denied
]
if unread or not read_at:
return programs
oldest = min(read_at)
try:
changed = provider.changed_since(oldest)
except Exception as exc:
logger.info(
"bounty scope change feed unavailable",
platform=provider.platform,
error=str(exc),
)
return programs
if changed is None:
return programs
return [
p
for p in programs
if not p.external_id or p.external_id in changed or p.scope_access == denied
]
def sync_settings(session: Session) -> tuple[str, datetime | None]:
row = session.execute(
text(
"SELECT bounty_sync_interval, bounty_synced_at FROM instance_settings LIMIT 1"
)
).first()
if not row:
return DEFAULT_SYNC_INTERVAL, None
return (row[0] or DEFAULT_SYNC_INTERVAL), row[1]
def sync_due(session: Session) -> bool:
"""Whether the schedule says to sync now."""
interval, last = sync_settings(session)
hours = SYNC_INTERVAL_HOURS.get(interval)
if hours is None:
return False
return last is None or (utc_now() - last) >= timedelta(hours=hours)
def mark_synced(session: Session) -> None:
session.execute(text("UPDATE instance_settings SET bounty_synced_at = now()"))
session.commit()
def unreachable_summary(scopes: list[BountyScope]) -> dict[str, int]:
"""What the program lists that no scan can reach, counted by asset type label."""
counts: dict[str, int] = {}
for scope in scopes:
if scope.target_value:
continue
label = asset_type_spec(scope.asset_type).label
counts[label] = counts.get(label, 0) + 1
return counts