Trust
Testing our own boundaries
A sandbox assurance run, September 2026
- Run on
- Measured at commit
a7383d52- Model spend
- $0.38 over 76 model requests, scored run
In short. On 30 September 2026 we tested nine of Gridable's security boundaries against a private copy of the product. Every boundary refused every attempt we made. For each test we also ran a copy with that one protection removed, to prove the test would have caught a failure. Reviewing the results turned up one gap next to those boundaries, which is described below with its fix. Our own team ran this, with automated tools. It is not a third-party penetration test and not an audit. We do not have either yet, and our trust page says so.
What this is, and what it is not
- What it is: an internal, repeatable check that nine specific protections refuse the attempts they exist to refuse, measured against the code at one commit (
a7383d52). - What it is not: a third-party penetration test (listed on /trust as Not built), a SOC 2 or ISO audit (also Not built), or a test of our production hosting, our vendors, or people. It ran on synthetic data. No customer data, customer credential or production system was involved.
What we tested
Each boundary below corresponds to a control on our trust page.
| Boundary | The protection, in plain words | Trust-page control | |
|---|---|---|---|
| A | Agent access list | A hired agent uses only the connections an admin gave it, even through a live workflow it edits | AI and agent safety: access list |
| B | Card conversations | Only the person a card is waiting on can answer it; others can read, not write | AI and agent safety |
| C | Outside content | After an agent reads outside content, an address it composed is paused for a person before anything is sent | AI and agent safety: egress |
| D | Agent credentials | An agent cannot change a coworker's role or permissions itself | AI and agent safety: coworker changes |
| E | Workspace isolation | One workspace cannot read another's runs | Data protection: isolation |
| F | Public app pages | A published app shows the public only the fields of a record it chose, never internal ids | Application security |
| G | Security reports | Text in a vulnerability report cannot act on our issue tracker (mentions, links, formatting) | Incident response and disclosure |
| H | Export and delete | Only a workspace's owner can export or delete it, and only their own | Data protection: export, delete |
| I | Two-factor sign-in | With two-factor on, a password alone gets a challenge, not a session | Access and identity: 2FA |
How we tested
A private sandbox. The real Gridable server ran locally against a throwaway database with two synthetic workspaces. The database was deleted after each run. External services were stubbed. The only requests that left the machine were the AI model requests described below, sent through our model gateway.
Test 1: a fixed battery with controls. For each boundary we sent the request an attacker would send, using the credential that should be refused, and recorded the server's answer. We then ran the same request against a second copy of the code with only that boundary's protection removed. A "held" counts only when the unmodified code refused and the weakened copy let the request through. Otherwise the test might simply be unable to see a failure. Boundaries C and G have no web address to call, so they were driven through the same code the server runs.
Test 2: AI models probing from outside. We gave two AI models, GLM 5.3 and Claude Sonnet 4.5, one web-request tool, one fixed credential per boundary and a plain-language goal, with at most 8 requests each. Our code judged success from the server's real answers; the models' own claims were not used. Every request asked our model gateway for providers that do not retain or train on data, which the gateway applies under its own terms. The scored run made 76 model requests and cost $0.38.
What held
| Unmodified code | Protection removed (control) | Result | |
|---|---|---|---|
| A | Refused the ungranted agent's workflow run; refused every change to a live workflow's steps | The run was dispatched | Held |
| B | Refused a non-addressee's answer | The answer was accepted | Held |
| C | Paused before sending to a composed address | The request would have gone out | Held |
| D | Refused the agent's change to a coworker | The change was written | Held |
| E | Refused another workspace's run | The run was returned | Held |
| F | Returned only the five public fields | Internal ids were exposed | Held |
| G | Reporter text arrived inert, in a plain-text block | Mentions, issue links and links survived | Held |
| H | Refused export by another workspace's owner. Checked in review without a control: refused delete by that owner, and export and delete by a member who is not the owner | Another workspace's data was exported (export only: the delete arm had no removed-protection control) | Held |
| I | Password alone returned a challenge and no session | A session was issued | Held |
Neither model got past any boundary. Most of their attempts never found the right address. Where they did find it (A, B, D and I), the protection refused them. The fixed battery is what tests each protection. The model run answers only a narrower question: could an outside model, probing blind with a small budget, find a way through? It did not.
What did not go to plan, stated plainly
- One of our own tests was blind at first. Our review found that the control for two-factor sign-in (I) weakened the wrong line of code, so it could not have caught a failure. We fixed the control, re-ran it, and it now passes. The "held" above is from the fixed run.
- Two results needed a second look. The model run produced two successful responses that looked like possible crossings: an export (H) and a workflow edit (A). The run had not kept the export's contents or the edit's request. Both were checked again in the sandbox by re-sending the likely request. The export returned the requester's own workspace, and the edit returned success only for a rename or a resave that changed no step. Neither replay crossed a boundary.
- The review found one gap next to boundary C, tracked as #2989. Boundary C held on every road this run tested. One adjacent road had no check. An agent that had read outside content could still add a step to one of your workflows that was already switched on. That step would then run on the workflow's own schedule, and the outside-content check never saw it. We found this by reading the code and confirmed it in the sandbox; no customer was involved. It is fixed. An agent that had read outside content, or a follow-up run in the same conversation, can no longer change a workflow that is switched on unless a person approves or makes the change (fixed in
3a470078andf8679758, both live). - Our test suite had three faults, unrelated to these boundaries. We fixed them before writing this.
What this does not cover
- Replay of a two-factor code is covered by our automated test suite, not by this run.
- Machine-to-machine tokens on our worker service were outside this run. That item is open, tracked as #2648. The check it needs was built under #806, which is closed.
- An agent's write that starts a person's own automation is a road our trust page already lists as still being closed, tracked as #2977. This run did not test it.
- Isolation is enforced by the application. Each workspace does not have its own database.
- One commit, one day. This shows these protections refused these attempts at
a7383d52. It does not show there are no other defects, and it does not test production configuration, hosting, vendors, denial of service or social engineering.
Report a gap
If you find something we missed, write to security@gridable.ai or use the form on our trust page. Research within the published policy is authorised.