
Security
This page explains how Excuto isolates audits, how we protect data, what we do not have yet, and how you report a security problem.
Last updated 2026-11-02
How audits are isolated
An audit is one automated test run of one website. A worker is a machine that runs audits. The deployment files of the worker set the controls in this section.
Each worker container runs one audit and then exits. Docker restarts it. The read-only file system and the memory-only /tmp keep nothing from the last audit.
Chromium sandbox
The worker opens each page in Chromium with the Chromium sandbox on. The worker image never sets the switch that turns it off. The browser starts with an empty environment, so the environment variables of the worker never reach it.
Container hardening
The compose file of the worker sets these limits:
- The container runs as a user without root rights.
- The root file system is read-only. Only
/tmp, which lives in memory, is writable. - The container drops all Linux capabilities except one that the Chromium sandbox needs.
- A process cannot gain new privileges.
- A seccomp profile from Playwright denies every system call that it does not allow.
- The container has a limit of 512 processes and 2 GB of memory.
- IPv6 is off in the container network.
Egress proxy
Every network connection from Chromium goes through a proxy on the same machine. The proxy looks up the host name once. If the answer is in a blocked range, the proxy refuses the request.
Then the proxy connects to the address that it checked. This stops DNS rebinding, where a name changes its address after the check. Only ports 80 and 443 pass. A browser flag stops WebRTC traffic that skips the proxy.
Private-address block
An audit must not reach an internal network or a cloud metadata service. We check this in five layers:
- When you submit a URL, our Functions resolve the host. They refuse the audit if any answer is in a private or reserved range.
- The worker resolves the host again before it opens a page.
- A guard inside the browser aborts requests to blocked IP addresses and to network schemes other than http and https.
- The egress proxy refuses blocked answers for every request.
- A firewall script in the worker deployment files rejects the same IPv4 ranges. The host must run the script, and must run it again after a reboot.
The blocked IPv4 ranges include 10.0.0.0/8, 127.0.0.0/8, 169.254.0.0/16 (the cloud metadata address), 172.16.0.0/12 and 192.168.0.0/16. The IPv6 list covers the matching private, local and reserved ranges.
The guard also refuses localhost, a URL with a user name or password, and any port other than 80 and 443.
How data is protected
An organization is the group that owns audits and their data. A member is a person who belongs to an organization. Staff are the people at Excuto who review findings. A finding is one problem that an audit reports. Evidence is the screenshots, videos and error records of a finding.
Access rules
- A member can read only the data of the organization that the member belongs to.
- A member sees the text and evidence of a finding only after staff approve it. The audit log can name a candidate finding earlier.
- Staff can read findings for review. See Human review.
- No web client can write to the database or to storage. Only our Functions and workers write.
- No web client can read API key records or read a file in storage directly.
- Each Function that a client calls checks the sign-in, the API key, or the share token before it acts.
Evidence links
Evidence files stay private in storage. A Function signs a link for a file when a member or staff asks for it. The link expires after 10 minutes. A Function signs only paths that belong to the audit in the request.
A member can also share a report with a link. The link holds a secret token of 128 random bits and shows approved findings only. The token is in the query string, so it can appear in request logs. The link does not expire. The member can revoke it at any time.
Secrets
The code of our Functions reads the Razorpay key id, key secret and webhook secret from Secret Manager. The worker reads its model API key from an environment file on the worker host. It reads a Google service-account key file from the disk of that host.
API keys
Excuto shows an API key once, when you create it. We store only a SHA-256 hash and a 12-character prefix. A key has 256 random bits. We compare keys in constant time. A revoked or expired key does not work.
Payment webhooks
Razorpay sends payment events to a webhook. The Function checks the HMAC-SHA256 signature of the raw body, with a constant-time compare. If the signature is wrong, the Function answers 400 and changes nothing.
A paid event must also match the stored amount and currency before we add credits. The card form is the hosted form of Razorpay. Our code does not store card data.
Data in transit
The web app talks to Firebase Authentication, Firestore, Cloud Functions and Cloud Storage over HTTPS. Google provides the TLS for these services. Our server calls to Razorpay use HTTPS. The worker calls the model provider at the address in its model setting. The default address uses HTTPS.
We plan to host the web app on Vercel. Vercel provides TLS for the sites that it hosts.
Data at rest
Google states that it encrypts data at rest in Firestore and Cloud Storage by default. This is a control of the provider, not one that we built. We do not manage the encryption keys.
Human review
Every finding starts as pending review. No member sees the text or evidence of a finding until staff approve it. An AI model can propose a finding. The model never decides that a bug exists, and it never publishes. Staff must approve each model finding.
Staff can read every audit, every finding (also pending and rejected ones) and every evidence file of every organization. Staff need this access to check that a finding is real and to repeat its steps.
Each staff decision is stored on the finding. The record holds the reviewer, the time, the review time in seconds, a reason and a note. The first decision wins. The Function refuses a second decision on the same finding.
Our code does not log when staff read evidence. Other people also hold wide access. Anyone with an owner or editor role in our Google Cloud project can read all data. The service-account key of the worker can read all data too.
What we do not have yet
Status of common controls
| Control | Status |
|---|---|
| SOC 2 | Not yet. |
| ISO 27001 | Not yet. |
| Third-party penetration test | Not yet. |
| Bug bounty | Not yet. We pay no rewards. You can still report a vulnerability. |
| Two-factor authentication | Not yet. Sign-in uses a password or Google sign-in. Staff use the same sign-in. |
| App Check (blocks calls from clients that are not our app) | Not yet. We decide on it before production opens to customers. |
| Single sign-on | Not yet. You can sign in with email and password, or with Google. |
| Data deletion tool | Not yet. No self-service deletion of an account or an organization, and no data export. |
If you need deletion today, email {{PRIVACY_EMAIL}}.
Report a vulnerability
Email {{SECURITY_EMAIL}} to report a vulnerability. The same contact is in our file at /.well-known/security.txt.
Include these facts in your report:
- What you found, and where.
- The steps that reproduce it.
- How we can contact you.
Safe harbor
We welcome good-faith security research. If you follow these rules, we will not take legal action against you for the research:
- Stay within the acceptable use policy.
- Test only your own organization and your own data.
- If you reach the data of another person, stop. Do not copy, keep or share it. Tell us.
- Do not run denial-of-service tests, spam, social engineering or physical attacks.
- Do not test the websites that our customers audit, or the services of our providers.
- Tell us before you publish, and give us time to fix the problem.
This promise covers our own systems only. We cannot speak for Google, Vercel or Razorpay.
What we do in return
We reply within {{SECURITY_RESPONSE_DAYS}} days with a first assessment. We keep you informed while we work. We fix a valid problem as fast as we can, and we tell you when the fix is live.
Incident response
If a security incident affects customer data, we tell the affected customers without undue delay. The notice says what happened and which data it affected.
We run no error-tracking service and no uptime monitor yet. Our Functions write logs to Cloud Logging.
Questions
Write to {{CONTACT_EMAIL}}. The other policies: