The argument is economic. A real adversary is not bound by your statement of work. Denying the testers the source narrows what they reach in the time you paid for, not what an attacker reaches in the time they are willing to spend.
What a source code assisted pentest is
An authorized security assessment in which the testing team holds read access to the application source while testing a running instance of it. NIST SP 800-115 frames testing along a spectrum of tester knowledge — no prior information through to full knowledge — and treats the choice as scoping, not rigour.[1]
The code is an input, not the deliverable. A finding qualifies only once the tester has reached the vulnerable behaviour on the running system and can reproduce it. The code explains why it happens and which other call paths share the defect; the running instance establishes that it happens.
How it compares to black box and grey box testing
| Model | Tester holds | Answers | Weakest at |
|---|---|---|---|
| Black box | Only what is publicly reachable | What can an unaided outsider find within the engagement window | Deep authorization and logic flaws behind authenticated, multi-step flows |
| Grey box | Credentials, some documentation | What can an authenticated user or a compromised account reach | Defects in rarely exercised code paths and in dependency usage |
| Source code assisted | Credentials plus read access to source and infrastructure code | Does the weakness exist, and everywhere it exists | Estimating how discoverable an issue is to an external attacker |
| Secure code review | Source, often without a running instance | Which patterns in the code are dangerous | Proving exploitability and filtering unreachable findings |
These are complements. The mature pattern is a source code assisted test as the recurring engagement, plus an occasional black box exercise scoped to measure external discoverability and detection response — a different question, answered by a different exercise.
What the source access actually buys you
The gain concentrates in weakness classes that stay invisible from outside until precisely the right request is made. MITRE's CWE Top 25 is a useful frame for which those are.[5]
- Ticked: Broken object and function level authorizationThe code shows which handlers check ownership and which inherit a check a second route never calls. From outside they look identical.
- Ticked: Business-logic and state-machine flawsDiscount stacking, replayed idempotency keys, over-refunds, skippable approvals: missing transitions in code, nothing in the traffic.
- Ticked: Multi-tenant isolationTenant scoping in an ORM base class, bypassed by one raw query. A single-line defect with total impact.
- Ticked: Cryptographic misuseFixed IVs, unauthenticated ciphertext, a signature check computed but never read. All healthy on the wire.
- Ticked: Secrets in historyA credential removed from the tree but still in an earlier commit reaches anyone who obtains the repository.
- Ticked: Dependency reachabilityWhether a flagged CVE sits on a path you call: urgent fix or backlog item.
- Dashed: Exposed services and transport securityAlready visible from outside.
- Dashed: Default credentials and unpatched edge infrastructureA black box test finds these faster.
What to provide, and what to withhold
Under-provisioning access is the most common way these engagements underdeliver: the tester spends the first third of the window waiting on the environment, and the rest covers less ground than a black box test would have.
- Ticked: Read-only repository accessManifests, lockfiles, infrastructure-as-code, CI config, and full history. History is where committed secrets live.
- Ticked: A running instance that matches productionDebug mode, disabled authorization, or a different database engine produces findings that do not transfer.
- Ticked: Test accounts at every privilege levelAt least two in separate tenants. Cross-tenant authorization cannot be tested with one account.
- Ticked: The intended authorization model, in writingWithout it, testers cannot separate a design from a defect, and you receive both as findings.
- Ticked: A named contact and an agreed stop conditionRules of engagement, escalation path, and the definition of unacceptable impact, settled before testing starts.[1]
- Dashed: Production dataDoes not improve the test. Only enlarges the cost of a mistake.
- Dashed: Live production secretsSame trade, same answer.
How the engagement should run
- Map the attack surface from the codeRoutes, consumers, scheduled jobs, admin entry points — a longer list than the product team keeps
- Locate the trust boundariesWhere input enters, authorization is decided, privilege changes hands
- Form ranked hypotheses“This endpoint should reject a document ID from another tenant”
- Test against the running instanceEach hypothesis, in order of impact
- Generalize every confirmed findingEvery other call site with the same defect
- Retest after remediationThe proof, then the adjacent call sites — partial fixes are the norm
OWASP's Code Review Guide organizes the boundary-mapping work well[3]; the Web Security Testing Guide covers technique for the exploitation half.[2]
How to judge the report you receive
PCI DSS guidance is explicit that a report documents methodology, scope, findings, and supporting evidence — not conclusions alone.[4] A code-assisted engagement earns four more tests.
| Check | What a good report shows | Warning sign |
|---|---|---|
| Proof, not pattern | A request and response, or a state change, that another engineer can reproduce | “The code appears to permit…” with no attempt recorded |
| File and line attribution | Each finding cites the exact location and commit that introduced the behaviour | Findings that could have been written without the repository |
| Completeness sweep | Every affected call site listed, not only the one that was exploited | One instance reported and closed, then the same bug recurs elsewhere |
| Negative results | Named controls that were tested and held, so you know what the coverage was | Only positives, leaving scope coverage entirely unstated |
Common pitfalls
- Accepting static output as findings. A scanner report with a vendor logo on it is not a penetration test. Unreachable code paths generate confident, expensive noise.
- Testing an environment that is not production-like. Configuration, feature flags, and data volume all change which findings are real.
- Reporting severity without reachability. A critical weakness in a path no request can reach outranks nothing. Severity is a function of what the finding can touch.
- Letting the evidence go stale. The test is accurate for the commit it ran against. Without retesting on change, its accuracy decays at the speed of your merge queue.
- Confusing the question. Source-assisted results say whether a defect exists, not how likely an outsider is to stumble on it. Do not use one to answer the other.