Scope: the production server, the CDP custody path that derives every X-login wallet, the public static surface, and the on-chain provenance of every contract behind the raise. Method: manual review of the full server tree, Semgrep across four rulesets, and live probes against production rather than trust in a status code.
H3High
Wallet identity ran on code defaults, and drift would have been silent
FixedRiskEvery X-login wallet is derived from three configuration values. Two of them were not set explicitly and fell back to values written in code, one of them tied to a separate project identifier. A change to any of them would have handed every user a newly derived, empty wallet. Login would still have succeeded, health would still have reported green, and the existing balance would have sat at an address the application no longer derives. Nothing would have alerted.
FixAll three values are now pinned explicitly, which also breaks the link to the separate project identifier. The application records a fingerprint of the three on first boot and refuses to start if it ever changes, so a silent re-derivation becomes a loud refusal. Verified before deployment against a changed value of each of the three and against an empty value, then verified after deployment that the recorded fingerprint matches the value computed from the live configuration beforehand, so no identity moved.
Resolved 10:39 UTC, login, key set and session flow all reconfirmed live
M8Medium
Stale-file guard missed several editor backup shapes
FixedRiskThe server already refused to serve snapshot copies of client files, and every snapshot then on disk was correctly blocked. The rule missed some shapes an editor or package manager can leave behind. A file in one of those shapes, dropped next to a served asset, would have published full client source.
FixThe rule now also covers trailing-tilde files, further editor swap files, merge rejects, package-manager leftovers, a keyword with digits appended, and long date suffixes. Validated against all 560 real files under the public root: the same 31 snapshots are blocked, 526 assets serve unchanged, and the only other responses are three pre-existing canonical redirects.
Resolved 10:29 UTC
M9Medium
A backup web-server config sat inside the live load path
FixedRiskThe web server loads every file in its enabled-sites directory, and a dated backup of a live config had been left there. It was parsed as real configuration and produced eight duplicate host definitions, all silently ignored. Load order meant the correct file won, so nothing was broken, but a later rename or a second backup would have reversed that without any error and without failing a config test.
FixThe stray file was moved out of the load path and retained elsewhere. Duplicate host warnings went from eight to zero, the config test passes, and all three hosts serve normally. No other strays remain.
Resolved 10:42 UTC
M10Medium
Login popup placed a serialized payload into script context unescaped
FixedRiskThe page that returns a login result to the opening window embedded a serialized object inside a script block without escaping the characters that can end that block early. The visible message was escaped; this copy was not. Not reachable in practice, because every message at the three call sites is a fixed string and the token cannot contain the relevant character, but the page holds the login token, so script execution there would be account compromise rather than defacement.
FixThe payload is now escaped for script context before it is written. Verified that a hostile message no longer emits a block-terminating sequence or any raw angle bracket, that the escaped payload still parses back to the exact original string, and that an ordinary message is unchanged. The result is posted only to the page's own origin, never to a wildcard.
Resolved 10:29 UTC
L3Low
Authenticated-encryption tag length not pinned
FixedRiskThe vault that encrypts user-supplied API credentials verified its authentication tag but did not pin the tag length, so a short tag would have been accepted. Hardening rather than a break, and the tag is only ever written by the server.
FixThe tag length is now pinned and a wrong-length tag is rejected outright. Verified that a normal round trip succeeds, a truncated tag is refused, and a single flipped bit is refused. The vault held no records at the time, so no stored data was affected.
Resolved 10:29 UTC
M11Medium
Production tree carries uncommitted changes
TrackingNoteThe production checkout holds a large number of uncommitted modifications, so there is no single revision that describes what is running and a rollback has no precise target. This is a release-process gap, not a reachable vulnerability.
Tracking, deploys to ship a recorded revision
M12Medium
Dependency advisories, three high
TrackingNoteTwenty one advisories across the dependency tree, of which three are high and all three trace to a single transitive package with no fixed version published upstream. The automatic remediation is not available here because it downgrades a core library by several major versions, which would be a larger risk than the advisories themselves.
Tracking, named gap, waiting on an upstream release
L4Low
Configuration snapshots and a stray empty database file on disk
TrackingNoteFour dated copies of the server configuration file sit beside the live one. All carry owner-only permissions and none is reachable from the web, but each widens the blast radius of a host compromise. A separate empty database file in the application root is a correctness hazard rather than an exposure: a tool opened without the configured path reads it and every check passes against nothing.
Tracking, removal pending sign-off, nothing is deleted unilaterally
L5Low
One fee destination written in code rather than configuration
TrackingNoteEvery fee destination is read from configuration except one, which appears as a literal in two places in the same file. Two copies of one address drift independently, and the value cannot be rotated without a code change and a redeploy.
Tracking, to move into configuration
V1Verified
Confirmed sound under this pass
VerifiedCheckedNo credentials written into source anywhere in the server tree. No injection path: every dynamic query fragment is built from fixed text and every value is bound. Token algorithms pinned at both verification points. Session cookies restricted from script access, sent only over TLS, limited on cross-site use, and revocable server side. The outbound proxy validates scheme and host against a single permitted destination and refuses sensitive sub-paths. Tickers outside plain ASCII are rejected as impersonation. Slippage is bounded on every route. Path traversal probes all fail closed. The nightly backup ran and reached off-site storage the morning of the review.
ToolingSemgrep across four rulesets returned 37 results. Thirty three were false positives on inspection, including every raw-markup warning where the value already passes through the escaping helper, and a traversal warning on a route that validates its identifier against a fixed pattern first. The remaining four are recorded above. No credential rule matched anywhere in the tree.
Reviewed 2026-09-22