Vulnerability disclosure policy
How to report something you have found, what we do with it, and how fast.
Last reviewed 27 October 2026.
In four lines
- Send it to security@barrierops.com.
- We acknowledge within 1 business day and either fix or explain.
- We ask for 90 days before you publish.
- There is no bounty. We say so rather than implying one.
What is in scope
BarrierOps has not yet taken on a customer, and no customer data has entered these systems. The scope below is what this policy covers rather than what is serving today — each row says which, so you can spend your evening on something that answers.
| In scope | What it is |
|---|---|
barrierops.com | This marketing site, its tools, its published files and /.well-known/* |
app.barrierops.com | The signed-in application. Deployed; it has no customers and holds no customer data. |
api.barrierops.com | The API, including /api/v1/ and the internal endpoints. Deployed; it serves the signed-in application, the free scan, the GitHub App and the public API, v1, and has no customers. |
*.barrierops.com | Each customer’s public barrier-report portal and report status pages — specified, and not built: there is no portal to reach today |
| The BarrierOps GitHub App | Its permissions, its webhook handling and its check runs. Every permission it requests is published on our security page. |
BarrierOpsBot/1.0 | Our crawler’s behaviour, its rate limiting, and anything it can be made to do |
| Our published artifacts | The 2028 checklist PDF and the OpenAPI document — including anything that can be smuggled through them. Our accessibility conformance report (VPAT) is not published yet and joins this row when it is. |
| Our email | Acknowledgement and digest templates, and anything injectable into them. The send path is not built, so there is nothing to inject into yet. |
If you find something on a customer’s own website while testing us, tell us and stop. We will route it to that entity through the channel they already publish. Do not test it further and do not publish it — it is a public body’s website, not ours, and the person who pays for a mistake there is a resident.
What is out of scope
| Out of scope | Where it goes instead |
|---|---|
| Our subprocessors’ own products | Their own disclosure programmes. Tell us as well and we will chase it. The register on our security page is the list this row means. |
| A customer’s own website, CMS or vendor system | Tell us; we route it. Do not test it. |
| Physical security, our offices, our post | Not in scope, and not something we will act on |
| Social engineering of our staff, our customers or our subprocessors | Never in scope. Do not phone our support address pretending to be a customer. |
| Denial of service, load testing, resource exhaustion | Never in scope, and it is the one thing that will end a conversation |
| Anything requiring access to another organisation’s data beyond proof | Stop at proof, and tell us — the rules of engagement below say what that means |
The register the first row means is on our security page, with each provider’s region and what it can reach.
What is not a vulnerability to us
Published because it saves you an afternoon and saves us a reply. Each row says why, and none of them means we think less of the person who sent it.
| Finding | Why we will close it |
|---|---|
| A missing security header on a static asset with no origin state | We set the headers that matter on the API and the app. A header on an immutable hashed asset changes nothing. |
| Self-XSS requiring the victim to paste into their own console | It is a browser feature and we cannot fix it |
| A rate limit on the public scan or the barrier form | Published, deliberate, and documented. Reporting the limit we publish is not a finding. |
| Software version disclosure without a demonstrated exploit | We publish our engine versions on purpose |
| Clickjacking on a page with no state to change | frame-ancestors 'none' is set everywhere including the public form. A framing report on a static article page is not a finding. |
| Missing SPF, DKIM or DMARC on a domain that sends no mail | Tell us anyway if it is a sending domain. On a parked one it is not a finding. |
| An automated scanner’s report with no reproduction | We will read it, and we will not triage it. One reproduced finding beats a 200-page PDF. |
| Absence of a bounty | It is stated in the summary and in security.txt. It is a decision, not an oversight. |
| Best-practice advice without a demonstrated impact | Send it — we will thank you and we will not treat it as a report under this policy |
What we commit to
Every window below is measured against 09:00–17:00 US Eastern, Monday to Friday, excluding US federal holidays — the same hours our security page and our about page state, from one constant.
| Commitment number | Commitment | Window |
|---|---|---|
| 1 | We acknowledge that a human has read your report | 1 business day |
| 2 | We tell you whether we consider it a vulnerability, and at what severity | 5 business days |
| 3 | We fix it, or we tell you in writing why we are not going to | A target date, with the triage decision |
| 4 | We keep you updated while it is open, without you having to ask | At least every 10 business days |
| 5 | We tell you when it is fixed, and how to verify | On the day it ships |
| 6 | We credit you by name if you want it, and never publish your identity without asking | With the advisory |
| 7 | We publish an advisory when a fix affected customer data, and we tell affected customers directly | Within 72 hours of becoming aware |
If we miss one of these windows, tell us, and if we still do not answer, publish. We would rather be embarrassed by a researcher than run a policy nobody can hold us to.
Outside the stated hours nothing acknowledges your message automatically — we have not built that yet, and saying otherwise would break the first window on this page before anybody read it. A report sent overnight is read in the morning. The one engineer on call outside hours is paged for outages and failed integrity checks of the evidence ledger, not for disclosure triage, and pretending otherwise would be a commitment we cannot keep.
What we ask of you
A good report is mostly a form somebody filled in without being given one. This is the form, and there is nothing to submit it through on purpose.
| Ask | Why |
|---|---|
| What you found, in one sentence | It routes the report |
| Where — the exact URL, endpoint, header or file | A finding we cannot locate is a finding we cannot reproduce |
| How to reproduce it, step by step | The single thing that decides whether this gets fixed this week or next quarter |
| What an attacker gets | Impact, in your own assessment. We will disagree politely if we do. |
| The account or API key you used, and its prefix | So we can find your requests. bo_test_ keys are preferred. |
| Timestamps in UTC, and your source IP addresses | So we can correlate against our logs |
| Whether you used automated tooling, and at what rate | So we do not investigate you as an incident |
| How you want to be credited, or that you do not | We ask rather than assume, and we never publish a name without being told to |
| Whether you have told anyone else | Not a trap. It changes our timeline, not our attitude. |
We do not publish a PGP key yet, so there is no encrypted channel to offer and we are not going to link one that does not resolve. Plain email is fine and most reports arrive that way. If a finding is too sensitive to send in the clear, say so in an empty message and we will arrange a channel before you send anything.
We do not require an NDA, an account, a legal agreement or a form. An email is a report.
Rules of engagement
- Test against the sandbox where you can. Test mode is keyed by bo_test_ and is backed by a seeded fixture organisation. It is realistic enough to find real bugs in and it touches no customer data: a bo_test_ key reads only the sandbox’s fictional organisations, City of Riverbend among them, and changes nothing. The API documentation says how to create one.
- Never access another organisation’s data beyond what proves the finding. One record id is proof; a dump is not. We will not treat a stop-at-proof as an intrusion. We will treat a dump as one.
- Do not modify or delete anything that is not yours, and do not create load that affects another customer.
- No denial of service, no brute forcing at volume, no automated scanning above 10 requests per second. Tell us your source addresses and we will allow them.
- No social engineering, no phishing, no physical access, and nothing involving our staff or our customers’ staff as targets.
- Do not use a finding to pivot into a customer’s own systems. The customer is a public entity and the blast radius is a city.
Safe harbour
We will not pursue or support legal action against you for security research conducted in good faith under this policy. That means: you followed the rules of engagement, you stopped at proof, you did not access, modify or retain data beyond what the finding required, and you gave us a reasonable chance to fix it before publishing.
If you are unsure whether something is in bounds, ask first at security@barrierops.com and we will answer. Asking has never once made a report worse.
This is a statement of our intent, not a licence to break a law we do not control. We cannot waive a third party’s rights, and we cannot promise what a subprocessor or a customer will do — which is why the scope section asks you to stop rather than continue when you find yourself on somebody else’s property.
There is no bounty
There is no bounty, no swag, and no payment of any kind. This is a small company, and a promise of money we could not fund reliably is one we would eventually break — which is exactly the failure mode this whole page is built to avoid.
What you get instead: an answer from a person, a fix with a date, a published advisory, and credit by name if you want it.
If a paid programme ever exists, it will be announced here, it will not be retroactive, and we will say that too.
The machine-readable version
The contents of /.well-known/security.txt
# BarrierOps — digital accessibility compliance for state and local government
# Policy: https://barrierops.com/security/disclosure
Contact: mailto:security@barrierops.com
Contact: https://barrierops.com/security/disclosure
Expires: 2027-10-27T00:00:00.000Z
Acknowledgments: https://barrierops.com/security/disclosure#acknowledgements
Preferred-Languages: en, ro
Canonical: https://barrierops.com/.well-known/security.txt
Policy: https://barrierops.com/security/disclosureServed as text/plain over HTTPS at the canonical path, per RFC 9116. Expires is generated from the last review date, never typed — the build fails rather than serving an expired file.
Preferred-Languages lists the languages a person here can actually read a report in. Listing a language nobody reads is a way of losing a report, so it is two rather than three: our Spanish privacy notice has not been read by a native speaker yet, and until it has, offering Spanish here would be offering a slower answer.
The live file is at /.well-known/security.txt, and it is the same bytes as the block above — the page reads the file rather than keeping a second copy of it.
Acknowledgements
No reports have been received under this policy yet. This list is not empty because anything was removed from it. When it stops being empty it will be dated.
Where else to write
| Subject | Where |
|---|---|
| A security finding | security@barrierops.com — This policy |
| Our crawler, including how to slow it down or block it | /bot — Which also reaches a person |
| Privacy, a resident’s data, a deletion request | /privacy — Which names the public entity to ask, and why it is them rather than us |
| Anything else | /contact |
Our crawler sends BarrierOpsBot/1.0 (+https://barrierops.com/bot), and the address inside that string is our crawler page. It is where a municipal administrator lands first after finding us in a log, which is why it is named here as well as in the table: a security finding should not end up filed as a crawler complaint.