Email security@plainreal.com. The same contact is published, with the
canonical URL and an expiry, at
/.well-known/security.txt
per RFC 9116.
Please report privately first. There is no bug bounty and no reward programme; saying so up front is fairer than letting you find out after the work.
Say what you tried and what happened. A saved copy of the page, the record you pasted, and the browser and version are usually enough to reproduce it.
One HTML file that checks an Ed25519 signature over a canonicalized JSON record,
plus its published test suite. It has no server, no build step at run time, no
dependencies, and no network access. connect-src 'none' is enforced by the
browser rather than promised in the copy.
That shape rules out most of what a report would usually cover, and rules a few things sharply in.
- A record that verifies when it should not, or fails when it should not.
- Any way the page transmits, stores, caches or logs what a reader pastes.
- Any way to make it execute attacker-controlled script, including through a crafted record, or through a page saved and reopened from disk.
- A defect in the vendored cryptography: canonicalization, SHA3-256, SHA-512, Ed25519, or the curve arithmetic under them.
- A published
index.htmlwhose SHA-256 does not match the signed release tag. - Anything in the page that overstates what a valid signature proves.
- The absence of a third-party audit. This is stated in the README; it is a known limitation, not a finding.
script-src 'unsafe-inline'in the Content-Security-Policy. It is unavoidable in a single self-contained file. Nothing in the page is written withinnerHTML,insertAdjacentHTMLordocument.write; every rendered value goes throughtextContent. There is no remote script source, noevaland nonew Function. So there is no injection sink for the directive to widen, and a report needs a working injection rather than the directive on its own. (Grepping forinnerHTMLreturns one hit: a comment saying not to use it.)- The demo public key embedded in the page. It signs the bundled sample only. The matching private key is not in this repository and is not published.
- A saved copy whose SHA-256 differs from the release. A copy produced by the page's own save button, or by the browser's Save Page As, is re-serialised from the live DOM. That is expected and is documented in the README. Fetch the file directly before comparing hashes.
- Missing security headers on a copy opened from
file://._headerscannot apply there, which is why the policy is duplicated in a<meta>tag.
A human reply, from one person, not a queue. If the report is valid, a fix and a new signed release; if it is not, the reasoning rather than a form letter. If something is a real limitation rather than a defect, it gets written into the README where readers will see it.
Each release publishes sha256(public/index.html) in its signed git tag, and
repeats it in the README. Trust the tag: a hash published only in the repository
it describes is circular, because whoever can edit one can edit the other.
git tag -v v1.0.0 # signature, and the hash in the message
shasum -a 256 public/index.html # compare