A source code security audit is one of the highest leverage assessments you can run, because it finds vulnerabilities at the source rather than guessing at them from the outside. Unlike a black box penetration test, an auditor with the code can reason about every path, see the auth logic in full, and trace tainted data from input to sink. This article explains what a thorough code audit actually covers, why automated scanners are only the starting point, and what the deliverable should look like.
Scanners are the floor, not the ceiling
Automated tools do real work and a good audit uses them, but they are the beginning. Static application security testing (SAST) flags patterns: SQL built by string concatenation, dangerous deserialisation calls, weak crypto primitives. Software composition analysis (SCA) tells you which dependencies carry known vulnerabilities. Both are necessary and both have well known limits. SAST produces false positives and misses logic flaws that no pattern describes. SCA tells you a vulnerable version is present but not whether the vulnerable code path is reachable in your application. The value of a human auditor is precisely in the gap these tools leave.
Manual review: where real findings live
The serious vulnerabilities in modern applications are usually logic and design flaws, and those require a person who understands the application's intent. A manual review concentrates on the areas where mistakes are most damaging.
Authentication and session management
The reviewer reads the actual auth flow: how sessions are created and invalidated, whether tokens are signed and verified correctly, how password reset and account recovery work (a perennial source of takeover bugs), and whether multi factor enrolment can be bypassed. JWT handling gets special attention, since algorithm confusion, missing signature verification and unbounded token lifetimes are common.
Authorisation
Most modern breaches exploit broken object level authorisation: the server trusts an identifier from the client and returns data the caller should not see. A scanner cannot know that /api/invoice/1042 belongs to a different tenant. A human reading the code can. The auditor checks that every sensitive operation enforces ownership and role on the server side, not just in the UI.
Injection
Beyond classic SQL injection, the reviewer traces user controlled data into every dangerous sink: command execution, template engines (server side template injection), LDAP and NoSQL queries, and file path operations. The question is always whether untrusted input reaches a sink without being parameterised or properly encoded.
Cryptography
Crypto misuse is subtle and scanners catch only the obvious cases. The auditor checks for hardcoded keys, predictable initialisation vectors, ECB mode, weak password hashing (anything that is not a modern memory hard function such as Argon2 or bcrypt with sane parameters), missing authentication on encrypted data, and use of non cryptographic randomness for security tokens.
Insecure deserialisation
Deserialising untrusted data into objects is a reliable path to remote code execution in many languages. The reviewer looks for native deserialisation of attacker controlled input, unsafe use of pickle, Java native serialization, or YAML loaders that instantiate arbitrary types, and recommends safe formats and allowlists.
Secrets in code and history
Hardcoded credentials, API keys and private keys are still found in a large share of audits. A thorough review scans not just the current tree but the full git history, because a secret removed in a later commit is still recoverable by anyone with the repository. Every finding here comes with two actions: remove the secret and rotate it, because a leaked credential must be assumed compromised.
Dependency and supply chain risk
Modern applications are mostly other people's code. SCA enumerates direct and transitive dependencies and maps them to known vulnerabilities, but a good audit goes further by reasoning about reachability (is the vulnerable function actually called), maintenance health (is the package abandoned), and provenance.
- SBOM: the audit should produce or validate a software bill of materials so you have a durable inventory of what ships in your product.
- Transitive depth: most risk hides several layers down, in dependencies your developers never chose directly.
- Supply chain hygiene: dependency confusion, typosquatting and unpinned versions are checked, along with whether your build verifies integrity (lockfiles, hashes).
Dependency review pairs naturally with a broader application security program, which we cover on our services page.
The deliverable: fixes, not just findings
A report that lists problems without showing how to fix them creates work rather than reducing risk. A strong code audit deliverable includes:
- Each finding with a clear description, the exact file and line, an assessment of exploitability and impact, and a severity that reflects real risk in context.
- Sample patches or concrete remediation code, so developers can see the secure version rather than infer it.
- Root cause grouping, so a single systemic issue (for example, a missing authorisation helper) is fixed once rather than findings being whacked individually.
- A prioritised plan that sequences the work by risk and effort.
Takeaways
A source code security audit combines automated breadth with human depth. SAST and SCA quickly surface known patterns and vulnerable dependencies, but the findings that matter most (broken authorisation, auth bypass, crypto misuse, insecure deserialisation) come from a skilled reviewer reading the code with intent. The best audits trace data from input to sink, check git history for secrets, build an SBOM, and hand back sample patches rather than a wall of findings. If you are shipping software that handles money, personal data or critical operations, a periodic code audit is one of the most cost effective controls available. Reach out via our contact page to scope one.
FAQ
How is this different from a penetration test? A penetration test attacks the running application from the outside with limited or no source. A code audit reviews the source directly, so it finds logic flaws and reaches code paths a black box test never triggers. They are complementary.
How often should we audit? Audit before major releases and after significant architectural change, and run continuous SAST and SCA in CI in between. The manual audit catches what automation cannot.