mirror of
https://github.com/yogeshojha/rengine.git
synced 2026-10-20 23:31:31 +02:00
a548afffa2e43ccc0bc7bbe7c2bb09dd424711b2
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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
|
||
|
|
0a69f3201c |
copy: state the fact, delete the explanation
Repo-wide sweep of every user-facing string: frontend components, routes, config and stores; API details and route docs; shared definitions and models; stage config help; report prose; MCP, toolbox, interest and connector text; notification and worker messages. Descriptions that explained, reassured, justified or restated their title are deleted. Confirm dialogs name what is removed and stop. Failed actions read "<Noun> not <verbed>", successes "<Noun> <verbed>". No reader address, no product-as-subject, no em-dashes or parentheticals in prose. Mirrors updated on both sides: EPSS band help, compare refusal reasons, interest labels. Four tests asserting on old wording follow the new text. The rules are condensed into CLAUDE.md under Writing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SK5o8xR96cbSnAfjYSTPSe |
||
|
|
0522444245 |
feat(reports): white-labelling, and a renderer that cannot be made to fetch
Uploaded typefaces and instance-wide branding defaults, which together make a report genuinely white-labellable: a client's own font, a logo set once, and a theme that names both. The security half came first, because the logo field was a live SSRF. It was a free-text string rendered into an <img src>, and WeasyPrint would have fetched whatever it pointed at from inside the worker. Nothing fetches now, at four layers. The load-bearing one is a WeasyPrint url_fetcher that allows data: URIs and files under three known roots and refuses everything else; it was checked against cloud metadata, internal https, loopback, /etc/passwd, a UNC host, ftp and protocol-relative references. Above it sit three input guards: a logo must be embedded data of a known image type, an uploaded theme may not use @import or a url() that is not data:, and authored markdown loses any image that is not embedded. A new feature that lets a report name a resource goes through the fetcher, not around it. A typeface is bytes, never a link. The family row records files named by us, so a hostile filename never reaches disk, and the family directory refuses anything resolving outside itself. A face is accepted only when the bytes agree: the signature is sniffed and then fontTools opens it and requires outlines, which is why fonttools is now an api dependency rather than only a WeasyPrint one. A 5-byte file with a valid TTF header and a 4 KB file of padding are both refused. The family name is banned from carrying quotes or braces because it is written into a quoted CSS font-family string. The standalone HTML export now embeds only the families the theme actually names, which halves it, and no longer carries file:// references that would have been dead outside the container. Instance defaults fill only what a template leaves blank, so setting a logo once never overrides a per-client deliverable that set its own. Verified: a real TTF family uploaded and embedded in a 53-page PDF under an uploaded theme with an uploaded logo, and every probe above refused. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017f2Mf2HGwFaTRnui4UxpRd |
||
|
|
424e6d6e23 |
feat(reports): a document engine, not an export
reNgine 2 could produce a PDF. This produces a deliverable, and it is built the way scan engines are built: sections are plug-and-play directories under reports/, each with a pydantic config model that IS the source of truth. That model feeds GET /reports/catalog, the builder renders its own form from it, and the API validates against it. Adding a section is a directory. Scan Engine is to stages what Report Template is to sections. WeasyPrint 69 renders it, chosen over Chromium and Typst after measuring what each can actually do. Only real CSS Paged Media gives a table of contents with true page numbers, running headers, a PDF outline and footnotes; SysReptor reaches the same verdict and pipes Chromium's HTML into WeasyPrint for exactly that reason. reNgine drops the Chromium hop because it templates in Jinja and draws its charts as SVG in Python, so a browser buys nothing and costs 400 MB per report. Typst was rejected because a user-uploadable theme would then have to be Typst code rather than CSS. A theme is data. shared/definitions/report_theme.py is the schema, six themes ship as YAML, and a user uploads their own; there is no markup in a theme, so there is no injection surface. Fonts are vendored so an offline instance still renders in Latin, Greek and Cyrillic. Everything the document claims is computed once, in reports/analysis/engine.py: a posture score whose every deduction is named, weaknesses ranked by the signals behind the rank rather than severity alone, attack paths that chain observations into a route, a remediation plan ordered by risk removed, per-dimension change against the previous run, and caveats that state what was never assessed. Two narrators read that one brief. Without AI the report still argues its case; with AI the prose is better and the model never sees a row, only the brief, capped and cached per check. An AI failure falls back and the report still completes. Generation is a job. POST /reports queues, the worker walks progress over SSE, and four formats come out of one render: PDF, self-contained HTML, Markdown and JSON. The estimate reads each section's own limits, so the page count it promises is close to the one it delivers. AI moves out of Settings into its own top-level page: connection, a model per tier, what it may be used for, what it has cost, and the narrative cache. Verified on a 1,379-host gov.cy scan: 124 pages in 19.5s across 19 sections, all six themes, and a broken key still producing a complete report. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017f2Mf2HGwFaTRnui4UxpRd |