Files
rengine/shared/models/instance_settings.py
T
Yogesh OjhaandClaude Opus 5 f3b406ec03 feat(freshness): re-check a certificate between scans, which is Class B's first job
The derivation rule names four classes and only three had anything implementing
them. Class B is "needs a per-asset request, so a background job with its own
freshness, never a stage that blocks", and a certificate is the clearest case:
it expires and renews on a schedule no scan is aligned to. A dashboard built on
a scan from six weeks ago reports every certificate that has auto-renewed since
as expiring or expired — 548 hosts on this box sit inside a 30-day window right
now, and on a 90-day ACME cycle every one of them would be wrong within a month.

shared/services/cert_freshness.py + app.tasks.freshness.certificates (beat, 4h).
The policy is the design: a certificate whose stored expiry is more than 30 days
away is re-asked after 12h, one already inside its renewal window after 4h,
nearest expiry first, 500 a pass, one tlsx handshake per host. Only the newest
scan of each target is refreshed — an older scan's rows are a record of that
run, not a claim about today. A host that did not answer keeps what the scan
found and still gets its stamp moved: silence is not evidence the certificate
changed, but an unreachable host must not be re-asked every four hours.

It is opt-in, not opt-out (instance_settings.cert_recheck_enabled, default
false, a switch in Settings). The threat feeds only download; this one
handshakes the operator's targets outside a scan, and nobody should discover
that after the fact.

Two tool bugs, both the shape CLAUDE.md already lists four of. CLIToolRunner.run
defaults to OutputFormat.PLAIN and only appends json_flag for JSONL, so passing
json_flag without the format runs the tool with no -json at all — the first live
pass answered 0 of 40. And tlsx writes every record it is going to write and
then does not exit (40 hosts: 16s to emit, still running at 90s), so the run is
bounded by budget(hosts) and Freshness.cut_short reports a pass that hit the
ceiling instead of swallowing it. Together: 300.2s and 0 answers became 15.6s
and 37 of 40.

19 tests, each proven to fail against 9 mutations of the policy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013dKUV96WkQQETZBMoAG98C
2026-09-11 08:46:32 +05:30

103 lines
3.7 KiB
Python

import uuid
from datetime import datetime
from pydantic import BaseModel
from sqlalchemy import Column
from sqlalchemy.types import JSON
from sqlmodel import Field, SQLModel, UniqueConstraint
from shared.definitions.bounty_programs import (
DEFAULT_FEED_INTERVAL,
DEFAULT_SYNC_INTERVAL,
)
from shared.enums.instance import InstanceMode
from shared.utils.datetime import utc_now
class InstanceSettings(SQLModel, table=True):
__tablename__ = "instance_settings"
__table_args__ = (
UniqueConstraint("singleton_key", name="uq_instance_settings_singleton"),
)
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
singleton_key: str = Field(default="instance", max_length=20)
instance_name: str = Field(default="reNgine", max_length=120)
timezone: str = Field(default="UTC", max_length=64)
mode: str = Field(default=InstanceMode.BUG_BOUNTY.value)
onboarding_completed: bool = Field(default=False)
onboarding_completed_at: datetime | None = Field(default=None)
onboarding_completed_by: uuid.UUID | None = Field(default=None)
onboarding_step: int = Field(default=0)
onboarding_state: dict = Field(
default_factory=dict, sa_column=Column(JSON, nullable=False)
)
scan_history_retention_days: int = Field(default=90)
screenshot_retention_days: int = Field(default=30)
ai_enabled: bool = Field(default=False)
ai_provider: str | None = Field(default=None)
ai_model: str | None = Field(default=None)
ai_api_key_encrypted: str | None = Field(default=None)
ai_features: dict = Field(
default_factory=dict, sa_column=Column(JSON, nullable=False)
)
report_defaults: dict = Field(
default_factory=dict, sa_column=Column(JSON, nullable=False)
)
threat_intel_auto_sync: bool = Field(default=True)
# it handshakes targets outside a scan, so it is opt-in, not opt-out
cert_recheck_enabled: bool = Field(default=False)
bounty_sync_interval: str = Field(default=DEFAULT_SYNC_INTERVAL, max_length=16)
bounty_synced_at: datetime | None = Field(default=None)
bounty_events_seen_at: datetime | None = Field(default=None)
bounty_feed_interval: str = Field(default=DEFAULT_FEED_INTERVAL, max_length=16)
bounty_feed_synced_at: datetime | None = Field(default=None)
bounty_settings: dict = Field(
default_factory=dict, sa_column=Column(JSON, nullable=False)
)
mcp_enabled: bool = Field(default=False)
mcp_settings: dict = Field(
default_factory=dict, sa_column=Column(JSON, nullable=False)
)
created_at: datetime = Field(default_factory=utc_now)
updated_at: datetime = Field(default_factory=utc_now)
class InstanceSettingsUpdate(BaseModel):
instance_name: str | None = None
timezone: str | None = None
mode: str | None = None
scan_history_retention_days: int | None = None
screenshot_retention_days: int | None = None
cert_recheck_enabled: bool | None = None
ai_enabled: bool | None = None
ai_provider: str | None = None
ai_model: str | None = None
ai_api_key: str | None = None
ai_features: dict | None = None
class InstanceSettingsRead(BaseModel):
id: uuid.UUID
singleton_key: str
instance_name: str
timezone: str
mode: str
onboarding_completed: bool
onboarding_completed_at: datetime | None
onboarding_completed_by: uuid.UUID | None
onboarding_step: int
onboarding_state: dict
scan_history_retention_days: int
screenshot_retention_days: int
cert_recheck_enabled: bool
ai_enabled: bool
ai_provider: str | None
ai_model: str | None
ai_api_key_masked: str | None
ai_configured: bool
ai_features: dict
capabilities: list[str]
created_at: datetime
updated_at: datetime