# About Excession Labs Source: https://penetrationtestservices.com/about/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Excession Labs is a security lab that runs penetration tests of web applications, APIs and SaaS platforms. Excession Labs is a security lab. It runs authorized security tests of web applications, APIs and multi-tenant SaaS platforms and publishes lab research. Penetration Test Services is its pentest service. ## How we deliver a test Our AI testing agents run test tasks in parallel under tool rules Excession Labs sets for the scope. Each tool runs on its own, waits for approval, or is blocked. An agent may only report a finding that links to its evidence. See [how it works](https://aipentestservices.com/how-it-works/). The agents do the testing work under the tool rules above. Excession Labs agrees the scope with you in writing, checks the evidence for each finding and writes the report. Our [methodology](/methodology/) covers the evidence standard, the controls we run and our safety limits. We will not: - Test a system without written authorization from its owner. - Publish or name a customer without permission. - Promise that an auditor or a customer will accept a report. We ask what the reader needs and write the report to show it. - Claim a finding we could not demonstrate. Unproven paths are labeled as inferred. ## Our security sites and contact Email [support@excessionlabs.ai](mailto:support@excessionlabs.ai), follow [@ExcessionLabs on X](https://x.com/ExcessionLabs), or visit [excessionlabs.ai](https://excessionlabs.ai/). To request a test, use the [request form](/contact/). --- # AI application security Source: https://penetrationtestservices.com/ai-application-security/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 AI features such as chatbots, RAG and agents need a different security test from a web app. See why, and where Excession Labs scopes AI system assessments. > **Short answer:** A web or API pentest tests code paths and access control. An AI feature adds a model that reads untrusted text and may call tools. That needs a different test. Excession Labs scopes assessments of LLM apps, RAG and agents on a separate site, AI Red Teaming Services. Use the standard web test for the rest of your product. You added a chatbot, a copilot or an agent to your product, and customers now ask about it in security reviews. A normal penetration test does not answer their questions. ## Why do AI features need a different test? A web app does what its code says. An AI feature does what a model decides after reading text, which may come from a user, a document, a web page or a tool result. Hostile text can push the model to leak data, ignore rules or call a tool it should not. Three things change. Untrusted text becomes instructions, so a hostile sentence in a retrieved document can change the model's behavior. Retrieval has its own access rules, and a search index may return a document that the current user must not see. Tools act with real permissions, so an agent that can send email or edit records turns a bad answer into a bad action. The standard test still applies. Sign-in, access control and API checks stay in scope. Our [web application](/web-application-penetration-testing/) and [API](/api-penetration-testing/) pages cover that layer. ## Where do you go for the AI part, and what do you bring? Excession Labs scopes assessments of AI systems on a separate site: [AI Red Teaming Services](https://airedteamingservices.com/). | Your situation | Start with | |---|---| | A normal product with an AI feature added | The web and API test, then an AI assessment of the feature | | Multi-tenant SaaS with AI search over customer data | [SaaS testing](/saas-penetration-testing/) for tenant isolation, then an AI assessment of retrieval | | An agent with tools and credentials | An AI assessment | | A customer asking about AI security | [Customer security review](/customer-security-review/), then the AI assessment | We do not claim past AI red-team engagements or results on these pages. We scope each AI assessment in writing with you. Bring the following: - A short description of the AI feature, its model provider and its tools. - The data sources it reads, and who may see each one. - Test accounts, with at least two tenants if the product is multi-tenant. - Written authorization from the system owner. [Request a pentest](/contact/) for the web and API layer, or scope the AI assessment at [airedteamingservices.com](https://airedteamingservices.com/). ## Questions ### Does a web app pentest cover my chatbot? It covers the web and API layer around the chatbot: sign-in, access control and the endpoints it calls. It does not test how the model responds to hostile text or how it uses tools. That needs an assessment built for AI systems. ### Which test do I need first? Start with the web and API test if the AI feature sits inside a normal product. Then scope the AI assessment for the feature itself. If the AI system is the product, begin with the AI assessment. ### Can prompt injection be fully prevented? No. It cannot be fully prevented today. Teams reduce the impact by limiting what the model can do, restricting tool permissions and checking outputs. An assessment shows which of those limits hold in your system. ## Sources - [OWASP Top 10:2025](https://owasp.org/www-project-top-ten/) --- # API penetration testing Source: https://penetrationtestservices.com/api-penetration-testing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 API penetration testing for REST and GraphQL APIs, mapped to the OWASP API Security Top 10 2023. Proven findings, a written report and fix guidance. > **Short answer:** API penetration testing checks whether your REST or GraphQL API enforces who can read and change what. We test against the OWASP API Security Top 10 2023: object-level and function-level authorization, mass assignment, rate limits, business flows and inventory. Each finding comes with request and response evidence, controls and fix guidance in a written report. Mobile apps, single-page apps and partners all call your API, and each call assumes the server decides who may do what. API penetration testing checks that the server enforces that on every route, including routes the front end never calls. ## What do we test in an API? We organize the work around the [OWASP API Security Top 10 2023](https://owasp.org/API-Security/editions/2023/en/0x11-t10/). We add business-specific checks that the list cannot name. | OWASP item | Question we answer | |---|---| | API1 Broken Object Level Authorization | Can user A read or change user B's invoice by changing an ID? | | API2 Broken Authentication | Do login, token refresh, password reset and API keys resist abuse? | | API3 Broken Object Property Level Authorization | Does the API return fields a user should not see? Can a user set fields such as `role` or `plan` in a write request? | | API4 Unrestricted Resource Consumption | Are rate, size, pagination and cost limits set and enforced? | | API5 Broken Function Level Authorization | Can a normal user call an admin or internal route? | | API6 Unrestricted Access to Sensitive Business Flows | Can a script abuse sign-up, checkout or invitations at scale? | | API7 Server Side Request Forgery | Can a URL parameter make the server fetch an internal resource? | | API8 Security Misconfiguration | Do CORS, errors, verbs and headers expose more than needed? | | API9 Improper Inventory Management | Do old versions, debug routes or undocumented endpoints remain live? | | API10 Unsafe Consumption of APIs | Does the API validate data from third-party APIs before it trusts it? | OWASP says attackers exploit BOLA "by manipulating the ID of an object that is sent within the request" ([API1:2023](https://api-security.owasp.org/editions/2023/en/0xa1-broken-object-level-authorization/)). So we test with two accounts and swap identifiers. API3 covers what older lists called mass assignment: a user able "to change, add/or delete the value of a sensitive object's property which the user should not be able to access" ([API3:2023](https://api-security.owasp.org/editions/2023/en/0xa3-broken-object-property-level-authorization/)). For more, see our guide to [IDOR and BOLA testing](/guides/idor-testing/) and the [API Top 10 test ideas](/guides/owasp-api-security-top-10/). For REST we test every verb on every route. For GraphQL we test authorization per resolver, query depth and cost, batching and introspection. ## How do we prove API findings? A finding needs a real effect, and a response code does not show one. We prove a write by reading the record back through a second session. We run controls beside each exploit request: a nonexistent ID, the wrong record type, another account's token, and an anonymous call. Controls that held are reported too. The [methodology](/methodology/) states the full standard. We bound rate-limit and abuse tests. [API4:2023](https://api-security.owasp.org/editions/2023/en/0xa4-unrestricted-resource-consumption/) says an API is vulnerable if limits are missing or set inappropriately. We agree volumes in scope, use test-owned accounts and state the cost of paid third-party calls in the report. > **Sample finding:** This is a demonstration. It uses a fictional app, Harbor Notes, and no real customer system was tested. > > **Finding A-04: A user can set their own plan through a profile update (High).** The API accepts a property the client should not control (API3:2023). > > Request as a free-plan user: > > ```http > PATCH /api/v1/me HTTP/1.1 > Host: api.example.com > Authorization: Bearer > Content-Type: application/json > > {"display_name":"Test A","plan":"enterprise"} > ``` > > Response, then a read-back through a second session (Observed): > > ```http > HTTP/1.1 200 OK > {"display_name":"Test A","plan":"enterprise"} > ``` > > Control: the same request with `"role":"admin"` was rejected with 403. That control held. Fix: use an allow list of writable fields on this route. ## What do you get, and what do we need from you? The report comes in HTML, PDF and Markdown, with proof-of-concept scripts. Each finding has a description, impact, verified results, reproduction steps and remediation. The report also has an executive summary, a findings table, a remediation plan and an evidence index. We save requests and responses with timestamps. We can retest fixes; see [retesting](/retesting/). To scope the test, we need: - Written authorization and the base URLs in scope. - An OpenAPI file, Postman collection or GraphQL schema, if you have one. - Test accounts for each role, and for at least two tenants or users. - How tokens are issued, how long they live, and how API keys work. - Any rate limits, WAF rules or bot protection that may block tests. - A contact who can read logs during the test. Effort depends on the number of routes or operations, roles, tenants and auth schemes, and on documentation quality, the environment and the retest. GraphQL schemas with many types take longer. We publish no prices and quote after scoping; see [pricing](/pricing/). Ready to scope an API test? [Request a pentest](/contact/). ## Questions ### Do you need our API documentation? It helps a lot. An OpenAPI file, a Postman collection or a GraphQL schema shortens discovery. Without them we map the API from the app and its JavaScript. Hidden or undocumented routes are a common finding, so we test those too. ### Do you test GraphQL? Yes. We check authorization on each resolver, query depth and cost limits, batching, and whether introspection exposes more than you intend. Introspection alone is not a finding. We report what an attacker can do with it. ### How do you test rate limits without harming production? We bound the test. We agree the volume and target in scope, use test-owned accounts, and state the cost of any paid third-party calls in the report. Password guessing and email sending are limited in the same way. ### Do you test internal or partner APIs? Yes, if they are in the written scope and you can give us credentials and an environment. Say who consumes the API during scoping. Partner tokens and service-to-service calls often have different authorization rules. ## Sources - [OWASP API Security Top 10 2023](https://owasp.org/API-Security/editions/2023/en/0x11-t10/) - [OWASP API1:2023 Broken Object Level Authorization](https://api-security.owasp.org/editions/2023/en/0xa1-broken-object-level-authorization/) - [OWASP API3:2023 Broken Object Property Level Authorization](https://api-security.owasp.org/editions/2023/en/0xa3-broken-object-property-level-authorization/) - [OWASP API4:2023 Unrestricted Resource Consumption](https://api-security.owasp.org/editions/2023/en/0xa4-unrestricted-resource-consumption/) --- # Request a penetration test Source: https://penetrationtestservices.com/contact/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Request a penetration test quote: tell us what to test, why and by when. We reply by email to book a short scoping call. Testing starts after written authorization. Answer a few questions so the first call is useful. We reply by email from support@excessionlabs.ai. [Use the form on this page](https://penetrationtestservices.com/contact/) After you send the form, we reply to agree a short call about the trigger for the test, the reader of the report and your deadline. Then we send a written scope and a quote. Testing starts after the system owner signs the authorization. Prefer email? Write to [support@excessionlabs.ai](mailto:support@excessionlabs.ai). Do not send passwords, keys or customer data by email or through this form. --- # Penetration test report for a customer security review Source: https://penetrationtestservices.com/customer-security-review/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Prepare a penetration test report for an enterprise customer security review: what buyers ask for, report versus summary letter, what to share and timing. > **Short answer:** Enterprise buyers often ask for a recent third-party penetration test as part of a security review. A useful answer is a scoped test of the system they will use, a report with evidence, and a plan for open findings. Excession Labs delivers that report. We cannot promise any buyer will accept it, so ask the buyer what they need first. A deal is waiting on a security review, and the buyer's questionnaire asks for a penetration test report. You need a test that matches the request, a report you can share and a list of what is still open. ## What do enterprise buyers ask for, and in what form? In the Cobalt State of Pentesting Report 2025, 59% of 450 survey respondents named third-party pentest reports as the assurance customers and regulators ask for most often, ahead of vulnerability scans and compliance certifications ([Cobalt, p.5](https://resource.cobalt.io/state-of-pentesting-2025)). This is a vendor survey, so treat it as one data point. The Cloud Security Alliance's Cloud Controls Matrix v4 includes a control, TVM-06, on periodic penetration testing by independent third parties ([CSA CCM v4](https://cloudsecurityalliance.org/artifacts/cloud-controls-matrix-v4)). Questionnaires based on it ask a similar question. Our guide to [pentest questions in security questionnaires](/guides/security-questionnaire-pentest-questions/) lists common wording and good answers. Buyers usually want to know: - Who tested, and were they independent of your engineering team? - When did the test happen, and what version was tested? - What was in scope, and what was left out? - What did the test find, by severity? - Which findings are fixed, and which are open? A report is the full technical document. A summary letter is a short statement of scope, dates, method and result. The buyer decides which form it accepts, and some ask for the report under NDA. Ask at scoping. | | Full report | Summary letter | |---|---|---| | Content | Every finding, evidence, reproduction, fixes | Scope, dates, method, result counts | | Risk when shared | Contains exploit detail | Low | | Typical use | Large buyers, security teams | Questionnaire attachments | Our guide to [attestation letters](/guides/pentest-attestation-letter/) covers what they should contain and their limits. If your buyer wants a letter or summary, tell us during scoping. ## How do we make the report useful for a review? We write the scope in plain terms: assets, environments, roles tested and exclusions. The report states the test method, the safety constraints and the severity scale. It lists "Controls that held", so a reviewer sees the protections that worked as well as the flaws. A "Limitations and open questions" section says what we could not confirm. We label evidence as "Observed" or "Browser-observed" and keep inferred paths apart. > **Sample report excerpt:** This is a demonstration. It uses a fictional app, Harbor Notes, and no real customer system was tested. > > **Scope statement.** Assets: `app.example.com` and `api.example.com`. Roles tested: Viewer, Editor, Admin in two test tenants. Excluded: third-party payment pages and denial-of-service testing. > > **Result summary.** 1 High, 2 Medium, 1 Low. Controls that held: tenant check on note read, session expiry, password-reset token reuse. See the full layout in the [sample report](/sample-report/). The report comes in HTML, PDF and Markdown with proof-of-concept scripts, an executive summary with priority actions, a remediation plan and an evidence index. After you fix findings, we can retest them; see [retesting](/retesting/). No buyer, auditor or regulator is promised to accept a report. ## What do we need from you, and what changes the quote? - The questionnaire or the buyer's written requirements, if you can share them. - The system, environment and roles the buyer will use. - Test accounts, documentation and a technical contact. - Your target date for sharing the report, so we can plan around it. - Written authorization from the system owner. Scope sets the quote: product size, roles, API surface, tenants and environment. A retest adds work. A tight deadline can change what we can offer, and we will tell you at scoping. We publish no prices; see [pricing](/pricing/). For SaaS products, see [SaaS penetration testing](/saas-penetration-testing/). To start, [request a pentest](/contact/). ## Questions ### Should we share the full report or a summary? Share what the buyer asks for and your policy allows. The buyer decides whether a short summary letter is enough or whether it needs the full report under NDA. The full report contains exploit detail, so share it with care. Ask the buyer which they need before you test. ### Will the buyer accept your report? We cannot promise that. Acceptance is the buyer's decision. Ask what they require: scope, date, tester independence and a statement on open findings. Share that list with us at scoping so the report covers it. ### What if the report shows open findings? Open findings are normal, and buyers look at how you handle them. Fix the serious ones, retest, and share the plan for the rest. See the retest page. ### How recent should the test be? Buyers set their own rule. Check the exact wording in your questionnaire and plan the test date against your sales calendar. ## Sources - [Cobalt State of Pentesting Report 2025, p.5](https://resource.cobalt.io/state-of-pentesting-2025) - [CSA Cloud Controls Matrix v4](https://cloudsecurityalliance.org/artifacts/cloud-controls-matrix-v4) --- # Developers and AI agents Source: https://penetrationtestservices.com/developers/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Machine-readable access to penetrationtestservices.com: OpenAPI description, API catalog, Markdown versions of every page, llms.txt and the rules for AI agents. Everything on penetrationtestservices.com is public and readable by software. Reading needs no account and no key. | What | Where | |---|---| | API description (OpenAPI 3.1) | [/openapi.json](/openapi.json) | | API catalog (RFC 9727) | [/.well-known/api-catalog](/.well-known/api-catalog) | | Rules for AI agents | [/auth.md](/auth.md) | | Site index for language models | [/llms.txt](/llms.txt), full text in [/llms-full.txt](/llms-full.txt) | | Markdown version of any page | add `index.md` to the page URL, or send `Accept: text/markdown` | | Sitemap and feed | [/sitemap.xml](/sitemap.xml), [/feed.xml](/feed.xml) | | Service status | [/api/health](/api/health) | ## Sending an enquiry `POST /api/enquiry` stores an enquiry and notifies Excession Labs. It needs a Cloudflare Turnstile token, which only a person completing the form on the site can produce. An agent helping someone get in touch should give them [the form](/contact/) or the address [support@excessionlabs.ai](mailto:support@excessionlabs.ai). The field list for each form is in the OpenAPI description. ## Fair use Read and cite freely. Keep request rates reasonable; the enquiry endpoint is rate limited. Do not try to bypass the human verification. Questions: [support@excessionlabs.ai](mailto:support@excessionlabs.ai). --- # Free penetration test pilot Source: https://penetrationtestservices.com/free-pentest/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Apply for a free pentest pilot from Excession Labs: a small written scope, real testing under written authorization, and every finding with evidence and a fix. > **Short answer:** The pilot is a small, real penetration test at no cost. We agree a narrow written scope, such as one critical user flow or a group of API endpoints, test it under written authorization, and give you every finding in that scope with evidence and fix guidance. Places are limited and reviewed by hand. ## What the pilot includes and who it is for A short call to agree what matters most in your application A narrow written scope: for example sign-up and login, one critical workflow, or a set of API endpoints Written authorization signed by the system owner before any testing Testing with test-owned accounts and data, under agreed limits Every finding in scope with evidence, reproduction steps and a fix Controls that held, and limitations of the pilot scope The pilot fits a team with a real application, in production or in a matching staging environment. Someone on the team must be able to authorize testing, usually the CTO, founder or head of engineering. If a hosting provider or a customer owns part of the system, they may need to agree too. The pilot does not cover systems you do not own, tests of a third party, networks or physical security. ## How to apply ApplyFill in the application form. ReviewWe review each application by hand. ScopeA short call, then a written scope and authorization. TestWe test the agreed scope under the agreed rules. ReportYou receive the findings with evidence and fixes. [Use the form on this page](https://penetrationtestservices.com/free-pentest/) Questions first? Email [support@excessionlabs.ai](mailto:support@excessionlabs.ai). See [what a report looks like](/sample-report/) and [how we test](/methodology/). ## Questions ### Is the pilot really free? Yes. You receive the full findings for the agreed scope, with evidence and fixes. There is no paywall on the results. Wider scope or later work is a separate, optional proposal. ### Why do you offer it? A sample report shows our format. Your own application shows how we test it and what we find. The pilot lets you compare our work with what you have seen before, on a scope small enough to do well. ### What is out of scope for a pilot? Anything outside the written scope, usually networks and cloud accounts, social engineering, denial-of-service testing and any system you cannot authorize. AI assistants and agents are assessed through [airedteamingservices.com](https://airedteamingservices.com/). ### How do you choose pilots? Places are limited and reviewed by hand. We agree a narrow written scope and need written authorization from the system owner before testing. --- # After the pentest: remediation, retest and close-out Source: https://penetrationtestservices.com/guides/after-the-pentest-remediation-and-retest/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 What to do after a penetration test: triage findings, assign fixes, verify them, request a retest and close out with evidence. Includes a tracker template. > **Short answer:** After a pentest, triage each finding, assign an owner and a due date, fix it, check the fix yourself with the reproduction steps, then ask the tester to retest. Close a finding only with evidence that the fix works. Keep the report, retest result and tickets together for customers and auditors. A pentest only lowers risk when the fixes ship and someone checks them. The steps are: triage, assign, fix, verify, retest, close out. Teams often stop after the first fixes. Cobalt's 2025 State of Pentesting Report, built on its platform data, reports that less than half (48%) of all findings ever get resolved, and 69% of the riskiest. It gives a median time to resolve of 67 days. These are one vendor's figures, but they show that findings stay open. ## How do you triage the findings and contain the worst? Read the report first; our guide to [how to read a penetration testing report](/guides/how-to-read-a-penetration-testing-report/) explains each section. Then triage in one short meeting, with an engineer who knows the code and a person who can decide on risk. For each finding, answer four questions: 1. Do we agree it is real? Reproduce it. If you cannot, ask the tester. 2. What is our priority? Severity is the tester's rating. Priority adds your context. 3. Who owns the fix? 4. When is it due? Look for patterns. Three findings that share one root cause, such as a missing ownership check in a shared helper, may need one fix. Look at the "controls that held" list too, so your fix does not break working protections. Contain the worst issues first. For a Critical or High finding that is easy to use, take a short-term step while the full fix is built: disable the feature, add a rule at the gateway, or limit access. Record the step. It does not close the finding. Also check for signs of past abuse. Search your logs for the request pattern in the reproduction steps. If you find real use before the test, treat it as an incident under your own process. ## How do you plan and make the fixes? Create one ticket per finding, or per root cause. Put these fields in each ticket: | Field | Content | |---|---| | Finding ID | From the report, for example F-02 | | Severity | The tester's rating, unchanged | | Priority | Your rating, with the reason | | Owner | One named person | | Due date | Based on your written target | | Fix approach | Short note, agreed with the tester's guidance | | Verification | How you will prove the fix works | | Status | Open, fixed, verified, retested, closed, accepted | Set due dates by severity and write the targets in your policy. PCI DSS 11.4.4 expects exploitable findings to be corrected in accordance with your risk assessment, and it does not name days. Choose targets your team can meet. When a date is missed, someone should decide what happens next. Fix the root cause. If the flaw was a missing ownership check on one endpoint, look for the same pattern on every endpoint that loads a record by ID. Add the check in one shared place if you can. Add a test that fails before the fix and passes after. Use the reproduction steps from the report. These tests stop the flaw from coming back. Keep the controls that held as tests, too. A fix that hides the symptom leaves the flaw open. Blocking one payload in a filter or hiding a button in the UI are examples. The retest looks for nearby bypasses. ## How do you verify a fix and request a retest? Run the report's reproduction steps against the fixed build, with test accounts. The result should differ from the original. Save the output with a date. Check three things: 1. The original request no longer works. 2. A small variation does not work either: a different ID, another role, another content type. 3. Normal use still works. The control tests should still pass. Run reproduction scripts only with authorization and with test-owned identifiers. Prefer staging. Ask for a retest when a group of fixes is verified in your environment and deployed to the tested environment. Do not send each fix alone, unless a finding is urgent. Batches save time for both sides. Send the tester: - The list of finding IDs you want retested. - The build or version that holds the fixes. - Any change to accounts, hosts or scope since the test. - Your notes on how each fix works. A retest checks each fix and looks for nearby bypasses. The result should state, per finding, one of: fixed, partly fixed, not fixed, or not retested. A good result has new evidence, such as the original request and its new response. See [retesting](/retesting/) for what Excession Labs's retest result looks like. We can retest fixed findings. Ask about terms when you scope. ## How do you track the fixes and close out? Copy this table into your tracker. The rows are examples from a fictional app, Harbor Notes. > **Demonstration only:** This is a demonstration. No real customer system was tested. The findings are fictional. | ID | Severity | Priority | Owner | Due | Status | Evidence of fix | |---|---|---|---|---|---|---| | F-01 | High | 1 | Dev lead | Set by policy | Retested | Retest output saved, dated | | F-02 | High | 1 | Backend | Set by policy | Fixed | Own check passed on build 412 | | F-03 | Medium | 2 | Platform | Set by policy | Accepted | Risk acceptance note, review date set | | F-04 | Low | 3 | Frontend | Next planned release | Open | None yet | A finding marked "fixed" with no evidence is not closed, so keep a status column and an evidence column. Close a finding when you have evidence that it is fixed, or a signed risk acceptance. Keep three things together: the original report, the retest result and the tickets with dates. Then do a short review: - What caused the flaws? Missing checks, no design review, no tests? - Which fix should become a standard, such as a shared authorization helper? - Which trigger should start the next test? See [how often should you pentest](/guides/how-often-should-you-pentest/). If a customer or auditor asks for proof, share what fits the request. A summary or an [attestation letter](/guides/pentest-attestation-letter/) is often enough for a customer. See [customer security reviews](/customer-security-review/) for what buyers ask. Ask the auditor what they need. A report alone may not meet a standard. Releases add new risk, so tie the next test to your release pace. A [recurring plan](/recurring-penetration-testing/) tests the changed areas after meaningful releases, and checks that old fixes still hold. ## Questions ### How fast should we fix findings? Set a target for each severity level and write it down. PCI DSS expects exploitable findings to be corrected under the entity's risk assessment. Set shorter targets for Critical and High. Pick targets you can keep. ### Do we need a retest for every finding? Retest at least the Critical, High and Medium findings, and any fix that changed authentication or access control. Low findings can often be checked by your own team with the reproduction steps. Ask your provider and auditor. ### What if we cannot fix a finding? Record a risk acceptance with the owner's name, the reason, the date and a review date. Add a compensating control if you have one. Do not leave the finding open without a decision. ### Can we tell customers about open findings? Share the current status carefully. A summary letter lists the scope, date and result counts and leaves out details of open weaknesses. Check your contract and legal advice. ## Sources - [Cobalt State of Pentesting Report 2025 (with Cyentia Institute), p.4: time to resolve and resolution rates](https://resource.cobalt.io/state-of-pentesting-2025) - [PCI DSS v3.2.1 to v4.0 Summary of Changes (r1): 11.4.4 corrections in accordance with risk assessment](https://listings.pcisecuritystandards.org/documents/PCI-DSS-v3-2-1-to-v4-0-Summary-of-Changes-r1.pdf) - [NIST SP 800-115: Technical Guide to Information Security Testing and Assessment](https://csrc.nist.gov/pubs/sp/800/115/final) --- # Authentication testing checklist: login, reset, MFA, sessions and OAuth Source: https://penetrationtestservices.com/guides/authentication-testing-checklist/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 An authentication testing checklist for web apps and APIs: login, password reset, MFA, sessions, OAuth and account enumeration, with proof and fixes. > **Short answer:** An authentication testing checklist covers every way a user proves identity and keeps a session: login, password reset, MFA, session cookies and tokens, OAuth or SSO, and account enumeration. Each area has its tests, and the page gives evidence and fixes per route. Run it with test accounts you own and within a written scope. An authentication testing checklist covers each way a user proves identity and keeps a session: login, password reset, MFA, sessions, OAuth and account enumeration. Authentication Failures is A07:2025 in the [OWASP Top 10](/guides/owasp-top-10-2025/). Broken Authentication is API2:2023 in the [API list](/guides/owasp-api-security-top-10/). Run the checklist only on systems you are authorized to test, and only with test accounts you own. It belongs to the scope of a [web application penetration test](/web-application-penetration-testing/). ## How do testers test login and password reset? Login tests check that a wrong credential fails the same way every time, and that the server limits repeated tries. - [ ] Wrong password and unknown user return the same message and about the same response time. - [ ] The app limits repeated failures per account and per source. See [rate limiting and credential stuffing](/guides/rate-limiting-and-credential-stuffing/). - [ ] Credentials travel only over HTTPS and never appear in URLs or logs. - [ ] A new session ID is issued at login (no session fixation). - [ ] Default and shared accounts are absent from production. - [ ] Every other login route (mobile API, legacy endpoint, SSO callback) applies the same checks. Evidence: two request and response pairs with timestamps, one for a valid user and one for an unknown user, plus the count of attempts before any lockout. Reset flows are an account takeover path, so testers check the token, how it is delivered and whether it can be reused. - [ ] The reset token is long, random and single-use. - [ ] The token expires, and a new request invalidates older tokens. - [ ] The reset link does not use a `Host` header that the client can change. - [ ] The response for a known and unknown email is the same. - [ ] After a reset, old sessions and tokens are revoked. - [ ] Security questions are absent or not the only check. > **Illustrative reset-token reuse check:** This is a demonstration. No real customer system was tested. The host is fictional. > > The tester requests a reset for their own test account, uses the link once, then replays it: > > ```http > POST /api/v1/password/reset HTTP/1.1 > Host: app.harbor-notes.example.com > Content-Type: application/json > > {"token": "", "new_password": ""} > ``` > > ```http > HTTP/1.1 400 Bad Request > Content-Type: application/json > > {"error": "token_used"} > ``` > > A `200` would have sent the tester to log in with the new password to show the effect. ## How do testers test MFA and sessions? MFA tests check that the second step is required, limited and tied to the first step. - [ ] The second step cannot be skipped by calling a later endpoint directly. - [ ] Codes expire and cannot be reused. - [ ] The app limits wrong codes per account and per session. - [ ] A code from one session does not work in another. - [ ] Recovery codes and "trust this device" cookies are bound to the user and expire. - [ ] Changing MFA settings needs a recent re-authentication. - [ ] Other login routes (API, SSO) enforce MFA too. Evidence: the request sequence showing which step the tester skipped, and the authenticated read-back that shows the session works or does not. Session tests check cookie flags, lifetime and what happens at logout. | Check | Expected | Flaw if | |---|---|---| | `Secure` flag | Set on the session cookie | Cookie sent over HTTP | | `HttpOnly` flag | Set | Script can read the cookie | | `SameSite` | `Lax` or `Strict` | Missing or `None` without need | | Logout | Server ends the session | Old cookie still works | | Password change | Other sessions end | Old sessions stay valid | | Idle and absolute timeout | Enforced on the server | Only the browser clears it | | Cookie value | Random, no user data | Guessable or editable | | Token (JWT) | Signature, expiry, audience checked | Unsigned or expired token accepted | A good fix sets the flags in one place: ```http Set-Cookie: session=; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=3600 ``` Evidence for logout: save the cookie, log out, replay the saved cookie against a protected route, and show the response. ## How do testers test OAuth, SSO and account enumeration? OAuth tests check the redirect and the token exchange. - [ ] The `redirect_uri` is matched exactly against a registered value. - [ ] The `state` parameter is random, bound to the session and checked on return. - [ ] Authorization codes are single-use and short-lived. - [ ] PKCE is used for public clients. - [ ] The app checks the ID token's issuer, audience, signature and expiry. - [ ] Account linking needs a verified email from the provider. - [ ] Scopes asked for match what the app needs. An unchecked `state` also opens login CSRF: see the [CSRF testing guide](/guides/csrf-testing/). Enumeration lets a caller learn which accounts exist. Compare the response body, status code, headers and timing for known and unknown users on login, reset, sign-up and invitation routes. A single difference is a finding. Its severity depends on how much the product relies on secrecy of who has an account. Fix it with one generic message ("If the account exists, we sent an email") and the same work on both paths so timing is alike. ## What does a good authentication finding look like? A good finding names the route, the test account, the steps, the evidence and the fix. It also lists the controls that held. The tester shows the effect, such as a working session after logout, because a "success" response is not proof. To fix authentication flaws, use a proven library or identity provider instead of custom code. Set short lifetimes for reset tokens and sessions, and revoke them on logout and password change. Limit attempts per account and per source, and apply the same rules to every login route. Log authentication events and review them. Then retest the fix against the same request pairs. ## In what order should you run the checks, and what do testers need? Start with the checks that change what you can test next. A weak lockout or a bypass in one flow can affect the rest of the test. 1. Map every login route: web form, mobile API, SSO callback, legacy endpoint and admin login. 2. Create test accounts for each role, with and without MFA. 3. Run the response comparison for enumeration on login, reset and sign-up. 4. Test sessions across login, logout and password change. 5. Test reset and MFA with owned accounts. 6. Test OAuth and SSO with a test identity provider tenant, if one exists. 7. Run bounded limit tests last, inside the window the scope allows. Run limit tests last because they can lock a test account. Create a spare account before you start. > **Illustrative finding write-up:** This is a demonstration. No real customer system was tested. The app and host are fictional. > > | Field | Detail | > |---|---| > | Title | Session stays valid after logout | > | Label | Observed | > | Route | `GET /api/v1/me` | > | Accounts | one test account created for this test | > | Steps | (1) Sign in and save the session cookie. (2) Call `/api/v1/me`: 200. (3) Sign out in the UI. (4) Replay the saved cookie. | > | Result | The replayed cookie returns 200 with the test user's profile. | > | Controls that held | A cookie with a changed last character returns 401. A cookie from before a password change returns 401. | > | Impact | A stolen cookie works after the user signs out. | > | Fix | Delete the session on the server at logout, and reject any ID that is not in the session store. | Testers need these items to cover every route: - Test accounts for each role, with MFA on and off. - A test inbox or mail catcher for reset and verification mail. - A list of identity providers and the redirect URLs they use. - The rules for lockout and session lifetime, if you have them. - A named contact for lockouts and mail questions. ## Questions ### Should testers try to guess real users' passwords? No. Password tests use test accounts that the tester owns, with bounded attempts that the scope states. Testing against accounts you do not own is not authorized, even inside an authorized scope. ### Is MFA enough to stop account takeover? MFA raises the cost of an attack, but flaws remain. Testers check whether the second step can be skipped, guessed without limits, replayed, or bypassed through recovery and other login routes. ### How long should a session last? It depends on the risk. OWASP guidance sets idle and absolute timeouts by application risk. Agree the values with your team, then test that the server enforces them and that logout ends the session on the server. ### Does the checklist apply to APIs that use tokens? Yes. Replace cookies with bearer tokens. Check signature validation, expiry, audience, refresh token rotation and revocation after logout or password change. ## Sources - [OWASP WSTG: Authentication Testing](https://wstg.owasp.org/v4.2/4-Web_Application_Security_Testing/04-Authentication_Testing/) - [OWASP WSTG: Session Management Testing](https://wstg.owasp.org/v4.2/4-Web_Application_Security_Testing/06-Session_Management_Testing/) - [OWASP Top 10:2025 Introduction](https://top10.owasp.org/2025/0x00_2025-Introduction) - [OWASP API Security Top 10 2023](https://api-security.owasp.org/editions/2023/en/0x11-t10) - [OWASP Authentication Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html) --- # Black box vs grey box vs white box penetration testing Source: https://penetrationtestservices.com/guides/black-box-grey-box-white-box-testing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 How black box, grey box and white box penetration tests differ, what each costs you in time and depth, and a decision table to choose one. > **Short answer:** Black box testing gives the tester no inside knowledge. Grey box gives partial knowledge, usually test accounts and API docs. White box gives full access, including source code. White box finds the most issues per hour of testing but needs the most preparation, so grey box is the best default for most web apps and APIs. Choose black box to see the outside view. Black box, grey box and white box describe how much the tester knows before the test starts. Black box means no inside knowledge. Grey box means partial knowledge, usually accounts and documentation. White box means full knowledge, including source code and architecture. The type changes what the tester can reach in a fixed time. The goal is the same for all three: find real weaknesses and prove them. ## What are black box, grey box and white box penetration tests? In a black box test, the tester starts with only a target, such as a domain name. There are no accounts and no documents. The tester must find everything from the outside. This is the view of an anonymous attacker, and it answers one question: what can a stranger do to your app? Black box has limits. Most of a SaaS app sits behind a login, so without an account much of the app stays untested. The tester also spends time on discovery that a list of endpoints from you would shorten. Many black box tests still need accounts. If sign-up is open and in scope, the tester can create them. If not, the owner should provide test accounts, and the test becomes grey box. In a grey box test, the tester gets some inside knowledge. The usual set is: - Two or more test accounts per role, in separate tenants if your app is multi-tenant. - API documentation, an OpenAPI file or a Postman collection. - A short description of the main user flows. - Contact details for someone who can answer questions. Grey box is the best default for web apps, APIs and SaaS products. Most serious flaws in these systems sit behind authentication: broken access control, tenant leaks, role bypass and logic errors. Proving them takes at least two accounts, because one account cannot show that Alice can read Bob's data. In a white box test, the tester gets full access: source code, architecture diagrams, configuration and often a developer to ask. Some teams call this a "crystal box" test. White box finds the most issues per hour, because the tester reads the code instead of guessing. The tester reads the authorization code, finds the route that skips a check, and proves the flaw on the running app. White box costs more preparation and more of your engineers' time. Code review also takes tester hours that a smaller budget may not cover. Sharing code needs trust, a signed agreement and a plan for how the code is stored and deleted. ## How do the three types compare, and which should you choose? | | Black box | Grey box | White box | |---|---|---|---| | Tester knowledge | None | Accounts, docs, flow notes | Code, architecture, configuration | | Models | Anonymous outsider | User or partner with an account | Insider or full audit | | Depth behind login | Low | High | Highest | | Setup work for you | Minimal | Moderate: accounts, docs | Highest: code access, briefings | | Time spent on discovery | Most | Some | Least | | Good at finding | Exposed services, weak login, public leaks | Access control, tenant isolation, logic flaws | Hidden routes, missing checks, unsafe code paths | | Weak at | Authenticated flaws | Flaws only visible in code | Can over-focus on code and miss deployment issues | | Your situation | Suggested type | Reason | |---|---|---| | Public marketing site, no login | Black box | Little hides behind the front door | | SaaS app with roles and tenants | Grey box | You need accounts to test isolation | | API used by a mobile app or partners | Grey box | Docs and tokens save time and widen coverage | | First pentest before launch, small team | Grey box | Best result for the time spent | | Customer asked for an "external" test | Black box, then grey box | Meets the wording and still reaches the app | | Payment, health or admin-heavy logic | White box, or grey box plus code review | Hidden logic is costly if wrong | | You want to check detection and response | Black box, or a red team | See the [red team guide](/guides/red-team-vs-penetration-testing/) | | Retest of earlier findings | Match the first test | Results stay comparable | If you are unsure, start with grey box and add a black box phase at the front if you want one. Proving authenticated flaws takes at least two accounts. ## Do the rules, standards and preparation change with the type? A black box test does not mean the tester has fewer rules. The same authorization and rules apply to all three types. The tester stays in scope, uses test data, and stops when a stop condition occurs. The [rules of engagement](/guides/penetration-testing-rules-of-engagement/) guide lists them. OWASP's ASVS says Level 1 is "not necessarily penetration testable by an external tester without internal access to documentation or code." A pure black box test cannot check every requirement of a verification standard, so plan for grey or white box access if your goal is to verify against one. For preparation, black box needs the least. Give the target list, the test window and a contact. Grey box adds accounts and docs. White box adds a repository or code archive. The full checklist is in [how to prepare for a penetration test](/guides/how-to-prepare-for-a-penetration-test/). ## Worked example: choosing for a SaaS startup > **Demonstration only:** This is a demonstration. No real customer system was tested. Harbor Notes is a fictional note-taking SaaS. It has three roles (owner, editor, viewer), a public REST API and a billing page. A large customer asks for a pentest report before it signs. The team has a small budget and a short deadline. Their questions: 1. Can one customer read another customer's notes? This needs two tenants and accounts. It points to grey box. 2. Can a viewer edit a note? This needs one account per role. Grey box again. 3. Is anything exposed to the internet that should not be? A short black box phase answers this. The plan: a short black box discovery phase, then grey box with six accounts (three roles in two tenants) and the OpenAPI file. No source code is shared. The written scope states this, so the customer's reviewer can see how deep the test went. If Harbor Notes later handles payments in its own code, the team can add a white box phase for the billing logic. Our [SaaS penetration testing](/saas-penetration-testing/) page explains the two-tenant method. For web apps in general, see [web application penetration testing](/web-application-penetration-testing/). ## Questions ### Which type is the most realistic? Black box is closest to an outside attacker with no access. But real attackers also buy or steal accounts, and they have time. Grey box models an attacker who already has an account, which is a common case for SaaS. ### Is white box testing the same as a code review? No. A code review reads source without running the app. A white box pentest gives the tester the code and then attacks the running system. The code helps the tester aim, and the live tests prove the impact. ### Can I mix the types in one project? Yes. A common plan starts with a short black box phase, then moves to grey box with accounts. Agree the phases in the written scope so both sides know what is expected. ### Does the type change the report? The report format stays the same. The scope section states what the tester knew. That context helps you judge what the results mean. ## Sources - [NIST SP 800-115: Technical Guide to Information Security Testing and Assessment](https://csrc.nist.gov/pubs/sp/800/115/final) - [OWASP Web Security Testing Guide](https://wstg.owasp.org/) - [OWASP ASVS 5.0.0: What is the ASVS (Level 1 and penetration testing)](https://github.com/OWASP/ASVS/blob/v5.0.0_release/5.0/en/0x03-What-is-the-ASVS.md) --- # Bubble app security: privacy rules, Data API and workflow API checklist Source: https://penetrationtestservices.com/guides/bubble-app-security/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 How to check your own Bubble app: privacy rules on every data type, Data API and Workflow API settings, search exposure and client-side data, with a test checklist. > **Short answer:** Bubble app security rests on privacy rules, which run on the server and grant access instead of denying it. Check every data type for a rule, test as a logged-out user and as a second user, review which Data API types are active, and review each backend workflow's authentication level and its Ignore privacy rules setting. Bubble apps keep their data on Bubble's servers, and the browser asks for it. Privacy rules decide what the server sends back. If a data type has no privacy rules, treat it as readable by anyone who can reach the app. A test checklist for your own Bubble app, and a sample privacy-rule design. Test only apps you own or have written permission to test. ## How do Bubble privacy rules work, and what does Bubble's checklist say? Privacy rules are conditions you set on each data type. They run on the server. Bubble's docs say they tell the server "to only send data to the browser, or write to the database, if certain conditions are met." Four behaviors cause most mistakes: - Rules grant, they do not deny. Bubble says: "you don't prohibit access, you grant it." Every rule a user matches adds its permissions, and they stack up. - "Everyone else" is a rule too. The editor adds an automatic rule for everyone else. If its boxes are ticked, every visitor gets those rights. Clear them unless the data is public. - Field permissions are narrow. "View all fields" means view. Bubble states it "doesn't stop someone from making changes to the field in a workflow." - Search is a separate switch. "Find in searches" applies to Do a search for. The record can still be fetched in other ways. A search in a workflow returns only data the current user can access. The editor marks each type as having privacy rules applied or as publicly visible. Sensitive data, Bubble says, needs privacy rules to count as secure. Bubble's own security checklist has items that testers verify: - "Add privacy rules to all private data types." - Use "Only when" conditions to protect data from unauthorized editing in workflows, or use auto-binding with privacy rules. - Bubble's JavaScript files expose details such as names and attributes of option sets, and information stored in the API Connector. Anything in an option set or a client-side API Connector field is visible to a visitor who reads the page's JavaScript, so keep keys and private logic out of both. ## How do you test a Bubble app? A checklist Use two normal users, A and B, a logged-out browser, and (if you use the APIs) the API tokens you issued. ### 1. Inventory the data types. - [ ] List every data type in the editor's Data tab. - [ ] Mark each as public, per-user, per-team or admin only. - [ ] Open each type's Privacy tab. Note any type shown as publicly visible. - [ ] Look for old test data types and records in the live app, and review their rules. ### 2. Review each rule set. - [ ] The "Everyone else" rule has no boxes ticked, unless the type is public. - [ ] Each rule's condition names the owner or role field that limits access. - [ ] Sensitive fields (email, phone, address, tokens, notes) have View permission only where needed. - [ ] "Find in searches" is off for private types, or is limited by the same condition. - [ ] Rules do not rely on chained references deeper than one level for search access. Bubble says those cannot grant search access. ### 3. Test from the browser. - [ ] Log out and open pages that show data. Watch the network tab for responses that hold more fields than the page shows. - [ ] As user B, load a page that shows A's record by its URL parameter or ID. - [ ] As user B, try edit and delete workflows on A's record. Read the record back as A to check the result. - [ ] Check auto-bound inputs. Auto-binding rules do not stop the same change in a workflow. See [IDOR testing](/guides/idor-testing/) and [multi-tenant isolation testing](/guides/multi-tenant-isolation-testing/) for the two-account method. ### 4. Review the Data API. - [ ] Open Settings, API. List the data types set as active in the Data API. Bubble says a type must be active to be reachable. - [ ] For each active type, review the Create via API, Modify via API and Delete via API permissions in its privacy rules. - [ ] Confirm the changes are deployed to the live version. Bubble notes settings must be deployed. - [ ] Call each active type with no token, with user A's token and with user B's token. Compare results to what each should see. - [ ] Note that PUT clears fields you do not send. Use PATCH for partial edits on test data. ### 5. Review the Workflow API and backend workflows. - [ ] List workflows marked as public API workflows. Private ones are not callable from outside the editor. - [ ] Check each authentication level. Bubble says "None required" lets any client trigger the workflow and "can make the workflow vulnerable to misuse." Use it only for sign-up and login. - [ ] "User & admin" lets any authenticated user run the workflow. Limit it with conditions and privacy rules. - [ ] For each workflow with Ignore privacy rules set, read the steps. It runs as an admin with all rights to the data. Test what a normal user or a logged-out caller can pass in. - [ ] Check parameters. A workflow that takes a record ID from the caller needs a check that the caller owns it. ### 6. Review client-visible data. - [ ] Search the page source and JavaScript for keys, tokens, and private option set values. - [ ] Review the API Connector. Bubble's checklist lists its stored information as visible in client JavaScript, so check Bubble's docs for which fields stay server-side. ## What does a safe privacy-rule design look like, and which mistakes break it? This design is for a Projects data type in an example app. Adapt it. | Rule name | Condition ("When") | Permissions | |---|---|---| | Owner | `Current User is This Project's Owner` | View all fields, Find in searches, Modify via API, Delete via API | | Team member | `This Project's Members contains Current User` | View all fields, Find in searches | | Everyone else (default) | none | Nothing ticked. No view, no search, no API access | Rules for the design: 1. Start with "Everyone else" empty, then add rules that grant access. 2. Add "Only when" conditions to editing workflows, so a user cannot change records they do not own. 3. Put private fields, such as internal notes, in a separate data type with a stricter rule. 4. Do not store secrets in option sets. Use a server-side mechanism for keys. 5. Re-test after every privacy-rule change. Common flaws in Bubble apps: - A data type with no privacy rules. New types start open until you add rules. - "Everyone else" left with View ticked. Every visitor can read the type. - Search open on private types. Find in searches lets any user list records. - Edit workflows without "Only when". Any user who can trigger the workflow can change any record it touches. - Backend workflows set to None required. A workflow meant for sign-up also changes user data. - Ignore privacy rules left on. A convenience setting turns into admin access. - Secrets in option sets or page elements. They ship to every browser. - Test data in the live app. Old test types hold real-looking records and weak rules. Each flaw has a test in the checklist above. After each fix, repeat the test with the same two accounts. ## How should you document and report the test? Keep a table with one row per data type. Columns: type name, who should see it, Everyone-else permissions, search setting, Data API status, result of the logged-out test, result of the second-user test. The table is your evidence and your regression list. Re-run it after each release that adds a data type or changes a rule. A good finding states the data type, the caller, the request and the proof. For example: "A logged-out caller reads `Invoice` records through the Data API. Cause: Everyone else has Find in searches ticked. Fix: clear the box and add an owner rule. Retest: logged-out call returns no records." Include a read-back from the owning account for writes. For risks across Bubble, Supabase and Firebase, see [no-code app security testing](/no-code-app-security-testing/). For the database-first view, see [Supabase security testing](/guides/supabase-security-testing/). ## Questions ### Are Bubble privacy rules enforced on the server? Yes. Bubble's checklist says privacy rules are applied server-side. They tell the server to send data to the browser, or write to the database, only if conditions are met. Hiding an element in the page is not a privacy control. ### Does the Data API respect privacy rules? Yes. Bubble's Data API page says writes depend on matching privacy-rule permissions, such as Create via API, Modify via API and Delete via API. It also says searches made through the Data API always need explicit permission. Test with real tokens to confirm your setup. ### What does Ignore privacy rules when running the workflow do? It makes a backend workflow run as an admin user with all rights to the data. Bubble tells you to use it with caution. Pair it with an authentication level and conditions, and test the workflow as a normal user and as a logged-out caller. ### What can people see in my app's JavaScript? Bubble's checklist says its JavaScript files expose items such as names and attributes of option sets, and information stored in the API Connector. Do not keep secrets in option sets or in client-visible API Connector fields. ## Sources - [Bubble Docs: Security checklist](https://manual.bubble.io/help-guides/security/security-checklist) - [Bubble Docs: Protecting data with privacy rules](https://manual.bubble.io/help-guides/data/the-database/protecting-data-with-privacy-rules) - [Bubble Docs: Data API requests](https://manual.bubble.io/core-resources/api/the-bubble-api/the-data-api/data-api-requests) - [Bubble Docs: The Workflow API](https://manual.bubble.io/core-resources/api/the-bubble-api/the-workflow-api) --- # Business logic vulnerabilities: examples, testing and fixes Source: https://penetrationtestservices.com/guides/business-logic-vulnerabilities/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 What business logic vulnerabilities are, why scanners miss them, examples in pricing, workflows and limits, how testers prove them and how to fix them. > **Short answer:** Business logic vulnerabilities are flaws in the rules of an application, not in its code syntax. Examples: changing a price in the request, skipping a payment step, reusing a coupon, or beating a quota. Scanners miss them because the requests look valid. Testers find them by learning the intended workflow, then breaking its assumptions with test accounts. A business logic vulnerability is a flaw in the rules of an application. The code runs as written, but the rules allow an outcome the business never intended. Examples: a price the client can change, a payment step the user can skip, or a coupon that works twice. Scanners miss these flaws because every request is valid HTTP. Testers find them with test accounts, and teams fix them by moving each rule to the server. We cover them in [web application penetration testing](/web-application-penetration-testing/). ## What are common business logic vulnerabilities, and why do scanners miss them? A common cause is that the server trusts something the client controls. The OWASP Web Security Testing Guide groups them in section 4.10, which covers data validation, forged requests, integrity checks, process timing, usage limits, workflow circumvention, misuse defenses, file uploads and payment functionality. | Pattern | Example | What the server should do | |---|---|---| | Client-set price | `price=1.00` in the checkout request | Read the price from the catalog | | Negative or huge quantity | `quantity=-5` lowers the total | Reject values outside a range | | Workflow skip | Call `/order/confirm` without `/order/pay` | Check state before each step | | Reused one-time value | Coupon, invite or reset code works twice | Mark it used in a transaction | | Race condition | Two parallel redeems both succeed | Use an atomic update or lock | | Limit bypass | Free plan limit checked only in the UI | Check the limit on the server | | Role or state confusion | Cancelled user still calls an API | Check account state on each call | | Rounding and currency | Many small refunds exceed the total | Cap by the order total in cents | OWASP API6:2023 names a related risk: Unrestricted Access to Sensitive Business Flows. It covers flows that harm the business when automated, such as buying out stock or abusing referrals. Scanners match known bad patterns, and a logic flaw has no signature. A request with `quantity=-5` is well-formed. Only someone who knows the cart total must never fall below zero can call it a bug. Scanners also work one request at a time, while logic flaws often need a sequence, two accounts or a timing window. A person must plan that sequence. ## How do testers find business logic flaws? Testers learn the intended workflow first. Then they list the assumptions behind each step and test each assumption. 1. Map the workflow. Walk the app as a normal user. Note every state: cart, paid, shipped, refunded, cancelled. 2. List the rules. Write what must always be true. Example: a refund never exceeds the amount paid. 3. List the assumptions. Ask what the server trusts: price, quantity, step order, user role, timing. 4. Break one assumption at a time. Change the value, skip the step, repeat the action, or run two requests together. 5. Check the result in the system of record. Look at the order total, the balance, the plan. 6. Run a control. Repeat the action in the normal way to show what the correct result looks like. > **Illustrative request and response:** This is a demonstration. No real customer system was tested. "Harbor Notes" is a fictional app, and the host is an example.com host. > > The browser sends this checkout request. The tester changes `unit_price_cents` to `100` using a test account and test payment mode: > > ```http > POST /api/v1/checkout HTTP/1.1 > Host: shop.harbor-notes.example.com > Authorization: Bearer > Content-Type: application/json > > {"plan": "team", "seats": 5, "unit_price_cents": 100} > ``` > > A vulnerable server accepts the price: > > ```http > HTTP/1.1 200 OK > Content-Type: application/json > > {"order_id": "o-5521", "total_cents": 500, "status": "paid"} > ``` > > The control request with the real catalog price returns `total_cents: 4500`. The tester then reads order `o-5521` back and checks the billing record. Read the billing record. Its stored total is the proof. Race condition tests send a small number of parallel requests, such as two or three, against a test-owned resource. Examples are redeeming one coupon twice or withdrawing one balance twice. The tester then reads the final state. State the bounds in the scope: how many requests, from which accounts, and what test data. Do not run floods on shared systems. For repeated-action limits such as login or SMS sends, see [rate limiting and credential stuffing](/guides/rate-limiting-and-credential-stuffing/). ## Which test cases should every app run, and how do teams fix the flaws? Adapt this starting list to your product. - [ ] Change price, discount, currency and tax fields in the request. - [ ] Send zero, negative, decimal and very large quantities. - [ ] Call later workflow steps without finishing earlier steps. - [ ] Replay a completed step (confirm, redeem, accept invite). - [ ] Use a one-time code twice, once from a second session. - [ ] Run two parallel requests for a single-use action. - [ ] Exceed a plan limit through the API. - [ ] Use a feature after downgrade, cancellation or role removal. - [ ] Apply a discount to an item the discount excludes. - [ ] Refund more than the amount paid, in pieces. - [ ] Access another user's order or invoice (an [IDOR test](/guides/idor-testing/)). To fix the flaws, move each rule to the server and write it once. The server computes prices, totals and entitlements from its own data and treats client values as requests. - State machines. Define the allowed transitions (cart to paid to shipped) and reject any other. - Atomic updates. Mark a coupon or token as used in the same transaction that grants the benefit. Use a unique constraint or row lock. - Invariants. Check rules at the end of each operation, for example `refunded = coupon.max_uses: abort if order.state != "cart": abort coupon.used_count += 1 order.discount = coupon.value_for(order.items) # computed server-side commit ``` ## When should you test business logic, and what does a finding look like? Test it when you add a money flow, a new role, a trial or plan change, or an approval step. The OWASP Top 10:2025 lists Insecure Design as A06 (see the [OWASP Top 10 2025 guide](/guides/owasp-top-10-2025/)), and API6 covers business flow abuse (see the [OWASP API Security Top 10 guide](/guides/owasp-api-security-top-10/)). A logic finding names the broken rule and the request that broke it. > **Illustrative finding write-up:** This is a demonstration. No real customer system was tested. The app is fictional. > > | Field | Detail | > |---|---| > | Title | Checkout accepts a client-set unit price | > | Label | Observed | > | Rule broken | The order total must equal the catalog price times the quantity. | > | Steps | Create a team plan order in test payment mode. Change `unit_price_cents` from 900 to 100 in the request. | > | Result | The order is created and marked paid at 500 cents. The billing record, read back, shows 500 cents. | > | Control that held | A negative seat count returns 400. | > | Impact | Any user can buy a plan at a price they choose. | > | Fix | Ignore the client price. Compute the total on the server from the catalog. | ## What should you tell testers about your rules? Testers cannot guess your rules, so give them a short brief: 1. The main workflows: sign-up, purchase, upgrade, cancel, refund, invite, approve. 2. The roles and what each may do. 3. The numbers that must hold: totals, balances, quotas, seat counts. 4. The "never" list: what must never happen, in plain words. 5. Test payment modes, test coupons and test data they may use. 6. Any action that costs money or sends messages, with a limit. Each line of the "never" list becomes a test case: "A refund never exceeds the amount paid" becomes a test that refunds in parts. The brief also feeds the scope for [web application penetration testing](/web-application-penetration-testing/). ## Questions ### Why can't automated scanners find business logic flaws? A scanner does not know your rules. A request that sets a negative quantity or skips a step is valid HTTP. Only a person who knows that the cart must total the right amount can say that the result is wrong. ### Is a business logic flaw a design bug or a code bug? Often both. OWASP lists Insecure Design as A06:2025 for flaws where the design lacks a control. A coding mistake can also break a good design. The fix is usually a server-side rule plus a test. ### How do testers avoid causing real financial harm? They use test accounts, test payment modes and test data only. The scope states the limits, such as no real card charges and a maximum number of requests. Anything with a cost is agreed first. ### What information helps testers most? A description of the intended workflows, the roles, the limits and the money flows. Tell testers what must never happen, such as a refund above the order total. That list becomes the test plan. ## Sources - [OWASP WSTG: Business Logic Testing (4.10)](https://wstg.owasp.org/v4.2/4-Web_Application_Security_Testing/10-Business_Logic_Testing/) - [OWASP WSTG-BUSL-01: Test Business Logic Data Validation](https://wstg.owasp.org/v4.2/4-Web_Application_Security_Testing/10-Business_Logic_Testing/01-Test_Business_Logic_Data_Validation/) - [OWASP API Security Top 10 2023: API6 Unrestricted Access to Sensitive Business Flows](https://api-security.owasp.org/editions/2023/en/0x11-t10) - [OWASP Top 10:2025 Introduction](https://top10.owasp.org/2025/0x00_2025-Introduction) --- # CSRF testing: how pentesters check tokens, SameSite and login CSRF Source: https://penetrationtestservices.com/guides/csrf-testing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 How to test CSRF protection: an 8-step method, a test-case table for tokens, Origin and SameSite, JSON endpoints, login CSRF, and proof in a real browser. > **Short answer:** CSRF (cross-site request forgery) makes a signed-in user's browser send an unwanted request to your app. Testers check that every state-changing request needs a valid anti-CSRF token or an equivalent check, that session cookies set SameSite, and that login is protected too. They prove it in a real browser, because browser rules decide whether the cookie is sent. CSRF stands for cross-site request forgery. It makes a signed-in user's browser send a request the user did not intend. The OWASP WSTG describes it as "an attack that forces an end user to execute unintended actions on a web application in which they are currently authenticated." CSRF testing checks that every state-changing request needs proof that it came from your own pages. Testers check tokens, SameSite cookies, JSON endpoints and login CSRF, and prove each result in a real browser. These checks are part of [web application penetration testing](/web-application-penetration-testing/). ## How does CSRF work, and what do SameSite cookies change? CSRF works because browsers attach cookies to requests automatically. If a page on another site can make the browser send a request to your app, the cookie goes along. The app then acts as the signed-in user. Three conditions must hold: 1. The action changes state (change email, transfer funds, add a user). 2. The app identifies the user only by cookies, with no extra proof. 3. The attacker can predict every parameter in the request. An anti-CSRF token breaks condition 3. A `SameSite` cookie breaks condition 2 when the browser withholds the cookie on the cross-site request. SameSite tells the browser when to send a cookie on cross-site requests. MDN explains the three values: - `Strict`: send the cookie only for requests from the same site. - `Lax`: also send it on cross-site top-level navigations that use a safe method. MDN says this "essentially means that the request causes the URL shown in the browser's address bar to change." A cross-site `fetch()` or `` does not qualify. A cross-site form `POST` does not qualify either. - `None`: send it on all requests. It needs the `Secure` attribute. If the attribute is missing, behavior depends on the browser. MDN says "Some browsers use `Lax` as the default value if `SameSite` is not specified." The Chromium project documents its move to Lax-by-default. MDN adds that this default is more permissive than an explicit `Lax`: cookies are also sent on cross-site `POST` requests made within two minutes of the cookie being set. Set the attribute yourself and do not rely on defaults: ```http Set-Cookie: session=; Path=/; Secure; HttpOnly; SameSite=Lax ``` "Same site" is not "same origin". A sibling subdomain such as `blog.example.com` is same-site with `app.example.com`, so a flaw on any sibling can send same-site requests with cookies. ## How do testers test for CSRF? Testers list the state-changing requests, then check each for a token or equivalent. They follow this order. 1. Inventory. List every request that changes state: forms, `fetch` calls, GraphQL mutations. Include the ones behind settings pages. 2. Check for a token. Look for a random value in a hidden field, header or cookie. 3. Remove the token. Replay the request with the token missing. Replay with an empty value. 4. Swap the token. Use a token from another test account. Use an old token from the same account. 5. Change the method. If `POST` is protected, try `GET` with the same parameters. 6. Check `Origin` and `Referer` handling. See if a request with a foreign `Origin` is accepted. 7. Check cookie attributes. Read `SameSite`, `Secure` and `HttpOnly` on the session cookie. 8. Prove it in a browser (next section). | Case | Request change | Safe result | Finding if | |---|---|---|---| | Token missing | Remove the CSRF field | 403 | Action succeeds | | Token empty | Send empty value | 403 | Action succeeds | | Token from account B | Use B's token for A's session | 403 | Action succeeds | | Token reuse after logout | Replay an old token | 403 | Action succeeds | | Method switch | `GET` instead of `POST` | 405 or 403 | State changes | | Foreign `Origin` | `Origin: https://evil.example.net` | 403 | Action succeeds | | Content type | `text/plain` body to JSON API | 415 or 403 | Action succeeds | | Cookie flags | Read `Set-Cookie` | `SameSite` set | Missing or `None` | ## Why does browser-observed proof matter? A browser decides which cookies go with a request, while `curl` sends whatever you give it. A request without a token, sent by `curl`, therefore does not prove CSRF. The attack works only if a real browser, on a real cross-site page, sends the session cookie. For this reason we label CSRF evidence as Browser-observed. We load a test page from a different site in Chromium, signed in as a test account, check whether the state changed, and read the change back. A bare success response is not proof. > **Illustrative CSRF proof:** This is a demonstration. No real customer system was tested. Hosts are fictional example.com hosts. > > A test account is signed in to `app.harbor-notes.example.com`. In the same Chromium profile, the tester opens a page on `lab.example.net` (a test host the tester controls) with a form that posts an email change: > > ```html > > > > ``` > > The browser sends: > > ```http > POST /settings/email HTTP/1.1 > Host: app.harbor-notes.example.com > Origin: https://lab.example.net > Cookie: session= > Content-Type: application/x-www-form-urlencoded > > email=tester%2Bcsrf%40example.com > ``` > > If the server returns `302` and the account page now shows the new email, CSRF is proven. If the browser omits the `Cookie` header (for example under `SameSite=Lax` on a cross-site `POST`), the request fails with `401`, and the control held. ## How do JSON endpoints and login CSRF differ? JSON APIs that use cookies still need CSRF protection. A cross-site form cannot send `application/json`, but it can send `text/plain` with a body that looks like JSON. If the server parses the body without checking the content type, the request goes through. Test by sending the JSON body with `Content-Type: text/plain` and `application/x-www-form-urlencoded`. A safe server returns `415 Unsupported Media Type` or checks a token. APIs that need an `Authorization: Bearer` header are not open to classic CSRF, because the browser does not add that header on its own. Login CSRF forces a victim to sign in as the attacker. The victim then uses the app without knowing it, and stores data in the attacker's account. It needs no existing session. To test it, check whether the login form has a token that is bound to a pre-login session. Submit the login form from a cross-site page with a test account's credentials, in a browser. If the browser ends up signed in as that test account, login CSRF is possible. Also check OAuth: a missing `state` parameter allows the same attack. See the [authentication testing checklist](/guides/authentication-testing-checklist/). ## How do teams fix CSRF? Use several defenses together, because no single one covers every case. - Synchronizer token or signed double-submit token on every state-changing request, including login. Bind it to the session. - `SameSite=Lax` or `Strict` on the session cookie, set explicitly. - Verify `Origin` (and `Referer` as a fallback) on state-changing requests. Reject foreign values. - Use only `POST`, `PUT`, `PATCH` or `DELETE` for changes. Never change state on `GET`. - Require a custom header or a JSON content type on APIs, and reject others. - Re-authenticate for sensitive actions such as email, password and payout changes. ```text on every non-GET request: if request.origin not in allowed_origins: reject 403 if request.header["X-CSRF-Token"] != session.csrf_token: reject 403 ``` After the fix, ask for a retest in a browser that repeats the same cross-site page and confirms the state does not change. Related reading: the [business logic guide](/guides/business-logic-vulnerabilities/) and the [OWASP Top 10 2025 guide](/guides/owasp-top-10-2025/). ## What are common false positives in CSRF testing? A missing token is not always a vulnerability. Testers check these cases before they report. - The endpoint needs a bearer header. The browser does not add it for another site, so classic CSRF does not apply. - The action is harmless. Changing a UI theme has little impact, and severity depends on what the request changes. - The cookie is not sent. A `SameSite=Strict` or `Lax` cookie may not reach the app on a cross-site `POST`. Prove it in the browser. - A second factor guards the action. If the request needs a fresh password or code, a forged request fails. - An `Origin` check blocks it. The server rejects foreign origins even with no token. The reverse also happens: a request that fails in a script may work in a browser because of cookies, redirects or `Origin` rules. So prove CSRF in a real browser and say so in the report. Report only what the browser showed, and put the rest in an open questions section. ## Questions ### Does SameSite=Lax make CSRF tokens unnecessary? No. SameSite is a strong extra layer, but it depends on the browser, on cookie settings, and on the definition of same-site. Subdomains, GET requests that change state, and the two-minute Lax-by-default window all leave gaps. Keep tokens. ### Are JSON APIs safe from CSRF? Not by default. If the API accepts cookies and also accepts form-encoded or text/plain bodies, a cross-site form can send the request. If it needs a bearer token in a header, the browser does not add it for another site. ### What is login CSRF? Login CSRF forces a victim's browser to sign in to an account the attacker controls. The victim then saves data into the attacker's account. It needs no existing session, so a missing token on the login form is enough. ### Why do testers need a real browser? Because a raw HTTP request skips browser rules. The browser decides which cookies to send, based on SameSite, origin and request type. A request that works in a script may fail in Chrome, and the reverse can also happen. ## Sources - [OWASP WSTG-SESS-05: Testing for Cross Site Request Forgery](https://wstg.owasp.org/v4.2/4-Web_Application_Security_Testing/06-Session_Management_Testing/05-Testing_for_Cross_Site_Request_Forgery/) - [OWASP Cross-Site Request Forgery Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html) - [MDN: Set-Cookie header (SameSite attribute)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie) - [Chromium: SameSite Updates](https://www.chromium.org/updates/same-site/) --- # Does SOC 2 require penetration testing? Source: https://penetrationtestservices.com/guides/does-soc-2-require-penetration-testing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Does SOC 2 require penetration testing? No. See the AICPA CC4.1 wording, why auditors still ask for a pentest report, and five questions to settle first. > **Short answer:** No. The AICPA Trust Services Criteria do not require a penetration test. The 2017 text names penetration testing once, as an example in a point of focus under CC4.1. Points of focus are not a checklist. Even so, many auditors and customers ask for a recent third-party pentest report. Agree scope, timing and report format with your auditor before you buy. SOC 2 does not require a penetration test. The AICPA Trust Services Criteria (TSC) set objectives for a service organization. They do not list mandatory technical tests. A pentest is one way to produce evidence for some of those objectives. Many companies still buy a pentest for their audit. The sections below cover the criteria text, common auditor requests and the questions to settle before scoping. > **Note:** This is published text and common practice, not audit advice. Your auditor decides what evidence they accept. A pentest report does not make an organization compliant. ## What does the SOC 2 text say about penetration testing? The word appears once in the 2017 criteria, in an example. In the 2017 TSC (March 2020 update of TSP section 100), the additional point of focus "Considers Different Types of Ongoing and Separate Evaluations" under CC4.1 reads: > "Management uses a variety of different types of ongoing and separate evaluations, including penetration testing, independent certification made against established specifications (for example, ISO certifications), and internal audit assessments." Two other passages limit how you read that sentence: - The criteria state that "Use of the trust services criteria does not require an assessment of whether each point of focus is addressed." Points of focus are examples. They are not a checklist. - CC7.1 says the entity "uses detection and monitoring procedures to identify (1) changes to configurations that result in the introduction of new vulnerabilities, and (2) susceptibilities to newly discovered vulnerabilities." Its point of focus "Conducts Vulnerability Scans" names scans "on a periodic basis and after any significant change in the environment". It does not name pentests. AICPA published a revision of the points of focus in 2022. As of November 2026 we quote the 2017 text above and name the edition. Read the 2022 file (free AICPA account) for the current wording. ## Why do auditors and customers still ask for a pentest, and which criteria does it support? Auditors choose their own evidence standard. A pentest report is easy to read and shows independent testing of a real system, so many auditors and many customers ask for one. A CPA firm, Linford & Co, states that a pentest "is not required" because the AICPA criteria do not provide the specific controls. Compliance platforms still collect pentest evidence. Vanta describes findings with severity, steps to reproduce and remediation advice, and says a retest can confirm a fix. These are vendor practices, not AICPA rules. The accurate wording is "many auditors expect it". "SOC 2 requires it" is wrong. Teams map a pentest to the criteria below. The mapping is a working assumption until your auditor agrees to it. | Criterion | What the 2017 text says (short) | How a pentest may support it | |---|---|---| | CC4.1 | Management selects, develops and performs ongoing and separate evaluations. Point of focus lists penetration testing as one type. | Shows a separate evaluation of the system, by a party outside the build team. | | CC7.1 | Detection and monitoring find new vulnerabilities and configuration changes. Point of focus names vulnerability scans. | Adds depth that scans miss, such as access control flaws. Scans are the named example. | | CC7.2 to CC7.4 | Monitor for anomalies, evaluate events, respond to incidents. | A test can show whether monitoring noticed the activity. This is optional evidence. | | CC8.1 | Changes are authorized, tested and approved. | A retest after a change can show that a fix was checked. | | CC3.2 | Risks are identified and analyzed. | Findings can feed the risk register. | Confirm each row with your auditor. Do not copy this table into your control narrative without that step. ## What should you agree with your auditor, and what should the report show? Ask five questions before you scope. The answers decide what the report must show. 1. Is a pentest in scope as evidence for our controls? Get the answer for your report type (Type I or Type II). 2. What must the scope cover? Production, staging, the web app, the API, internal systems? Auditors often care about the systems in your system description. 3. Who may perform the test? Some auditors want an independent third party. Some accept an internal team. Ask. 4. What timing do they want? For a Type II period, a test early in the period lets the fix and retest fall inside it. Confirm the dates. 5. What report form do they accept? Full report, executive summary, or a letter. See [what a pentest attestation letter contains and its limits](/guides/pentest-attestation-letter/). Put the answers in an email thread and keep it with your audit evidence. No SOC 2 text defines a pentest report format. Auditors and platforms usually look for the same basic items: - Dates of testing and the tested environment. - Scope: hosts, apps, APIs, roles tested. - Method and tester independence. - Findings with severity, evidence and fix guidance. - Fix status, and a retest result if you fixed findings. Our guide on [how to read a penetration testing report](/guides/how-to-read-a-penetration-testing-report/) explains each section. Audit evidence usually also includes the fix tickets and the retest, as covered in [after the pentest: remediation and retest](/guides/after-the-pentest-remediation-and-retest/). ## How often should you test for SOC 2, and how do Type I and Type II reports change the plan? The criteria set no interval. Customers may also set their own rule in a contract or questionnaire. See [how often should you pentest](/guides/how-often-should-you-pentest/) for the trigger list. Some teams test once per audit period and again after large changes to authentication, authorization or data handling, because those changes alter what the first test covered. Check this against what your auditor and customers ask. A Type I report looks at the design of controls on one date. A Type II report tests how the controls operated over a period, over a period set by the audit. The criteria are the same, but the evidence differs. For a Type I report, a recent pentest report is a point-in-time proof that you test. For a Type II report, the auditor looks for proof across the period. That can mean one test plus its fixes and retest, or several tests after major releases. Ask which your auditor expects. A simple plan for a first Type II period: 1. Test early, so findings and fixes fall inside the period. 2. Track each finding as a ticket with an owner and a date. 3. Retest the fixed findings and keep the result. 4. Test again after a large change, such as a new login system or a new tenant model. ## What are the common mistakes? These mistakes come up often: - Buying a scan and calling it a pentest. Scans and pentests are different activities. Name them correctly in your evidence. - Testing the wrong environment. A test of a staging copy with different settings may not match the system in your description. - Skipping the retest. A report with open high findings and no fix record leaves the auditor with questions. - Late timing. A test in the last week of the period leaves no time to fix and verify. - Over-claiming. Writing "SOC 2 compliant pentest" on a report. No such label exists in the criteria. ## Questions to take to your auditor - Does our system description name penetration testing as a control? - Which criteria will this report support? - Do you accept a summary or letter, or do you need the full report? - Must all high findings be fixed and retested before the period ends? - Does a vulnerability scan alone meet your evidence need for CC7.1? See [SOC 2 penetration testing](/soc-2-penetration-testing/) for our service. Excession Labs does not promise that any auditor will accept a report, so ask your auditor first. ## Questions ### Which SOC 2 criterion is a pentest evidence for? Teams most often map it to CC4.1 (ongoing and separate evaluations) and sometimes to CC7.1 (detection of vulnerabilities). Your auditor decides which criteria your evidence supports. Ask which control in your own system description the report will back. ### Does a Type I report need a pentest? The criteria do not say so for either report type. A Type I report looks at control design on one date. A Type II report tests operation over a period. Your auditor sets the evidence they want, so ask early. ### Can a vulnerability scan replace a pentest for SOC 2? Sometimes. CC7.1 points of focus name vulnerability scans, not pentests. Some auditors accept scans alone. Others ask for both. The answer depends on your auditor and your customers, so confirm it in writing. ### Should the pentest fall inside the audit period? A test early in a Type II period lets the fix and the retest also fall inside it. This is common practice, not a rule in the criteria. Confirm the dates your auditor wants. ## Sources - [AICPA: 2017 Trust Services Criteria (with revised points of focus, 2022)](https://www.aicpa-cima.com/resources/download/2017-trust-services-criteria-with-revised-points-of-focus-2022) - [AICPA TSP section 100, 2017 Trust Services Criteria (March 2020 update), hosted copy](https://arpio.io/wp-content/uploads/2020/08/trust-services-criteria.pdf) - [Linford & Co: What is penetration testing for SOC 2?](https://linfordco.com/blog/what-is-penetration-testing-soc-2/) - [Top Floor Security: Does SOC 2 require a penetration test?](https://topfloorsecurity.com/insights/does-soc-2-require-a-penetration-test/) - [Vanta Help: Penetration testing](https://help.vanta.com/en/articles/12511759-penetration-testing) --- # Firebase security rules testing: Firestore, Storage and the emulator Source: https://penetrationtestservices.com/guides/firebase-security-rules-testing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Firebase security rules testing for Firestore, Storage and Realtime Database: working unit tests for the Local Emulator Suite, safe example rules, and a checklist. > **Short answer:** Firebase Security Rules are the access control for Firestore, Cloud Storage and Realtime Database when clients connect directly. Test them with the Local Emulator Suite and the @firebase/rules-unit-testing library. Write one allowed and one denied case per path, test as signed-out, owner and other-user callers, and never ship allow read, write: if true. Firebase Security Rules decide who can read and write your data when a mobile or web client talks to Firebase directly. Firebase's own docs say: "Firebase Security Rules stand between your data and malicious users." They cover Cloud Firestore, Realtime Database and Cloud Storage. If a rule is too open, anyone with your project's public config can use it. Test rules with the Local Emulator Suite, using the safe examples and checklist. Test only projects you own or have written permission to test. ## What do Firebase Security Rules protect, and which rules are unsafe? Rules protect direct client access. They do not protect server access. Firebase states that the server client libraries "bypass all Cloud Firestore Security Rules". Code that runs with the Admin SDK or a service account needs its own checks. Three facts apply to every test: - The Firebase config is public. The API key and project ID in a web app are not secrets. Rules are the control. - Rules are not filters. A Firestore query succeeds only if the rules allow every document it could return. Test the exact queries your app runs. - Rules do not combine across products. Firestore, Storage and Realtime Database each have their own rules file. A good Firestore ruleset does not protect your Storage bucket. Two rule patterns are unsafe. Firebase's Firestore docs warn against the first one: "NEVER use this rule set in production; it allows anyone to overwrite your entire database." ```text // Unsafe: open to everyone match /{document=**} { allow read, write: if true; } // Unsafe: any signed-in user, including self-registered ones match /{document=**} { allow read, write: if request.auth != null; } ``` Firebase notes that basic rules like these "are not recommended for production applications". The second pattern checks only that a user is signed in. It does not check who owns the data. ## What do safe rules look like, and how do you unit test them? The rules below use an example app with `users` and `projects` collections, and a Storage path per user. Adapt the names. ```text rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { // Default: deny everything not listed below match /{document=**} { allow read, write: if false; } // A user reads and edits only their own profile match /users/{userId} { allow read: if request.auth != null && request.auth.uid == userId; allow create: if request.auth != null && request.auth.uid == userId && request.resource.data.keys().hasOnly(['name', 'bio']); allow update: if request.auth != null && request.auth.uid == userId && request.resource.data.diff(resource.data) .affectedKeys().hasOnly(['name', 'bio']); allow delete: if false; } // Project members can read; only the owner can write match /projects/{projectId} { allow read: if request.auth != null && request.auth.uid in resource.data.memberIds; allow write: if request.auth != null && request.auth.uid == resource.data.ownerId; allow create: if request.auth != null && request.resource.data.ownerId == request.auth.uid; } } } ``` ```text rules_version = '2'; service firebase.storage { match /b/{bucket}/o { // Each user uploads images to their own folder, up to 5 MB match /users/{userId}/{fileName} { allow read: if request.auth != null && request.auth.uid == userId; allow write: if request.auth != null && request.auth.uid == userId && request.resource.size { testEnv = await initializeTestEnvironment({ projectId: "demo-rules-test", firestore: { rules: readFileSync("firestore.rules", "utf8") }, }); }); afterEach(() => testEnv.clearFirestore()); after(() => testEnv.cleanup()); it("alice writes her own profile, not bob's", async () => { await testEnv.withSecurityRulesDisabled(async (ctx) => { await setDoc(doc(ctx.firestore(), "users/bob"), { name: "Bob" }); }); const db = testEnv.authenticatedContext("alice").firestore(); await assertSucceeds(setDoc(doc(db, "users/alice"), { name: "Alice" })); await assertFails(setDoc(doc(db, "users/bob"), { name: "Changed" })); }); it("alice cannot add an isAdmin field to her profile", async () => { const db = testEnv.authenticatedContext("alice").firestore(); await assertFails(setDoc(doc(db, "users/alice"), { name: "Alice", isAdmin: true })); }); it("signed-out users cannot read profiles", async () => { const db = testEnv.unauthenticatedContext().firestore(); await assertFails(getDoc(doc(db, "users/alice"))); }); ``` Run the suite from the CLI with the emulator started for the test run, for example `firebase emulators:exec --only firestore "npm test"`. Check the Emulator Suite docs for the flags your CLI version supports. If tests time out or show `ECONNREFUSED`, the emulator is probably not running. ## What is the test checklist? Test every path in every rules file. Use four callers: signed out, owner, other signed-in user, and a user with a role claim, if you use roles. For Firestore: - [ ] Search for `if true`, and for `request.auth != null` used alone. - [ ] Each collection has a read case and a write case for each caller. Include `create`, `update` and `delete` apart. - [ ] Owner checks compare `request.auth.uid` to a stored owner field, not a client-sent value. - [ ] Updates cannot change protected fields such as `role`, `ownerId`, `plan` or `isAdmin`. - [ ] Queries (list) work only with the filters your rules need, and fail without them. - [ ] Subcollections and collection-group queries have their own rules. For Cloud Storage: - [ ] Paths are scoped to the owner or the tenant. - [ ] Size and content type limits exist on uploads. - [ ] Read access is not wider than the app needs. Test a read of another user's path. - [ ] Public download URLs for private files are reviewed. A shared download token works outside the rules. For Realtime Database: - [ ] The root has no `".read": true` or `".write": true` for private data. - [ ] Child paths use `auth.uid` checks and `.validate` rules. For auth and server-side code: - [ ] Anonymous sign-in is off, or its rules are as strict as for other users. - [ ] Role claims are set only by trusted server code. - [ ] Cloud Functions and Admin SDK code check authorization, because rules do not apply there. See the [authentication testing checklist](/guides/authentication-testing-checklist/). For the two-account method on object access, see [IDOR testing](/guides/idor-testing/) and [multi-tenant isolation testing](/guides/multi-tenant-isolation-testing/). ## What are the common mistakes, and how do you report and test them? Look for these flaws in a Firebase review: - Test-mode rules left in place. Rules written for development stay live, sometimes with an expiry date nobody tracks. - Owner checks that trust the client. A rule compares a field in `request.resource.data` to the user, but a different path lets the user change the owner field. - Role fields the user can write. A user sets `role: "admin"` on their own document. - Open reads for lists. A rule that allows `read` on a collection also lets clients list the whole collection. - Storage rules that differ from Firestore rules. The database is locked, and the bucket is open. - Unvalidated writes. No type, size or field checks, so a client can store anything. To run the tests in CI, add the unit tests to your build. Start the emulator, run the suite, and fail the build on any failed assertion. For each change to a rules file, write the denied-case test first, then the allowed-case test. Keep the rules in version control and review them like code. Test before you deploy, and deploy rules only from the main branch. A good finding names the path, the caller, the operation and the proof. For example: "A signed-out client reads `/orders/{id}` from Firestore. Cause: rule `allow read: if true` on `orders`. Fix: replace with an owner check and add a denied-case unit test. Retest: `assertFails` passes in CI." Add the failing unit test to your suite so the flaw cannot return unseen. For risks across Bubble, Supabase and Firebase, see [no-code app security testing](/no-code-app-security-testing/). The database-first sibling guide is [Supabase security testing](/guides/supabase-security-testing/). ## Questions ### Do Security Rules apply to the Admin SDK? No. Firebase documents that the server client libraries bypass all Cloud Firestore Security Rules. Rules protect access from mobile and web clients. Protect server code with its own authorization checks and least-privilege service accounts. ### Are Firestore rules filters? No. Firestore rules do not filter query results. A query succeeds only if the rules allow every document it could return. Write queries with the same constraints as your rules, and test the exact queries your app sends. ### Is request.auth != null enough for a rule? Rarely. It only checks that someone is signed in, and anyone can often create an account. Firebase marks such basic rules as not recommended for production. Compare request.auth.uid to the owner of the document, or check a role claim. ### Can I test rules without touching production data? Yes. The Local Emulator Suite runs Firestore, Storage and Realtime Database locally. Firebase says the v9 rules-unit-testing library never touches your production resources. Use a demo project ID for tests. ## Sources - [Firebase Docs: Firebase Security Rules](https://firebase.google.com/docs/rules) - [Firebase Docs: Unit-test your Security Rules with the Local Emulator Suite](https://firebase.google.com/docs/rules/unit-tests) - [Firebase Docs: Cloud Firestore Security Rules basics](https://firebase.google.com/docs/firestore/security/get-started) - [Firebase Docs: Security Rules and queries (rules are not filters)](https://firebase.google.com/docs/firestore/security/rules-query) - [Firebase Docs: Cloud Storage Security Rules basics](https://firebase.google.com/docs/storage/security) - [Firebase Docs: Firebase Local Emulator Suite](https://firebase.google.com/docs/emulator-suite) --- # HIPAA penetration testing: what the Security Rule says today and what HHS proposed Source: https://penetrationtestservices.com/guides/hipaa-penetration-testing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 HIPAA penetration testing requirements as of November 2026: the Security Rule in force does not name a pentest, and the January 2025 HHS proposal is not final. > **Short answer:** As of November 2026 the HIPAA Security Rule in force does not mention penetration testing. It requires a risk analysis and a periodic evaluation. In January 2025 HHS proposed a pentest at least every 12 months by a qualified person. That proposal is not final. The regulatory agenda lists a final rule date of July 2027. The HIPAA Security Rule now in force does not mention penetration testing. In January 2025 HHS proposed a rule that would require one at least every 12 months. As of 9 November 2026 that proposal is not final. The two facts are separate. Each has its own text and source. > **Note:** This is regulatory text and status, not legal advice. Ask your privacy or compliance counsel what applies to you. A pentest report does not make an organization HIPAA compliant. ## What does the Security Rule require, and what did HHS propose? The rule in force (45 CFR Part 164, Subpart C) asks for risk analysis and periodic evaluation. It does not name pentests. We searched the eCFR text of all of Subpart C (45 CFR 164.302 to 164.318), current to 5 November 2026. The word "penetration" does not appear in it. Two standards apply to testing: - Risk analysis (164.308(a)(1)(ii)(A)): conduct an accurate and thorough assessment of the potential risks and vulnerabilities to the confidentiality, integrity and availability of electronic protected health information (ePHI). - Evaluation (164.308(a)(8)): "Perform a periodic technical and nontechnical evaluation, based initially upon the standards implemented under this rule and, subsequently, in response to environmental or operational changes affecting the security of electronic protected health information". A pentest can serve as part of a technical evaluation. The rule does not say how often, and does not say a pentest is required. You choose methods based on your risk analysis. HHS published a notice of proposed rulemaking (NPRM) on 6 January 2025 (90 FR 898, document 2024-30983, RIN 0945-AA22). Comments closed on 7 March 2025. The proposal would add a standard for vulnerability management at proposed 164.312(h). The proposed text includes: | Proposed provision | Text (short quotes) | |---|---| | 164.312(h)(2)(i)(A) | Conduct automated vulnerability scans "in accordance with the covered entity's or business associate's risk analysis ... or at least once every six months, whichever is more frequent." | | 164.312(h)(2)(iii) | "Penetration testing. Perform penetration testing of the covered entity's or business associate's relevant electronic information systems by a qualified person." | | Qualified person | "a person with appropriate knowledge of and experience with generally accepted cybersecurity principles and methods for ensuring the confidentiality, integrity, and availability of electronic protected health information." | | 164.312(h)(2)(iii)(B) | Pentests "must be performed at least once every 12 months or in accordance with the covered entity's or business associate's risk analysis ..., whichever is more frequent." | | 164.308(b)(2)(ii) | A covered entity would "obtain written verification from the business associate at least once every 12 months" that technical safeguards are deployed. | These are proposals. They have no legal force until HHS publishes a final rule. ## What is the status as of November 2026, and what should you watch? The proposal is still not final. The Federal Register lists the January 2025 NPRM as the only action under RIN 0945-AA22 since it was issued. The Unified Agenda entry on Reginfo.gov (agenda 202510) shows the stage "Long-Term Actions" and a final rule date of "07/00/2027". The status leaves two points open: - July 2027 is a target date for issuing a final rule. It is not a compliance deadline. - The rule might change before it is final, or it might not be finalized at all. This status changes. Check these three sources before you rely on it: - The Federal Register entry for RIN 0945-AA22, for any new proposed or final rule document. - The Reginfo.gov agenda entry for the same RIN, for the stage and date. - HHS Office for Civil Rights guidance pages, for enforcement notes. A move out of "Long-Term Actions" is the signal to start a plan. A published final rule document starts a compliance clock that its own text defines. ## Why do healthcare customers ask for a pentest, and how should you plan? They must manage risk, and they pass that duty to vendors. A covered entity needs assurance from its business associates, and a pentest report is a common form of proof. Contracts, security questionnaires and cyber insurance carry these demands. The Security Rule text does not. If you sell to healthcare, expect questions like those in [security questionnaire pentest questions](/guides/security-questionnaire-pentest-questions/). Prepare a short, honest answer: when you last tested, the scope, who tested, and the fix status. Plan for the proposal without waiting for it. A yearly web and API test, with retest, is easy to explain under the current rule and fits the proposal. It costs you little if the rule does not pass. A workable plan: 1. Put pentesting into your risk analysis as a technical evaluation method. 2. Set an interval you can defend, for example once a year and after major changes. See [how often should you pentest](/guides/how-often-should-you-pentest/). 3. Define the "qualified person" now: experience, method, independence from the build team. 4. Keep the report, fix tickets and retest evidence together. 5. Re-check the rule status every quarter. ## What should the test cover, and what should the record show? Scope the test around where ePHI lives and moves. For a web app or API, that means: - Authentication, sessions and account recovery. - Authorization between users, roles and tenants. - API endpoints that return patient or member records. - File upload and export paths. - Logging and access paths for admin tools. Use test accounts and synthetic records. Do not put real ePHI into a test environment you do not control. Sign a written authorization first, as covered in [penetration testing rules of engagement](/guides/penetration-testing-rules-of-engagement/). If the tester might see real ePHI, ask your privacy counsel whether you need a business associate agreement with the testing firm. ## How does this compare with other frameworks, and what should you not say? Among the five frameworks in the table, only HIPAA has a pentest requirement that is proposed but not in force. The table shows the position as of November 2026. | Framework | Pentest named in the text? | Interval | |---|---|---| | HIPAA Security Rule (in force) | No | None set | | HIPAA NPRM (proposed, not final) | Yes, proposed 164.312(h)(2)(iii) | At least every 12 months, or per risk analysis if more frequent | | [PCI DSS v4.0.1](/guides/pci-dss-penetration-testing-requirements/) | Yes, Requirement 11.4 | At least every 12 months and after significant change | | [SOC 2 TSC (2017)](/guides/does-soc-2-require-penetration-testing/) | One example in a CC4.1 point of focus | None set | | [ISO/IEC 27001:2022](/guides/iso-27001-penetration-testing/) | No | None set | For the PCI DSS wording, see [PCI DSS penetration testing requirements](/guides/pci-dss-penetration-testing-requirements/). Avoid these three claims: - "HIPAA requires an annual pentest." Not today. It is proposed. - "HIPAA certified pentest." We found no HHS program that certifies pentest reports. Ask for the facts of the test instead. - "A pentest makes us HIPAA compliant." Compliance covers policy, training, risk analysis, access control, breach response and more. Auditors and customers read your records, not the proposal. Under the current rule, a defensible record has five parts: 1. A dated risk analysis that names web and API risks to ePHI. 2. A documented decision on technical evaluation methods, including pentesting and vulnerability scanning. 3. A pentest report with scope, method, findings and tester details. 4. Fix tickets and a retest result. 5. A review of the results by the person who owns security. If a final rule arrives, update the interval and the "qualified person" definition. The records stay useful. ## Questions to take to counsel or your compliance lead - Does our risk analysis name pentesting as an evaluation method? - Do our customers' contracts set a pentest interval? - Is the tester a business associate in our setup? - How do we keep ePHI out of the test? - Who owns the decision if the rule is finalized with a short compliance window? For a sister topic, see [does SOC 2 require penetration testing](/guides/does-soc-2-require-penetration-testing/). ## Questions ### Does HIPAA require an annual penetration test today? No. The Security Rule now in force requires a risk analysis and a periodic technical and nontechnical evaluation. It does not name penetration testing or set a pentest interval. The annual test is in a proposal from January 2025 that is not final. ### If the rule is not final, why do healthcare customers ask for a pentest? Covered entities must assess risk and may require evidence from business associates. A pentest is a common way to show technical testing. Contracts and questionnaires set these demands, not the Security Rule text itself. ### What would the proposed rule change for business associates? The proposal would add a written verification step. A covered entity would obtain written verification from the business associate at least once every 12 months that technical safeguards are deployed. That needs a written analysis by a qualified person. It applies only if the rule is finalized. ### Is a HIPAA pentest the same as a HIPAA risk analysis? No. A risk analysis is a required process that finds risks to electronic protected health information. A pentest tests whether specific weaknesses can be exploited. A pentest can feed the risk analysis, but it does not replace it. ## Sources - [Federal Register: HIPAA Security Rule To Strengthen the Cybersecurity of Electronic Protected Health Information (NPRM, 90 FR 898, 6 January 2025)](https://www.federalregister.gov/documents/2025/01/06/2024-30983/hipaa-security-rule-to-strengthen-the-cybersecurity-of-electronic-protected-health-information) - [Federal Register: NPRM full text, document 2024-30983](https://www.federalregister.gov/documents/full_text/text/2025/01/06/2024-30983.txt) - [Reginfo.gov: Unified Agenda entry for RIN 0945-AA22](https://www.reginfo.gov/public/do/eAgendaViewRule?pubId=202510&RIN=0945-AA22) - [eCFR: 45 CFR Part 164, Subpart C (Security Standards for the Protection of Electronic Protected Health Information)](https://www.ecfr.gov/current/title-45/subtitle-A/subchapter-C/part-164/subpart-C) - [PCI Security Standards Council: Document library (PCI DSS v4.0.1)](https://www.pcisecuritystandards.org/document_library/) - [HHS: HIPAA Security Rule guidance](https://www.hhs.gov/hipaa/for-professionals/security/index.html) --- # How often should you pentest? Standards, triggers and a schedule Source: https://penetrationtestservices.com/guides/how-often-should-you-pentest/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 How often should you pentest? The PCI DSS 11.4 wording, SOC 2 and ISO 27001 position, ten changes that should trigger a test, and a schedule table by profile. > **Short answer:** PCI DSS v4.0.1 asks for internal and external penetration tests at least once every 12 months and after any significant change. SOC 2 and ISO 27001 set no fixed interval. For software teams the practical rule is: test before launch, after meaningful changes to authentication, authorization, data handling or architecture, and at least once a year. Test at least once a year, and again after any significant change. That is the wording of PCI DSS, the one major standard that sets a frequency. For most software teams, "significant change" is the useful part, because a yearly test cannot protect code that shipped last week. ## What do PCI DSS, SOC 2 and ISO 27001 say about pentest frequency? PCI DSS v4.0.1 requires internal and external penetration tests at least once every 12 months, and after any significant infrastructure or application change. These are requirements 11.4.2 (internal) and 11.4.3 (external) in the standard's numbering. As of November 2026, v4.0.1 is the published version. PCI SSC published it on 11 June 2024 as a limited revision and retired v4.0 on 31 December 2024. In June 2026 the council opened a request for comments on the next iteration, with no new version date stated. The rest of requirement 11.4 matters for frequency too: | Requirement | What it asks | |---|---| | 11.4.1 | A documented pentest methodology. Results kept at least 12 months. | | 11.4.2 | Internal test: every 12 months and after significant change. | | 11.4.3 | External test: every 12 months and after significant change. | | 11.4.4 | Exploitable findings are corrected, and the test is repeated to verify the fix. | | 11.4.5 | If segmentation isolates the card data environment: test segmentation every 12 months and after changes. | | 11.4.6 | Service providers only: segmentation tests every six months. | | 11.4.7 | Multi-tenant service providers support customers' external tests. | PCI SSC's own summary of changes confirms 11.4.1, 11.4.4 and 11.4.7. The interval wording for 11.4.2, 11.4.3, 11.4.5 and 11.4.6 comes from vendor pages that quote the standard. The standard PDF is behind a click-through gate. Read the exact text, and ask your QSA which items apply. Our guide on [PCI DSS penetration testing requirements](/guides/pci-dss-penetration-testing-requirements/) goes deeper. Requirement 11.4 has no merchant-level condition in the sources we read. Merchant level decides how you validate, not whether a requirement applies. Ask your QSA. SOC 2 and ISO 27001 set no fixed interval. SOC 2's Trust Services Criteria do not name penetration testing as a required activity. Some buyers ask for a recent test. Check yours. See [does SOC 2 require penetration testing](/guides/does-soc-2-require-penetration-testing/). ISO/IEC 27001 asks you to manage technical vulnerabilities and test security. It leaves the method and the interval to your risk assessment. When a customer contract names a frequency, that frequency is your requirement. ## What should trigger a pentest, and how do you choose an interval? Test after a change that alters what an attacker can reach. The standard's phrase "significant change" is not defined in a way that fits every team, so write your own list. This starting set suits web apps, APIs and SaaS products. | Trigger | Why it matters | |---|---| | New or rebuilt login, SSO, MFA or password reset | Authentication flaws give access to everything | | New role, permission model or admin area | Access-control bugs follow permission changes | | Multi-tenant change: new tenant model, sharing, invitations | Tenant leaks are costly | | New public API or major API version | New endpoints bring new object-level checks | | Payment, billing or entitlement logic | Business logic flaws turn into money loss | | File upload, import, export or webhooks | Common source of injection and data exposure | | Move of hosting, cloud account or framework | Defaults and settings change | | Acquisition or new integration with partner data | New trust boundary | | Major incident or a critical finding in a related system | Check for the same flaw elsewhere | | Customer or auditor request | A contract or deadline is a trigger too | Small UI changes, copy edits and most bug fixes rarely need a test. Group them into the next planned test. Start from your release pace and your data risk. These defaults are our suggestions, not standard requirements. | Your profile | Suggested pattern | |---|---| | Pre-launch product, first public release | One test before launch. See [pre-launch security testing](/pre-launch-security-testing/). | | Ships weekly, handles customer data | Full test yearly, plus a scoped test after each meaningful change set | | Ships monthly, regulated or card data | Yearly test and tests after significant change, per the standard | | Rarely changes, low-risk data | Yearly test, or after any change to login or data handling | | Enterprise customers ask for reports | Test on a rhythm that keeps the report under 12 months old | Teams that ship often get more from a recurring plan with scoped tests after releases than from one large test. [Recurring penetration testing](/recurring-penetration-testing/) describes the recurring testing that Excession Labs offers after releases. Vendor surveys report how often their respondents test. The numbers differ by survey and by who was asked. Cobalt's State of Pentesting Report 2025 is one example: a vendor survey of 450 respondents. Use your own release pace, data risk and contracts. ## How do you keep a schedule from slipping? Tie the test to events you already track. Put a "security test needed?" check in your release review, use the trigger list above as the questions, and name an owner. Plan the fix time too, because a test helps only if the fixes land and are verified. PCI DSS 11.4.4 expects the test to be repeated to verify corrections, so leave time for fixes and a retest. See [after the pentest: remediation and retest](/guides/after-the-pentest-remediation-and-retest/). > **Demonstration only:** This is a demonstration. No real customer system was tested. Harbor Notes is a fictional SaaS app that ships twice a week and stores customer notes. A customer contract asks for a pentest "at least annually". The team plans one full grey box test each year in March, and their release review asks the trigger questions. In June they rebuild their invitation flow, which changes who can join a tenant. That is a tenant-boundary change, so they book a scoped test of invitations and roles. In November they add a public API and test the new endpoints before the release. Over the year they run one full test and two scoped tests, and always hold a report under 12 months old. ## Questions ### Is once a year enough? It meets the time interval in PCI DSS. It does not cover the weeks between tests: if you ship fast, a flaw can go live a week after the test ended. Add tests after significant changes. ### Do I need a new pentest for every release? No. Most releases are small. Test when a release changes something an attacker cares about: login, permissions, tenant boundaries, payments, file handling or new public endpoints. A scoped test of the changed area is often enough. ### Does a vulnerability scan replace a pentest between tests? A scan is a useful routine check, but it does not do the same job. It finds known patterns. It does not test business logic or chain issues. PCI DSS lists vulnerability scans and penetration tests as separate requirements. ### What is an industry average for pentest frequency? We do not quote one. Survey samples differ in size, buyer type and wording, and vendors run most of the surveys. Set your interval from your own release pace, risk and contracts. ## Sources - [Cobalt: State of Pentesting Report 2025](https://resource.cobalt.io/state-of-pentesting-2025) - [PCI SSC blog: Just Published: PCI DSS v4.0.1](https://blog.pcisecuritystandards.org/just-published-pci-dss-v4-0-1) - [PCI DSS v3.2.1 to v4.0 Summary of Changes (r1, May 2022)](https://listings.pcisecuritystandards.org/documents/PCI-DSS-v3-2-1-to-v4-0-Summary-of-Changes-r1.pdf) - [PCI SSC Information Supplement: Penetration Testing Guidance v1.1](https://listings.pcisecuritystandards.org/documents/Penetration-Testing-Guidance-v1_1.pdf) - [PCI SSC blog: Request for Comments PCI DSS v4.0.1 (3 June 2026)](https://blog.pcisecuritystandards.org/request-for-comments-pci-data-security-standard-pci-dss-v4.0.1) --- # How to prepare for a penetration test: a checklist Source: https://penetrationtestservices.com/guides/how-to-prepare-for-a-penetration-test/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 How to prepare for a penetration test: write the scope, create test accounts, load test data, pick an environment, name contacts and set a freeze window. > **Short answer:** To prepare for a penetration test, agree the scope in writing, create test accounts for each role and tenant, load safe test data, choose an environment, allowlist the tester, name two contacts, and freeze risky changes during the test window. Share API docs. Tell your hosting provider and security team. Preparation decides how much the tester can do in the time you pay for. Each hour the tester waits for an account or an answer is an hour not spent testing. Most of the work is simple: write the scope, create accounts, load test data, pick the environment and name contacts. ## What should you decide first, and how do you define the scope? Decide what you want to learn. A pentest can serve a customer review, a launch gate, an audit, or a general check. The goal sets the scope, the depth and the report you need. Then decide the test type. The choice between black, grey and white box changes what you must prepare. See [black box, grey box and white box testing](/guides/black-box-grey-box-white-box-testing/). For web app, API and SaaS tests, grey box (test accounts and documentation) lets the tester spend the agreed time on testing instead of discovery. The scope is a written list of what the tester may test and what is off limits. List each item exactly: domains, subdomains, API base URLs, app versions and environments. Also list what is out of scope, such as third-party services, payment processors, other customers' systems and anything you do not own. Write one line for each of these: - Assets in scope (host names and URLs). - Assets out of scope. - Roles and tenants to test. - Known issues the tester should skip. - Special worries, such as "billing" or "file import". A tester can only test what you list, and an asset missing from the list gives you false confidence in coverage. For the full authorization and scope structure, read [penetration testing rules of engagement](/guides/penetration-testing-rules-of-engagement/). You also need written authorization from the system owner. If you do not own the system, for example a customer-hosted instance or a third-party platform, get permission from the owner. A client's permission does not always cover its hosting or software providers. Check each provider's testing policy. ## What accounts and test data does the tester need? Give the tester at least two accounts for every role, and put them in at least two separate tenants if your app is multi-tenant. One account cannot prove that user A can read user B's data. A good set for a SaaS product: | Account | Purpose | |---|---| | Tenant A owner | Highest normal role in tenant A | | Tenant A editor | Mid role in tenant A | | Tenant A viewer | Lowest role in tenant A | | Tenant B owner | Second tenant, for cross-tenant tests | | Tenant B viewer | Second tenant, low role | | Platform admin (if in scope) | Internal admin paths | Use strong, unique passwords. Share them through a secrets manager or an encrypted channel, not by plain email. If you use MFA, give the tester a way to pass it: a test seed, a bypass for test accounts, or a mailbox they can reach. Check that every account can sign in before the test starts. Use test data only. Create records in each tenant so the tester has something to try to reach: notes, invoices, files, users. Mark them as test data, for example with a name prefix. Do not load real customer data. If you must test production, use test-owned accounts and records. Destructive proofs should only touch records the tester created. If your app sends emails, texts or calls paid APIs, say how many the tester may trigger. Tests of uploads, password guessing, email sending and paid third-party APIs should be bounded and their cost stated. ## Which environment should you use, and what should you share? Choose staging if it matches production in code, configuration, integrations and roles. Choose production if staging differs in ways that matter, and apply stricter rules there. | Option | Good | Watch for | |---|---|---| | Staging copy | Safe for destructive proofs | Config drift hides real flaws | | Production, test accounts | Real configuration | Rate limits, side effects, alerts | | Pre-launch environment | Finds flaws before users arrive | Must match what will launch | Whichever you pick, freeze it for the test window. Share what helps the tester aim: - An OpenAPI file, GraphQL schema, or Postman collection. - A short description of main user flows and roles. - A diagram of the main components and third-party services. - A list of known issues and past findings. - For white box tests: a code archive or repository access, and who to ask. You do not need a polished document. A rough note is fine. For APIs, a collection that works end to end saves the most time. See [API penetration testing](/api-penetration-testing/) for what we ask for. ## How do you handle allowlists, contacts and the freeze window? Decide whether to allowlist the tester's source IPs in your WAF, rate limiter and bot protection. Two common choices: 1. Allowlist: the tester spends time on the app, not on the WAF. You may miss flaws that the WAF would hide anyway. 2. No allowlist: the test shows how the WAF behaves. The tester may be blocked and lose time. A common pattern is to allowlist for the main test and then run a short extra check without it. Write the choice in the scope. Tell your security team and any managed monitoring provider about the test window, the source IPs and the contact. This prevents a false incident. Check your cloud and hosting providers' penetration testing policies, and send notice if they ask. Name two people who can answer questions during the test: a technical contact and a backup. Add an escalation contact who can stop the test, and give phone numbers or a chat channel. If the tester finds a serious issue, they need someone to tell right away. Agree how fast the tester reports a serious finding, and put that rule in the rules of engagement. A freeze window is a period when you do not deploy changes to the tested environment. If code changes during the test, results may no longer match: the tester may report a flaw you already removed, or miss a new one. Freeze changes to the in-scope environment for the test window. Allow emergency fixes, and tell the tester when they happen. Plan the freeze around your release calendar. ## Preparation checklist Copy this list into your ticket system. Scope and authorization: - [ ] Goal of the test written down - [ ] Assets in scope listed exactly - [ ] Out-of-scope assets listed - [ ] Written authorization from the system owner - [ ] Hosting and cloud provider policies checked Access: - [ ] Accounts for each role and at least two tenants - [ ] Credentials shared through a secure channel - [ ] MFA path for test accounts - [ ] Each account tested by sign-in before the start Data and environment: - [ ] Environment chosen and matches production - [ ] Test data loaded and marked - [ ] Limits set for emails, uploads and paid APIs - [ ] Backups or a restore plan in place Documents: - [ ] API docs or collection - [ ] Flow notes and architecture sketch - [ ] Known issues list People and time: - [ ] Technical contact and backup named - [ ] Escalation contact who can stop the test - [ ] Security team and monitoring provider told - [ ] Allowlist decision made - [ ] Freeze window set If you are preparing for a launch, read [pre-launch security testing](/pre-launch-security-testing/). To start a conversation about a scope, use the [contact page](/contact/). ## Questions ### Should we test production or staging? Staging is safer, but only if it matches production in code, configuration and integrations. A staging copy that differs can hide flaws. Some teams test production with test-owned accounts and strict rules. Decide with your tester and write it in the scope. ### Should we tell our security team and hosting provider? Yes. Tell your security team so they do not treat the test as a real attack. Check your hosting and cloud provider's policy on security testing, and send notice if they ask for it. ### Do we have to fix known bugs first? No. But tell the tester about known issues. Otherwise you pay to find them again. Mark them as known in the scope so the tester can skip them or confirm them quickly. ### How much time will our engineers spend? Less than for white box, more than for black box. Expect setup work before the test and short questions during it. Name a contact who can answer within the day. The scope should state the effort. ## Sources - [OWASP Web Security Testing Guide](https://wstg.owasp.org/) - [NIST SP 800-115: Technical Guide to Information Security Testing and Assessment](https://csrc.nist.gov/pubs/sp/800/115/final) - [PTES Pre-engagement (Internet Archive capture, 2025-01-03)](https://web.archive.org/web/20250103002608/http://www.pentest-standard.org/index.php/Pre-engagement) --- # How to read a penetration testing report, section by section Source: https://penetrationtestservices.com/guides/how-to-read-a-penetration-testing-report/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 How to read a penetration testing report section by section: the 13 parts, an annotated sample finding, severity versus priority, and how to plan fixes. > **Short answer:** Read a penetration testing report in this order: the executive summary and priority actions, the findings at a glance, then each finding's impact, evidence and fix. Severity says how bad a flaw is in general. Priority says what your team fixes first, given your system. Reproduce each finding before you close it. Read the executive summary first, then the findings at a glance, then each finding in order of severity. Treat the report as a work list. Each finding should lead to a fix, a check, and a close-out. Excession Labs reports use this structure. Other providers use different section names, but the parts are similar. To see the structure with real formatting, open the [demonstration report](/sample-report/). ## What are the sections of a pentest report, and how do you read the opening ones? An Excession Labs report has these sections, in this order: 1. Project details: scope, assets and the severity scale. 2. Assessment method. 3. Safety constraints. 4. Evidence labels. 5. Executive summary with priority actions. 6. Findings at a glance. 7. Reproduction environment. 8. One section per finding: description, impact, verified results, reproduction, remediation. 9. Controls that held. 10. Remediation plan. 11. Limitations and open questions. 12. Evidence index. 13. Appendix of reproduction scripts. The opening sections tell you what the report covers and how far to trust it. Read them before the findings, because they set the limits of every claim after them. Project details: check the scope. Does the list of assets match what you asked for? Anything not listed was not tested. Check the severity scale and its definitions here too. Assessment method: this says what the tester did: direct HTTP requests, browser tests, review of client-side code, comparison of anonymous and signed-in users. A black box report and a grey box report mean different things. See [black box, grey box and white box testing](/guides/black-box-grey-box-white-box-testing/). Safety constraints: this lists what the tester did not do, how risky tests were bounded, and which test-owned accounts and records were used. Evidence labels: "Observed" means the tester reproduced it. "Browser-observed" means it was shown in a real browser. A path labeled as inferred was not proven, so do not treat it as confirmed. The executive summary gives the overall picture and the priority actions. Use it to brief management. The findings at a glance table lists every finding with its ID, title and severity. Use it to build your ticket list. Check that the summary agrees with the table. If the summary says "two high findings", the table should show two. A report that disagrees with itself needs a question to the tester. ## What is in a finding? Each finding has five parts, and a sample finding may add a Controls note. Read all of it before you open a ticket. | Part | What it tells you | What to check | |---|---|---| | Description | What the flaw is, in plain words | Can your team name the affected feature? | | Impact | What an attacker gains | Does it match your data and users? | | Verified results | The evidence | Requests, responses, timestamps, read-back from a second account | | Reproduction | Steps to see it yourself | Can a developer follow them? | | Remediation | How to fix | Does the fix match your stack? | The verified results part is the proof. A bare success response is not proof: a write is proven when the changed record is read back through another session, so look for that read-back. > **Demonstration only:** This is a demonstration. No real customer system was tested. "Harbor Notes" is a fictional app and `app.example.com` is a reserved example host. **F-02: Any signed-in user can read another tenant's notes (High)** *Description.* The `GET /api/notes/{id}` endpoint checks that the caller is signed in. It does not check that the note belongs to the caller's tenant. *Impact.* A user in tenant A can read any note in tenant B by guessing or enumerating numeric IDs. Notes may hold customer data. *Verified results.* Observed. With Bob's session (tenant B), a request for note 4021, created by Alice (tenant A), returned HTTP 200 and Alice's content. *Controls.* A request for a nonexistent ID returned 404. A request with no token returned 401. A request with Bob's token for Bob's own note returned 200. So the server checks identity but not ownership. *Reproduction.* Sign in as the test user in tenant B. Send the request below with the ID of a note that belongs to tenant A. ```http GET /api/notes/4021 HTTP/1.1 Host: app.example.com Authorization: Bearer ``` *Remediation.* Load the note with the tenant ID in the query. Return 404 when the note is not in the caller's tenant. Add a test that fails when a second tenant can read the note. ## What is the difference between severity and priority, and how do you reproduce a finding? Severity is the tester's rating of how bad a flaw is in general. Priority is your decision on what to fix first. Severity comes from a written scale. Excession Labs uses four levels: Critical, High, Medium and Low, each with a written definition. CVSS vectors are not added by default. If your program needs CVSS, say so during scoping. For reference, FIRST's CVSS v4.0 qualitative bands are Low 0.1 to 3.9, Medium 4.0 to 6.9, High 7.0 to 8.9 and Critical 9.0 to 10.0. Priority adds your context: - Exposure: is the endpoint public or internal? - Data: does it touch customer data, money or credentials? - Effort to fix: a one-line fix can go first even at Medium. - Chains: two Medium findings may combine into a High impact. The report may say so. - Deadlines: a customer review or audit date can raise priority. Write your priority next to the tester's severity. Keep both. Run the reproduction steps in a test environment with a test account. Reproduction shows that your team understands the flaw and gives you a before-and-after check for the fix. Follow these rules: 1. Run scripts only with authorization and only with test-owned identifiers. 2. Prefer a staging copy. Reproduction scripts are read-only where possible, but check each one. 3. Save the output before the fix and after the fix. 4. If you cannot reproduce a finding, do not close it. Tell the tester and share your environment details, because the tester may have used a state you do not have. ## What do the last sections mean, and what should you check before you share the report? The controls that held section lists protections that the tester tried to break and could not. Keep these controls working when you change code: a fix for one finding must not remove a control that held. Turn them into regression tests. The remediation plan puts the fixes in order. Use it as the base of your backlog, then add owners and dates. Our guide to [after the pentest: remediation and retest](/guides/after-the-pentest-remediation-and-retest/) gives a full workflow. When fixes are done, ask for a retest. See [retesting](/retesting/). The limitations section states what the tester could not check or could not confirm. An open question is neither a confirmed issue nor a clean result, so decide whether to scope it into the next test. The evidence index lists saved requests and responses with timestamps. Key evidence can carry a SHA-256 manifest, so you can check that files were not changed. Keep these files with the report, because auditors and customers may ask for them. Check that it does not contain secrets, live tokens or customer data, and check your contract for confidentiality terms. For customers, consider a shorter summary or an [attestation letter](/guides/pentest-attestation-letter/). The PCI SSC guidance says its report outlines "are only suggested outlines and do not define specific reporting requirements for PCI DSS penetration tests". Ask your auditor what they need. ## Questions ### How long should a pentest report be? Length depends on scope and the number of findings. Do not judge quality by page count. Judge it by whether each finding has evidence, reproduction steps and a clear fix, and whether the scope and limits are stated. ### What if I disagree with a severity rating? Ask the tester to explain the rating. Severity scales have written definitions, so you can compare the finding to the definition. If your context changes the risk, record your own priority. Do not edit the tester's severity. ### Do I need CVSS scores in the report? Only if a customer, auditor or program asks for them. Excession Labs does not add CVSS vectors by default. Tell your provider during scoping if you need them. ### Can I share the full report with a customer? A full report holds sensitive detail about open weaknesses. You can share a shorter summary or an attestation letter instead, or share the report under an NDA after the fixes. Decide before you share, and check your contract. ## Sources - [FIRST: Common Vulnerability Scoring System v4.0 specification (qualitative severity ratings)](https://www.first.org/cvss/v4.0/specification-document) - [PCI SSC Information Supplement: Penetration Testing Guidance v1.1 (report outlines, section 5.2)](https://listings.pcisecuritystandards.org/documents/Penetration-Testing-Guidance-v1_1.pdf) - [NIST SP 800-115: Technical Guide to Information Security Testing and Assessment](https://csrc.nist.gov/pubs/sp/800/115/final) --- # IDOR testing: how pentesters find broken object-level authorization Source: https://penetrationtestservices.com/guides/idor-testing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 IDOR testing step by step: prove it with two accounts and a read-back, use a 7-case test table and demo requests, and fix BOLA with a server-side check. > **Short answer:** IDOR (insecure direct object reference) means a user can reach another user's record by changing an identifier in a request. OWASP's API list calls it BOLA, API1:2023. Testers prove it with two test accounts: account A requests account B's object, and a read-back confirms the effect. The fix is an ownership check on every object access, on the server. IDOR stands for insecure direct object reference. It means a user can reach another user's record by changing an identifier in a request. OWASP's API list calls the same class of flaw Broken Object Level Authorization (BOLA), API1:2023. It sits under A01:2025 Broken Access Control in the web Top 10. OWASP's WSTG defines IDOR this way: it "occurs when an application provides direct access to objects based on user-supplied input." ## What does an IDOR look like, and how do testers prove it? An IDOR needs two things: an object identifier the client controls, and a server that does not check who owns the object. The identifier can be a number, a UUID, a slug or a filename. It can sit in the path, the query string, the JSON body, a header or a cookie. > **Illustrative IDOR: Harbor Notes:** This is a demonstration. No real customer system was tested. Harbor Notes is a fictional app and example.com is a reserved example domain. > > Test user A owns note 1042. Test user B owns note 2077. A sends this request with A's own token: > > ```http > GET /api/v1/notes/2077 HTTP/1.1 > Host: api.harbor-notes.example.com > Authorization: Bearer > ``` > > A vulnerable server answers: > > ```http > HTTP/1.1 200 OK > Content-Type: application/json > > {"id": 2077, "owner_id": 88, "title": "B private note", "body": "..."} > ``` > > The `owner_id` (88) is B's account, not A's. A safe server answers `403 Forbidden` or `404 Not Found`. Testers prove an IDOR with two accounts, a controlled request, controls, and a read-back. A single odd response is not proof. Our order is: 1. Create two test-owned accounts in the same role (A and B). Create known data in each, marked as test data. Get written authorization for the scope first. 2. Record the baseline. Fetch B's object with B's token. Save the response. 3. Replay with A's token. Send the same request with A's token. Save the response. 4. Compare. If A receives B's data, the read is broken. 5. Test writes safely. For a write, change only B's test record (for example, a title). Then read it back through B's session. A bare `200 OK` is not proof: the read-back is. 6. Run controls. Send a nonexistent ID, A's own ID, B's ID with no token, and B's ID with a token that has a bad signature. Controls that held go in the report. > Evidence we keep for an IDOR finding: both requests and responses with timestamps, the object's owner field, the read-back from the owning account, and the control results. Evidence labels are "Observed" for reproduced results. ## Where should you look for object references, and which test cases matter most? Check every place the client names an object, including routes that nobody tests by hand. | Location | Example | Why it gets missed | |---|---|---| | URL path | `/notes/2077` | Obvious, so often checked | | Query string | `?invoice_id=551` | Added later by a different developer | | JSON body | `{"project_id": 12}` | Hidden from the browser address bar | | Header or cookie | `X-Account-Id: 88` | Trusted as if the server set it | | Batch and bulk routes | `POST /notes/bulk-get` | One ID in the list skips the check | | Export and report jobs | `GET /exports/9001/download` | Check applies at creation, not download | | File and image URLs | `/files/7f3a.../report.pdf` | Served by a storage layer without checks | | GraphQL arguments | `note(id: 2077)` | Authorization set on the query, not the resolver | Run each case for read and write, and for any action verb (delete, share, publish, archive). The table shows the read and write cases. | Case | Request | Expected | Finding if | |---|---|---|---| | Horizontal read | A reads B's object | 403 or 404 | 200 with B's data | | Horizontal write | A edits B's object | 403 or 404 | B's read-back shows the change | | Sibling route | A uses `/export` for B's object | 403 or 404 | File contains B's data | | Method swap | A uses PUT where only GET was checked | 403 or 405 | Change persists | | Parameter in body | A sends B's `project_id` in a POST | 403 or 404 | New object attached to B's project | | Nonexistent ID | A requests ID 999999999 | 404 | n/a (control) | | Anonymous | No token, B's ID | 401 | 200 | For multi-tenant products, repeat the matrix between two tenants. The [multi-tenant isolation testing guide](/guides/multi-tenant-isolation-testing/) covers that. ## What mistakes hide an IDOR, and how do teams fix it? These traps hide an IDOR. - Trusting the status code. A `200` with an empty body may be a safe empty result. A `404` may be a real object that is hidden only by the front end. Compare bodies and read back. - Testing only the ID in the path. The same object may be reachable through a body field. - Using one account. You cannot prove cross-account access from one session. - Using real customer data. Use test-owned records only. - Confusing IDOR with enumeration. A response that differs between "exists" and "does not exist" leaks information, but it is a lesser finding than reading the data. Fix IDOR with a server-side ownership check on every object access. Do not rely on hidden IDs, front-end checks or route names. Put the check in one shared place, such as a policy function or a data-access layer, and pass the caller's identity into every query. In pseudo-code: ```text function getNote(caller, noteId): note = db.find(Note, id = noteId) if note is null: return NotFound if not policy.canRead(caller, note): # owner, shared-with, or role rule return NotFound # same answer as missing, no leak return note ``` Better, scope the query so a foreign row never loads: ```text SELECT * FROM notes WHERE id = :noteId AND tenant_id = :callerTenantId AND owner_id = :callerId; ``` Apply the same check to list, search, export, batch and file routes, and add an automated test that runs the two-account matrix on every new route. To avoid leaking which IDs exist, return the same status for "missing" and "not yours". ## What should you do after a fix, and what does a finding look like? Ask for a retest. The retest repeats the original request pair with the same accounts, checks the controls again, and checks the sibling routes, because a fix on one route can miss another. If your product serves several customers from one database, test tenant boundaries next. The [SaaS penetration testing](/saas-penetration-testing/) page and the [API penetration testing](/api-penetration-testing/) page describe those tests. For the wider list of API risks, see the [OWASP API Security Top 10 guide](/guides/owasp-api-security-top-10/). A finding gives the developer what they need to reproduce and fix the issue. > **Illustrative finding write-up:** This is a demonstration. No real customer system was tested. Names and hosts are fictional. > > | Field | Detail | > |---|---| > | Title | Invoice download works for any signed-in user | > | Label | Observed | > | Route | `GET /api/v1/invoices/{id}/pdf` | > | Accounts | test user A (tenant 1), test user B (tenant 1), created for this test | > | Result | A downloads B's invoice PDF. The file contains the marker `B-invoice-001`, which shows whose data came back. | > | Read-back | B's session shows the same invoice, unchanged, so the test caused no side effect. | > | Controls that held | The list route returns only the caller's invoices. A request for a missing ID returns 404. A request with no token returns 401. | > | Fix | Check invoice ownership in the download handler as well as in the list handler. | ## How do you keep IDOR out for good? Fixing one route leaves the next one open, so build the check into the way routes are written. - One access layer. Route every object fetch through a function that takes the caller. - Default deny. A new route with no policy should refuse access. - Tests per route. Add a two-account test to the build for every new object type. - Review IDs in code review. Any handler that reads an ID from the request needs an ownership line. - Logging. Log denied object accesses, and alert on a burst of denials by one user. ## Questions ### Are random UUIDs enough to prevent IDOR? No. A random ID makes guessing hard, but IDs leak through links, logs, emails, shared pages and other API responses. The server must still check ownership. Treat unpredictable IDs as defense in depth, not as access control. ### Can a scanner find IDOR? Rarely. A scanner sees one session and does not know which object belongs to whom. Finding IDOR needs two accounts and knowledge of what each user may see. ### Does a 403 on the first try mean the endpoint is safe? Not yet. Testers also try other methods, other routes for the same object, batch and export endpoints, and IDs in the body or headers. One route can check ownership while a sibling route does not. ### What is the difference between IDOR and BFLA? IDOR/BOLA is about which object you can reach. BFLA (Broken Function Level Authorization, API5:2023) is about which function you can call. An admin-only delete route used by a member is BFLA. A member reading another member's note is BOLA. ## Sources - [OWASP API Security Top 10 2023: API1 Broken Object Level Authorization](https://api-security.owasp.org/editions/2023/en/0xa1-broken-object-level-authorization/) - [OWASP WSTG-ATHZ-04: Testing for Insecure Direct Object References](https://wstg.owasp.org/v4.2/4-Web_Application_Security_Testing/05-Authorization_Testing/04-Testing_for_Insecure_Direct_Object_References/) - [OWASP Top 10:2025 Introduction](https://top10.owasp.org/2025/0x00_2025-Introduction) --- # Penetration testing guides Source: https://penetrationtestservices.com/guides/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Practical guides on planning a pentest, web and API testing, SaaS isolation, compliance evidence and reading a pentest report, from Excession Labs. Written for founders, engineering leads and security teams who need to plan a test, prepare for one, or act on a report. Each guide has example requests labeled as demonstrations, checklists or requirement text with sources. - [After the pentest: remediation, retest and close-out](https://penetrationtestservices.com/guides/after-the-pentest-remediation-and-retest/): What to do after a penetration test: triage findings, assign fixes, verify them, request a retest and close out with evidence. Includes a tracker template. - [Authentication testing checklist: login, reset, MFA, sessions and OAuth](https://penetrationtestservices.com/guides/authentication-testing-checklist/): An authentication testing checklist for web apps and APIs: login, password reset, MFA, sessions, OAuth and account enumeration, with proof and fixes. - [Black box vs grey box vs white box penetration testing](https://penetrationtestservices.com/guides/black-box-grey-box-white-box-testing/): How black box, grey box and white box penetration tests differ, what each costs you in time and depth, and a decision table to choose one. - [Bubble app security: privacy rules, Data API and workflow API checklist](https://penetrationtestservices.com/guides/bubble-app-security/): How to check your own Bubble app: privacy rules on every data type, Data API and Workflow API settings, search exposure and client-side data, with a test checklist. - [Business logic vulnerabilities: examples, testing and fixes](https://penetrationtestservices.com/guides/business-logic-vulnerabilities/): What business logic vulnerabilities are, why scanners miss them, examples in pricing, workflows and limits, how testers prove them and how to fix them. - [CSRF testing: how pentesters check tokens, SameSite and login CSRF](https://penetrationtestservices.com/guides/csrf-testing/): How to test CSRF protection: an 8-step method, a test-case table for tokens, Origin and SameSite, JSON endpoints, login CSRF, and proof in a real browser. - [Does SOC 2 require penetration testing?](https://penetrationtestservices.com/guides/does-soc-2-require-penetration-testing/): Does SOC 2 require penetration testing? No. See the AICPA CC4.1 wording, why auditors still ask for a pentest report, and five questions to settle first. - [Firebase security rules testing: Firestore, Storage and the emulator](https://penetrationtestservices.com/guides/firebase-security-rules-testing/): Firebase security rules testing for Firestore, Storage and Realtime Database: working unit tests for the Local Emulator Suite, safe example rules, and a checklist. - [HIPAA penetration testing: what the Security Rule says today and what HHS proposed](https://penetrationtestservices.com/guides/hipaa-penetration-testing/): HIPAA penetration testing requirements as of November 2026: the Security Rule in force does not name a pentest, and the January 2025 HHS proposal is not final. - [How often should you pentest? Standards, triggers and a schedule](https://penetrationtestservices.com/guides/how-often-should-you-pentest/): How often should you pentest? The PCI DSS 11.4 wording, SOC 2 and ISO 27001 position, ten changes that should trigger a test, and a schedule table by profile. - [How to prepare for a penetration test: a checklist](https://penetrationtestservices.com/guides/how-to-prepare-for-a-penetration-test/): How to prepare for a penetration test: write the scope, create test accounts, load test data, pick an environment, name contacts and set a freeze window. - [How to read a penetration testing report, section by section](https://penetrationtestservices.com/guides/how-to-read-a-penetration-testing-report/): How to read a penetration testing report section by section: the 13 parts, an annotated sample finding, severity versus priority, and how to plan fixes. - [IDOR testing: how pentesters find broken object-level authorization](https://penetrationtestservices.com/guides/idor-testing/): IDOR testing step by step: prove it with two accounts and a read-back, use a 7-case test table and demo requests, and fix BOLA with a server-side check. - [ISO 27001 penetration testing: what the standard asks and auditors check](https://penetrationtestservices.com/guides/iso-27001-penetration-testing/): ISO 27001 penetration testing requirements: the standard does not name a pentest. See Annex A 8.8, 8.29 and 8.34, an evidence pack, and what to ask your auditor. - [Multi-tenant isolation testing: how to check that tenants cannot see each other](https://penetrationtestservices.com/guides/multi-tenant-isolation-testing/): Multi-tenant security testing for SaaS: a two-tenant matrix of 12 areas (IDs, search, exports, caches, files), a sample leaking request and row-level security fixes. - [OWASP API Security Top 10 (2023): the list and how to test each risk](https://penetrationtestservices.com/guides/owasp-api-security-top-10/): The OWASP API Security Top 10 2023 list with exact IDs from API1 to API10, plus test ideas, sample requests, a test-case table and the usual fix for each risk. - [OWASP Top 10 2025: the list and what pentesters check for each item](https://penetrationtestservices.com/guides/owasp-top-10-2025/): OWASP Top 10:2025 with exact IDs, a one-line meaning for each category, a 2021 to 2025 mapping table and the checks a penetration tester runs. - [PCI DSS penetration testing requirements: Requirement 11.4 explained](https://penetrationtestservices.com/guides/pci-dss-penetration-testing-requirements/): PCI DSS penetration testing requirements 11.4.1 to 11.4.7 (v4.0.1) in one table: test frequency, who may test, segmentation, and questions for your QSA. - [Penetration testing rules of engagement: what to agree in writing](https://penetrationtestservices.com/guides/penetration-testing-rules-of-engagement/): Pentest rules of engagement: what to agree in writing (authorization, scope, test windows, off-limits actions, stop conditions, data) plus a template outline. - [Pentest attestation letter: what it contains and what it cannot prove](https://penetrationtestservices.com/guides/pentest-attestation-letter/): Pentest attestation letter: what it contains, a letter outline you can adapt, how it differs from a full report, and what no letter can prove to a buyer. - [Rate limiting and credential stuffing: how to test safely](https://penetrationtestservices.com/guides/rate-limiting-and-credential-stuffing/): Credential stuffing testing and rate limit tests done safely: OWASP OAT-008, a bounded test design, test cases for login, reset and MFA, and how to fix gaps. - [Red team vs penetration testing: what is the difference?](https://penetrationtestservices.com/guides/red-team-vs-penetration-testing/): Red team vs pentest: how the two differ in goal, scope, stealth and who knows, with an 11-row comparison table and a decision table for choosing one. - [Security questionnaire pentest questions: what buyers ask and how to answer](https://penetrationtestservices.com/guides/security-questionnaire-pentest-questions/): Pentest questions in security questionnaires (SIG, CAIQ, custom): 14 common questions with honest answer patterns, the evidence to attach, and what to avoid. - [Supabase security audit: RLS, API keys, storage and RPC checklist](https://penetrationtestservices.com/guides/supabase-security-testing/): Supabase security audit for your own project: an 8-step checklist for RLS, publishable and secret keys, views, storage and RPC, with example policies. - [What is penetration testing? A plain guide for software teams](https://penetrationtestservices.com/guides/what-is-penetration-testing/): What is penetration testing? What a pentest produces, the main types, how it differs from a vulnerability scan, five phases and one worked example finding. --- # ISO 27001 penetration testing: what the standard asks and auditors check Source: https://penetrationtestservices.com/guides/iso-27001-penetration-testing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 ISO 27001 penetration testing requirements: the standard does not name a pentest. See Annex A 8.8, 8.29 and 8.34, an evidence pack, and what to ask your auditor. > **Short answer:** ISO/IEC 27001:2022 does not require a penetration test by name. Pentest evidence can support Annex A 8.8 Management of technical vulnerabilities and 8.29 Security testing in development and acceptance. Control 8.34 covers protecting systems during audit testing. Your scope, risk assessment and Statement of Applicability decide what evidence you need, so confirm it with your certification body. ISO/IEC 27001:2022 does not require a penetration test by name. It requires an information security management system (ISMS) that manages risk. Annex A lists controls. Ask your certification body which evidence it accepts for 8.8 and 8.29. > **Note:** ISO text is paid and copyrighted. The control names below follow ISO/IEC 27001:2022 Annex A. We describe the intent in our own words, so read your copy of the standard and ISO/IEC 27002:2022 for the full text. A pentest report does not certify anything. ## Which Annex A controls relate to penetration testing, and does ISO 27001 name it? Three controls in the Technological group (theme 8) relate to pentests. A fourth control covers independent review. | Annex A control | Name (2022) | What it is about | How pentest evidence may fit | |---|---|---|---| | 8.8 | Management of technical vulnerabilities | Get information about technical vulnerabilities of systems in use, assess your exposure, and take action. | A pentest finds vulnerabilities that scans miss, and shows you acted on them. | | 8.29 | Security testing in development and acceptance | Define and implement security testing processes in the development life cycle. | A pentest before launch or after a major release is one form of security testing. | | 8.34 | Protection of information systems during audit testing | Plan and agree audit tests on operating systems so they do not disrupt operations. | Applies to the rules of engagement for the test itself. | | 5.35 | Independent review of information security | Review the approach to managing information security at planned intervals and after significant change. | A third-party test can be an input to this review. | Control 8.29 merges two 2013 controls, A.14.2.8 (system security testing) and A.14.2.9 (system acceptance testing). It is not one of the 11 controls new in 2022. Control 8.8 merges older vulnerability controls. No. The Annex A control text does not name pentests. ISO/IEC 27002:2022, the guidance document, mentions penetration testing as one option in its advice for these controls. Guidance is not a requirement, and a certification audit tests against ISO/IEC 27001, not against each guidance sentence. The accurate statement is: "ISO/IEC 27001:2022 does not name penetration testing as mandatory. Controls 8.8 and 8.29 are where auditors usually accept it as evidence." ## What do auditors look for, and how do you scope a pentest for 8.8 and 8.29? Certification auditors check that your ISMS does what it says. For testing, they usually look for four items: 1. A documented process. A vulnerability management or secure development procedure that says when tests happen and who does them. 2. A link to risk. Your risk assessment and Statement of Applicability say why you chose the test type and frequency. 3. Evidence it happened. A dated report with scope, method and findings. 4. Evidence you acted. Tickets, fixes and retest results, with owners and dates. [After the pentest: remediation and retest](/guides/after-the-pentest-remediation-and-retest/) covers this loop. An auditor may flag open high findings that have no plan. Match the scope to your ISMS scope. If your certificate covers a SaaS product, the test should cover the product's production or production-like environment. If it covers only an office network, a web app test may be out of scope for the audit. The audit scope statement tells you. Scope checklist: - ISMS scope statement and the systems it names. - Environments tested (production, staging) and why. - Applications and APIs in scope, with the roles and test accounts. - Out-of-scope items and the reason. - Test window, and a contact for stop conditions. - Who does the test and how you verify their competence. Control 8.34 matters here. Agree the test in writing, limit the access you give, and keep audit trails. Our guide on [penetration testing rules of engagement](/guides/penetration-testing-rules-of-engagement/) gives an outline. Control 8.34 reminds you that testing live systems carries risk. Agree limits in writing before the test: - No denial-of-service tests on production. - Test accounts and test records only for any change or delete proof. - A named contact who can stop the test. - A cap on bulk actions such as password attempts and email sends. - A rule for what the tester does if real personal data appears. ## How often should you test, and how does this relate to other frameworks? The standard sets no interval. Your risk assessment sets it. A common pattern is one test a year plus tests after major changes, because that is easy to defend. Control 8.29 points to testing when you build or change systems. See [how often should you pentest](/guides/how-often-should-you-pentest/). Write the interval and its reason into your procedure, then follow it. An auditor checks your own rule, not a rule from the standard. A vulnerability scan is automated and broad. A pentest is manual and deep. ISO/IEC 27002 guidance discusses both for managing technical vulnerabilities. A common setup uses both: scans on a schedule for 8.8, and a pentest for depth and for 8.29 before releases. NIST SP 800-115 gives a neutral definition of each technique if you need one for your procedure. Teams often hold more than one framework. One test can feed several, if the scope fits each. ISO/IEC 27001 gives the most freedom, because the risk assessment sets the test. PCI DSS sets a fixed interval and tester rules for the cardholder data environment. SOC 2 leaves the evidence choice to the auditor. Plan one scope that meets the strictest reader, then ask each auditor to confirm it. A report written for one framework may not fit the others, because dates, scope and tester independence may differ. ## What evidence should you keep, and what mistakes weaken it? ISO/IEC 27001 clause 7.2 asks you to confirm that people doing work that affects ISMS performance are competent. Keep simple proof for the tester: relevant experience, the method used, and who reviewed the findings. No ISO text asks for a specific certificate. An evidence pack for one audit cycle can be small. This example fits a SaaS company whose certificate scope is its product and the team that runs it. | Item | Example content | Supports | |---|---|---| | Procedure | "Security testing standard": test before major releases and once a year | 8.29 | | Risk entry | Risk of access control flaws in the customer API, with the test as a treatment | 8.8, 8.29 | | Scope letter | Written authorization and scope for the test, signed by the system owner | 8.34 | | Report | Dated pentest report with findings and method | 8.8, 8.29 | | Tracker | Tickets for each finding with owner and dates | 8.8 | | Retest note | Result of the retest, with the date | 8.8 | | Review record | Management review minute that discusses the results | 5.35 | Keep the pack in one folder so you can show the auditor the full path from risk to fix. Mistakes that weaken pentest evidence for ISO 27001: - A pentest done, but no procedure that says why and when. - A report with no link to the risk register. - Findings closed in a tracker with no evidence of the fix. - A scope that does not match the ISMS scope. - A control marked "not applicable" in the Statement of Applicability, while a test report exists for the same area. ## Questions to take to your certification body - Which Annex A controls in our Statement of Applicability need testing evidence? - Do you accept one annual test plus tests after major releases? - Do you want the full report or a summary? - How do you view testers from our own company? - Which open findings are acceptable at the audit date? - Do you expect the retest inside the same audit cycle? For a deeper view of the report form, see [how to read a penetration testing report](/guides/how-to-read-a-penetration-testing-report/). For the SOC 2 equivalent, see [does SOC 2 require penetration testing](/guides/does-soc-2-require-penetration-testing/). ## Questions ### Is a pentest mandatory for ISO 27001 certification? Not by name. The standard sets controls and a risk-based process. Your risk assessment and Statement of Applicability decide which tests you need. Ask your certification body whether it expects security testing evidence, and in which form. ### Is Annex A 8.29 a new control in the 2022 edition? No. 8.29 merges two 2013 controls, A.14.2.8 (system security testing) and A.14.2.9 (system acceptance testing). Some vendor pages call it new. It is not on the list of the 11 new controls. ### Can a vulnerability scan satisfy control 8.8? Control 8.8 is about managing technical vulnerabilities. Scans are a normal way to find them. Guidance in ISO/IEC 27002 mentions both pentests and vulnerability assessments. Whether scans alone are enough for you is your auditor's decision. ### Must the tester be certified or from an accredited firm? ISO/IEC 27001 names no tester certificate. Auditors may ask for evidence that the tester was competent, as clause 7.2 asks for competence in general. Keep proof of tester experience with the report. ## Sources - [ISO: ISO/IEC 27001:2022 Information security management systems](https://www.iso.org/standard/27001) - [ISO: ISO/IEC 27002:2022 Information security controls](https://www.iso.org/standard/75652.html) - [ISMS.online: ISO 27001 Annex A 8.29 Security testing in development and acceptance](https://www.isms.online/iso-27001/annex-a-2022/8-29-security-testing-in-development-acceptance-2022/) - [ISMS.online: ISO 27001 Annex A 8.34 Protection of information systems during audit testing](https://www.isms.online/iso-27001/annex-a-2022/8-34-protection-information-systems-during-audit-testing-2022/) - [NIST SP 800-115: Technical Guide to Information Security Testing and Assessment](https://csrc.nist.gov/pubs/sp/800/115/final) --- # Multi-tenant isolation testing: how to check that tenants cannot see each other Source: https://penetrationtestservices.com/guides/multi-tenant-isolation-testing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Multi-tenant security testing for SaaS: a two-tenant matrix of 12 areas (IDs, search, exports, caches, files), a sample leaking request and row-level security fixes. > **Short answer:** Multi-tenant isolation testing checks that one customer (tenant) of a SaaS product cannot read or change another tenant's data. Testers create two tenants with test data, run the same actions from each, and look for any cross-tenant read or write. They cover IDs, search, exports, caches, files, background jobs and roles. Fixes enforce the tenant at the data layer. Multi-tenant isolation testing checks that one customer of a SaaS product (a tenant) cannot read or change another tenant's data. Testers create two tenants with known test data, run every feature from each, and look for any cross-tenant effect. A tenant leak is a serious SaaS finding, because it exposes one customer's data to another. It is a form of broken access control, A01:2025 in the OWASP Top 10, and a tenant-level version of [IDOR](/guides/idor-testing/). ## How do testers set up a two-tenant test, and what does the matrix cover? Set up two tenants that are as alike as possible. Use the same plan, the same roles and the same feature flags, so that any difference in results points to a flaw. 1. Create Tenant A and Tenant B. Mark both as test accounts. 2. In each tenant, create a member and an admin. 3. In every feature, create a known record. Give each record a distinct, readable marker, such as `A-note-001` and `B-note-001`. 4. Record every ID the app returns: users, projects, files, invoices, API keys, webhooks. 5. Get the system owner's written authorization before testing. The markers make a leak easy to prove: if tenant A's response contains `B-note-001`, the leak needs no interpretation. Run each row from tenant A's member, tenant A's admin and an anonymous caller. Target tenant B's data each time. | Area | What testers try | Proof of a leak | |---|---|---| | Object IDs | A requests B's project, invoice or user by ID | B's marker in the response | | Lists and pagination | A lists objects with a changed `tenant_id`, `org` or `account` parameter | B's rows in the list | | Search | A searches for `B-note` | B's records in results | | Exports and reports | A starts an export, then fetches B's export job ID | B's file content | | Files and images | A opens B's file URL | B's file bytes | | Caches | A and B request the same URL in turn | One tenant sees the other's cached body | | Background jobs | A triggers a job with B's resource ID | B's data in the job output | | Webhooks and integrations | A points an integration at B's resource | B's data delivered | | Invitations | A invites an email into B's org by changing the org ID | New member appears in B | | Roles | A member calls an admin route of A, then of B | Admin action succeeds | | Billing and entitlements | A reads or changes B's plan, seats or limits | B's plan shows a change | | Admin and support tools | A reaches staff-only routes | Any response with other tenants' data | ## How does a tenant leak look in a request, and where does it hide? A tenant leak often appears when a request carries a tenant identifier that the server trusts. > **Illustrative cross-tenant read:** This is a demonstration. No real customer system was tested. The app, hosts and data are fictional. > > Tenant A's member calls the project list and swaps the tenant header: > > ```http > GET /api/v1/projects HTTP/1.1 > Host: api.harbor-notes.example.com > Authorization: Bearer > X-Tenant-Id: tenant-b > ``` > > A vulnerable server returns tenant B's rows: > > ```http > HTTP/1.1 200 OK > Content-Type: application/json > > {"items": [{"id": "p-300", "name": "B-project-001", "tenant": "tenant-b"}]} > ``` > > The marker `B-project-001` proves the leak. A safe server ignores the header and returns tenant A's projects, or returns `403`. The tester then runs the controls: tenant A's own ID, a nonexistent tenant ID, B's header with no token, and B's header with a token for a different, valid tenant. Controls that held go in the report. Shared layers outside the main query path hide tenant leaks. App code may filter by tenant on the main table and forget the same filter elsewhere. - Caches. A cache key made from the URL alone (without tenant or user) can serve one tenant's page to another. Test by requesting the same path from both tenants and comparing bodies. - Search indexes. The index may hold all tenants' documents, with the filter applied only in some query types. Test with search, autocomplete and "similar items" features. - File storage. Files may live in one bucket with guessable or leaked keys. Test whether a file URL works without a token, or with another tenant's token. - Background jobs and exports. A job may run with a service identity that has access to all tenants. Test by passing another tenant's resource ID. - Logs and audit views. An activity feed may include other tenants' events. ## How do testers prove cross-tenant writes, and how do teams fix isolation flaws? Writes need the same care as reads. Change only a test-owned record in tenant B, then read it back through B's session. A `200 OK` from the write request is not proof, while the read-back from B shows whether the change took effect. For destructive actions such as delete or role change, use a throwaway record created for the test. State this in the scope. The report should say which test records the testers changed. Enforce the tenant at the data layer, not in each handler. Take the tenant from the verified session or token, never from a header, body field or URL that the client controls. For row-level security in PostgreSQL, a policy filters rows by a setting that the app sets per request from the session (see the [PostgreSQL row security docs](https://www.postgresql.org/docs/current/ddl-rowsecurity.html)). A sketch: ```sql ALTER TABLE projects ENABLE ROW LEVEL SECURITY; ALTER TABLE projects FORCE ROW LEVEL SECURITY; CREATE POLICY tenant_isolation ON projects USING (tenant_id = current_setting('app.tenant_id')::uuid); ``` The app sets `app.tenant_id` from the authenticated session at the start of each request. The app's database role must not own the table or have the `BYPASSRLS` attribute, because table owners skip the policy unless FORCE is set, and BYPASSRLS roles always skip it. For the other layers, include the tenant ID in every cache key, search filter, file path and job payload, and give background workers a per-tenant scope. Add a build test that runs two tenants through each new route. ## What does a tenant isolation finding look like, and what evidence should you keep? A finding names the shared layer that failed and shows the marker that crossed the boundary. > **Illustrative finding write-up:** This is a demonstration. No real customer system was tested. The app and tenants are fictional. > > | Field | Detail | > |---|---| > | Title | Search returns documents from other tenants | > | Label | Observed | > | Route | `GET /api/v1/search?q=B-note` | > | Accounts | member of tenant A, member of tenant B, both created for this test | > | Result | A's search returns tenant B's note `B-note-001`. The main list route returns only tenant A data. | > | Controls that held | Direct fetch of B's note by ID returns 404. Export jobs are scoped to the caller's tenant. | > | Cause (inferred) | The search index holds all tenants and the filter is missing in this query type. This is an inference, so we label it separately from the observed result. | > | Fix | Add the tenant filter inside the search layer, from the session. Add a test with two tenants. | Keep evidence that lets someone else replay the test. - Request and response pairs, with timestamps, for tenant A and tenant B. - The marker that crossed the boundary. - The read-back from the owning tenant for any write. - The control results. - A note of which test records were changed or deleted. Our [methodology](/methodology/) page describes the evidence labels we use. Put inferred causes on their own line, as the sample does. ## What should you do next? Run the matrix on every release that adds a new object type, export or integration. The [SaaS penetration testing](/saas-penetration-testing/) page describes the related service. The IDOR basics are in the [IDOR testing guide](/guides/idor-testing/). For the API risk list, see the [OWASP API Security Top 10 guide](/guides/owasp-api-security-top-10/). Billing and entitlement flaws are a business logic topic: see [business logic vulnerabilities](/guides/business-logic-vulnerabilities/). ## Questions ### How many tenants do testers need? At least two, with the same plan and roles. A third tenant helps for sharing and invitation tests. Each tenant needs a member and an admin account, and known test data in every feature. ### Is a separate database per tenant safer than shared tables? It narrows the blast radius of a missing filter, but it does not remove the risk. Caches, queues, file storage, search indexes and admin tools can still be shared. Test those layers whatever the database design. ### Can we run isolation tests in production? Yes, if the tests use only test-owned tenants and the scope says so in writing. A staging copy with synthetic data is often the better choice. Never test with real customer tenants. ### What is row-level security? Row-level security (RLS) is a database feature that filters rows by a policy for each query. It enforces the tenant check in the database, so a forgotten WHERE clause in app code does not expose rows, unless the role bypasses the policy. ## Sources - [OWASP API Security Top 10 2023: API1 Broken Object Level Authorization](https://api-security.owasp.org/editions/2023/en/0xa1-broken-object-level-authorization/) - [OWASP Top 10:2025 Introduction](https://top10.owasp.org/2025/0x00_2025-Introduction) - [PostgreSQL documentation: Row Security Policies](https://www.postgresql.org/docs/current/ddl-rowsecurity.html) - [OWASP Multi-Tenant Application Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Multi_Tenant_Security_Cheat_Sheet.html) --- # OWASP API Security Top 10 (2023): the list and how to test each risk Source: https://penetrationtestservices.com/guides/owasp-api-security-top-10/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 The OWASP API Security Top 10 2023 list with exact IDs from API1 to API10, plus test ideas, sample requests, a test-case table and the usual fix for each risk. > **Short answer:** The OWASP API Security Top 10 is OWASP's list of the most critical API risks. The current edition is 2023, from API1:2023 Broken Object Level Authorization to API10:2023 Unsafe Consumption of APIs. We list all ten with exact names and give test ideas for each risk, a sample request pair and the usual fixes. The OWASP API Security Top 10 is OWASP's ranked list of API risks. The edition on the OWASP site as of November 2026 is 2023. Its ten items run from API1:2023 to API10:2023. We checked the [2023 list](https://api-security.owasp.org/editions/2023/en/0x11-t10) on 2026-11-09 and did not find a newer final edition. We use the list as the coverage map for [API penetration testing](/api-penetration-testing/). The web counterpart is the [OWASP Top 10:2025](/guides/owasp-top-10-2025/). ## What is the OWASP API Security Top 10 2023 list, and how do testers check each risk? | ID | Name | |---|---| | API1:2023 | Broken Object Level Authorization | | API2:2023 | Broken Authentication | | API3:2023 | Broken Object Property Level Authorization | | API4:2023 | Unrestricted Resource Consumption | | API5:2023 | Broken Function Level Authorization | | API6:2023 | Unrestricted Access to Sensitive Business Flows | | API7:2023 | Server Side Request Forgery | | API8:2023 | Security Misconfiguration | | API9:2023 | Improper Inventory Management | | API10:2023 | Unsafe Consumption of APIs | API3:2023 merges two 2019 items: Excessive Data Exposure and Mass Assignment. The 2019 item "Lack of Resources & Rate Limiting" is now API4:2023 Unrestricted Resource Consumption. ### API1:2023 Broken Object Level Authorization (BOLA) The API lets a caller reach objects that belong to someone else. OWASP says attackers exploit it "by manipulating the ID of an object that is sent within the request." Test ideas: - Send account B's object IDs with account A's token, in paths, query strings, bodies and headers. - Test read, update and delete on every object type. - Check batch, export and search endpoints that return many objects. The [IDOR testing guide](/guides/idor-testing/) gives the full method. ### API2:2023 Broken Authentication Login, token or recovery flows let someone act as another user. Test ideas: - Check token expiry, reuse after logout and signature checks. - Look at password reset and MFA endpoints for weak limits. - Compare error messages for valid and invalid users. The [authentication testing checklist](/guides/authentication-testing-checklist/) has more checks. ### API3:2023 Broken Object Property Level Authorization A caller can read or write a property they should not touch. This includes mass assignment. Test ideas: - Compare the response fields for a user and an admin. Look for fields such as internal notes or other users' emails. - Add extra fields such as `role` or `plan` to an update request, then read the object back. > **Illustrative request pair: mass assignment check:** This is a demonstration. No real customer system was tested. The host and app are fictional. > > ```http > PATCH /api/v1/profile HTTP/1.1 > Host: api.harbor-notes.example.com > Authorization: Bearer > Content-Type: application/json > > {"display_name": "Test A", "role": "admin"} > ``` > > ```http > HTTP/1.1 200 OK > Content-Type: application/json > > {"display_name": "Test A", "role": "member"} > ``` > > The server ignored `role`, so the control held. The tester still reads the profile back in a second request, because a "200 OK" says nothing about the stored value. ### API4:2023 Unrestricted Resource Consumption The API has no sensible limit on request size, rate, cost or count. OWASP says an API is vulnerable if limits such as execution timeouts, memory and file descriptors are "missing or set inappropriately." Test ideas: - Send a bounded burst of requests to a route and watch for HTTP 429. - Ask for a very large page size or a deeply nested query, within an agreed limit. - Check limits on SMS, email and paid third-party calls. Bound the cost and state it in the scope. See [rate limiting and credential stuffing](/guides/rate-limiting-and-credential-stuffing/). ### API5:2023 Broken Function Level Authorization A low-privilege caller can call admin or other-role functions. Test ideas: - Call every admin route with a member token. - Change the HTTP method (GET to PUT or DELETE) on a route that only allowed GET. - Look in client bundles for hidden admin routes. ### API6:2023 Unrestricted Access to Sensitive Business Flows An attacker can automate a flow, such as buying, booking or signing up, in a way that harms the business. Test ideas: - Repeat a coupon, referral or reservation action at a bounded rate, with test data. - Check whether the API tells one user from a script. The [business logic guide](/guides/business-logic-vulnerabilities/) covers this theme. ### API7:2023 Server Side Request Forgery The API fetches a URL that the caller controls. Test ideas: - Find parameters such as `url`, `callback` and `avatar_url`. - Point them at a host you control and check the server request. Do not target internal systems unless the scope allows it. ### API8:2023 Security Misconfiguration Settings leave the API open: verbose errors, open CORS, missing TLS, default credentials. Test ideas: - Review CORS rules, error output and the HTTP methods allowed. - Check that production does not expose debug or documentation endpoints without need. ### API9:2023 Improper Inventory Management Old versions or undocumented endpoints stay live. Test ideas: - Try `/v1/` routes when `/v2/` is current. - Compare the API description with endpoints seen in client code and traffic. - Check that staging hosts are not public. ### API10:2023 Unsafe Consumption of APIs Your API trusts data from third-party APIs too much. Test ideas: - Identify integrations that feed data into your system. - Check whether you validate that data like user input. - Check redirects and timeouts when the third party fails. ## What does a good API test case look like, and how do teams fix these risks? A test case names the endpoint, the caller, the input, the expected result and the evidence. We run controls beside each exploit request: a nonexistent ID, a correct-owner request and an anonymous call. | Test case | Caller | Request | Expected result | Evidence | |---|---|---|---|---| | BOLA read | User A token | `GET /api/v1/notes/{B's note id}` | 403 or 404 | Status, body, timestamp | | BOLA write | User A token | `PUT /api/v1/notes/{B's note id}` | 403 or 404 | Read-back from B's session | | Function access | Member token | `DELETE /api/v1/admin/users/{id}` | 403 | Status and unchanged user | | Property write | User A token | `PATCH` with `role` | Field ignored | Read-back of profile | Put authorization in one shared layer, and check the owner of each object in every handler or in a policy engine. Allow-list the fields each role can read and write. Set rate, size and cost limits per route. Keep an API inventory, retire old versions, and treat third-party responses as untrusted input. > **What we need to start:** Share an OpenAPI file or Postman collection, two accounts per role and the written scope before the test starts. ## What does a sample finding look like, and what do you share before an API test? A finding should let a developer reproduce the issue and verify the fix. > **Illustrative finding: API1:2023 BOLA on note read:** This is a demonstration. No real customer system was tested. The app and host are fictional. > > | Field | Detail | > |---|---| > | Title | Any signed-in user can read another user's note | > | Label | Observed | > | Route | `GET /api/v1/notes/{id}` | > | Accounts | test user A and test user B, both created for this test | > | Steps | (1) Create note 2077 as B. (2) Request it with A's token. (3) Compare to the baseline from B's token. | > | Result | A receives B's note body. The `owner_id` field shows B. | > | Controls that held | A nonexistent ID returns 404. No token returns 401. Update and delete return 403. | > | Impact | Any user can read any note by changing the ID. | > | Fix | Check that the caller owns the note, or has a share, before returning it. | Testers work faster and cover more with clear inputs. Prepare these items. 1. An API description. An OpenAPI file, Postman collection or GraphQL schema. Include internal and mobile-only routes if they are in scope. 2. Accounts. Two per role, plus an admin. Mark them as test accounts. 3. Test data. A known record of each type in each account. 4. Limits. Maximum request rate, forbidden actions (for example no real emails, no card charges) and the test window. 5. A contact. Someone who can pause the test. Without a description, testers must find routes in client code and traffic. That takes longer and may miss routes the client never calls, which is where API9:2023 problems can sit. ## Questions ### How is the API Top 10 different from the web Top 10? The API list targets risks specific to APIs, such as object and property level authorization and resource limits. The web list covers broader categories. Both put access control near the top. Test an API-backed web app against both. ### Do GraphQL APIs fall under the same list? Yes. The risks apply to any API style. GraphQL adds points to test: query depth and cost limits, introspection in production, and authorization on each resolver as well as on the endpoint. ### What do testers need to start an API test? An API description (OpenAPI file or Postman collection), at least two accounts per role, and a scope agreed in writing. Without a description, testers must discover endpoints from client code, which takes longer. ### Is there a newer API Top 10 than 2023? As of November 2026, the OWASP site hosts the 2023 edition as its latest. We did not find a newer final edition. Check the OWASP API Security project page before you cite it in a contract. ## Sources - [OWASP API Security Top 10 2023](https://api-security.owasp.org/editions/2023/en/0x11-t10) - [API1:2023 Broken Object Level Authorization](https://api-security.owasp.org/editions/2023/en/0xa1-broken-object-level-authorization/) - [API3:2023 Broken Object Property Level Authorization](https://api-security.owasp.org/editions/2023/en/0xa3-broken-object-property-level-authorization/) - [API4:2023 Unrestricted Resource Consumption](https://api-security.owasp.org/editions/2023/en/0xa4-unrestricted-resource-consumption/) --- # OWASP Top 10 2025: the list and what pentesters check for each item Source: https://penetrationtestservices.com/guides/owasp-top-10-2025/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 OWASP Top 10:2025 with exact IDs, a one-line meaning for each category, a 2021 to 2025 mapping table and the checks a penetration tester runs. > **Short answer:** The OWASP Top 10 is a ranked awareness list of the most critical web application security risks. As of November 2026, the latest released edition is OWASP Top 10:2025. It has ten categories, from A01 Broken Access Control to A10 Mishandling of Exceptional Conditions. The exact names and the checks a pentester runs for each follow. The OWASP Top 10 is a ranked list of the most critical web application security risks. As of November 2026 the latest released edition is OWASP Top 10:2025. The [OWASP project page](https://owasp.org/www-project-top-ten/) says: "The most current released version is the OWASP Top 10 2025." It lists 2021 and 2017 as earlier editions. We checked this on 2026-11-09. The 2025 list has exact IDs, a one-line meaning and the first checks for each item. Treat the Top 10 as a coverage map for [web application penetration testing](/web-application-penetration-testing/). It gives no test steps. ## What is on the OWASP Top 10:2025 list, and what changed from 2021? OWASP states three changes in its [2025 introduction](https://raw.githubusercontent.com/OWASP/Top10/master/2025/docs/en/0x00_2025-Introduction.md). Two categories are new: A03 Software Supply Chain Failures and A10 Mishandling of Exceptional Conditions. SSRF, which was A10:2021, is now part of A01. A02 Security Misconfiguration moved up from fifth to second place. Cryptographic Failures fell from second to fourth, and Injection fell from third to fifth. The release date of the 2025 edition is not on the OWASP pages we read, so we do not state one. | ID | Name | |---|---| | A01:2025 | Broken Access Control | | A02:2025 | Security Misconfiguration | | A03:2025 | Software Supply Chain Failures | | A04:2025 | Cryptographic Failures | | A05:2025 | Injection | | A06:2025 | Insecure Design | | A07:2025 | Authentication Failures | | A08:2025 | Software or Data Integrity Failures | | A09:2025 | Security Logging & Alerting Failures | | A10:2025 | Mishandling of Exceptional Conditions | The OWASP web page writes "and" in place of "&" in A09. The names above follow the project's own source Markdown. ## What do testers check for each category? Each entry has a one-line meaning and the checks to run first. The checks are our testing priorities, not OWASP's wording. ### A01:2025 Broken Access Control Users can act outside their intended permissions. The 2025 introduction reports A01 in 3.73% of tested applications on average. Testers check: - Object access. Replace IDs in URLs, bodies and headers with IDs from a second account. See [IDOR testing](/guides/idor-testing/). - Function access. Call admin or owner endpoints with a low-role token. - Server-side fetches. Test any feature that loads a URL the user supplies (the SSRF cases now inside A01). ### A02:2025 Security Misconfiguration Settings, defaults or cloud permissions leave the system more open than intended. Testers check: - Debug pages, stack traces and default credentials. - Missing or weak security headers and permissive CORS rules. - Public storage buckets, open admin panels and verbose error output. ### A03:2025 Software Supply Chain Failures Code, packages, build steps or update channels bring in untrusted or vulnerable components. Testers check: - Client-side bundles and dependency manifests for known-vulnerable libraries. - Third-party scripts loaded without integrity checks. - Exposed CI files, source maps and package registry tokens. ### A04:2025 Cryptographic Failures Sensitive data is not protected in transit or at rest, or the cryptography is weak or misused. Testers check: - TLS configuration and cleartext fallbacks. - Password storage, token formats and signing key handling that the app exposes. - Secrets in responses, URLs and client code. ### A05:2025 Injection Untrusted input reaches an interpreter such as SQL, a shell, a template engine or the browser (cross-site scripting). Testers check: - Every input that reaches a query, command or template, with safe payloads and controls. - Reflected and stored output in a real browser. - Second-order paths, where saved input is used later. ### A06:2025 Insecure Design The design lacks a needed control, so no code fix alone removes the risk. Testers check: - Workflow rules, limits and approval steps. See [business logic vulnerabilities](/guides/business-logic-vulnerabilities/). - Missing abuse limits on sensitive actions. - Trust placed in client-side values such as prices and roles. ### A07:2025 Authentication Failures Login, session or recovery logic lets someone act as another user. Testers check: - Password reset, MFA and session handling. Use the [authentication testing checklist](/guides/authentication-testing-checklist/). - Account enumeration and weak lockout rules. - Session cookies after logout and after a password change. ### A08:2025 Software or Data Integrity Failures The app trusts code or data without checking where it came from. Testers check: - Unsigned or unchecked update and plugin channels. - Deserialization of data that the client controls. - Tampering with signed values such as cookies and webhooks. ### A09:2025 Security Logging & Alerting Failures Security events are not logged, or nobody sees the alerts. Testers check: - Whether failed logins, access denials and admin actions create log entries, when the customer gives log access. - Whether log entries carry secrets or full tokens. - Whether alert paths fire during bounded test activity. ### A10:2025 Mishandling of Exceptional Conditions The app fails open or leaks data when something unexpected happens. Testers check: - Malformed, oversized and missing input on each endpoint. - Whether errors skip an authorization or validation step. - Whether error messages expose internals. ## How do you use the list in a real test? Use the Top 10 to check that no category is skipped, then spend time where the risk is. A01, A06 and A07 need the most manual work: they need two accounts and knowledge of the app's rules, and a scanner reports only what it sees in a single session. > **Coverage sketch for a test plan:** This is an illustrative planning table. It is not a finding. > > | Area | Top 10 items | Evidence we want | > |---|---|---| > | Roles and objects | A01, A06 | Request pair from account A and account B, plus a read-back | > | Login and recovery | A07 | Reset link reuse test, session check after logout | > | Inputs and output | A05, A10 | Safe payload, control input, browser proof for XSS | > | Config and dependencies | A02, A03 | Header review, library list with versions | A finding needs evidence of a real effect. For an access control issue, that means two test accounts, a request from the wrong account, and a read-back that shows the data or the change. A bare "200 OK" proves nothing. We also run controls (a nonexistent ID, the right account, an anonymous caller) and report the controls that held. Fix the cause of each issue. Add an authorization check in one shared place. Remove debug settings from production builds. Pin and review dependencies. Make errors deny access by default. Then ask for a retest, so the same evidence checks the fix. For API-specific risks, read the [OWASP API Security Top 10 guide](/guides/owasp-api-security-top-10/). ## How does the 2025 list map to the 2021 list? Many customer questionnaires still cite the 2021 list. Each 2021 item maps to its 2025 home below, based on the changes OWASP states in its introduction. We checked the [2021 list](https://top10.owasp.org/2021) on 2026-11-09. | 2021 item | 2025 home | Note | |---|---|---| | A01 Broken Access Control | A01:2025 | Still first. SSRF now sits inside it | | A02 Cryptographic Failures | A04:2025 | Fell from second to fourth | | A03 Injection | A05:2025 | Fell from third to fifth | | A04 Insecure Design | A06:2025 | Same category, new position | | A05 Security Misconfiguration | A02:2025 | Rose from fifth to second | | A06 Vulnerable and Outdated Components | A03:2025 | Expanded to Software Supply Chain Failures | | A07 Identification and Authentication Failures | A07:2025 | Renamed Authentication Failures | | A08 Software and Data Integrity Failures | A08:2025 | Renamed Software or Data Integrity Failures | | A09 Security Logging and Monitoring Failures | A09:2025 | Renamed Security Logging & Alerting Failures | | A10 Server Side Request Forgery | A01:2025 | Rolled into Broken Access Control | | none | A10:2025 | New: Mishandling of Exceptional Conditions | If a buyer asks for "OWASP Top 10 coverage", state the edition in the scope. The checks overlap, so one test plan can cover both lists. ## What does the Top 10 not tell you? The Top 10 ranks risk categories. It does not say which of your features carry the risk, how to test them, or what counts as a pass. - Ten categories hide hundreds of weakness types. A01 alone groups 40 CWEs, according to the 2025 introduction. - Your own risk order may differ. A banking app and a blog differ on A06 and A09. - For test steps, use the OWASP Web Security Testing Guide. For a requirements list, use OWASP ASVS. - A Top 10 label on a finding says nothing about test quality. Ask the tester for evidence on each finding and for the list of controls that held. In a pentest report, the Top 10 mapping should appear as a label on each finding. ## Questions ### Is the OWASP Top 10 a compliance standard? No. OWASP calls it an awareness document. It does not list test steps or pass-fail rules. Use it to plan coverage, then use the OWASP Web Security Testing Guide or ASVS for detailed checks. If an auditor names the Top 10, ask which edition they mean. ### Where did SSRF go in the 2025 list? OWASP rolled Server-Side Request Forgery (A10:2021) into A01:2025 Broken Access Control. Testers still check it as its own technique: they look for features that fetch a URL the user supplies. ### Does a clean scan mean the Top 10 is covered? No. Scanners find some injection, misconfiguration and outdated-component issues. They rarely prove access control or design flaws, because those need two accounts and knowledge of the app's rules. ### Should we test against the 2021 list or the 2025 list? Use the latest released edition, which is 2025. Many customer questionnaires still cite 2021. A 2025-based test adds supply chain and error handling checks. ## Sources - [OWASP Top 10 project page](https://owasp.org/www-project-top-ten/) - [OWASP Top 10:2025](https://top10.owasp.org/2025) - [OWASP Top 10:2025 Introduction (source Markdown)](https://raw.githubusercontent.com/OWASP/Top10/master/2025/docs/en/0x00_2025-Introduction.md) - [OWASP Top 10:2021](https://top10.owasp.org/2021) - [OWASP Web Security Testing Guide](https://wstg.owasp.org/) --- # PCI DSS penetration testing requirements: Requirement 11.4 explained Source: https://penetrationtestservices.com/guides/pci-dss-penetration-testing-requirements/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 PCI DSS penetration testing requirements 11.4.1 to 11.4.7 (v4.0.1) in one table: test frequency, who may test, segmentation, and questions for your QSA. > **Short answer:** PCI DSS v4.0.1 Requirement 11.4 covers penetration testing. It asks for a defined methodology (11.4.1), internal and external tests at least every 12 months and after significant change (11.4.2, 11.4.3), fixes and retests (11.4.4), and segmentation tests (11.4.5, 11.4.6). Testers must be a qualified internal resource or qualified external third party with organizational independence. PCI DSS Requirement 11.4 covers penetration testing. In PCI DSS v4.0.1 it has seven sub-requirements, 11.4.1 to 11.4.7. The frequency and tester rules for each follow, then the questions to take to your assessor. > **Note:** This page paraphrases the standard. The standard document is behind a click-through download at PCI SSC. Check exact wording in your own copy of v4.0.1 and ask your QSA or acquirer which requirements apply to you. A pentest report does not make an organization compliant. ## Which version applies, and what do Requirements 11.4.1 to 11.4.7 say? PCI DSS v4.0.1 is the only active version. PCI SSC published it on 11 June 2024 as a "limited revision" with no added or deleted requirements, and retired v4.0 on 31 December 2024. Requirements that v4.0 marked as best practice only became mandatory on 31 March 2025. PCI SSC still called v4.0.1 "the currently published" version on 3 June 2026, when it opened a request for comments on the next iteration. No new version date was given. Old pages and articles use v3.2.1 numbering, where pentests sat under 11.3. In v4.0 and later, the number is 11.4. The table paraphrases each sub-requirement and gives the frequencies the standard sets. We confirmed 11.4.1, 11.4.4 and 11.4.7 against the PCI SSC Summary of Changes. Confirm the rest in the standard. | Req. | What it asks for | Frequency or trigger | Who | |---|---|---|---| | 11.4.1 | A defined, documented, implemented pentest methodology. It covers industry-accepted approaches, the whole cardholder data environment (CDE) perimeter and critical systems, tests from inside and outside the network, application-layer and network-layer tests, segmentation and scope-reduction checks, review of threats from the past 12 months, a documented approach to risk from exploitable findings, and retention of results. | Methodology exists. Pentest results and remediation results kept at least 12 months. | Entity | | 11.4.2 | Internal penetration test, performed per the methodology. | At least once every 12 months, and after any significant infrastructure or application upgrade or change. | A qualified internal resource or qualified external third party. Organizational independence of the tester exists. | | 11.4.3 | External penetration test, performed per the methodology. | Same as 11.4.2. | Same as 11.4.2. | | 11.4.4 | Exploitable vulnerabilities and security weaknesses found are corrected as the entity's risk assessment sets, and the testing is repeated to verify the corrections. | After each test that finds issues. | Entity, with a retest. | | 11.4.5 | If segmentation isolates the CDE from other networks, segmentation controls are tested. | At least once every 12 months, and after any change to segmentation controls or methods. | Qualified, independent tester, as in 11.4.2. | | 11.4.6 | Service providers only: the same segmentation testing, more often. | At least once every six months, and after any change. | Qualified, independent tester. | | 11.4.7 | Multi-tenant service providers support their customers' requests for external penetration testing (under 11.4.3 and 11.4.4). | Ongoing. | Service provider. | Some blog pages swap 11.4.2 and 11.4.3, place the six-month interval under 11.4.5, or say 11.4.1 requires independence. Check the standard. ## Who may perform the test, and what counts as a significant change? The tester must be a "qualified internal resource or qualified external third party", with organizational independence. Independence means the tester is not part of the team that manages the systems under test. PCI SSC's Penetration Testing Guidance v1.1 (September 2017) adds: - "The penetration tester must be organizationally separate from the management of the target systems." - A third party doing your PCI DSS assessment cannot also do the pentest if it installed, maintained or supported the target systems. - Certifications such as OSCP, CEH, GIAC and CREST are "not required". PCI SSC does not validate or endorse them. - Qualifications "cannot be met by certifications alone". Check actual engagement experience. The standard asks for a test after any "significant" upgrade or change but does not list the cases. Your methodology and your assessor define it. Common triggers are a new payment page, a new authentication flow, a new public API, a change to network segmentation and a move to a new hosting setup. Write your definition into the methodology under 11.4.1, then apply it. For web and API teams, the trigger list in [how often should you pentest](/guides/how-often-should-you-pentest/) is a starting point. ## How does segmentation testing work, and what should the report contain? Segmentation testing tries to cross the boundary between the CDE and the rest of the network. If your assessor reduces PCI scope because of segmentation, the tests must show the boundary holds. The test covers every segmentation control, not a sample. Service providers repeat it at least every six months (11.4.6). Segmentation tests are network-level work, and a web application pentest has a different scope. Agree the split in the written scope; the [penetration testing rules of engagement](/guides/penetration-testing-rules-of-engagement/) guide lists the scope items. PCI DSS sets no report format. PCI SSC guidance says its report outlines "are only suggested outlines and do not define specific reporting requirements for PCI DSS penetration tests." Your QSA will want evidence that the test followed your methodology, covered the agreed scope, and had qualified, independent testers. They will also want proof that you fixed findings and retested (11.4.4). A useful report has: - Dates, scope and the CDE boundary tested. - Method and how it maps to your 11.4.1 methodology. - Tester name, qualifications and independence statement. - Findings with severity, evidence and fix advice. - Retest dates and results. To read such a report, see [how to read a penetration testing report](/guides/how-to-read-a-penetration-testing-report/). ## Where does a web application pentest fit, and what is a practical test plan? A web or API pentest can support the application-layer part of 11.4.2 and 11.4.3. The methodology under 11.4.1 asks for application-layer tests that find, at minimum, the vulnerability types in Requirement 6.2.4. Your QSA decides whether a given web test satisfies your scope. Our [web application penetration testing](/web-application-penetration-testing/) page lists what we test. We make no claim that any assessor accepts it. Treat this short plan for a web or API team as a draft for your QSA to review. 1. Write the methodology (11.4.1). One page that names your approach, scope rules, tester qualifications, retention and risk rating method. 2. Set the schedule (11.4.2, 11.4.3). Once every 12 months, plus a trigger list for significant change. 3. Pick the tester. Document qualifications and independence. Keep this with the report. 4. Run the test and fix. Track each finding to closure with the dates (11.4.4). 5. Retest. Keep the retest result with the original report. Keep both for at least 12 months (11.4.1). 6. Test segmentation if you use it (11.4.5, 11.4.6). Include the control list and results. 7. Service providers: support customers (11.4.7). Have a process to answer customer test requests. ## What mistakes weaken an 11.4 program, and what should you ask your QSA? Common mistakes: - A test that covers the website but not the payment page or the API behind it. - No written definition of "significant change", so no test after a major release. - An internal tester who also manages the systems tested. - A report with findings but no retest evidence. - Old v3.2.1 language in the methodology, such as references to 11.3. - Results not retained for 12 months. Questions to take to your QSA: - Which of our systems are in the CDE, and which tests do you expect on each? - What is our definition of a "significant change"? - Does our internal team meet your view of organizational independence? - What proof of tester qualification do you want? - Do you want the full report, or is a summary enough? - Which retest evidence closes 11.4.4? ## Questions ### Do only Level 1 merchants need a pentest? The text of Requirement 11.4 has no merchant-level condition. The merchant level sets how you validate (for example, a report on compliance or a self-assessment questionnaire). Which requirements apply to you is a decision for your acquirer, card brand or QSA. Ask them. ### Does the tester need a certification such as OSCP? PCI SSC guidance says certifications are not required. It also says qualifications cannot be met by certifications alone. Look at real engagement experience, and keep evidence of the tester's qualifications for your assessor. ### Can our own security team run the test? The requirement allows a qualified internal resource. That person needs organizational independence from the systems tested. PCI SSC guidance says the tester must be organizationally separate from the management of the target systems. Confirm your setup with your QSA. ### Is a vulnerability scan the same as a pentest for 11.4? No. PCI DSS treats them as separate activities. Scans sit under other parts of Requirement 11. A pentest is a manual, goal-driven test that tries to exploit weaknesses. Your methodology under 11.4.1 should say how the two differ. ## Sources - [PCI SSC blog: Just Published: PCI DSS v4.0.1](https://blog.pcisecuritystandards.org/just-published-pci-dss-v4-0-1) - [PCI SSC: PCI DSS v3.2.1 to v4.0 Summary of Changes (r1, May 2022)](https://listings.pcisecuritystandards.org/documents/PCI-DSS-v3-2-1-to-v4-0-Summary-of-Changes-r1.pdf) - [PCI SSC: Information Supplement, Penetration Testing Guidance v1.1 (September 2017)](https://listings.pcisecuritystandards.org/documents/Penetration-Testing-Guidance-v1_1.pdf) - [PCI SSC blog: Request for Comments, PCI DSS v4.0.1 (3 June 2026)](https://blog.pcisecuritystandards.org/request-for-comments-pci-data-security-standard-pci-dss-v4.0.1) - [PCI SSC Document Library (PCI DSS v4.0.1 standard, click-through download)](https://www.pcisecuritystandards.org/document_library/) --- # Penetration testing rules of engagement: what to agree in writing Source: https://penetrationtestservices.com/guides/penetration-testing-rules-of-engagement/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Pentest rules of engagement: what to agree in writing (authorization, scope, test windows, off-limits actions, stop conditions, data) plus a template outline. > **Short answer:** Rules of engagement are the written terms of a penetration test. They state who authorizes the test, what is in and out of scope, when testing happens, which actions are off limits, when the tester must stop, how data is handled, and who to call. Agree them before testing starts. This page is not legal advice. Rules of engagement (RoE) are the written terms that govern a penetration test. They protect both sides: the owner knows what the tester will do, and the tester knows what they may do. Without them the tester has no permission, and the same actions look like an attack. Excession Labs tests only with written authorization from the system owner. This page is not legal advice. Ask a lawyer to review your own documents. ## What do rules of engagement cover, and what goes into an authorization letter? They set permission, limits and a shared plan, and pentesting standards put this work first. NIST SP 800-115 treats planning as the first phase of a test. PTES, a community method, opens with pre-engagement. Good rules reduce three risks. Testing without permission can break computer misuse laws. A careless test can take down a service or damage data. Without a list, tests grow beyond the agreed scope. An authorization letter is a signed statement that the owner permits the test. Keep it short and exact. It contains: 1. Name and role of the signer, and a statement that they have authority over the systems. 2. Name of the testing company and the testers' contact. 3. The systems in scope, by name and address. 4. The start and end dates. 5. A statement that testing is permitted within the rules document. 6. Signature and date. If a third party hosts any in-scope system, check its policy. PTES warns that a client's permission may not cover its third-party providers. For large cloud platforms, the providers publish their own testing policies. Read the policy for each provider you use. ## How do you set scope, test windows and off-limits actions? Scope is the list of what may be tested. List each target by exact host name, URL, API base path, environment and version. Add the roles and tenants the tester may use. State what is out of scope. Examples: production payment processors, third-party SaaS tools, other customers' systems, employees, office networks. The tester does not touch a target that is not listed. "Our website" is too vague for a scope; "app.example.com and api.example.com, staging, build 2026.11" works. State the dates and the hours when active testing may run. Add times when it may not: peak sales hours, batch jobs, release freezes. Say whether you want the test during business hours or off-peak, and state the time zone. Say also if the tester may run heavy requests, such as a large number of login attempts, and when. Write the forbidden actions out. Common items for web app, API and SaaS tests: - Denial of service or load testing. - Social engineering of your staff or customers, unless scoped. - Attacks on third-party systems. - Deleting or changing records that the tester did not create. - Reading or copying real customer data beyond what proves a flaw. - Sending bulk email or text messages. - Persistent changes to production configuration. Also set bounds. If a test sends email, uploads files, guesses passwords or calls paid third-party APIs, state the maximum and who pays. Excession Labs bounds these tests and states their cost in the report. Destructive proofs use only test-owned accounts and records, marked as test data. ## What are the stop conditions, data handling rules and contacts? A stop condition is an event that makes the tester pause and call you. Agree them before the test. Typical ones: - The service shows errors or slows down because of testing. - The tester sees real customer data that should not be exposed. - The tester finds a critical flaw that is easy to use and affects live users. - The tester finds signs that someone else has already compromised the system. - The tester is asked to go outside scope. - Your contact asks the tester to stop. For each one, write who is called, by what channel, and how quickly, with a time you define in your own document. A tester who finds a serious flaw should not wait for the final report. State what data the tester may see, store and keep. Cover: - Where test evidence is stored, and in what form (encrypted storage). - Who can access it. - How long it is kept, and how it is deleted. - How credentials and tokens are handled, and when they are revoked. - Whether real personal data may appear in evidence. If it does, the tester redacts it. - How the final report is delivered and who may receive it. If the tester gets source code, say how it is stored and when it is deleted. For personal data under a privacy law, ask your counsel which terms you need. List names, roles, phone numbers and chat channels for both sides. On your side, name a technical contact, a backup, an escalation contact who can stop the test, and the security team contact who handles alerts, so they know the test is real. On the tester's side, name the lead. ## A template outline Use these headings as a start and fill each with exact facts. The outline is not a legal document. ```text RULES OF ENGAGEMENT: 1. Parties and purpose 1.1 System owner and signer 1.2 Testing provider 1.3 Goal of the test 2. Authorization 2.1 Statement of permission 2.2 Third-party systems and provider policies 2.3 Signature and date 3. Scope 3.1 In-scope assets (host, URL, environment, version) 3.2 Out-of-scope assets 3.3 Roles, tenants and accounts provided 3.4 Test type (black, grey or white box) 3.5 Known issues to skip 4. Schedule 4.1 Start and end dates 4.2 Allowed hours and time zone 4.3 Blackout and freeze periods 5. Allowed methods 5.1 Tools and request limits 5.2 Bounded tests (uploads, email, password guessing, paid APIs) 6. Off-limits actions 7. Stop conditions and escalation 7.1 Events that pause the test 7.2 Who is called, how, and how soon 7.3 Rule for critical findings found during the test 8. Data handling 8.1 Test data and test-owned records 8.2 Evidence storage, access and retention 8.3 Credentials: issue, use, revoke 8.4 Report delivery and recipients 9. Contacts 9.1 Technical, backup, escalation (system owner) 9.2 Lead and backup (testing provider) 9.3 Security team and monitoring provider 10. Changes to the rules (written change process) 11. Reporting and retest 11.1 Report format and severity scale 11.2 Retest terms 12. Signatures ``` ## How do the rules relate to the rest of the work? After you sign, finish the steps in [how to prepare for a penetration test](/guides/how-to-prepare-for-a-penetration-test/). Then the test runs, and the report records what the tester did under these rules. The "safety constraints" section of an Excession Labs report restates the key limits, so you can check that the tester stayed inside them. See [our methodology](/methodology/). The [free pentest pilot](/free-pentest/) follows the same rule: you give written authorization from the system owner before testing starts. ## Questions ### Are rules of engagement a contract? They are often part of the contract or attached to it, but a lawyer should decide that for you. Some teams use a separate scope document and an authorization letter. This page is not legal advice. ### Who signs the authorization? A person who has the authority to permit testing of the systems in scope. That is usually an executive or the system owner. If a third party hosts the system, you may also need their permission. ### Can the rules change during the test? Yes, with a written change. If the tester finds something that needs a wider scope, they stop and ask. Both sides record the change before the tester acts on it. ### What is the difference between scope and rules of engagement? Scope says what may be tested. Rules of engagement cover scope plus how, when and under what limits the test runs. Many teams keep scope inside the rules document. ## Sources - [NIST SP 800-115: Technical Guide to Information Security Testing and Assessment (planning and rules of engagement)](https://csrc.nist.gov/pubs/sp/800/115/final) - [PTES Pre-engagement (Internet Archive capture, 2025-01-03)](https://web.archive.org/web/20250103002608/http://www.pentest-standard.org/index.php/Pre-engagement) - [PCI SSC Information Supplement: Penetration Testing Guidance v1.1](https://listings.pcisecuritystandards.org/documents/Penetration-Testing-Guidance-v1_1.pdf) --- # Pentest attestation letter: what it contains and what it cannot prove Source: https://penetrationtestservices.com/guides/pentest-attestation-letter/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Pentest attestation letter: what it contains, a letter outline you can adapt, how it differs from a full report, and what no letter can prove to a buyer. > **Short answer:** A pentest attestation letter is a short document from the testing provider that confirms a test took place. It usually states the dates, scope, method, tester and a results summary. No standard defines it, and acceptance varies by buyer or auditor. A letter cannot show that a system is secure. Ask your provider whether they issue one. A pentest attestation letter is a short document from the party that did a penetration test. It confirms that the test took place and gives a few facts about it. It is not a report, and no standard defines its form. Buyers sometimes ask for a letter when a full report holds sensitive detail. > **Note:** Excession Labs' published materials do not say we issue attestation letters. If you want one, ask your provider whether they issue one and what it says. A letter does not make anyone compliant, and no auditor or customer is bound to accept it. ## What is a pentest attestation letter, and what does it contain? The letter is a summary statement signed by the testing provider. It says what was tested, when, by whom and, in broad terms, with what outcome. It is also called a letter of attestation, a pentest summary letter or a pentest certificate. The terms vary. We found no standard, including PCI DSS, SOC 2 or ISO/IEC 27001, that defines the letter. Descriptions come from providers, who note that acceptance varies. PCI SSC's own guidance says even its report outlines "are only suggested outlines and do not define specific reporting requirements for PCI DSS penetration tests." Most letters share these core items, and each answers a question a reader would ask. | Item | Why the reader wants it | |---|---| | Provider name and contact | To know who stands behind the statement | | Client name | To tie the letter to the right company | | Test dates (start and end) | To judge how recent the test is | | Scope: apps, APIs, environments, roles | To see what was and was not covered | | Test type: black, grey or white box | To judge the depth of the view | | Method and reference (for example OWASP WSTG, NIST SP 800-115) | To see the approach | | Tester names or roles, and independence statement | To judge competence and objectivity | | Result summary: counts by severity | To see the size of the issues found | | Fix and retest status | To see whether issues were closed | | Limits and exclusions | To prevent over-reading | | Date of the letter, and a signature | To tie the statement to a person | ## A letter outline you can adapt This outline is generic. No auditor requires it as a form. ```text [Provider letterhead] Penetration test summary letter Client: [Company name] Provider: [Provider name] Report reference: [Report ID and date] Test period: [Start date] to [End date] Retest date(s): [Dates, if any] 1. Scope Applications / APIs / environments tested: [list] Accounts and roles used: [list] Out of scope: [list and reason] 2. Approach Test type: [black / grey / white box] Method references: [for example OWASP WSTG, OWASP API Security Top 10] Testers: [names or roles]; independent of the client's build team: [yes/no] 3. Results at the end of the test Critical: [n] High: [n] Medium: [n] Low: [n] Status at [date]: [n] fixed and retested, [n] open 4. Limits This letter summarizes a point-in-time test limited to the scope above. It does not state that the systems are free of vulnerabilities, and it does not replace the full report. Signed: [Name, role] Date: [Date] ``` ## What are the limits of a letter, and when is it useful? A letter has five limits. State them to anyone who reads it. 1. Point in time. The system changes after the test. The letter covers the test window only. 2. Scope bound. Anything outside the listed scope is not covered. 3. No proof of absence. A test finds issues in the time and scope it had. It does not show that none remain. 4. Not a standard. No framework defines it, so acceptance depends on the reader. 5. Thin evidence. A reader cannot check findings, severity or fixes from a letter alone. They rely on the signer's word. For those reasons, an auditor or buyer may ask for the report, or for a call with the tester. NIST SP 800-115 describes the value of reporting findings with enough detail to act, and a letter is deliberately short. A letter fits when: - A buyer's security team wants proof of a recent test, but you cannot send a report with details of weaknesses. - A questionnaire asks for a "pentest certificate" and a summary meets the need. See [security questionnaire pentest questions](/guides/security-questionnaire-pentest-questions/). - The buyer screens first and will ask for the report later if you pass. Our page on [pentest reports for customer security reviews](/customer-security-review/) covers how the report and letter fit into a buyer review. ## How does a letter differ from a report, a summary and a retest letter? The three documents differ as follows. | Document | Length | Holds | Typical reader | |---|---|---|---| | Full report | Tens of pages | Findings, evidence, reproduction, fix advice, method | Your engineers, auditors under NDA | | Executive summary | 1 to 3 pages | Scope, risk themes, priority actions, counts | Management, some buyers | | Attestation letter | 1 page | Dates, scope, tester, counts, limits, signature | Buyer procurement, quick screens | Send the lightest document that answers the reader's question, and send more only when they ask. ## What mistakes make a letter weak, and should it name your system? - No scope. "A pentest was done" tells the reader nothing. List what was tested. - No dates. An undated letter cannot be judged for age. - Marketing language. Words such as "passed" or "certified secure" mislead, because a test has findings, not a pass mark. - No limits. A letter without a limits section invites over-reading. - Hidden open findings. If high findings are open, a letter that lists only fixed ones misleads the reader. - Mismatch with the report. Counts and dates must match the report exactly. A first letter states the results at the end of the test. A retest letter states the status after fixes, which shows the work was closed. A retest letter lists the original date, the retest date, and the count of findings fixed, partly fixed and still open. If you plan to retest, ask the provider for one letter after the retest instead of two. That avoids two documents with different counts. A letter should state the date of the test and the date of any retest. A letter dated before the retest shows open findings that you may already have fixed, so order the letter after the retest. See [retesting](/retesting/) for what a retest result looks like, and [how to read a penetration testing report](/guides/how-to-read-a-penetration-testing-report/) for the report behind the letter. Yes, by product name and environment, such as "Example Cloud web app, production". Do not list internal host names, IP addresses or account names. A letter goes to outsiders, so give enough detail to be true and specific, and none that helps an attacker. ## What should you ask your provider and the reader before ordering? - Do you issue a letter, and is it included in the price or extra? - Who signs it, and in what role? - Can the letter state the retest result? - Which items does the letter list, and can I change the template? - What happens if findings are still open on the date of the letter? - Do you allow me to share the letter with customers? If a provider cannot answer these, ask for a sample letter with the client name removed. Ask the auditor or buyer first. A short email saves the cost of a letter nobody uses. Questions for them: - "Will you accept a summary letter, or do you need the full report?" - "What items must the letter list?" - "Must the letter be dated within a set period?" - "Do you accept a letter from the same firm that did the test, or do you need an independent source?" For the SOC 2 view of evidence, see [does SOC 2 require penetration testing](/guides/does-soc-2-require-penetration-testing/). ## Questions ### Is an attestation letter the same as a pentest report? No. A report holds findings, evidence and fix advice. A letter is a short statement that a test was done, with a few facts. A letter helps when you cannot share the report. Some buyers still ask for the report. ### Will an auditor or customer accept a letter instead of a report? It depends on the auditor or customer. No standard defines a letter, and vendors who describe letters say acceptance varies. Ask the person who will read it, before you order one. ### Can we write the letter ourselves? A letter has value because an independent party signs it. A letter written by the tested company is a self-statement. If an internal team did the test, label the letter that way and state their role plainly. ### Should the letter list the vulnerabilities? Usually only counts by severity and the fix status. A letter goes to people outside your team. Details of open weaknesses should stay in the report, which you share under NDA if at all. ## Sources - [Blaze Infosec: Pentest attestation letter](https://www.blazeinfosec.com/post/pentest-attestation-letter) - [PCI SSC: Information Supplement, Penetration Testing Guidance v1.1 (Section 5.2, report outlines)](https://listings.pcisecuritystandards.org/documents/Penetration-Testing-Guidance-v1_1.pdf) - [NIST SP 800-115: Technical Guide to Information Security Testing and Assessment](https://csrc.nist.gov/pubs/sp/800/115/final) --- # Rate limiting and credential stuffing: how to test safely Source: https://penetrationtestservices.com/guides/rate-limiting-and-credential-stuffing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Credential stuffing testing and rate limit tests done safely: OWASP OAT-008, a bounded test design, test cases for login, reset and MFA, and how to fix gaps. > **Short answer:** Credential stuffing is the automated use of stolen username and password pairs to log in (OWASP OAT-008). Testers check whether login, reset, MFA and API routes limit repeated attempts, using test accounts they own, a stated request budget and a stop rule. They never test accounts they do not own. Fixes combine per-account and per-source limits, detection and MFA. Credential stuffing is the automated use of stolen username and password pairs to log in. OWASP lists it as Automated Threat OAT-008 and describes it as "mass log in attempts used to verify the validity of stolen username/password pairs." Rate limiting is one control that slows attackers and raises their cost. Testers check rate limits and credential stuffing defenses with test accounts they own, a request budget and a stop rule, and never with accounts they do not own. This work belongs in the [authentication testing](/guides/authentication-testing-checklist/) part of a pentest. ## How do credential stuffing and brute force differ, and which routes need rate limits? Credential stuffing tries pairs that were leaked from other sites. Brute force guesses passwords. Both send many login attempts, but they look different in logs and need different limits: | Attack | Input | Typical pattern | Control that helps | |---|---|---|---| | Credential stuffing (OAT-008) | Leaked pairs, one try per account | Many accounts, few tries each, many sources | Per-source and global limits, breach-password checks, MFA, bot detection | | Brute force | Guessed passwords | Many tries on one account | Per-account lockout or delay | | Password spraying | A few common passwords | Many accounts, one password each | Detection across accounts, MFA | A per-account lockout alone does not stop credential stuffing, because each account sees only one or two tries. Test every route that checks a secret or costs money. OWASP API4:2023 says an API is vulnerable when limits are "missing or set inappropriately." - Login (web, mobile API, legacy routes). - Password reset request and token submission. - MFA code entry and resend. - Sign-up and email verification. - Account lookup and invitation routes. - Anything that sends email or SMS, or calls a paid third-party API. - Search, export and other heavy routes. ## How do you design a safe, bounded test? A safe test has a written scope, owned accounts, a budget and a stop rule. Set all four before you send any request. 1. Authorization. Get written permission from the system owner. State the hosts and routes. 2. Test accounts only. Create accounts that the tester owns. Use unique, fake passwords. Do not use leaked credential lists or real customer emails. 3. Request budget. Set a maximum number of requests per route, and a maximum rate. For example, 20 attempts over one minute against one test account. 4. Cost cap. For email, SMS or paid APIs, set a maximum count and state the cost. Use test inboxes and numbers you own. 5. Stop rules. Stop at the first sign of a lockout, a 429, a slowdown, a block page, or any report from the owner's contact. 6. Window and contact. Run in an agreed window with a named contact who can pause the test. 7. Record everything. Save requests, responses and timestamps. > **Authorization limit:** Test rate limits and login controls only on accounts you own and on systems you are authorized to test. A written scope for the system does not give permission to test other people's accounts. ## What do the test cases look like, and how do you read the results? Each case has a bounded request count and a clear result. Run the control first: a normal login with the right password. | Case | Action (test account, bounded) | Good result | Finding if | |---|---|---|---| | Per-account limit | 10 wrong passwords for one test account | Delay, lockout or 429 within a few tries | All 10 get the same fast reply | | Correct password after failures | Right password after the limit | Blocked or step-up while the limit is active | Login succeeds without delay | | Per-source limit | 20 wrong logins across 5 owned test accounts, from one address | 429 or block | No limit across accounts | | Reset request flood | 5 reset requests for one test inbox | Throttled, same message | Five emails sent | | MFA code tries | 10 wrong codes in one session | Session invalidated or throttled | Unlimited tries | | API route | Same login via the mobile API route | Same limits as web | No limit on the API route | | Header trust | `X-Forwarded-For` set to a different value | Limit unchanged | Limit resets when the header changes | | Response consistency | Compare locked and open account replies | Generic message | Message reveals lockout and account existence | The `X-Forwarded-For` check shows whether the limit trusts a client-set header. Report that as a design flaw. Do not build a rotation tool. > **Illustrative bounded test:** This is a demonstration. No real customer system was tested. The host and account are fictional. > > A tester sends 10 wrong-password logins for one owned test account, 1 per second: > > ```http > POST /api/v1/login HTTP/1.1 > Host: app.harbor-notes.example.com > Content-Type: application/json > > {"email": "tester-a@example.com", "password": "wrong-value-01"} > ``` > > Attempts 1 to 5 return `401`. Attempt 6 returns: > > ```http > HTTP/1.1 429 Too Many Requests > Retry-After: 900 > Content-Type: application/json > > {"error": "too_many_attempts"} > ``` > > The limit works for this account. The tester then repeats the test through the mobile route `/api/m/login` to check that the same limit applies. A working control slows or stops attempts on every route. Check what the limit keys on (account, source, device), whether it covers every route, and whether the reply leaks anything. Report what held as well as what failed. The finding "mobile login has no limit" reads better with the web route's limit listed as a control that held. ## How do teams fix weak rate limits, and how do you scope the checks? Use layers, because each control covers a gap in another: - Limit per account and per source, with a global cap on failures. - Slow responses after repeated failures (progressive delay) before a hard lockout. A hard lockout lets an attacker lock real users out. - Check new passwords against known breached lists. - Offer MFA, and require it for risky logins. - Detect bots and unusual patterns, for example many accounts from one source. - Return the same reply for unknown users, locked users and wrong passwords. - Do not trust client headers for the source address. Take it from your edge proxy. - Alert on login failure spikes. ```text key = hash(account_id) + ":" + source_ip failures = counter.increment(key, window = 15 minutes) if failures > 5: respond 429 with Retry-After, log "rate_limited" global_failures = counter.increment("login", window = 1 minute) if global_failures > threshold: raise alert ``` Ask your tester to include rate limit checks in the authentication scope, with the budget in writing. For API routes, share the API description so testers cover every login and reset route; see [API penetration testing](/api-penetration-testing/). The wider API risk list is in the [OWASP API Security Top 10 guide](/guides/owasp-api-security-top-10/). For abuse of buying, booking or sign-up flows, read the [business logic guide](/guides/business-logic-vulnerabilities/). ## What does a rate limit finding look like? A finding states which route lacked a limit, the budget used and what the tester saw. > **Illustrative finding write-up:** This is a demonstration. No real customer system was tested. The app and host are fictional. > > | Field | Detail | > |---|---| > | Title | Mobile login route has no attempt limit | > | Label | Observed | > | Route | `POST /api/m/login` | > | Accounts | one test account created for this test | > | Budget | 20 wrong-password attempts over 20 seconds, then one correct login | > | Result | All 20 attempts return `401` with no delay. The correct login then succeeds. | > | Controls that held | The web route `POST /api/v1/login` returns `429` after 5 failures. Unknown and known users get the same reply. | > | Impact | An attacker who uses the mobile route faces no limit on attempts. | > | Fix | Apply the same limit in the shared login service for all routes. | Put the budget in the finding so a reader can repeat the test. ## How should you plan the cost of a test? Some routes cost money or cause side effects. List them before the test and set a cap for each: | Route type | Cost or side effect | Cap to set | |---|---|---| | Email send | Provider fee, risk to sender reputation | A few messages to owned inboxes | | SMS or voice | Per-message fee | A few messages to owned numbers | | Paid third-party API | Per-call fee | A stated count and a stated price | | File upload | Storage and scan cost | A few small files | | Heavy search or export | Server load | One request at a time, small size | Write the caps in the scope. Report the actual count and cost in the final report. If a cap is reached before the limit appears, report that as a result: "no limit seen within the cap". ## Questions ### Will a rate limit test lock out real users? It can, if you aim it at real accounts. Safe tests use test accounts only. Lockout is per account in many systems, so a test on a test account should not affect real users. Confirm with the owner before you start. Agree the rules and a contact in writing first. ### How many requests is a safe test? There is no universal number. Choose a budget from the control you want to test. If the limit is 5 failures in 15 minutes, 10 to 20 attempts show whether it works. State the number, the accounts and the stop rule in the scope. ### Is a CAPTCHA a rate limit? No. A CAPTCHA adds friction but is not a limit on attempts. Testers check the server-side limit too, since attackers can solve or bypass CAPTCHAs. ### Should the test try IP rotation to evade a limit? We do not recommend evasion. A test shows whether the control exists and what it keys on (account, IP, device). Describe that and the fix. Evasion tooling is out of scope for this guide. ## Sources - [OWASP Automated Threats: OAT-008 Credential Stuffing](https://owasp.github.io/www-project-automated-threats-to-web-applications/assets/oats/EN/OAT-008_Credential_Stuffing) - [OWASP API Security Top 10 2023: API4 Unrestricted Resource Consumption](https://api-security.owasp.org/editions/2023/en/0xa4-unrestricted-resource-consumption/) - [OWASP Credential Stuffing Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Credential_Stuffing_Prevention_Cheat_Sheet.html) - [OWASP WSTG: Testing for Weak Lock Out Mechanism](https://wstg.owasp.org/v4.2/4-Web_Application_Security_Testing/04-Authentication_Testing/03-Testing_for_Weak_Lock_Out_Mechanism) --- # Red team vs penetration testing: what is the difference? Source: https://penetrationtestservices.com/guides/red-team-vs-penetration-testing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Red team vs pentest: how the two differ in goal, scope, stealth and who knows, with an 11-row comparison table and a decision table for choosing one. > **Short answer:** A penetration test looks for as many weaknesses as possible in a defined scope, with the defenders usually aware. A red team exercise pursues an objective, such as reaching a set of data, while testing how well the organization detects and responds. Most teams need pentests first. Red teams suit organizations with working detection. A penetration test finds weaknesses in a defined scope. A red team exercise tests whether the whole organization can detect and stop an attacker who pursues a goal. Both are authorized, and they differ in aim, scope, stealth and output. The NIST glossary, citing CNSSI 4009-2022, defines a red team as "a group of people authorized and organized to emulate a potential adversary's attack or exploitation capabilities". It adds that the aim is to strengthen security by showing the effects of successful attacks and showing defenders which measures work in a live setting. That job is wider than a pentest. ## What is the main difference, and how do they compare? A pentest asks: which weaknesses exist here, and how bad are they? A red team exercise asks: can an attacker reach this goal, and will we notice and respond? A pentester wants breadth and tries to find and prove many flaws in the scope. A red team wants one path to a goal, picks the quietest route and may leave other flaws alone. | | Penetration test | Red team exercise | |---|---|---| | Main question | What weaknesses exist and what do they allow? | Can an attacker reach an objective, and do we detect and respond? | | Goal | Many findings, proven | One or a few objectives (for example, reach a data set) | | Scope | Defined systems: an app, an API, a network | Broad: systems, people, process, sometimes physical sites | | Stealth | Not needed. Defenders often know. | Central. The team tries to avoid detection. | | Who knows | Security and engineering, usually | A small group, often only senior staff | | Duration | Short, fixed window | Longer, often weeks | | Methods | Technical testing within the rules | Any approved route, such as phishing, stolen credentials, chained flaws | | Output | Findings list with fixes | Attack story, detection gaps, response timeline, plus fixes | | Success measure | Coverage and quality of findings | Objective reached or stopped, time to detect, time to respond | | Best when | You need to find and fix weaknesses | Your controls and team are in place and you want to test them | | Cost and effort | Lower | Higher | ## What does a pentest give you, and what does a red team give you? A pentest gives breadth and a fix list. The tester reports every flaw found in scope, with evidence and fix guidance, which is what a software team needs before a launch or a customer review. A red team may report only the path it used and skip other flaws if the path was easy. The report tells one attack story and does not list every flaw. A red team tests your detection and response: whether alerts fire, whether staff notice, and how fast the team acts. A pentest usually does not test this, because defenders know about it. A red team can also cross boundaries, from a phishing email to a cloud account to a database. A pentest of one app does not follow that chain. ## Which one do you need? A decision table | Your situation | Choose | |---|---| | First test, no recent report | Pentest | | Customer or auditor asks for a pentest report | Pentest | | Launching a new app or API | Pentest. See [pre-launch testing](/pre-launch-security-testing/) | | You fixed pentest findings and have security monitoring | Consider a red team | | You want to test your security operations team | Red team, or a purple team exercise | | You have no alerting or incident process | Build that first. Then red team | | You want to test staff against phishing | Scoped social engineering test or a red team | | You ship AI features, agents or LLM apps | See the AI red teaming guide, below | A company can use both over time: a pentest each year and after major changes, and a red team once its defense is mature. ## What about purple teams, AI red teaming and rules of engagement? A purple team is a joint exercise. Attackers run techniques, defenders watch in real time, and both tune detection together. It teaches faster than a blind red team but does not test surprise. The word "red team" is also used for testing AI models and applications. That work tests prompt injection, data leaks through retrieval and unsafe agent actions, and it differs from the classic exercise described here. For AI systems, read [AI red teaming vs penetration testing](https://airedteamingservices.com/guides/ai-red-teaming-vs-penetration-testing/) on airedteamingservices.com. If your product has an AI feature, our [AI application security](/ai-application-security/) page explains why it needs a different test. Both need written authorization and clear limits. A red team needs stricter rules because the test is broad and stealthy. Define the objective, the off-limits actions, the stop conditions and who knows, and name a small group that can call the exercise off. See [rules of engagement](/guides/penetration-testing-rules-of-engagement/). ## A short example > **Demonstration only:** This is a demonstration. No real customer system was tested. Harbor Notes is a fictional SaaS app. It has done two yearly pentests and fixed the findings. It now runs a small security team that watches alerts. > > The company wants to know whether an attacker could reach customer note data without being noticed. A pentest of the app would find flaws, but the defenders would know the test was running. So the company agrees a red team objective: "Reach the notes database from an outside position." Only three managers know, and the exercise runs under written rules. The result is a timeline: the attackers got a foothold through a phished test account, the alert fired after some hours, and the response had gaps. The report lists the fixes for each gap. To plan the next step, read [how often should you pentest](/guides/how-often-should-you-pentest/). Our [services](/services/) page lists what we test. ## Questions ### Is a red team better than a pentest? No. They answer different questions. A pentest finds many weaknesses and gives a fix list. A red team tests whether your people and tools notice and stop an attack. Without basic fixes in place, a red team will find easy paths. ### What is a purple team? A purple team exercise has attackers and defenders work together. The attacker runs a technique, and the defender checks whether it was seen and how to improve detection. It is more of a shared training and tuning exercise than a contest. ### Does Excession Labs offer red team exercises? Our focus is web app, API and SaaS penetration testing. The comparison above should help you choose. We do not list full red team exercises as a service on this site. ### Do small companies need a red team? Usually not yet. If you lack security monitoring or an incident process, a red team shows only that. Start with a pentest and fix the findings. Consider a red team when you have a team that watches alerts. ## Sources - [NIST CSRC Glossary: red team (CNSSI 4009-2022)](https://csrc.nist.gov/glossary/term/red_team) - [NIST SP 800-115: Technical Guide to Information Security Testing and Assessment](https://csrc.nist.gov/pubs/sp/800/115/final) - [PTES Pre-engagement (Internet Archive capture, 2025-01-03)](https://web.archive.org/web/20250103002608/http://www.pentest-standard.org/index.php/Pre-engagement) --- # Security questionnaire pentest questions: what buyers ask and how to answer Source: https://penetrationtestservices.com/guides/security-questionnaire-pentest-questions/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Pentest questions in security questionnaires (SIG, CAIQ, custom): 14 common questions with honest answer patterns, the evidence to attach, and what to avoid. > **Short answer:** Security questionnaires ask the same pentest questions: do you test, how often, who tests, what scope, how fast you fix, and can we see a report. A table pairs each question with an answer pattern you fill in with facts, plus the evidence to attach. Never claim more than your last report shows. Security questionnaires ask about penetration testing in a few standard ways. Buyers want to know whether you test, how often, who tests, what the scope was, and whether you fix what you find. Cobalt's State of Pentesting Report 2025 found that third-party pentest reports are the most common assurance that customers and regulators ask for, at 59% of 450 survey respondents. It is a vendor survey, so treat it as one data point. > **Note:** Treat the answer patterns as templates and fill them with facts from your own last report. A pentest does not prove that a system is secure, and no answer should say so. ## Which questionnaires ask about pentests? The common sets are SIG, CAIQ and custom spreadsheets. - CAIQ is the Consensus Assessments Initiative Questionnaire from the Cloud Security Alliance. It accompanies the Cloud Controls Matrix (CCM) v4. CCM control TVM-06, "Penetration Testing", asks you to define, implement and evaluate processes and technical measures for the periodic performance of penetration testing by independent third parties. Check the exact question wording in the current CAIQ file from CSA. - SIG is the Standardized Information Gathering questionnaire from Shared Assessments. It is licensed, so the full question text is not public. Use the copy your customer sends. - Custom questionnaires come from enterprise procurement teams. They often reuse SIG or CAIQ wording and add their own lines. ## What are the common pentest questions and good answers? Replace the brackets with facts. If a fact is not true, change the answer. | # | Common question | Answer pattern | Evidence to attach | |---|---|---|---| | 1 | Do you perform penetration testing? | "Yes. Our last test was [month, year], on [web app and API]." | Letter or report summary | | 2 | How often? | "[Once a year] and after [defined changes]." Say what triggers a test. | Policy excerpt | | 3 | Who performs it? | "[An independent third party / internal team separate from engineering], [name]." | Provider name, tester role | | 4 | What is in scope? | List apps, APIs, roles, environments. State what was excluded and why. | Scope section of the report | | 5 | Is it black, grey or white box? | State the type. See [black box vs grey box vs white box](/guides/black-box-grey-box-white-box-testing/). | Report scope page | | 6 | Does it cover the OWASP Top 10? | "Testing covered access control, authentication, injection and [list], as the report's method section shows." Do not claim full coverage. | Method section | | 7 | When was the last test? | Give the date. If older than your stated interval, say so and give the next date. | Report date | | 8 | Were critical or high findings found? | "Yes/No. [N] critical, [N] high. [N] fixed and retested; [N] open with dates." Counts only. | Remediation tracker | | 9 | How fast do you fix? | State your policy targets by severity, then your actual record for this test. | Policy, ticket dates | | 10 | Do you retest fixes? | "Yes. Fixed findings were retested on [date]." | Retest note | | 11 | Can we see the report? | "We share [a summary / the report under NDA / a letter]." | Document | | 12 | Do you test third-party components? | State how: scope of the test, your supplier review process. Do not claim to test a vendor's systems. | Vendor policy | | 13 | Do you test after major releases? | State the trigger list. See [how often should you pentest](/guides/how-often-should-you-pentest/). | Release process | | 14 | Is the tester certified? | Name the experience. | Tester bio | ## How do you write honest answers, and what evidence should you attach? Apply these rules to every pentest answer: 1. Answer the question asked. Do not answer a vulnerability scan question with pentest facts, or the reverse. 2. Use dates and counts. "Tested in September 2026; 0 critical, 1 high fixed and retested" is a fact. "Regularly tested" is not. 3. State scope limits. If staging was tested and production was not, say so. 4. Do not promise results. Never write "no vulnerabilities" or "secure". A test finds issues in the time and scope it had. 5. Match the report. If the questionnaire answer and the report disagree, the buyer will find it. 6. Name open items. Say how many are open and when you plan to close them. Offer the lightest document that fits the question. A buyer who needs more will ask for it. - A summary letter of the test: date, scope, tester and result counts. See [pentest attestation letter](/guides/pentest-attestation-letter/) for contents and limits. - The executive summary of the report. - The full report, under NDA, when the buyer asks for it. - Your policy for testing frequency and fix targets. - A retest note that shows closed findings. Check what the testing agreement lets you share. Reports often hold details about your own system and sometimes about third parties. ## Why does the same question get asked three ways, and how do you keep answers consistent? Questionnaires overlap because they come from different sources. One may ask for "annual third-party penetration testing", another for "independent testing", another for "technical testing of applications". Answer each with the same facts in the words of the question. Keep one page in your own docs with your test dates, scope, provider and counts, and update it after each test. The next questionnaire then becomes a copy-and-check job. The service page for [customer security reviews](/customer-security-review/) describes how a test report fits this flow. To keep answers consistent across customers, keep one internal record and copy from it. Include: - Date, scope and provider of each test. - Counts by severity at the report date and now. - The policy text on frequency and fix targets. - A list of documents you may share, and under which terms. - The name of the person who approves security answers. Review the record after every test, major feature release or incident, so that sales and the security team answer from the same facts. ## How do you answer when the answer is no, and what should you avoid? For a no answer, state the fact, state what you do instead, and give a date. - "We have not had a third-party penetration test. We run [dependency scans and code review]. A third-party test of [web app and API] is planned for [month, year]." - "Our last test was in [month, year], which is outside our 12-month target. The next test is booked for [month, year]." Do not describe other work as a pentest. If you have a test from another source, such as a bug bounty program, say that exactly. See [bug bounty vs pentest](https://pentestcompare.com/guides/bug-bounty-vs-pentest/) for how the two differ. Avoid these: - Claiming a standard "requires" the test when it does not. - Claiming that an auditor or customer accepted your report before they said so. - Saying "continuous" testing when you test on a schedule. - Copy-pasting an old answer after the facts changed. - Listing exploit details or open weaknesses in a questionnaire. ## A short checklist before you submit - [ ] The test date and scope in the answer match the report. - [ ] Counts by severity match the tracker. - [ ] Open findings have owners and dates. - [ ] You checked the sharing terms of your testing agreement. - [ ] A second person read the answers. ## Questions ### Should we send the full pentest report to a customer? Usually not by default. A full report lists weaknesses and reproduction steps. Share it only under an NDA, or share a summary or letter first. Check your contract and your testing agreement before you share anything. ### What if our last pentest is older than a year? Say so, with the date. Then state what you did since: scope of recent tests, fixes and your plan for the next test. A dated, honest answer beats a vague one. ### Can we answer yes if we only ran a vulnerability scan? No. A scan is not a pentest, and buyers know the difference. Answer the scan question as scans and the pentest question as pentest. If you did not run a pentest, say no and give the date you plan one. ### How do we handle open findings in an answer? State the count by severity and the fix status, not the details. For example: no open critical findings, two medium findings with fixes scheduled. Do not describe open weaknesses in the questionnaire text. ## Sources - [Cloud Security Alliance: Cloud Controls Matrix v4 and CAIQ v4](https://cloudsecurityalliance.org/artifacts/cloud-controls-matrix-v4) - [Shared Assessments: SIG Questionnaire](https://sharedassessments.org/sig/) - [Cobalt: State of Pentesting Report 2025](https://resource.cobalt.io/state-of-pentesting-2025) - [PCI SSC: Information Supplement, Penetration Testing Guidance v1.1](https://listings.pcisecuritystandards.org/documents/Penetration-Testing-Guidance-v1_1.pdf) --- # Supabase security audit: RLS, API keys, storage and RPC checklist Source: https://penetrationtestservices.com/guides/supabase-security-testing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Supabase security audit for your own project: an 8-step checklist for RLS, publishable and secret keys, views, storage and RPC, with example policies. > **Short answer:** A Supabase security audit checks eight areas: the keys in the shipped client, anonymous table access, cross-user access (IDOR), grants to the anon and authenticated roles, views, RPC functions, storage policies and auth settings. It builds on Row Level Security on every exposed table. Test each as an anonymous caller and as a second user. Keep the secret key on the server. Your database is reachable from the browser. Supabase exposes tables through an API, so Row Level Security (RLS) policies are your access control. If a policy is missing or wrong, anyone with your public key can read or write data. An eight-step test checklist and safe example policies follow. Run the tests only on projects you own or have written permission to test. ## What is Row Level Security, and which API key should ship in the client? RLS is a Postgres feature that limits which rows a role can read or change. Supabase's docs state the main rule: "Enable RLS on every table in an exposed schema." A table in an exposed schema without RLS is readable and writable by any role with a grant on it. Once you enable RLS, no data is accessible through the API with a publishable key until you create policies. That default-deny state is the safe one. Watch for three mistakes: RLS never enabled, a policy that is too loose, and a path that skips RLS. Only the publishable key (or the legacy anon key) belongs in client code. Supabase's API key page lists four types: | Key | Role | Safe in a browser or app? | RLS applies? | |---|---|---|---| | Publishable (`sb_publishable_...`) | `anon`, or `authenticated` when signed in | Yes | Yes | | anon (legacy JWT) | Same as publishable | Yes | Yes | | Secret (`sb_secret_...`) | `service_role` | No. Backend only | No, it bypasses RLS | | service_role (legacy JWT) | `service_role` | No. Backend only | No, it has `BYPASSRLS` | The docs say a secret key provides "full access to your project's data, bypassing Row Level Security" and must never go into "a browser, a shipped application, or source control." Supabase also says it plans to retire the legacy JWT keys by the end of 2026. Check the docs for your migration date. ## How do you test a Supabase project? A checklist Use two test users, A and B, and an anonymous caller. Run the checks in order. ### 1. Find the keys in the shipped client. - [ ] Search the JS bundles for `service_role`, `sb_secret_`, and long JWTs. Decode any JWT and read its `role` claim. - [ ] Confirm only a publishable or anon key appears. - [ ] Check git history, `.env` files committed by mistake, and mobile app builds. ### 2. List tables as an anonymous caller. - [ ] Request each table with only the public key and no user token. - [ ] Record which tables return rows, and compare to what should be public. ```bash # Anonymous read test against your own project (replace the placeholders) curl -s "https://YOUR-PROJECT.supabase.co/rest/v1/notes?select=id&limit=1" \ -H "apikey: YOUR_PUBLISHABLE_KEY" ``` An empty array `[]` can mean "no rows" or "RLS blocks you". Add a row you own first, then retest, so you can tell the two apart. ### 3. Test cross-user access (IDOR). - [ ] As user A, create a record. As user B, request A's record by ID. - [ ] As user B, try `PATCH` and `DELETE` on A's record. Then read it back as A to check it did not change. A `204` response alone is not proof, because an RLS-filtered write can match zero rows. - [ ] Try to insert a row with `user_id` set to A's ID. See [IDOR testing](/guides/idor-testing/) and [multi-tenant isolation testing](/guides/multi-tenant-isolation-testing/) for the method. ### 4. Check grants. - [ ] List grants for `anon` and `authenticated` on each table. Supabase says to revoke all grants, then grant back only what each role needs. - [ ] Look for `INSERT`, `UPDATE`, `DELETE` grants that no feature uses. ### 5. Check views. - [ ] List views in exposed schemas. Views bypass RLS by default. - [ ] On Postgres 15 and later, check for `security_invoker = true`. ### 6. Check functions (RPC). - [ ] List functions in exposed schemas. PostgREST exposes them as `/rpc/function_name`. - [ ] Postgres grants `EXECUTE` on new functions to `PUBLIC` by default. Check who can call each one. - [ ] Review each `SECURITY DEFINER` function. It runs with its owner's rights and can bypass RLS. It needs its own checks of `auth.uid()` and its inputs. ### 7. Check storage. - [ ] List buckets and mark which are public. A public bucket serves objects to anyone with the URL. - [ ] Review policies on `storage.objects`. Supabase notes that by default, storage allows no uploads without RLS policies. - [ ] Test upload, read, overwrite and delete as an anonymous caller, as user A and as user B, on A's path. ### 8. Check auth settings. - [ ] Confirm that email confirmation, password rules and redirect URLs match your intent. See the [authentication testing checklist](/guides/authentication-testing-checklist/). ## What do safe example policies and automated RLS tests look like? The policies use an example `notes` table. Adapt the names and run them on a test database first. ```sql -- Enable RLS and revoke broad default grants, then grant back only what is needed alter table public.notes enable row level security; revoke all on public.notes from anon, authenticated; grant select, insert, update, delete on public.notes to authenticated; -- Owners can read their own rows create policy "notes_select_own" on public.notes for select to authenticated using ( (select auth.uid()) = user_id ); -- Owners can insert rows only for themselves create policy "notes_insert_own" on public.notes for insert to authenticated with check ( (select auth.uid()) = user_id ); -- Owners can update their rows, and cannot hand them to someone else create policy "notes_update_own" on public.notes for update to authenticated using ( (select auth.uid()) = user_id ) with check ( (select auth.uid()) = user_id ); -- Owners can delete their rows create policy "notes_delete_own" on public.notes for delete to authenticated using ( (select auth.uid()) = user_id ); -- Views must run with the caller's permissions (Postgres 15+) create view public.my_notes with (security_invoker = true) as select id, title, created_at from public.notes; ``` Supabase's docs use the wrapped `(select auth.uid())` form, which lets Postgres cache the value once per statement. When no user is signed in, `auth.uid()` returns null, so the equality fails and access is denied. Supabase documents pgTAP tests for RLS, run with `supabase test db`. Create a test file with `supabase test new notes_rls`. The docs also give three rules to copy: - Switch role and identity with `set local role` and `set local request.jwt.claim.sub`. - Do not prove an allowed write with `lives_ok`, because it also passes when the write matched zero rows. Return the row and assert on it. - After a denied write, check that the target row is unchanged. Run the tests in your CI pipeline, so a migration that drops a policy fails the build. ## What are the common Supabase mistakes? Look for these mistakes in a Supabase-backed app: - RLS never enabled. A table was created from the dashboard or a migration without `enable row level security`. - Policy `using (true)`. It reads as "allow all" and exposes every row to the role it names. - Insert without a check. A policy that has `using` but no `with check` on insert or update lets a user write a `user_id` that is not theirs. - Trusting user-editable metadata. Policies that read `user_metadata` from the JWT can be changed by the user. Use server-controlled data for roles. - Secret key in a front-end bundle. A build tool inlined the wrong variable. - Edge Functions that skip checks. A function uses the secret key, so RLS no longer applies, and it does not verify the caller. - Public buckets for private files. Invoices or ID photos sit in a bucket that serves any URL. Add a unit test for each fix, so it stays fixed. ## How should you handle credentials during a test, and what do testers report? Use test users and test data. If you suspect a secret key has shipped in a client, treat it as exposed: rotate it, then review project logs for use. Never paste a real key into a ticket or chat. To prove exposure, show the key's type and where it appeared, with the value masked. A good finding names the table, the role, the request and the proof. For example: "As an anonymous caller, `GET /rest/v1/invoices` returned rows from other tenants. Cause: RLS is off on `public.invoices`. Fix: enable RLS, add owner-scoped policies, revoke default grants." Evidence includes the request, the response, and a read-back from the owning account. For platform-specific risks across Bubble, Supabase and Firebase apps, see [no-code app security testing](/no-code-app-security-testing/). The matching Firebase guide is [Firebase Security Rules testing](/guides/firebase-security-rules-testing/). ## Questions ### Is the Supabase anon key a secret? No. The anon key (legacy) and the newer publishable key are designed to ship in client code. Supabase says they only reach what Row Level Security allows. That makes your RLS policies the real access control. The service_role and secret keys must stay on the server. ### Why can users still read a table after I added a policy? Policies decide row access only if RLS is on, and table grants still apply. Supabase documents that adding policies does not take back default grants to anon and authenticated. Review grants, and enable RLS before you rely on any policy. ### Do Supabase views respect RLS? Not by default. Supabase says views bypass RLS by default because they are usually created by the postgres user. On Postgres 15 and later, create the view with security_invoker = true so the view uses the caller's permissions. ### Can I test RLS without hitting production? Yes. Use a local or branch database and the Supabase CLI. Supabase documents pgTAP tests for RLS, run with supabase test db. Test with the same roles and JWT claims that real callers use. ## Sources - [Supabase Docs: Row Level Security](https://supabase.com/docs/guides/database/postgres/row-level-security) - [Supabase Docs: Understanding API keys](https://supabase.com/docs/guides/api/api-keys) - [Supabase Docs: Storage access control](https://supabase.com/docs/guides/storage/security/access-control) - [Supabase Docs: Database functions](https://supabase.com/docs/guides/database/functions) - [PostgREST: calling functions (RPC)](https://postgrest.org/en/stable/references/api/functions.html) - [PostgreSQL: CREATE FUNCTION (privileges and SECURITY DEFINER)](https://www.postgresql.org/docs/current/sql-createfunction.html) --- # What is penetration testing? A plain guide for software teams Source: https://penetrationtestservices.com/guides/what-is-penetration-testing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 What is penetration testing? What a pentest produces, the main types, how it differs from a vulnerability scan, five phases and one worked example finding. > **Short answer:** A penetration test is an authorized attempt to break into a system, done by a tester who acts like an attacker. The tester proves which weaknesses are real and shows the impact. You receive a written report with evidence, severity ratings and fix guidance. A scan lists possible issues. A pentest proves which ones can be used. Penetration testing is an authorized, time-boxed attempt to break into a system. A tester uses the same methods as an attacker to find weaknesses, prove which ones are real, and show what an attacker could reach. The output is a written report that your team can act on. A pentest needs written permission from the system owner. Without it, the same actions are an attack. ## What does a penetration test produce, and what types are there? A pentest produces evidence. A good report states each finding, how the tester proved it, how bad it is, and how to fix it. At the end you should hold: - A scope statement: what was tested, and what was not. - A list of findings, each with a severity rating. - Evidence for each finding: requests, responses, screenshots or scripts. - Steps to reproduce, so your developers can see the problem themselves. - Fix guidance for each finding. - A list of controls that held, so you know what worked. Excession Labs delivers reports in HTML, PDF and Markdown, with proof-of-concept scripts. To check the structure, see [a full demonstration report](/sample-report/). To use the report, read [how to read a penetration testing report](/guides/how-to-read-a-penetration-testing-report/). The type names the target, and each type needs different skills and tools. | Type | Target | Typical issues | Excession Labs | |---|---|---|---| | Web application | Sites and web apps | Broken access control, injection, session flaws, business logic | Yes | | API | REST and GraphQL back ends | Object-level authorization, mass assignment, missing rate limits | Yes | | SaaS platform | Multi-tenant apps with roles and billing | Tenant isolation, role checks, entitlement bypass | Yes | | Network | Hosts, ports, internal networks | Open services, weak configuration, lateral movement | No | | Cloud | Cloud accounts and configuration | Over-broad roles, public storage | No | | Mobile | iOS and Android apps | Insecure storage, weak transport, exposed keys | No | | Social engineering | People | Phishing, phone pretexts | No | Excession Labs tests web apps, APIs and SaaS products. If you need a network, cloud, mobile or social engineering test, ask the provider about those skills before you sign. For the three types we do, read the pages on [web application penetration testing](/web-application-penetration-testing/), [API penetration testing](/api-penetration-testing/) and [SaaS penetration testing](/saas-penetration-testing/). ## How is a pentest different from a vulnerability scan, and what are its phases? A scan is automated and broad. A pentest is guided by a person or an agent with a goal, and it proves impact: a scanner reports that something may be wrong, and a tester shows what an attacker can do with it. | | Vulnerability scan | Penetration test | |---|---|---| | Method | Automated checks against known signatures | Manual and tool-assisted testing with judgment | | Output | List of possible issues | Proven findings with evidence | | False positives | Common, you must triage | Reduced, each finding is reproduced | | Business logic | Mostly missed | A main focus | | Chaining | No | Yes, small issues combined into real impact | | Typical use | Frequent hygiene checks | Periodic assurance, release checks, customer reviews | For more depth, read the neutral comparison at [pentestcompare.com: pentest vs vulnerability scan](https://pentestcompare.com/guides/pentest-vs-vulnerability-scan/). Methods name the steps differently. Five phases follow: plan, discover, attack, report and retest. NIST SP 800-115, published in 2008 and still listed as final, describes four phases: planning, discovery, attack and reporting. [PTES](https://web.archive.org/web/20250103002608/http://www.pentest-standard.org/index.php/Pre-engagement), a community method last edited in 2014 (archived capture of the Pre-engagement page), lists seven sections, from pre-engagement to reporting. Neither is a current requirement, but both are useful models. 1. Plan: agree scope, rules, accounts and contacts. You sign the authorization. See [rules of engagement](/guides/penetration-testing-rules-of-engagement/). 2. Discover: the tester maps the app: pages, endpoints, roles, inputs and technologies. 3. Attack: the tester tries to use weaknesses. A good tester also runs controls: requests that should fail, to confirm that protections work. 4. Report: the tester writes findings with evidence and fixes. 5. Retest: after your team fixes issues, the tester checks each fix. See [after the pentest](/guides/after-the-pentest-remediation-and-retest/). The OWASP Web Security Testing Guide is a free, detailed list of web test cases. Testers can use it as a checklist for the discover and attack phases. ## A worked example: one finding from start to end > **Demonstration only:** This is a demonstration. No real customer system was tested. "Harbor Notes" is a fictional app. > > Harbor Notes lets a user open a note at `/api/notes/4021`. The tester signs in as Alice and opens her own note. The tester then signs in as Bob and requests Alice's note ID with Bob's session. > > ```http > GET /api/notes/4021 HTTP/1.1 > Host: app.example.com > Authorization: Bearer > ``` > > The server returns Alice's note. That is a broken object-level authorization flaw. A scanner does not know that note 4021 belongs to Alice, so it does not flag it. > > The tester then runs controls. A request for a note ID that does not exist returns 404. A request with no token returns 401. The controls show that the server checks identity but skips the ownership check. The report records the finding, the controls, and the fix: check that the note belongs to the caller before you return it. ## When should you get a pentest, and what are its limits? Get one before launch, before a customer security review, after major changes, and on a regular schedule. The timing depends on how often you ship. Read [how often should you pentest](/guides/how-often-should-you-pentest/) for the standards' wording and release triggers. Also decide how much the tester knows in advance, which changes cost and depth. See [black box, grey box and white box testing](/guides/black-box-grey-box-white-box-testing/). A pentest shows what a tester found within a scope and a time limit. New code brings new risk after the test ends. A report is not a compliance certificate. If a standard applies to you, ask your auditor what they need. ## Questions ### Is penetration testing legal? It is legal when the system owner gives written authorization first. Testing without that permission can break computer misuse laws. A signed scope and authorization letter protects both sides. Ask a lawyer about your own situation. This page is not legal advice. ### How long does a penetration test take? It depends on scope: the number of apps, roles, API endpoints and integrations. A small web app takes less time than a multi-tenant platform. Your provider should give a duration in the written scope. We do not publish fixed durations. ### Will a pentest break my production system? It can if the tester is careless. Agree the rules first: test accounts, rate limits, forbidden actions and stop conditions. You can test a staging copy or use test-owned data in production. Read the rules of engagement guide for the full list. ### Who should run a penetration test? Use a tester who is independent of the team that built the system. Ask for a sample report, the method, and how findings are proven. Certifications can help but do not replace evidence of real engagement work. ## Sources - [NIST SP 800-115: Technical Guide to Information Security Testing and Assessment (2008, final)](https://csrc.nist.gov/pubs/sp/800/115/final) - [OWASP Web Security Testing Guide](https://wstg.owasp.org/) - [PCI SSC Information Supplement: Penetration Testing Guidance v1.1 (2017)](https://listings.pcisecuritystandards.org/documents/Penetration-Testing-Guidance-v1_1.pdf) - [PTES Pre-engagement (Internet Archive capture, 2025-01-03)](https://web.archive.org/web/20250103002608/http://www.pentest-standard.org/index.php/Pre-engagement) --- # Penetration testing services for web apps, APIs and SaaS Source: https://penetrationtestservices.com/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Authorized penetration testing of web applications, APIs and multi-tenant SaaS by Excession Labs. Evidence for every finding, fixes and a retest. Penetration tests that prove every finding Excession Labs is a security lab that tests web applications, APIs and multi-tenant SaaS, and every finding in our report shows the request and response that prove it, what an attacker gains, and the fix. Request a testSee the sample report Demonstration F-01, Harbor Notes (demo app)Critical Any signed-in user can export another workspace's notes Assetapi.harbor-notes.exampleEvidenceObserved, read back from tenant BStatusOpen GET /v1/workspaces/ws_test_B/export Authorization: Bearer <tenant A member> → 200 OK, 41 notes, owner ws_test_B # control: nonexistent workspace GET /v1/workspaces/ws_test_ZZ/export → 404 Not Found Fix: check in the export handler that the caller belongs to the workspace. A check in the UI does not protect the API. Each finding also has reproduction steps and a retest resultOpen the sample report What we test Applications your customers sign in to. Most of our time goes into access control: whether one user can read or change another user's data, and what the API accepts without checking. Web applicationsAccess control, sessions and business logic. Roles, sharing, invitations, uploads, payments and admin paths. Web app testing APIsREST and GraphQL, documented or not. Object-level authorization, mass assignment, rate limits and data exposure. API testing Multi-tenant SaaSOne customer must never reach another. Two test tenants, every object type, every export and search path. SaaS testing or tenant isolation testing No-code and AI-built appsBubble, Supabase, Firebase and fast-built apps. Privacy rules, row-level security, exposed keys and workflow endpoints. Platform testing Before launchTest the release before customers do. Find the exploitable issues while fixing them is still cheap. Pre-launch testing For a customer reviewEvidence for an enterprise deal. A report your buyer's security team can read, with fixes verified. Customer security reviews Building with LLMs or agents? Those parts need a different test: AI application security assessments. How we prove a findingIf an API answers "200 OK" to a request that should fail, we still have to prove that something happened. We read the changed record back through a second account, show a cross-site attack working in a real browser, or show the exposed data from a test-owned record. Each attack also runs with controls: a nonexistent ID, the wrong record type, an unsigned token and an anonymous caller.Excession Labs is a security lab. Our AI testing agents run these checks across every role and endpoint in parallel, under tool rules we set for your scope. We agree the scope with you, check the evidence for each finding and write the report.Read the methodology or about Excession Labs. ObserveTenant A's token requests tenant B's export. The API returns 200 and 41 notes. ControlSame request for a workspace that does not exist returns 404, so the 200 is not a generic response. Read backTenant B's own session lists the same 41 note IDs. The data really belongs to tenant B. BoundOnly test-owned workspaces were used. No real customer data was read. VerdictConfirmed. Rated Critical, with a fix and a retest step. (Demonstration example.) What you get A written scope and rules of engagement that you approve An executive summary with the few actions that matter most For each finding: description, impact, verified results, reproduction steps and a fix Controls that held, so fixes do not break working protections A remediation plan and a list of limitations and open questions Evidence files and reproduction scripts, in HTML, PDF and Markdown A retest of fixed findings, agreed in the scope Open the sample report Short callWhat triggered the test, who will read the report, and your deadline. Written scope and authorizationHosts, roles, test windows, off-limits actions and contacts. The system owner signs. TestingAgainst the agreed rules, with bounded tests and test-owned data. ReportEvery finding with evidence, reproduction and a fix. Fix and retestWe verify your fixes and record the result. What you receive and how pricing worksYou receive a report in HTML, PDF and Markdown with proof-of-concept scripts, a fix for each finding and a list of the controls that held. We publish no prices. We quote after a short call and a written scope, and the pricing page lists what changes the quote. Request a testOpen the sample report Start with a free pilotFor a small number of teams with a real application and a reason to use the result, we run a free pilot. The scope is small and written down. You get every finding in that scope, with evidence and fix guidance, at no cost. Apply for a pilotPricing and scopeGuides ## Questions ### What does a penetration test from Excession Labs cover? Web applications, APIs (REST and GraphQL) and multi-tenant SaaS platforms. We agree the exact hosts, roles and actions in a written scope before testing. AI features such as chat assistants and agents are assessed separately through [airedteamingservices.com](https://airedteamingservices.com/). ### How much does a penetration test cost? We quote after a short scoping call, because the price depends on the number of applications, user roles, API endpoints and environments, and on your deadline. See [pricing and scope](/pricing/) for what changes a quote. Small teams can [apply for a free pilot](/free-pentest/). ### Do you test production? We prefer a staging environment with realistic data. If production is the only option, we agree limits in writing first: no destructive actions on real data, bounded tests of email, uploads and paid third-party services, and a contact who can stop testing at any time. ### Will our auditor or customer accept the report? Nobody can promise that. What we can do is ask what the reviewer needs during scoping, and write a report that shows scope, method, evidence and fixes clearly. Check the [sample report](/sample-report/) and share it with the reviewer before you buy. ### Do you use AI to test? Yes. Our AI testing agents run test tasks in parallel, as each user role, under tool rules we set for your scope. Excession Labs checks the evidence for each finding and writes the report. [How AI-assisted testing works](https://aipentestservices.com/how-it-works/). --- # How we test: our penetration testing methodology Source: https://penetrationtestservices.com/methodology/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Pentest methodology of Excession Labs: written authorization, evidence standard, controls, safety limits, evidence labels, severity scale and report contents. > **Short answer:** We test only with written authorization, inside a written scope. A finding counts only when we can show its effect, for example by reading a changed record back through another account or showing an attack in a real browser. Controls run next to every attack. Destructive proofs use test-owned data only. The report labels how each result was observed. ## Authorization, scope and test methods We test only systems whose owner has authorized the test in writing. The scope names the hosts, user roles, test windows, actions that are off limits and the people to contact. If something outside the scope looks relevant, we stop and ask. We choose from six methods for each application: - Direct HTTP requests against the web application and API, with each user role. - Browser-observed tests in Chromium. We prove cross-site request forgery and stored cross-site scripting in a real browser, in addition to reading the raw response. - Client-side review of the JavaScript bundles your application ships. - Differential comparison of anonymous, low-privilege and high-privilege callers on the same request. - Read-back from a second account, to prove that a change or exposure crosses an account or tenant boundary. - Control requests next to each attack request, described below. Our AI testing agents run these tasks in parallel under tool rules Excession Labs sets for your scope. Excession Labs checks the evidence for each finding and writes the report. For more, read [how AI-assisted testing works](https://aipentestservices.com/how-it-works/). We draw test ideas from public references such as the OWASP Web Security Testing Guide, the OWASP API Security Top 10 and OWASP ASVS. They guide coverage and do not certify anything. ## Evidence standard, controls and safety limits A finding goes in the report as confirmed only when we can show its effect: - A write is proven by reading the changed record back through another session. A response that says "success" is not proof. - An exposure is proven by showing the exposed data from a test-owned record. - A browser attack is proven by the attack working in a real browser. Each attack request has controls: a nonexistent identifier, the wrong record type, an unsigned or wrongly signed token, another account's token, and an anonymous caller. Controls show whether the first result means what it seems to mean. When a control shows that it does not, we downgrade or drop the finding. We also report controls that held: protections we tried to break and could not. Your engineers can then avoid weakening them while they fix other issues. Safety limits: - Destructive proofs use only test-owned accounts and records, marked as test data. - Tests of uploads, password guessing, email sending and paid third-party services are bounded. The report states what each cost. - Reproduction scripts are read-only where possible, and must only be run with authorization and test-owned identifiers. ## Evidence labels and severity | Label | Meaning | |---|---| | Observed | We reproduced the behavior during the assessment | | Browser-observed | A real browser demonstrated the behavior | | Inferred | A likely path we did not fully demonstrate. It is labeled as inferred and never presented as confirmed | Open questions go into a "Limitations and open questions" section, not into the confirmed impact. We rate each finding on a four-level scale with a written definition for each level. The definitions appear in every report, so a reader can see why a finding is rated High. | Rating | Typical meaning | |---|---| | Critical | An attacker can read or change data across tenants, or take over accounts or the platform, with little effort and no special access. Fix before the next release | | High | An attacker can cause serious harm to users or data, but needs a signed-in account, a user action, or a chain of two weaknesses | | Medium | The weakness has a real effect, but the effect is limited by scope, by required conditions, or by other controls that work | | Low | The weakness gives small information or hardening value. It does not give direct access to data or functions | We do not add CVSS scores by default. If your program needs CVSS vectors, tell us during scoping. ## The report Each report holds project details and scope, the method, safety constraints, evidence labels, an executive summary with priority actions, findings at a glance, the reproduction environment, a section per finding (description, impact, verified results, reproduction, remediation), controls that held, a remediation plan, limitations and open questions, an evidence index, and reproduction scripts. We deliver it in HTML, PDF and Markdown. See the [sample report](/sample-report/). Requests and responses are saved with timestamps. Key evidence can carry a SHA-256 manifest so you can check that it has not changed. ## Sources - [OWASP Web Security Testing Guide](https://owasp.org/www-project-web-security-testing-guide/) - [OWASP API Security Top 10 (2023)](https://owasp.org/API-Security/editions/2023/en/0x11-t10/) - [OWASP Application Security Verification Standard](https://owasp.org/www-project-application-security-verification-standard/) - [NIST SP 800-115, Technical Guide to Information Security Testing and Assessment](https://csrc.nist.gov/pubs/sp/800/115/final) --- # Security testing for no-code and AI-built apps Source: https://penetrationtestservices.com/no-code-app-security-testing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Security audit for Bubble, Supabase, Firebase and AI-built apps: privacy rules, RLS, Security Rules, exposed keys and workflow APIs, with a sample finding. > **Short answer:** Apps built on Bubble, Supabase or Firebase, or with AI coding tools, keep their data rules in platform settings instead of server code. Failures to check include missing privacy rules, tables without row level security, open Firebase rules and secret keys in the browser. We test those rules from the outside with test accounts and report what an attacker can read or change. If you built your product on Bubble, Supabase or Firebase, or with an AI coding tool, the platform handles the back end and your data rules live in settings. One wrong setting can expose a whole table. ## What goes wrong on these platforms, and what do the platform docs say? The browser talks to the data layer directly, and anyone can send the same requests with a different account or with none. The rules on the data layer are therefore the security boundary. | Platform | What we test | Issue it finds | |---|---|---| | Bubble | Privacy rules, Data API and Workflow API settings, search constraints | A data type readable by anyone, or fields inferred through search | | Supabase | RLS on every table, policies per role, storage policies, RPC functions, key use | A table readable or writable through the public API, or a service key in the browser | | Firebase | Security Rules for Firestore, Realtime Database and Storage, auth checks in rules | Rules that let any visitor, or any signed-in user, read other users' data | | Any, including AI-built | Keys and tokens in the client bundle, hidden routes, access control | Secrets in JavaScript, admin routes with no server check | In Bubble, privacy rules are "conditions you set up on each data type to protect your data from being viewed and edited by unauthorized users." Bubble describes them as additive: "you don't prohibit access, you grant it", and the user gets "the combined access of all matching rules, not the strictest one." The docs also say "Searches made through the Data API always need explicit permission", and warn that allowing a constraint on a field users cannot view "can expose that field's values through search inference." ([Bubble Docs](https://manual.bubble.io/help-guides/data/the-database/protecting-data-with-privacy-rules); see also the [security checklist](https://manual.bubble.io/help-guides/security/security-checklist)). Our guide to [Bubble app security](/guides/bubble-app-security/) goes further. The Supabase docs say a secret key "bypasses every Row Level Security policy you have", and to never put one "in a browser, a shipped application, or source control." A publishable key (the legacy `anon` key) is "safe to expose online" because it "only reaches what Row Level Security allows" ([Supabase API keys](https://supabase.com/docs/guides/api/api-keys)). The RLS guide says to enable RLS on every table in an exposed schema ([Supabase RLS](https://supabase.com/docs/guides/database/postgres/row-level-security)). See [Supabase security testing](/guides/supabase-security-testing/). Firebase says that because clients reach data directly, "Firebase Security Rules are the only safeguard blocking access for malicious users", and warns that a deployed app "is publicly accessible, even if you haven't launched it." It does not recommend leaving data open to any signed-in user ([Firebase rules basics](https://firebase.google.com/docs/rules/basics)). Read [Firebase Security Rules testing](/guides/firebase-security-rules-testing/). ## How do we test these apps? We call the data APIs directly, as an attacker would: with an anonymous session, with user A and with user B. We list what each can read and write. We review the client JavaScript for keys, project IDs and hidden endpoints, then check what each key reaches. A finding needs evidence of a real effect, so we prove a write by reading the record back through another session. We run controls beside each request (a nonexistent ID, a wrong record type, another account's token) and report the controls that held. See the [methodology](/methodology/). > **Sample finding:** This is a demonstration. It uses a fictional app, Harbor Notes, on a fictional Supabase project, and no real customer system was tested. > > **Finding N-02: The `profiles` table is readable by any visitor (High).** RLS is not enabled on the table. > > ```http > GET /rest/v1/profiles?select=email,plan&limit=2 HTTP/1.1 > Host: demo-project.example.com > apikey: > ``` > > ```http > HTTP/1.1 200 OK > [{"email":"a@example.com","plan":"pro"},{"email":"b@example.com","plan":"free"}] > ``` > > Observed with no user session. Control: the `notes` table, which has RLS, returned an empty list for the same call. Fix: enable RLS on `profiles` and add a policy limited to the owner. ## What do you get, what do we need from you, and what changes the quote? You get a report in HTML, PDF and Markdown with proof-of-concept scripts, an executive summary, and a fix for each finding that names the platform setting to change. We can retest fixes; see [retesting](/retesting/). If you are about to launch, read [pre-launch security testing](/pre-launch-security-testing/). - Written authorization from the system owner. - The app URL and the platform in use. - Test accounts for each role, at least two users. - Optional: read-only access to privacy rules, RLS policies or Security Rules. - A list of third-party services the app calls. - A technical contact. The number of data types or tables, roles, workflows, API routes and integrations drives effort, and so does a retest. See [pricing](/pricing/). We publish no prices. For a small app, ask about the [free pentest pilot](/free-pentest/), or [request a pentest](/contact/). ## Questions ### Is a Supabase anon key in my front-end code a problem? Not by itself. Supabase says the publishable (formerly anon) key is safe to expose online, because it only reaches what Row Level Security allows. The risk is a table with RLS off, a policy that is too loose, or a secret key in the browser. ### Do Bubble apps need a pentest? Bubble runs the platform, but your privacy rules, workflows and API settings are yours. A test checks whether those settings protect your data from users who call the app directly. ### Can you test an app that an AI coding tool built? Yes. We test the running app and API from the outside, as we test any app. AI-generated code is not a separate category for us. We look at access control, keys in client code, rules and configuration. ### Do you need access to my Supabase or Firebase console? Not for the core test. Black-box and grey-box testing use test accounts. Read-only access to rules and policies speeds up the work and makes fixes more exact. Tell us at scoping what you can share. ## Sources - [Bubble Docs: Protecting data with privacy rules](https://manual.bubble.io/help-guides/data/the-database/protecting-data-with-privacy-rules) - [Bubble Docs: Security checklist](https://manual.bubble.io/help-guides/security/security-checklist) - [Supabase Docs: Row Level Security](https://supabase.com/docs/guides/database/postgres/row-level-security) - [Supabase Docs: Understanding API keys](https://supabase.com/docs/guides/api/api-keys) - [Firebase Security Rules](https://firebase.google.com/docs/rules) - [Firebase Security Rules: basics](https://firebase.google.com/docs/rules/basics) --- # Pre-launch security testing for web apps and APIs Source: https://penetrationtestservices.com/pre-launch-security-testing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Pre launch security testing for a web app or API: the seven areas to test first, how to set up staging, and what the pentest report should give your team. > **Short answer:** Pre-launch security testing is a penetration test of a web app or API before real users and data arrive. Test a build that is close to final, in a staging environment that matches production. Start with access control, authentication, and payments or personal data. Fix, then retest, before launch. Once real users arrive, a flaw costs more to fix and can expose real data. A pre-launch penetration test can find serious problems while you can still change the plan. ## What should we test first, and how should staging look? Test the places where one mistake has the largest effect. [OWASP Top 10:2025](https://owasp.org/www-project-top-ten/) puts Broken Access Control first, so start there. | Priority | Area | Issue it finds | |---|---|---| | 1 | Access control | User A can read or change user B's data by changing an ID | | 2 | Authentication and sessions | Weak reset flows, tokens that never expire, MFA bypass | | 3 | Payments and entitlements | A user reaches paid features, or changes a price or quantity | | 4 | Personal and sensitive data | Fields in API responses that the screen never shows | | 5 | File upload and import | Unsafe file types, files served to the wrong user | | 6 | Secrets and client code | Keys, tokens and hidden routes in JavaScript bundles | | 7 | Rate limits | Unbounded sign-up, login or email sending | The [web application](/web-application-penetration-testing/) and [API](/api-penetration-testing/) pages cover each area in more detail. Make staging match production: same code, same auth settings, same API gateway rules. Load it with test data that looks real but is not. Give us test accounts for each role, in two separate users or tenants. Turn off real email and payment sending, or point them at sandboxes. Tell us about any WAF or rate limit so we can plan around it. Freeze the build we test. Give us the version or commit tag. Our guide on [how to prepare for a penetration test](/guides/how-to-prepare-for-a-penetration-test/) lists freeze windows, allowlists and contacts. ## How we prove findings and what you get A finding needs evidence of a real effect. We prove a write by reading the record back through another session. We run controls beside every exploit request: nonexistent IDs, wrong record types, another account's token and anonymous calls. The report lists the controls that held, so you know what to keep when you change code. > **Sample finding:** This is a demonstration. It uses a fictional app, Harbor Notes, and no real customer system was tested. > > **Finding L-01: A client bundle exposes an admin route (Medium).** Review of `main.4f2a.js` on staging showed a path the interface never links. > > ```http > GET /api/admin/users?limit=5 HTTP/1.1 > Host: staging.example.com > Cookie: session= > ``` > > ```http > HTTP/1.1 200 OK > {"users":[{"email":"a@example.com","role":"user"}, ...]} > ``` > > Observed with a normal user session. The same call without a session returned 401, which held. Fix: require the admin role on the server for every `/api/admin` route. You get a report in HTML, PDF and Markdown, with proof-of-concept scripts. It contains an executive summary with priority actions, a findings table, and one section per finding with impact, reproduction and remediation. After your fixes, we can retest (see [retesting](/retesting/)). If you built the product on a no-code platform or with AI coding tools, read about [no-code app security testing](/no-code-app-security-testing/). ## What we need from you and what changes the quote We need: - Written authorization from the system owner. - A staging URL and the build or tag to test. - Test accounts for each role, in at least two users or tenants. - Docs, an API collection or a route list. - Details of integrations and what we must not touch. - A technical contact, and your planned launch date so we can discuss fix time. Scope sets the price: features, roles, API routes, integrations and how close staging is to production. A retest adds work. We publish no prices (see [pricing](/pricing/)). Early-stage and want to try us? Ask about the [free pentest pilot](/free-pentest/). Otherwise [request a pentest](/contact/). ## Questions ### How late can we test before launch? Leave time to fix and retest. A test the day before launch finds problems you cannot fix in time. Test a build that is feature-complete, then freeze changes to the areas we test. Plan a fix window after the report. ### Can we test on staging instead of production? Yes. We prefer staging for pre-launch work. Staging must match production in code, configuration and auth. If staging differs, tell us what differs. Findings that depend on production-only settings need a short check there. ### What if the app is still changing? Agree a build or release tag to test. We test that version and report against it. Later changes are out of scope until a new test or retest. Large late changes are why teams schedule a second, smaller test. ### Do we need a full test, or can we start smaller? You can start with the highest-risk areas: sign-in, access control, payments and file handling. A narrow scope costs less and gets answers sooner. Say what you will not test, so the report states it. ## Sources - [OWASP Top 10:2025](https://owasp.org/www-project-top-ten/) - [OWASP Web Security Testing Guide](https://wstg.owasp.org/) --- # Penetration testing pricing: what changes the quote Source: https://penetrationtestservices.com/pricing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Penetration testing cost at Excession Labs: ten scope factors that change the quote, what every quote includes, and how to request a written proposal. > **Short answer:** We quote each test after a short scoping call, because the price depends on how much there is to test: applications, user roles, API endpoints, tenants and environments, plus your deadline and retest needs. Every quote lists what it includes. We do not publish fixed prices yet. Small teams can apply for a free, scoped pilot. ## What changes the quote Each factor changes how much testing time your scope needs. We ask about all of them on the scoping call. | Factor | Why it matters | What to have ready | |---|---|---| | Applications and hosts | Each web app, API and admin panel is a separate attack surface | The list of domains and which are in scope | | User roles | Access control is tested between every pair of roles | Role names and one test account per role | | API size | Each endpoint and object type needs authorization checks | OpenAPI file, Postman collection or GraphQL schema | | Tenants | Multi-tenant products need cross-tenant tests for every object type | Two test tenants with sample data | | Integrations | Payments, email, uploads and SSO add flows and abuse cases | Which integrations are live in the test environment | | Testing depth | Black box takes longer to reach the same coverage than grey box | Docs, accounts and, if possible, source access | | Environment | Production needs tighter limits and more coordination | A staging environment with realistic data, if you have one | | Deadline | A fixed report date can change how we plan the work | The date and who will read the report | | Report needs | Some reviewers ask for specific sections or a summary they can share | The request or questionnaire you received | | Retest | Fix verification takes time after your changes ship | When you expect to deploy fixes | ## What every quote includes and how to get one Every quote includes: - A written scope: hosts, roles, test windows, off-limits actions and contacts. - Testing under agreed rules, with bounded tests and test-owned data. - A report with evidence, reproduction steps and a fix for every finding, plus controls that held, a remediation plan and limitations. See the [sample report](/sample-report/). - Retest terms, stated in the quote. To get a quote: 1. [Send a request](/contact/) with the application, the reason and your deadline. 2. We reply by email to arrange a short call. 3. We send a written scope and quote. 4. Testing starts after the system owner signs the authorization. You can buy in three ways: | Option | Fits when | Next step | |---|---|---| | Free pilot | You want to judge our work on a small, real scope | [Apply](/free-pentest/) | | One-off test | A customer, auditor or launch needs a report | [Request a quote](/contact/) | | Recurring testing | You ship often and want each significant release tested | [How it works](/recurring-penetration-testing/) | ## Questions ### Why don't you publish a price? Two applications with the same number of pages can need very different effort. One may have three roles and a public API, the other a single role. A fixed price would overcharge the simple case or skip work in the complex one, so we write a quote for your actual scope. ### What do other providers charge? Published prices vary a lot, and the units differ: per test, per day, per year or credits. PentestCompare lists [published prices with their units and sources](https://pentestcompare.com/guides/penetration-testing-cost/). ### Is the retest included? We agree retest terms in each quote, so you know before you sign which findings are retested and when. ### Can we start with something small? Yes. A [free pilot](/free-pentest/) covers a small written scope, such as one critical user flow or a set of API endpoints. You keep every finding in that scope. --- # Privacy notice Source: https://penetrationtestservices.com/privacy/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 What Penetration Test Services collects when you visit or send a request, why, where it is stored, how long we keep it and how to contact Excession Labs about your data. Effective 9 November 2026. Penetration Test Services (penetrationtestservices.com) is operated by **Excession Labs**. Contact: [privacy@excessionlabs.ai](mailto:privacy@excessionlabs.ai). ## When you send a form We collect what you type in the form, for example your name, work email, company, application address and your answers about the test you need. We also record: - the page you sent it from, the first page you visited in that browser session, the referring website and any campaign tags in the link (such as `utm_source`); - your country, your browser's user-agent string and a one-way hash of your IP address, used to stop abuse. We use these details only to reply to you, to scope and deliver the work you asked about, and to understand which pages bring requests. We store submissions in a Cloudflare D1 database and send a copy to the Excession Labs team mailbox by email. Do not send passwords, API keys, system prompts or real customer data through a form or by email. We agree access to test systems separately and in writing. ## Spam protection Forms use [Cloudflare Turnstile](https://www.cloudflare.com/turnstile-privacy-policy/) to check that a person sent them. Turnstile processes signals from your browser to do this. ## Analytics We use [PostHog](https://posthog.com/privacy) (EU cloud) in cookieless mode to count page views and clicks on calls to action. It does not set cookies and does not record sessions. PostHog may use your IP address to estimate your location. We also keep our own log of some clicks (for example on calls to action or links to other sites) with the page, time and country, without cookies. The site keeps the landing page, referrer and campaign tags of your visit in your browser's session storage, so a form can include them. Session storage is cleared when you close the tab. ## Sharing We do not sell your data and we do not use advertising pixels on this site. Service providers that process data for us: Cloudflare (hosting, database, email routing and Turnstile) and PostHog (analytics). ## How long we keep it We keep enquiries for as long as we need them to reply, to do the work you asked for, and to follow up on that business relationship. Then we delete them. You can ask us to delete them at any time. ## Your choices Email [privacy@excessionlabs.ai](mailto:privacy@excessionlabs.ai) to see, correct, export or delete the data we hold about you. You can block analytics with any content blocker; the site works without it. --- # Recurring penetration testing after each release Source: https://penetrationtestservices.com/recurring-penetration-testing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Recurring vs continuous penetration testing: we test web apps and APIs after meaningful releases, scope only what changed, and verify fixes from the last report. > **Short answer:** Recurring penetration testing means repeating a scoped test after meaningful releases, instead of once a year. Each round covers what changed: new features, new roles, new API routes and fixes from the last report. Excession Labs agrees the scope for each round in writing. It is release-driven testing and does not monitor your system around the clock. If your product ships every week, a single annual pentest describes a version of the app that no longer exists. Recurring penetration testing repeats the test after the releases that matter, so the evidence stays current. We test at agreed points. We do not watch your system around the clock, so this is not continuous testing. ## When should you test again? Test after each meaningful release. A change is meaningful when it can alter who may do what or where data flows. Common triggers: - A new role, permission or sharing feature. - A new API route, version or public integration. - A change to sign-in, SSO, MFA or password reset. - A change to billing, plans or entitlements. - A new file upload, import or export. - A new tenant model, database or storage layout. - Fixes from the last report, which need verification. Standards set minimums that a fast-moving team may need to exceed. PCI DSS v4.0.1 requires penetration tests at least once every 12 months and after significant change (requirement 11.4; see the [PCI DSS v4.0 summary of changes](https://listings.pcisecuritystandards.org/documents/PCI-DSS-v3-2-1-to-v4-0-Summary-of-Changes-r1.pdf); confirm the wording with your assessor). The [PCI SSC](https://blog.pcisecuritystandards.org/just-published-pci-dss-v4-0-1) states v4.0.1 is the only active version. In the Cobalt 2025 survey, 27% of respondents tested annually and 30% quarterly ([Cobalt, Figure 5](https://resource.cobalt.io/state-of-pentesting-2025)). The guide [how often should you pentest](/guides/how-often-should-you-pentest/) covers the rest. ## What does a recurring round cover, and how do we prove findings? Each round has a written scope in three parts. | Part | What it covers | |---|---| | Scope delta | What changed since the last test: features, roles, routes, integrations | | Fix verification | Each finding from the last report, checked again | | Regression checks | Controls that held last time, tested again near the changes | A delta round is smaller, so it costs less than a first test. The first round sets the baseline: a full test of the app, with a report that lists controls that held. The evidence standard does not change between rounds. A finding needs evidence of a real effect. We read the changed record back through another session, run controls next to each exploit request, and save requests and responses with timestamps. The [methodology](/methodology/) page has the details. > **Sample finding:** This is a demonstration. It uses a fictional app, Harbor Notes, and no real customer system was tested. > > **Finding R-05 (round 2): New share links ignore the note's owner check (Medium).** Release 2.8 added share links. The previous round tested `/api/notes/{id}` only. > > ```http > POST /api/notes/7731/share HTTP/1.1 > Host: api.example.com > Authorization: Bearer > ``` > > Note 7731 belongs to user B. The response was `201 Created` with a link, and the link opened the note anonymously (Observed). Control: note `999999` returned 404. Fix: apply the same owner check on the share route. ## What you get and what we need from you Each round produces a report in HTML, PDF and Markdown, with proof-of-concept scripts. The report contains an executive summary, a findings table, per-finding sections and a remediation plan. A fix-verification section states the result for each earlier finding. See [retesting](/retesting/) for what a retest result looks like. For each round we need: - A release note or change list since the last round. - A build or tag to test, and a staging or production plan. - Updated accounts and roles, including any new ones. - Updated API docs or a route list. - The last report's open findings and what you changed. - Written authorization for each round, and a technical contact. ## What changes the quote? The size of the scope delta, the number of new roles and routes, and the number of fixes to verify set the quote. So does the number of rounds per year. We publish no prices and quote after scoping (see [pricing](/pricing/)). The guide [after the pentest: remediation and retest](/guides/after-the-pentest-remediation-and-retest/) covers what happens after the report. To start, [request a pentest](/contact/). ## Questions ### How often should we test? Test after changes that touch authentication, access control, payments, data models or new integrations. Standards such as PCI DSS set a minimum of once every 12 months and after significant change. See our guide on how often to pentest. ### Is this the same as continuous testing? No. We test at agreed points, after releases that matter. We do not watch your system around the clock. Say what triggers a new round, and we agree the scope each time. ### Does each round retest old findings? Fixes from the last report can be included in the next round's scope. We verify that each fix works and that nearby controls still hold. Agree this at scoping. ### Can we change the scope between rounds? Yes. Scope follows your product. Each round has its own written scope, so new features, roles and APIs are added when they ship. ## Sources - [PCI SSC blog: Just Published: PCI DSS v4.0.1](https://blog.pcisecuritystandards.org/just-published-pci-dss-v4-0-1) - [PCI DSS v3.2.1 to v4.0 Summary of Changes, r1](https://listings.pcisecuritystandards.org/documents/PCI-DSS-v3-2-1-to-v4-0-Summary-of-Changes-r1.pdf) - [Cobalt State of Pentesting Report 2025, Figure 5](https://resource.cobalt.io/state-of-pentesting-2025) --- # Research Source: https://penetrationtestservices.com/research/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Lab research by Excession Labs on deliberately vulnerable applications, with the requests, evidence and code to reproduce every result. We publish two kinds of evidence, and we label each one. **Lab research** tests deliberately vulnerable open-source applications in our own lab. Anyone can rerun it: each study lists the application version, the test setup, the requests and responses, and what we got wrong as well as what we found. **Case studies** come from authorized tests of real systems. We publish one only with the system owner's written permission, with identifying details removed. Research is in progress. --- # Penetration testing retest: checking that fixes work Source: https://penetrationtestservices.com/retesting/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 A penetration testing retest checks that fixed findings are really fixed. See what we check, the four result statuses with a sample, and what you receive. > **Short answer:** A penetration testing retest repeats the original proof for each finding you fixed, then reports whether the issue is closed. Excession Labs can retest fixed findings. A good retest result shows the original request, the new response, the controls that still hold, and a status for each finding: fixed, partly fixed, not fixed or not retested. Once your team has fixed the findings in a report, a retest shows whether the fixes work. It uses the same proof as the original finding. ## Why retest, and what does a retest check? Fixes often close one path and leave another. In the Cobalt State of Pentesting Report 2025, less than half (48%) of all findings ever get resolved, and the median time to resolve is 67 days ([Cobalt, p.4](https://resource.cobalt.io/state-of-pentesting-2025)). PCI SSC's v4.0 summary of changes says exploitable vulnerabilities found during a penetration test are corrected "in accordance with the entity's assessment of the risk" (requirement 11.4.4) ([PCI SSC](https://listings.pcisecuritystandards.org/documents/PCI-DSS-v3-2-1-to-v4-0-Summary-of-Changes-r1.pdf)). Confirm the wording with your assessor. For each finding you fixed, we do four things. 1. Replay the original proof against the fixed build. 2. Run the original controls again: nonexistent IDs, wrong record types, another account's token, anonymous calls. 3. Try close variants: other routes, other verbs, other roles. 4. Check that the protections that held in the first test still hold. ## What does a retest result look like? Each finding gets a status and the evidence behind it. | Status | Meaning | |---|---| | Fixed | The original proof no longer works. Variants and controls were checked. | | Partly fixed | The original path is closed but a variant still works. | | Not fixed | The original proof still works. | | Not retested | The fix was not deployed, or the finding was accepted as a risk. | > **Sample retest result:** This is a demonstration. It uses a fictional app, Harbor Notes, and no real customer system was tested. > > **Finding H-02: A user can read another user's note. Status: Partly fixed.** > > Original request on the fixed build: > > ```http > GET /api/notes/7731 HTTP/1.1 > Host: app.example.com > Cookie: session= > ``` > > ```http > HTTP/1.1 403 Forbidden > ``` > > The original path is closed (Observed). The variant `GET /api/notes/7731/history` still returned `200 OK` with the note text of user B (Observed). Controls held: note `999999` returned 404, anonymous returned 401. Fix: apply the owner check to the history route. You get an updated report in HTML, PDF and Markdown. It keeps the original finding, adds the retest result and the evidence, and lists any new issues found near the fix. You can show it to an auditor or a customer who asks whether findings were closed. We do not promise that any buyer or auditor will accept it. See [customer security review](/customer-security-review/). ## What we need from you and what changes the quote Before a retest we need: - The list of findings you fixed, and a short note on each fix. - The fixes deployed to the environment we test. - Working test accounts for every role from the first test. - A technical contact for questions. - Written authorization that still covers the scope. The guide on [what happens after a pentest](/guides/after-the-pentest-remediation-and-retest/) covers triage and fix order. If you test after each release, see [recurring penetration testing](/recurring-penetration-testing/). The number of findings to verify, their type, the number of roles and the state of the environment set the effort. Retest terms are agreed in the scope for your project. We publish no prices (see [pricing](/pricing/)). Have a report and want the fixes checked? [Request a retest](/contact/). ## Questions ### How many retests can we have? Retest terms are agreed in the scope for your project. Ask at scoping what your project includes. Fixing the findings in one batch and retesting them together keeps the result clear. ### What do we need to do before a retest? Deploy the fixes to the environment we test, tell us which findings you changed, and keep the test accounts working. A short note on each fix helps us choose the right checks. ### What if a fix fails? We report the finding as not fixed or partly fixed, with the request and response that show it. We give updated guidance. You can fix again and ask for another check. ### Can a retest find new issues? Yes, near the fix. A fix often moves the weakness. We check nearby routes and the controls from the first test. A full new test is a separate scope. ## Sources - [PCI DSS v3.2.1 to v4.0 Summary of Changes, r1](https://listings.pcisecuritystandards.org/documents/PCI-DSS-v3-2-1-to-v4-0-Summary-of-Changes-r1.pdf) - [Cobalt State of Pentesting Report 2025, p.4](https://resource.cobalt.io/state-of-pentesting-2025) --- # SaaS penetration testing for multi-tenant products Source: https://penetrationtestservices.com/saas-penetration-testing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 SaaS penetration testing for multi-tenant products: eight areas we test, the two-tenant method, a sample finding, and what the report contains. > **Short answer:** SaaS penetration testing checks that one customer cannot reach another customer's data, and that roles, invitations, billing and admin functions behave as designed. Excession Labs uses a two-tenant method: two separate tenants, at least two roles each, and read-back proof from a second account. You get a written report with evidence, controls and fix guidance. If you sell software to businesses, each customer expects you to keep their data apart from everyone else's. SaaS penetration testing checks that separation, plus the paths enterprise buyers ask about: roles, invitations, billing and admin access. | You get | Detail | |---|---| | Scope | Web app, API and tenant boundaries, agreed in writing and authorized by the system owner | | Report | HTML, PDF and Markdown, with proof-of-concept scripts | | Proof | Request and response evidence for each finding, with controls | | Controls that held | The isolation checks that passed, listed in the report | | Fix guidance | Remediation for each finding, and a plan across findings | | Next step | [Open the sample report](/sample-report/) or [request a test](/contact/) | ## What do we test in a SaaS product? Broken access control is first on the [OWASP Top 10:2025](https://owasp.org/www-project-top-ten/). In SaaS, a tenant boundary can hold in the interface and fail in the API. | Area | Question we answer | |---|---| | Tenant isolation | Can a user in tenant A read, change or delete a record in tenant B by changing an ID? | | Search, export and reports | Do search results, CSV exports and dashboards filter by tenant on the server? | | Files and attachments | Can a file URL from tenant A be opened by tenant B or by an anonymous user? | | Roles and permissions | Can a Viewer do what an Editor does? Can a Member do what an Admin does? | | Invitations and sign-up | Can an invite be reused, redirected to another tenant, or accepted with a different role? | | Billing and entitlements | Can a user reach paid features, exceed seat or usage limits, or abuse trials? | | Admin and support paths | Can an ordinary user reach internal admin routes, impersonation or feature flags? | | Integrations | Do API keys, webhooks and OAuth apps stay inside their tenant and scope? | For the tenant boundary alone, see [tenant isolation testing](/tenant-isolation-testing/). The SaaS work sits on top of the checks described in [web application](/web-application-penetration-testing/) and [API testing](/api-penetration-testing/). ## How does the two-tenant method work? We ask for two tenants with at least two roles in each, and create test objects in each tenant. Then we replay the in-scope requests from tenant A with tenant B's identifiers, and the reverse. We repeat it for every role. Each result is compared with controls: a nonexistent ID, a wrong record type, another account's token and an anonymous call. A cross-tenant effect counts only when we read the changed or exposed record back through the second tenant's session. The guide on [multi-tenant isolation testing](/guides/multi-tenant-isolation-testing/) has the full matrix. > **Sample finding:** This is a demonstration. It uses a fictional app, Harbor Notes Teams, and no real customer system was tested. > > **Finding S-03: The export endpoint ignores the tenant (High).** A user in tenant `t_acme` requests an export with a report ID from tenant `t_globex`. > > ```http > POST /api/exports HTTP/1.1 > Host: app.example.com > Cookie: session= > Content-Type: application/json > > {"report_id":"rpt_55120"} > ``` > > ```http > HTTP/1.1 202 Accepted > {"export_id":"exp_9021","status":"queued"} > ``` > > The download from `exp_9021` contained rows owned by `t_globex` (Observed, read back with the acme session). Controls: report `rpt_00000` returned 404, and an anonymous call returned 401. Fix: resolve the report by tenant and ID together, in one query. ## What do you receive, and what do we need from you? You get a report in HTML, PDF and Markdown, with proof-of-concept scripts. It contains an executive summary with priority actions, a findings table, and one section per finding with impact, verified results, reproduction and remediation. A "Controls that held" section lists the isolation checks that passed, which enterprise buyers ask about. We can retest fixes (see [retesting](/retesting/)). For buyer reviews, see [customer security review](/customer-security-review/). For the test we need: - Written authorization from the system owner. - Two test tenants with test data, marked as test data. - At least two roles per tenant, with credentials for each. - A description of the role model and plan tiers. - Documentation or a route list for the API. - Any SSO, SCIM or webhook setup that is in scope, plus a contact for questions. ## How does pricing work? We publish no prices. We quote after a short call and a written scope, and the quote lists what it includes. | What changes the quote | Why | |---|---| | Roles and tenant-scoped objects | Each pair of roles and each object type needs its own cross-tenant checks | | Integrations, SSO and SCIM | Each adds flows and abuse cases | | Admin and billing paths | Each is a separate surface | | API surface | More routes need more authorization checks | | Retest | We agree retest terms in the quote | [Pricing](/pricing/) lists every factor. Small teams can [apply for a free pilot](/free-pentest/). ## What is the next step? Read the [sample report](/sample-report/), then [request a SaaS pentest](/contact/). We reply with questions for a short scoping call. ## Questions ### Why does a SaaS product need a different test from a normal web app? A SaaS product serves many customers from one system. The worst failure is one tenant seeing another's data. A normal test may use one account and miss this. We test with two tenants and several roles so cross-tenant access shows up. ### Do you need production data? No. We ask for test tenants with test data that you mark as such. We do not need real customer records. If you can only test in production, we use test-owned tenants and say so in the scope. ### Will you test billing and plan limits? Yes, where they are in scope. We check whether a user can reach paid features without the plan, change seat counts, or reuse trial and coupon flows. Tests that call a paid third-party API are bounded and their cost is stated in the report. ### Do you test single sign-on and SCIM? If SSO or provisioning is in scope, we check how identities map to tenants and roles. We need a test identity provider or a staging configuration. Tell us at scoping which protocols you support. ## Sources - [OWASP Top 10:2025, A01 Broken Access Control](https://owasp.org/www-project-top-ten/) - [OWASP API1:2023 Broken Object Level Authorization](https://api-security.owasp.org/editions/2023/en/0xa1-broken-object-level-authorization/) --- # Sample penetration testing report for a fictional app Source: https://penetrationtestservices.com/sample-report/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 A full sample penetration testing report for a fictional SaaS app: scope, method, nine findings with requests and evidence, controls that held, and a fix plan. > **Short answer:** This sample penetration testing report shows what Excession Labs delivers: project details, method, safety limits, evidence labels, an executive summary, nine findings with HTTP evidence and fixes, controls that held, a remediation plan and an evidence index. The target is a fictional app called Harbor Notes. > **Demonstration:** This is a demonstration report for a fictional application. No real customer system was tested, and the findings below were written to show our report format and evidence standard. > > Harbor Notes, its hosts, accounts, identifiers and file hashes are invented. Request and response bodies are shortened for reading. 01Project details 02Assessment method and evidence labels 03Safety constraints 04Executive summary 05Findings at a glance 06Reproduction environment 07Findings F-01 to F-09 08Controls that held 09Remediation plan 10Limitations and open questions 11Evidence index ## Project details The report covers a grey-box assessment of the Harbor Notes web application and JSON API. | Item | Value | |---|---| | Client | Harbor Notes (fictional) | | Report title | Web application and API security assessment | | Assessment type | Authorized grey-box web and API assessment with a final written report | | Testing window | 2026-10-19 to 2026-10-23 | | Report date and version | 2026-11-09, version 1.0 | | Authorization | Written scope and authorization signed by the system owner before testing | | Test accounts | Two tenants, three roles per tenant (owner, admin, member), created for this test and marked as test data | We tested only the assets marked Yes. | Asset | Type | In scope | |---|---|---| | `https://app.harbor-notes.example` | React single-page app, Node.js backend | Yes | | `https://api.harbor-notes.example/v1/` | JSON API used by the app | Yes | | Client-side JavaScript bundles served by the app | Static files | Yes, read-only review | | Admin panel at `/admin` on the app host | Web UI and API routes | Yes | | Payment provider checkout pages | Third-party | No | | Email delivery provider | Third-party | No | | Denial-of-service and load testing | Method | No | | Social engineering of staff | Method | No | Features reached through the in-scope hosts: workspaces, notes, sharing links, file attachments, invitations, billing plans, a search endpoint and the admin panel. The severity scale has four levels. We add no CVSS vectors by default. If your program needs CVSS, say so during scoping and we agree the method in writing. | Level | Definition | |---|---| | Critical | An attacker can read or change data across tenants, or take over accounts or the platform, with little effort and no special access. Fix before the next release. | | High | An attacker can cause serious harm to users or data, but needs a signed-in account, a user action, or a chain of two weaknesses. | | Medium | The weakness has a real effect, but the effect is limited by scope, by required conditions, or by other controls that work. | | Low | The weakness gives small information or hardening value. It does not give direct access to data or functions. | We set each level from the effect we proved. When a control changes our view, we change the level and say so in the finding. ## Assessment method and evidence labels We tested the application as an authenticated user with known roles. We started from the client JavaScript bundle and the observed API traffic, then tested each object type and function against the roles. We used these methods: 1. Direct HTTP requests: we sent requests with an HTTP client, with and without credentials, and saved each request and response with a timestamp. 2. Browser-observed tests: we checked cross-site request forgery and script injection in a real Chromium browser. A script that runs in a browser is stronger proof than a payload that appears in a response body. 3. Client bundle review: we read the JavaScript bundles to list API routes, feature flags and client-side role checks that the server may not repeat. 4. Differential comparison: we sent the same request as an anonymous caller, as each role in tenant A and as each role in tenant B, then compared status codes and bodies. 5. Read-back from a second account: when a request changed data, we read the record back through a different session. A "200 OK" or `{"success": true}` is not proof of an effect. 6. Controls: we ran control requests next to each exploit request: nonexistent identifiers, wrong record types, unsigned or wrongly signed cookies, another account's token, and anonymous against signed-in comparisons. Every finding carries one evidence label. The label tells you how strongly the evidence supports the claim. | Label | Meaning | |---|---| | Observed | We reproduced the effect with direct requests and confirmed it by read-back or a control. | | Browser-observed | We showed the effect in a real Chromium browser session. | | Inferred | We believe the path exists from code review or indirect signs, but we did not prove it. We label it separately and keep it out of confirmed impact. | Open questions go to the section "Limitations and open questions", never into confirmed impact. ## Safety constraints Destructive proofs used only test-owned accounts and records. Each record we created carried the marker `EXC-TEST` in its title, so the client can find and remove it. | Area | Limit applied | |---|---| | Accounts and data | Only the six test accounts and records they own. No real user data was read. Where a finding crossed tenants, tenant B was a test tenant we owned. | | Uploads | At most 12 files, each under 4 KB, text and SVG only. We deleted them after the test. | | Password guessing | We guessed no passwords on any account. The reset test used one test account and at most 60 reset requests. | | Email | Reset and invitation emails went only to test inboxes. The bounded reset test sent 60 emails. | | Paid third-party APIs | None called. Billing routes were read, not executed. | | Scripts | Reproduction scripts are read-only where possible. Run them only with authorization and with test-owned identifiers. | ## Executive summary We found nine issues in Harbor Notes: one Critical, three High, three Medium and two Low. In one issue, a signed-in user in one test tenant downloaded the exported workspace of another test tenant. We proved this with two test tenants we own. 9Findings 1Critical 3High 3Medium 2Low 7Controls that held Counts come from the findings table, as of the report date 2026-11-09. One finding (F-08) started as High and ended as Low after a control showed our first reading was wrong. We kept it in the report to show the change. The root causes fall into three groups. Object access checks are missing on some routes (F-01, F-04). Browser protections are missing or incomplete (F-02, F-03, F-07). Abuse limits and lifecycle rules are missing (F-05, F-06). The two Low findings are hardening items. Priority actions: 1. Add a workspace ownership check to the export download route (F-01). Until it ships, disable the route or block cross-tenant reads at the API gateway. 2. Encode note titles where they are rendered, and add a Content Security Policy (F-02). 3. Bind the login flow to a CSRF token and set `SameSite` on the session cookie (F-03). 4. Ignore the `role` field from the client on invitation requests (F-04). 5. Add per-account and per-address limits to password reset (F-05). Actions 1 to 4 close the High and Critical findings. Action 5 is the first Medium item and uses a standard control. ## Findings at a glance Every finding appears here with its severity, current status and evidence label. Status is the state at report delivery. No fixes have been verified yet. | ID | Title | Severity | Status | Evidence label | |---|---|---|---|---| | F-01 | Cross-tenant export download through predictable export IDs | Critical | Open | Observed | | F-02 | Stored XSS in shared note titles | High | Open | Browser-observed | | F-03 | Login CSRF on the sign-in endpoint | High | Open | Browser-observed | | F-04 | Invitation endpoint accepts a client-chosen role | High | Open | Observed | | F-05 | No rate limit on password reset | Medium | Open | Observed | | F-06 | Share links survive when a note becomes private | Medium | Open | Observed | | F-07 | Attachments served inline with the uploaded content type | Medium | Open | Browser-observed | | F-08 | Audit log readable by the member role | Low | Open, downgraded from High | Observed | | F-09 | Search errors disclose internal index names | Low | Open | Observed | ## Reproduction environment All requests used these values, so you can reproduce each finding with the same setup. | Item | Value | |---|---| | Tenant A | Workspace `ws_test_A`, users `owner-a`, `admin-a`, `member-a` | | Tenant B | Workspace `ws_test_B`, users `owner-b`, `admin-b`, `member-b` | | Test data marker | Titles start with `EXC-TEST` | | HTTP client | `curl` 8.x and a scripted client that saves each exchange with a UTC timestamp | | Browser | Chromium, default profile, no extensions, fresh profile for each browser test | | Authentication | Bearer token in the `Authorization` header for API calls. Session cookie `hn_session` for the web app. | | Token placeholders | `$TOKEN_OWNER_A`, `$TOKEN_MEMBER_B` and so on. Real token values are not printed in this report. | ## F-01: Cross-tenant export through predictable export IDs Critical Evidence label: **Observed** ### Description Harbor Notes lets a workspace owner export all notes and attachments as a ZIP archive. The app creates an export job and then downloads the result from `GET /v1/exports/{export_id}/download`. Export IDs are sequential integers with a prefix, for example `exp_000412`, then `exp_000413`. The download route checks that the caller has a valid token. It does not check that the caller belongs to the workspace that owns the export. The route that lists exports for a workspace (`GET /v1/workspaces/{id}/exports`) does check membership. The download route does not repeat the check. ### Impact A signed-in member of tenant B downloaded the full export of tenant A. The attacker needs no special role (Observed). Export IDs are sequential, so we infer that exports of other tenants can be reached the same way (Inferred). We did not walk the ID range. An export holds every note in the workspace, the member list and all attachments. Together these make up the full content of a customer account. We rate this Critical because it needs no chain, no user action and no privilege. ### Verified results We created an export in tenant A, then fetched it with a token for a member of tenant B. Tenant B received the archive that belongs to tenant A. | Step | Account | Result | |---|---|---| | Create a note `EXC-TEST marker-7f3a` in `ws_test_A`, then start an export | `owner-a` | `exp_000412` created, status `ready` | | Download `exp_000412` | `member-b` | 200, ZIP of 2,318 bytes | | Open the archive | n/a | Contains the note `EXC-TEST marker-7f3a` from tenant A | | Download `exp_000412` with no token | anonymous | 401 | The marker note was written by tenant A and never shared with tenant B. Its presence in the archive that `member-b` downloaded is the read-back proof. Evidence: `ev-01-export-create.json`, `ev-02-export-xtenant.http`, `ev-03-readback.json`. We tested only IDs we created, did not walk the ID range, and opened no export we did not create. ### Reproduction Step 1. Tenant A creates an export. ```http POST /v1/workspaces/ws_test_A/exports HTTP/1.1 Host: api.harbor-notes.example Authorization: Bearer $TOKEN_OWNER_A Content-Type: application/json {"include_attachments": true} ``` ```http HTTP/1.1 202 Accepted Content-Type: application/json {"id": "exp_000412", "workspace_id": "ws_test_A", "status": "ready"} ``` Step 2. A member of tenant B requests the same export. ```http GET /v1/exports/exp_000412/download HTTP/1.1 Host: api.harbor-notes.example Authorization: Bearer $TOKEN_MEMBER_B ``` ```http HTTP/1.1 200 OK Content-Type: application/zip Content-Disposition: attachment; filename="ws_test_A-export.zip" Content-Length: 2318 ``` The `filename` value names tenant A's workspace, so the response itself shows the cross-tenant effect. ### Controls | Control request | Result | What it shows | |---|---|---| | Download `exp_999999` (nonexistent) as `member-b` | 404 | The route does look up the record | | Download `exp_000412` with no token | 401 | Authentication works | | `GET /v1/workspaces/ws_test_A/exports` as `member-b` | 403 | The list route checks membership | | Download with a token whose signature we changed | 401 | Token signatures are verified | | Download `exp_000412` as `member-a` | 200 | Same-tenant access works, as designed | The control with the list route shows that a membership check exists in the codebase. The download route is missing it. ### Remediation Check workspace membership in the download handler, after the lookup and before any file is returned. Return 404 for exports the caller may not read, so the response does not confirm that an ID exists. ```js // GET /v1/exports/:exportId/download const exp = await db.exports.findById(req.params.exportId); if (!exp) return res.status(404).json({ error: "not_found" }); const member = await db.memberships.find({ workspaceId: exp.workspaceId, userId: req.user.id, }); if (!member || !can(member.role, "workspace:export")) { return res.status(404).json({ error: "not_found" }); } return streamExport(exp, res); ``` Also replace sequential IDs with random 128-bit identifiers. Random IDs make enumeration harder but do not replace the access check. Add an automated test that requests each object route with a token from another tenant and expects 404. Review other routes that take an object ID in the path for the same missing check. ## F-02: Stored XSS in shared note titles High Evidence label: **Browser-observed** ### Description When a user shares a note, the "Shared with me" list shows the note title to each recipient. The React app renders the title with `dangerouslySetInnerHTML` in the list item component. The API stores the title as typed and does not encode it. A title that contains HTML runs in the recipient's browser when the recipient opens the "Shared with me" page. ### Impact An attacker with any account can run script in the app origin as the recipient. The script can read data that the recipient's session can read, and it can call the API as the recipient. If the recipient is a workspace owner, the script can create invitations or exports in their name. The attacker needs one user action: the recipient must open the list. The recipient does not need to open the note. We rate this High and not Critical because it needs a user action and a sharing relationship. ### Verified results We shared a note from `member-a` to `owner-b` (both test accounts). The title contained a harmless marker script. When we opened "Shared with me" as `owner-b` in Chromium, the script ran in the origin `https://app.harbor-notes.example` and wrote a marker into the page. | Check | Result | |---|---| | Script ran in Chromium as `owner-b` | Yes, marker text `EXC-TEST xss-fired` added to the page | | Script origin | `https://app.harbor-notes.example` | | Session cookie readable by script | No, `hn_session` is `HttpOnly` | | API call made by the script as `owner-b` | Yes, `GET /v1/me` returned the profile of `owner-b` | | Same title shown in the note editor | Encoded as text, no script ran | The editor encodes the title and the list does not, so the flaw is in one component. Evidence: `ev-04-xss-browser.png`, `ev-05-xss-console.json`. The payload used only the marker and the `/v1/me` read. We did not send data to any outside host. ### Reproduction ```http PATCH /v1/notes/note_test_0031 HTTP/1.1 Host: api.harbor-notes.example Authorization: Bearer $TOKEN_MEMBER_A Content-Type: application/json {"title": "EXC-TEST "} ``` ```http HTTP/1.1 200 OK Content-Type: application/json {"id": "note_test_0031", "title": "EXC-TEST "} ``` Then share the note with `owner-b` and open `/shared` as `owner-b` in Chromium. The `onerror` handler sets `document.body.dataset.exc`, and the test reads it with the browser developer tools. ### Controls | Control | Result | |---|---| | Title with plain text only | Rendered as text | | The same title in the note editor | Encoded, no script | | `Content-Security-Policy` response header | Not present on the app host | | `HttpOnly` flag on `hn_session` | Set, so the script could not read the cookie | | Title rendered on the public share page | Encoded, no script (the public page uses a different component) | ### Remediation Remove `dangerouslySetInnerHTML` from the list component and render the title as a React text node. ```jsx // SharedNoteItem.jsx - + {note.title} ``` Add a Content Security Policy that blocks inline script, for example `script-src 'self'; object-src 'none'; base-uri 'none'`. A policy limits the effect of the next injection bug. Add a lint rule that fails the build on `dangerouslySetInnerHTML` without an approved sanitizer. Search the other list components for the same pattern. ## F-03: Login CSRF on the sign-in endpoint High Evidence label: **Browser-observed** ### Description `POST /v1/auth/login` accepts a form-encoded body and sets the session cookie. It has no CSRF token and no `Origin` check. The `hn_session` cookie is set with `SameSite=None`. A page on another site can submit a login form that signs the victim's browser in to the attacker's account. ### Impact The victim keeps using Harbor Notes without noticing that the session belongs to the attacker's account. Notes and attachments that the victim writes then go to the attacker. This is a data-loss path for users who do not notice the change of account. We rate this High because the effect is a silent data transfer and users may paste sensitive text into notes. We did not prove a path to account takeover. ### Verified results We hosted a test page on a separate origin we control. The page auto-submitted a login form with the credentials of `member-b`. We opened the page in a Chromium profile that held no session. | Check | Result | |---|---| | Chromium profile after the test page loaded | Signed in as `member-b` on `app.harbor-notes.example` | | Request sent with `Origin: https://csrf-test.example` | 302, `Set-Cookie: hn_session=...; SameSite=None; Secure; HttpOnly` | | JSON login with `Content-Type: application/json` | 415, so only the form route is exposed | Evidence: `ev-06-csrf-login.html`, `ev-07-csrf-browser.png`. ### Reproduction ```http POST /v1/auth/login HTTP/1.1 Host: api.harbor-notes.example Origin: https://csrf-test.example Content-Type: application/x-www-form-urlencoded email=member-b%40test.harbor-notes.example&password= ``` ```http HTTP/1.1 302 Found Location: https://app.harbor-notes.example/ Set-Cookie: hn_session=; Path=/; Secure; HttpOnly; SameSite=None ``` ### Controls | Control | Result | |---|---| | Login with a wrong password | 401, no cookie | | Same request with JSON content type | 415, so a cross-site JSON form post is not possible | | CSRF on `POST /v1/notes` (state change after login) | Blocked, because that route needs the bearer token from the app | | Cookie flags | `Secure` and `HttpOnly` set | The CSRF control on `POST /v1/notes` shows that state-changing API routes are protected. Only the sign-in route is open. ### Remediation Set `SameSite=Lax` on the session cookie unless a cross-site flow needs `None`. Check the `Origin` header on the login route and reject any origin that is not on the allow list. Add a double-submit CSRF token to the login form. ```js const ALLOWED = new Set(["https://app.harbor-notes.example"]); app.post("/v1/auth/login", (req, res, next) => { const origin = req.get("Origin"); if (!origin || !ALLOWED.has(origin)) { return res.status(403).json({ error: "bad_origin" }); } next(); }); ``` After login, show the signed-in account name in the app header, so users can see which account they use. ## F-04: Invitation endpoint accepts a client-chosen role High Evidence label: **Observed** A workspace member can invite a user as admin. `POST /v1/workspaces/{id}/invitations` reads the `role` field from the request body and does not compare it to the caller's role. The `member-a` account invited a second test account as `admin`. After acceptance, `GET /v1/workspaces/ws_test_A/members` as that account showed role `admin`. Evidence: `ev-08-invite-role.http`. Controls: the same request with `role: "owner"` returned 400, a request for a nonexistent workspace returned 404, and the web UI hides the role selector from members. The UI check is client-side only. Fix: derive the allowed roles from the caller's role on the server, and reject any role that is higher than the caller's own role. ## F-05: No rate limit on password reset Medium Evidence label: **Observed** ### Description `POST /v1/auth/password-reset` sends a reset email to the address in the request. The route has no limit by address, by IP or by account. It returns the same response for known and unknown addresses. ### Impact An attacker can send many reset emails to one user. This floods the user's inbox and costs Harbor Notes email sending fees. It can also hide a real security email among many others. The route does not reveal whether an account exists. We did not find a way to guess or bypass the reset token. We rate this Medium because the effect is nuisance and cost, and it gives no access. ### Verified results We ran a bounded test against one test account (`member-a`) with a test inbox. We sent 60 requests in 90 seconds from one IP address and stopped there. | Measure | Result | |---|---| | Requests sent | 60 | | Responses with 202 | 60 | | Responses with 429 or any block | 0 | | Reset emails received in the test inbox | 60 | | Cost of the test | 60 emails to a test inbox, no paid API called | All 60 emails arrived, so the server applied no limit in that window. Evidence: `ev-09-reset-burst.json`, `ev-10-reset-inbox.json`. We chose 60 because it is large enough to show that no short-window limit exists and small enough to stay within the safety limit in this report. We did not test higher counts. ### Reproduction ```http POST /v1/auth/password-reset HTTP/1.1 Host: api.harbor-notes.example Content-Type: application/json {"email": "member-a@test.harbor-notes.example"} ``` ```http HTTP/1.1 202 Accepted Content-Type: application/json {"status": "if_account_exists_email_sent"} ``` Repeat the request 60 times with a test-owned address and count the 202 responses. ### Controls | Control | Result | |---|---| | Request for an unknown address | Same 202 body, no account enumeration | | Use of an old reset token | 400, tokens are single-use | | Reset token randomness | No pattern seen in 5 samples | | Reuse of a token after a password change | 400 | ### Remediation Limit requests by address and by IP. As an example for this app, start with 3 requests per address per hour and 20 per IP per hour. Return the same 202 response when a limit applies, so the limit does not leak account state. ```js const resetLimiter = rateLimit({ windowMs: 60 * 60 * 1000, limit: 3, keyGenerator: (req) => req.body.email?.toLowerCase() ?? req.ip, handler: (req, res) => res.status(202).json({ status: "if_account_exists_email_sent" }), }); app.post("/v1/auth/password-reset", resetLimiter, resetHandler); ``` Add an alert for a high rate of reset requests, and consider a CAPTCHA after repeated requests. ## F-06: Share links survive when a note becomes private Medium Evidence label: **Observed** When an owner sets a note to private, the public share link `https://app.harbor-notes.example/s/{token}` still returns the note. We created a link for a test note, switched the note to private in the UI (the UI showed "Private"), and requested the link as an anonymous caller. The response was 200 with the note body. The link stayed valid after we also removed the sharing record in the UI. A fresh link for the same note had a different token, so the old one was never revoked. Evidence: `ev-11-share-after-private.http`. Controls: a link with one character changed returned 404, tokens were 22 random characters, and deleting the note made the link return 404. Fix: when a note changes to private, delete or revoke its share tokens in the same database transaction. Add an optional expiry date for share links, and show the owner a list of active links. ## F-07: Attachments served inline with the uploaded content type Medium Evidence label: **Browser-observed** The download route `GET /v1/files/{id}` returns the file with the content type chosen at upload and with `Content-Disposition: inline`. It sets no `X-Content-Type-Options: nosniff` header. An SVG file that contained a marker script ran in Chromium when we opened the file URL directly. The script ran on the API origin `api.harbor-notes.example`, not on the app origin. We did not test cookie scope. As we read the app, the session cookie is not sent to the API host and the API needs a bearer token that lives in app memory, so we infer that the script could not read the token (Inferred). This inference is why we rate the finding Medium and not High. If the two hosts ever share a registrable cookie scope or a token store, the finding becomes High. The upload test used 3 files under 4 KB, which we deleted. Evidence: `ev-12-svg-inline.png`. Fix: serve user files from a separate sandbox domain, send `Content-Disposition: attachment` for any type other than a short allow list of images, set `X-Content-Type-Options: nosniff`, and add `Content-Security-Policy: sandbox` on file responses. ## F-08: Audit log readable by the member role Low Evidence label: **Observed**. Downgraded from High. ### Description `GET /v1/admin/audit-log` returned 200 for `member-a`. The route sits under the admin prefix, so we first recorded it as High: an admin function open to the lowest role. ### What changed our view We ran the controls after the first observation. They showed that our first reading was wrong. | Check | Result | Effect on severity | |---|---|---| | Entries in the response, tenant A caller | Only `ws_test_A` entries | No cross-tenant exposure | | Compare with the "Activity" page in the app, as `member-a` | Same events | The UI already shows these events to members | | `POST /v1/admin/users/{id}/disable` as `member-a` | 403 | Admin write functions are protected | | `GET /v1/admin/users` as `member-a` | 403 | Admin read functions are protected | | Extra fields in the audit log compared with the Activity page | `ip_address` of each actor | The only real difference | The route is a read-only view of events that members can already see. The only extra data is the IP address of other members in the same workspace. We changed the level from High to Low. Evidence: `ev-13-auditlog-member.http`, `ev-14-auditlog-controls.http`. ### Remediation Remove `ip_address` from the member view, or restrict the route to admin and owner roles. Name the route outside the `/admin` prefix if it is meant for all members, so route names match their access rules. ## F-09: Search errors disclose internal index names Low Evidence label: **Observed** `GET /v1/search?q=` with a malformed filter value returns a 500 response whose body includes the internal index name (`notes_v7_tenant_shard_3`) and the search library version. The text helps an attacker plan later tests. It gives no access to data. We tested with three malformed values on test data, and a valid query returned normal results with tenant scoping intact. A cross-tenant query with a `workspace_id` filter for tenant B returned only tenant A results. Evidence: `ev-15-search-error.http`. Fix: catch search errors, log the detail on the server, and return a generic error with a request ID. ## Controls that held These protections worked under test. Fixes must not remove them. | Control | What we tried | Result | |---|---|---| | Workspace list routes check membership | `member-b` requested lists of tenant A notes, members and exports | 403 on each route | | Note read, edit and delete by ID | `member-b` used note IDs from tenant A | 404, no data returned | | Token signature checks | Tokens with a changed signature or algorithm `none` | 401 | | Session cookie flags | Inspected `hn_session` | `Secure` and `HttpOnly` set | | Admin write routes | `member-a` called user disable and plan change routes | 403 | | Search tenant scoping | Query with `workspace_id` of tenant B from tenant A | Only tenant A results | | Reset tokens | Reuse, and use after a password change | 400, single-use | We list these because the fixes for F-01 and F-04 touch the same authorization code. A regression test for each control keeps the fix from weakening it. ## Remediation plan The plan orders actions by the risk they remove. Owners are placeholders. Effort is S (under one day of work), M (a few days) or L (more than a week). | Action | Findings | Owner | Effort | |---|---|---|---| | Add a workspace membership check to the export download route and switch to random IDs | F-01 | Backend team | S | | Add cross-tenant tests for every route that takes an object ID | F-01, F-04 | Backend team | M | | Render note titles as text and add a Content Security Policy | F-02 | Frontend team | M | | Check `Origin` on login, set `SameSite=Lax`, add a CSRF token | F-03 | Backend team | S | | Validate invitation roles against the caller's role | F-04 | Backend team | S | | Add rate limits and alerts to password reset | F-05 | Backend team | S | | Revoke share tokens when a note becomes private, add expiry | F-06 | Backend team | M | | Move user files to a sandbox domain, add `nosniff` and attachment headers | F-07 | Platform team | L | | Restrict the audit log route and remove actor IP from the member view | F-08 | Backend team | S | | Return generic search errors with a request ID | F-09 | Backend team | S | After the fixes ship, ask for a retest of F-01 to F-04. A retest uses the same requests and the same controls. ## Limitations and open questions The points here are limits of this assessment, not confirmed findings. - Time-boxed: Testing ran for five days against a fixed scope. A lack of findings in an area does not show that the area has no issues. - Grey-box accounts: We tested with the roles we were given. Roles that the client adds later were not tested. - Unproven chain (inferred): F-02 and F-04 might combine: script in an owner's session could send an invitation with a chosen role. We did not run this chain, so we do not count it as impact. - Open question on export history. We do not know whether other tenants' exports were downloaded before this test. The client should check access logs for `GET /v1/exports/*/download` where the caller's workspace differs from the export's workspace. - Open question on F-07 and cookie scope. The client should confirm that the app and API hosts will never share a cookie scope. - Not tested: Third-party checkout pages, the email provider, load behavior, and the mobile apps. ## Evidence index Each item is a saved request, response, screenshot or script output with a UTC timestamp. The hashes below are placeholders for this demonstration. In a real report, the SHA-256 manifest lets you check that a file has not changed. | File | Finding | SHA-256 (placeholder) | |---|---|---| | `ev-01-export-create.json` | F-01 | `3b1f9c07a2de45e18c60b7f4d29a51ce8e3f0a6b7d4c92e15f8a30bd6c7e2194` | | `ev-02-export-xtenant.http` | F-01 | `a94c5e2d7b01f386c4d9e0a7152b8f63de4a19c07b5e3f82a6d1c940e7b3582f` | | `ev-03-readback.json` | F-01 | `7e20d4b9c15a83f6e02b7d94a1c5f3086b9e4d27a0c18f5e3b6d92a4c07e1f58` | | `ev-04-xss-browser.png` | F-02 | `c58a1e93f2b04d76a9e1c3058d7f2b64e0a93c1d58b7f4e26a0d9c315b8e7a42` | | `ev-05-xss-console.json` | F-02 | `1d93b7e5a08c42f6d1b9e3705a4c8f26b0e7d15a93c4f8e2061b7d5a39c0e4f8` | | `ev-06-csrf-login.html` | F-03 | `e4072b9d1c85a3f6b0d7e2941a5c8f3d60b9e72a14c5d8f3e0a6b19c7d2f4e85` | | `ev-07-csrf-browser.png` | F-03 | `5f1a8c3e90b7d24a6e1c05f9b83d7a42c0e6f19b5a3d8e7c24f0b61a9d3c5e78` | | `ev-08-invite-role.http` | F-04 | `92c6e0a4b81d37f5a9c2e60d4b7f18a3e5c09d26b4f7a81e3c0d59b2a6f4e170` | | `ev-09-reset-burst.json` | F-05 | `b30d7a15e9c4f862a0d3b7e51c9f4a68d2e0b73c5a1f8e94d6b0a27c3e5f1980` | | `ev-10-reset-inbox.json` | F-05 | `46e9b2c0d71a58f3e4b6a09d2c7f1583b0e8a4d69c2f5e170a3b8d4c96e1f2a7` | | `ev-11-share-after-private.http` | F-06 | `d85a3f0c6e1b94a27d5c0e8f3b6a1942c7e0d5b83a9f4e2160c7b5d1a8e3f904` | | `ev-12-svg-inline.png` | F-07 | `0a7c4e9b2d6f1583e0a9c7b4d21f6e85a3c0b97d4e2f1a68c5b0d3e7f9a41c26` | | `ev-13-auditlog-member.http` | F-08 | `f2b81d6a4c0e9573b1d8f2a6e04c7b93d5a0e1f84c6b2a9d73e5f0c1b8a4d627` | | `ev-14-auditlog-controls.http` | F-08 | `6c0e3a9d5b71f248e8a0c4d6b2f93e17a5d0c8b4e6f1a39d2c7b05e4f8a1d3c9` | | `ev-15-search-error.http` | F-09 | `89d4b1f7e0a3c652d9b8e4a07f1c5d3e2b60a9c8d4f7e15a3b0c6d92e8f4a1b5` | ### Appendix: reproduction scripts The proof-of-concept scripts ship with the report as files, one per finding. They are read-only where possible and take identifiers as arguments, for example `repro/f01_readback.sh ws_test_A exp_000412`. Run them only with authorization from the system owner and with test-owned identifiers. *This page is a demonstration. The [methodology](/methodology/) page describes how we work. To ask about a test of your own application, [contact us](/contact/). For a small, scoped test at no cost, see the [free pentest pilot](/free-pentest/).* ## Questions ### Is this a real customer report? No. Harbor Notes is a fictional application and no real system was tested. We wrote the findings to show our report format and evidence standard. The hosts use the reserved .example domain, and the hashes in the evidence index are placeholders. ### What format do you deliver? We deliver the report in HTML, PDF and Markdown, plus proof-of-concept scripts. The scripts are read-only where possible. You must run them only with authorization and with test-owned identifiers. ### Do you use CVSS? Not by default. We use a four-level scale (Critical, High, Medium, Low) with a written definition for each level. If your program needs CVSS vectors, tell us during scoping and we agree the method in writing. ### Does every finding have proof? Each finding carries an evidence label. A finding is confirmed only when we show a real effect, for example a change that a second account reads back. Inferred paths are labeled separately, and open questions stay out of confirmed impact. --- # Penetration testing services Source: https://penetrationtestservices.com/services/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Penetration testing services from Excession Labs for web apps, APIs and SaaS, plus pre-launch tests, tests after releases and customer security reviews. Every service listed here uses the same method: a written scope, testing under agreed rules, evidence for every finding, and a retest of your fixes when the scope includes one. Pick the page that matches your situation, or [tell us what you need](/contact/). - [AI application security](https://penetrationtestservices.com/ai-application-security/): AI features such as chatbots, RAG and agents need a different security test from a web app. See why, and where Excession Labs scopes AI system assessments. - [API penetration testing](https://penetrationtestservices.com/api-penetration-testing/): API penetration testing for REST and GraphQL APIs, mapped to the OWASP API Security Top 10 2023. Proven findings, a written report and fix guidance. - [Penetration test report for a customer security review](https://penetrationtestservices.com/customer-security-review/): Prepare a penetration test report for an enterprise customer security review: what buyers ask for, report versus summary letter, what to share and timing. - [Security testing for no-code and AI-built apps](https://penetrationtestservices.com/no-code-app-security-testing/): Security audit for Bubble, Supabase, Firebase and AI-built apps: privacy rules, RLS, Security Rules, exposed keys and workflow APIs, with a sample finding. - [Pre-launch security testing for web apps and APIs](https://penetrationtestservices.com/pre-launch-security-testing/): Pre launch security testing for a web app or API: the seven areas to test first, how to set up staging, and what the pentest report should give your team. - [Recurring penetration testing after each release](https://penetrationtestservices.com/recurring-penetration-testing/): Recurring vs continuous penetration testing: we test web apps and APIs after meaningful releases, scope only what changed, and verify fixes from the last report. - [Penetration testing retest: checking that fixes work](https://penetrationtestservices.com/retesting/): A penetration testing retest checks that fixed findings are really fixed. See what we check, the four result statuses with a sample, and what you receive. - [SaaS penetration testing for multi-tenant products](https://penetrationtestservices.com/saas-penetration-testing/): SaaS penetration testing for multi-tenant products: eight areas we test, the two-tenant method, a sample finding, and what the report contains. - [SOC 2 penetration testing requirements and scope](https://penetrationtestservices.com/soc-2-penetration-testing/): SOC 2 penetration testing requirements: the criteria do not name a pentest, yet auditors and customers ask for one. See the TSC wording and the scope to agree. - [Tenant isolation testing for multi-tenant SaaS](https://penetrationtestservices.com/tenant-isolation-testing/): Tenant isolation testing for SaaS by Excession Labs: a two-tenant matrix, object types, search and export paths, caches, files, proof standard and report. - [Web application penetration testing: what we test and prove](https://penetrationtestservices.com/web-application-penetration-testing/): Web application penetration testing, black box or grey box: the eight areas we test, a sample finding with evidence, and the written report you receive. ## Not sure which one fits? If a customer asked for a pentest report, start with [customer security reviews](/customer-security-review/). If you are about to launch, read [pre-launch testing](/pre-launch-security-testing/). If you ship every week and want each big change tested, see [recurring testing](/recurring-penetration-testing/). If your product includes chat assistants, retrieval or agents that take actions, those parts need an [AI application security assessment](https://airedteamingservices.com/). The rest of the application is tested here. --- # SOC 2 penetration testing requirements and scope Source: https://penetrationtestservices.com/soc-2-penetration-testing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 SOC 2 penetration testing requirements: the criteria do not name a pentest, yet auditors and customers ask for one. See the TSC wording and the scope to agree. > **Short answer:** SOC 2 does not explicitly require a penetration test. The AICPA Trust Services Criteria mention penetration testing as one example of an evaluation, in a point of focus under CC4.1. Customers or auditors may ask for one. Ask your auditor first. Excession Labs delivers the test and report. We cannot promise acceptance. If you are preparing for a SOC 2 report, or a customer asked whether you have one, and someone says you need a pentest, start with what the criteria say. Then agree the scope with your auditor before testing. ## What does SOC 2 say about penetration testing? SOC 2 does not explicitly require a penetration test. The AICPA Trust Services Criteria mention it once as an example. In the 2017 criteria (March 2020 update), the CC4.1 point of focus "Considers Different Types of Ongoing and Separate Evaluations" says: "Management uses a variety of different types of ongoing and separate evaluations, including penetration testing, independent certification made against established specifications (for example, ISO certifications), and internal audit assessments." ([AICPA TSP 100, copy hosted by Arpio](https://arpio.io/wp-content/uploads/2020/08/trust-services-criteria.pdf)). CC7.1 is separate. It covers detection and monitoring, and its point of focus "Conducts Vulnerability Scans" names scans, not pentests (same source). AICPA also publishes a 2022 revision of the points of focus. We could not read that file, because it needs an account. Check the wording in the edition your auditor uses ([AICPA](https://www.aicpa-cima.com/resources/download/2017-trust-services-criteria-with-revised-points-of-focus-2022)). The guide [does SOC 2 require penetration testing](/guides/does-soc-2-require-penetration-testing/) discusses this in full. ## What should you ask for and agree before testing? Requests come from your auditor, who looks at the controls you described; from customer questionnaires; and from your own policy, which may say you test every year. Before scoping, ask each of them whether they need any of these: - A penetration test report from a third party, dated within the window your auditor requires. - Scope that covers the systems in the SOC 2 boundary. - Evidence that findings were triaged and fixed, with dates. - Proof of a retest for serious findings. - A statement of tester independence from your engineering team. The criteria do not set these requests, so ask your auditor what they need. The guide on [security questionnaire pentest questions](/guides/security-questionnaire-pentest-questions/) lists typical wording. Then agree these points before testing, with your auditor and with us. | Item | Why it matters | |---|---| | Systems in the SOC 2 boundary | The test should cover the production app and API that the report describes | | Environment | Production, or staging that matches it | | Roles and tenants | Access control testing needs several roles and at least two tenants | | Dates | Match the audit period and your policy | | Severity scale | Say how you will track Critical, High, Medium and Low findings | | Retest | Decide how you will show that serious findings were fixed | | Report handling | Who may see the full report, and whether a summary letter is needed | We do not add CVSS vectors by default. If your auditor or customer wants them, say so at scoping. ## How do we prove findings, and what do you get? A finding needs evidence of a real effect, such as a record read back through another session. We run controls beside each exploit request and list the controls that held. Evidence is saved with timestamps. Your auditor can follow each finding from the claim to the proof. > **Sample finding:** This is a demonstration. It uses a fictional app, Harbor Notes, and no real customer system was tested. > > **Finding C-01: Reset tokens stay valid after use (Medium).** > > ```http > POST /api/auth/reset HTTP/1.1 > Host: app.example.com > Content-Type: application/json > > {"token":"rst_demo_8841","password":"NewDemoPass-1"} > ``` > > The first call returned `204 No Content`. Replaying the same token returned `204` again, and the second password worked at login (Observed). Control: a token with a changed last character returned 400. Fix: invalidate the token on first use. You get a report in HTML, PDF and Markdown, with proof-of-concept scripts, an executive summary and a remediation plan. We can retest fixed findings (see [retesting](/retesting/)). For customer questions, see the [customer security review](/customer-security-review/) page. We do not promise that any auditor or customer will accept a report. ## What we need from you and what changes the quote We need: - Written authorization from the system owner. - The SOC 2 system description or boundary, if you can share it. - Auditor requirements, in writing if possible. - Test accounts for each role, with two tenants if you are multi-tenant. - API docs or a route list. - A technical contact, and your audit dates. The size of the boundary, the number of roles and API routes, the environment and the retest set the quote. We publish no prices (see [pricing](/pricing/)). Ready to scope? [Request a pentest](/contact/). ## Questions ### Does SOC 2 require a penetration test? Not by an explicit rule. The criteria list penetration testing as one example of a separate evaluation. Whether you need one depends on your controls, your auditor and your customers. The guide [does SOC 2 require penetration testing](/guides/does-soc-2-require-penetration-testing/) goes into more depth. ### Will our auditor accept your report? We cannot promise that. The auditor decides what evidence they need. Ask them for their requirements before scoping: scope, timing, tester independence and how they want open findings handled. We write the report to match what you tell us. ### Do we need a SOC 2 pentest before the audit window starts? Ask your auditor. Timing depends on your audit period and your control descriptions. Some teams test before the period to fix issues, then test again during it. Plan the dates with the auditor. ### Is a vulnerability scan enough? A scan and a pentest are different activities. The criteria name vulnerability scans in a separate point of focus under CC7.1. Your control description should say which you do. Ask your auditor what they want to see. ## Sources - [AICPA: 2017 Trust Services Criteria (With Revised Points of Focus, 2022)](https://www.aicpa-cima.com/resources/download/2017-trust-services-criteria-with-revised-points-of-focus-2022) - [AICPA TSP 100, 2017 Trust Services Criteria (March 2020 update), copy hosted by Arpio](https://arpio.io/wp-content/uploads/2020/08/trust-services-criteria.pdf) --- # Tenant isolation testing for multi-tenant SaaS Source: https://penetrationtestservices.com/tenant-isolation-testing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Tenant isolation testing for SaaS by Excession Labs: a two-tenant matrix, object types, search and export paths, caches, files, proof standard and report. > **Short answer:** Tenant isolation testing checks that one customer of a SaaS product cannot read or change another customer's data. Excession Labs runs the same actions from two test tenants, across every object type, search, export, cache and file path. A cross-tenant effect counts only when the other tenant's session reads it back. The report includes controls that held. Tenant isolation testing checks that one customer of your SaaS product cannot read or change another customer's data. Excession Labs tests this boundary with two test tenants, and we report a leak only when we can prove it. A tenant leak is broken access control, which is A01 in the [OWASP Top 10:2025](https://owasp.org/www-project-top-ten/). At object level it is the risk OWASP lists as [API1:2023](https://api-security.owasp.org/editions/2023/en/0xa1-broken-object-level-authorization/). It is also the finding an enterprise buyer's security team looks for first. ## How does the two-tenant test work? We ask for two test tenants that are as alike as possible: same plan, same roles, same feature flags. Each tenant needs at least a member and an admin. In every feature we create a record with a readable marker, such as `A-note-001` in tenant A and `B-note-001` in tenant B. Then we run each in-scope action from tenant A with tenant B's identifiers, and the reverse, once per role and once anonymously. If tenant A's response contains `B-note-001`, the leak needs no interpretation. The [multi-tenant isolation testing guide](/guides/multi-tenant-isolation-testing/) has the full 12-area matrix and a worked request, so this page covers only what the service includes. ## What do we test? We test every place where the server could return or change data without checking the tenant. This table shows the areas and what we try in each. | Area | What we try | Proof of a leak | |---|---|---| | Object types | Fetch, change and delete B's projects, invoices, users, API keys and webhooks by ID from A | B's marker in a response, or a changed B record | | Lists and filters | Change `tenant_id`, `org` or `account` parameters and headers | B's rows in a list | | Search | Search, autocomplete and "similar items" for `B-note` | B's records in results | | Exports and reports | Start an export in A, then request B's export or report ID | B's file content | | Files and images | Open B's file URL with no token, and with A's token | B's file bytes | | Caches | Request the same URL from A and B in turn | One tenant sees the other's cached body | | Background jobs | Trigger a job in A with B's resource ID | B's data in the job output | | Roles and admin paths | Call admin routes of tenant B as a member of A | An admin action succeeds | | Invitations and billing | Invite into B's org, read or change B's plan and seats | A new member in B, or a changed plan in B | ## Where do search, export and cache paths break? They break where the main query filters by tenant and a second code path does not. App code often checks the tenant on the main table and forgets it in the search index, the export worker or the cache key. For search, we test every query type, because an index can hold all tenants' documents with the filter applied to only some of them. For exports, we start a job as A and then fetch B's job and file IDs, since a worker may run with an identity that can read every tenant. For caches, we request the same path as both tenants and compare bodies. A cache key built from the URL alone can serve one tenant's page to the other. For files, we check whether a storage URL works without a token or with another tenant's token. A guessable or leaked key is a finding even when the application interface hides it. ## What do row-level security pitfalls look like? Row-level security (RLS) moves the tenant check into the database, but it fails in a few ways that we test for. The [PostgreSQL documentation](https://www.postgresql.org/docs/current/ddl-rowsecurity.html) describes the rules behind each one. - The application's database role owns the table. Table owners skip the policy unless the table uses `FORCE ROW LEVEL SECURITY`. - The role has the `BYPASSRLS` attribute, which skips every policy. - The tenant setting comes from a header or body field that the client controls, not from the verified session. - Some tables have a policy and others, such as join tables, do not. We cannot read your database policies in a black-box test. We find these faults through behavior: a request that should return nothing returns B's rows. If you give us the schema or the policy files, we check them as well. ## What counts as proof? A finding needs evidence of a real effect. For a read, the response contains the other tenant's marker. For a write, we change only a test-owned record in tenant B and read it back through B's own session. A bare "200 OK" is not proof. Every result runs next to controls: A's own ID, a nonexistent ID, a wrong record type, another account's token and an anonymous call. We save requests and responses with timestamps. Destructive proofs use throwaway records created for the test, and the scope names them. > **Sample cross-tenant finding:** This is a demonstration. It uses a fictional app, Harbor Notes Teams, and no real customer system was tested. > > **Finding T-02: The export download ignores the tenant (High).** A member of tenant `t_acme` requests the download of an export job that belongs to `t_globex`. > > ```http > GET /api/exports/exp_9021/download HTTP/1.1 > Host: app.example.com > Cookie: session= > ``` > > The response held rows with the marker `B-note-001` (Observed, and tenant B's own session lists the same rows). Controls: `exp_00000` returned 404, and an anonymous call returned 401. Fix: resolve the export by tenant and ID together, in one query. ## What do you receive? You receive a written report in HTML, PDF and Markdown, with proof-of-concept scripts. It has an executive summary with priority actions, a findings table, and one section per finding: description, impact, verified results, reproduction and fix. A "Controls that held" section lists the isolation checks that passed, which is the part a buyer's security team asks to see. It also lists limitations and open questions. Open the [sample report](/sample-report/) to see the format before you buy. We can retest fixed findings (see [retesting](/retesting/)). For general SaaS scope, such as roles, billing and integrations, see [SaaS penetration testing](/saas-penetration-testing/). ## What does it cost? We publish no prices. We quote after a short call and a written scope. The number of tenant-scoped object types, roles, integrations and API routes drives effort. [Pricing](/pricing/) lists every factor. To scope a tenant isolation test, [request a pentest](/contact/). Small teams can [apply for a free pilot](/free-pentest/). ## Questions ### Is tenant isolation testing the same as multi-tenant penetration testing? They overlap. Tenant isolation testing focuses on the boundary between customers. A multi-tenant penetration test also covers the rest of the application, such as sessions, roles and business logic. We scope either one, or both together. ### Does a database per tenant remove the need for this test? No. Caches, queues, file storage, search indexes and admin tools can still be shared between tenants. We test each shared layer whatever the database design. ### What do you need to start? Written authorization from the system owner, two test tenants with test data, at least two roles in each tenant, and a route list or API description. We agree a safe channel for credentials during scoping. ### Do you test with real customer tenants? No. We use test-owned tenants and records. If production is the only option, the written scope says so and sets limits on destructive actions. ## Sources - [OWASP Top 10:2025, A01 Broken Access Control](https://owasp.org/www-project-top-ten/) - [OWASP API1:2023 Broken Object Level Authorization](https://api-security.owasp.org/editions/2023/en/0xa1-broken-object-level-authorization/) - [OWASP Multi-Tenant Application Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Multi_Tenant_Security_Cheat_Sheet.html) - [PostgreSQL documentation: Row Security Policies](https://www.postgresql.org/docs/current/ddl-rowsecurity.html) --- # Web application penetration testing: what we test and prove Source: https://penetrationtestservices.com/web-application-penetration-testing/ Publisher: Excession Labs (penetrationtestservices.com) Last updated: 2026-11-09 Web application penetration testing, black box or grey box: the eight areas we test, a sample finding with evidence, and the written report you receive. > **Short answer:** Web application penetration testing is a scoped, authorized attempt to break your app the way an attacker would, then report what worked. Excession Labs tests authentication, sessions, access control, injection, business logic, uploads and client-side code. A finding is reported only with evidence of a real effect. You get an HTML, PDF and Markdown report with fix guidance. If your web application holds customer data or money, you need to know what a determined user could do to it before a customer or an attacker finds out. Each finding comes with proof of its effect. ## What do we test, and when do we use black box or grey box? We test the places where web apps fail in practice. [OWASP Top 10:2025](https://owasp.org/www-project-top-ten/) ranks Broken Access Control first, so most of our effort goes to who can do what. The [OWASP Web Security Testing Guide](https://wstg.owasp.org/) gives us the test structure. | Area | Question we answer | |---|---| | Authentication | Can someone log in as another user through login, reset, MFA or invitation flows? | | Sessions | Do cookies and tokens expire, rotate and get revoked when they should? | | Access control | Can user A read or change user B's record by changing an ID? Can a normal user call an admin function? | | Injection | Does user input reach a database, a template or a command without safe handling? | | Business logic | Can a user skip a step, reuse a coupon, change a price or exceed a limit? | | File uploads | Can a user upload a file type or content that the app then runs or serves unsafely? | | Client-side | Do the JavaScript bundles expose keys, hidden endpoints or trust decisions? Can stored XSS or CSRF be shown in a browser? | | Configuration | Do headers, error messages and exposed paths leak more than they should? | We test with direct HTTP requests, and with Chromium for issues that need a real browser. We also review client-side JavaScript bundles and compare what an anonymous caller and a signed-in caller can do. In a black-box test we start as an outsider with no accounts. In a grey-box test you give us accounts, roles and documentation. We recommend grey box for most apps, because the access-control questions in the table need at least two accounts. Read [black, grey and white box testing](/guides/black-box-grey-box-white-box-testing/) if you are unsure. ## How do we prove a finding? A finding needs evidence of a real effect. A write is proven by reading the changed record back through another session. A bare "success" response is not proof. The [methodology](/methodology/) page states the full standard. We run controls next to each exploit request: a nonexistent identifier, a wrong record type, another account's token, an anonymous request. If the control behaves like the exploit, we have not found a flaw. Controls that held are listed in the report, so a fix does not break a working protection. We label evidence. "Observed" means we reproduced it. "Browser-observed" means we showed it in a real browser. Inferred paths are labeled separately. Open questions go in a "Limitations and open questions" section, not into confirmed impact. > **Sample finding:** This is a demonstration. It uses a fictional app, Harbor Notes, and no real customer system was tested. > > **Finding H-02: A user can read another user's note (High).** Object-level authorization is missing on the note endpoint. See [IDOR testing](/guides/idor-testing/) for the method. > > Request as user A (account `a@example.com`) for a note owned by user B: > > ```http > GET /api/notes/7731 HTTP/1.1 > Host: app.example.com > Cookie: session= > ``` > > Response (Observed): > > ```http > HTTP/1.1 200 OK > Content-Type: application/json > > {"id":7731,"owner":"b@example.com","title":"Q4 plan"} > ``` > > Controls: note `999999` returned 404. An anonymous request returned 401. Both held. Fix: check that the note's owner matches the session user on every read. ## What you get and what we need from you You get a written report in HTML, PDF and Markdown, plus proof-of-concept scripts. It has an executive summary with priority actions, a findings-at-a-glance table, and one section per finding with description, impact, verified results, reproduction steps and remediation. Severity uses four levels: Critical, High, Medium and Low, each with a written definition. We do not add CVSS vectors by default. Tell us at scoping if your program needs them. After you fix findings, we can retest them. See [retesting](/retesting/). The [sample report](/sample-report/) shows the layout. To start, we need: - Written authorization from the system owner. - The URLs and environments in scope, and anything out of scope. - At least two test accounts, owned by you and marked as test data. - API documentation, a collection or a route list, if you have one. - A technical contact who can answer questions during the test. - Notes on rate limits, WAF rules or alerts that may trigger. The guide on [how to prepare for a penetration test](/guides/how-to-prepare-for-a-penetration-test/) has a longer checklist. ## What changes the quote? The price follows scope. The main drivers are the number of roles, the number of distinct features and API routes, the environment (staging or production), whether you want grey-box access, and whether you need a retest. We publish no prices and quote after a short call and a written scope (see [pricing](/pricing/) for what a quote includes). Excession Labs also runs AI-assisted testing for web apps and APIs with its AI testing agents. [AI Pentest Services](https://aipentestservices.com/) describes that method. Ready to scope a test? [Request a pentest](/contact/) or ask about the [free pentest pilot](/free-pentest/). ## Questions ### What is the difference between black-box and grey-box web app testing? In a black-box test we start with no accounts, as an outsider would. In a grey-box test you give us accounts, roles and documentation. Grey-box testing gives access to the signed-in app that black-box testing lacks. Most web apps need at least two test accounts, so grey-box is the usual choice. ### Do you test with a scanner or by hand? We use direct HTTP requests and a real browser, and we compare what anonymous and signed-in users can do. Scanner output alone is not a finding. Each finding needs evidence of a real effect that we reproduced. ### Can you test our production system? Yes, if you give written authorization and agree the limits in scope. Destructive proofs use only test-owned accounts and records. We bound tests of uploads, password guessing, email sending and paid third-party APIs, and the report states their cost. ### Which standards do you test against? We map test areas to the OWASP Top 10:2025 and the OWASP Web Security Testing Guide. These are test references, not certifications. Tell us during scoping if your buyer or auditor names another standard. ## Sources - [OWASP Top 10:2025](https://owasp.org/www-project-top-ten/) - [OWASP Web Security Testing Guide](https://wstg.owasp.org/) - [OWASP WSTG-ATHZ-04: Testing for Insecure Direct Object References](https://wstg.owasp.org/v4.2/4-Web_Application_Security_Testing/05-Authorization_Testing/04-Testing_for_Insecure_Direct_Object_References/)