May 29th, 2026, posted in for_founders
by Adelina
You've built something. Maybe it's a SaaS product that's starting to get real traction. Maybe it's a web app with a few thousand users and a growth curve that's finally pointing in the right direction. Maybe it's an internal platform your company depends on every day.
Things are working. Nobody has complained. No alarms have gone off. So why would you spend time and money auditing your own code for security issues?
But then, you hear about security breaches on the news. Thousands of people’s passwords, addresses, phone numbers or even banking info getting out because of small vulnerabilities in one app’s code. And you start to panic: how secure is your app?
Security has to be built into the development process from the beginning. That time you spend making code secure is nothing compared to what you’d spend if you deal with a security breach.
So in this article, we’re going to talk about what code security actually means, why it matters more than most people realize, and how a proper security review works: especially the kind that goes beyond what automated scanners can find.
What is code security, and why does it matter?
Before diving into reviews and methodologies, it's worth establishing what we mean by "code security" and why it actually matters.
Code security refers to the practices, patterns, and principles that make software resistant to unauthorized access, data breaches, manipulation, and abuse. It's a property of the entire system: the way authentication is handled, how data flows through the application, what happens when unexpected input arrives, how services communicate with each other, and dozens of other design and implementation decisions.
Think of it like the structural integrity of a building. You can't just bolt on structural integrity after the fact. It has to be designed in: in the foundation, the walls, the connections between floors. And just like a building, software can look perfectly fine from the outside while having serious problems on the inside.
The reason this matters so acutely today is scale. An insecure piece of code that sits in a product used by ten people is a different risk than the same code in a product used by 100,000.
As your user base grows, as you add integrations, as you handle more sensitive data, the blast radius of a security failure grows with it. And so does your responsibility.
And unlike most software bugs, security vulnerabilities don't always announce themselves. A performance issue slows your app down and users notice. A security issue might sit dormant for months or years; and when it's exploited, you may not even know it happened until the damage is already done.
Here are a few examples of common code security vulnerabilities that show up in real codebases:
- SQL injection: this is one of the oldest vulnerabilities in the book. If your application constructs database queries by interlocking user-supplied input directly into a query string, an attacker can manipulate that input to change what the query does. They might be able to dump your entire user database, bypass authentication, modify records, or even execute commands on the server. Modern ORM frameworks and parameterized queries solve this cleanly, but the vulnerability still appears in legacy code.
- Cross-site scripting (XSS): XSS vulnerabilities occur when an app renders user-supplied content in a browser without properly sanitizing it, allowing an attacker to inject malicious scripts that run in other users' browsers. The attack surface here is surprisingly broad. A comment field, a profile name, a URL parameter, a search query: any piece of user input that eventually gets rendered as HTML is a potential XSS vector if it isn't handled correctly.
- Broken authentication and session management: Common issues include weak password policies, predictable or long-lived session tokens, missing limits to how many times one can login (leading to brute force attacks), insecure password reset flows, not invalidating sessions on logout, and improperly implemented multi-factor authentication.
- Insecure direct object references (IDOR): Imagine a document management app where documents are accessed via a URL like /api/documents/4821. If the application doesn't verify that the authenticated user has permission to view document 4821 (it just checks that they're logged in) then any user can access any document simply by guessing or incrementing the ID. This kind of vulnerability is surprisingly common in applications built quickly without a security-first mindset.
- Sensitive data exposure: Many apps handle sensitive data (user emails, passwords, payment information, health records, business data) and they don't always handle it carefully enough. Some common issues are: storing passwords in plaintext or with weak hashing, transmitting sensitive data over unencrypted channels, logging sensitive information (like tokens or credentials) in application logs, storing API keys or secrets in environment variables that end up in version control, and not encrypting sensitive fields in the database.
- Security misconfigurations: A significant category of real-world breaches comes not from clever hacking but from basic misconfigurations: default credentials left in place, debug mode left enabled in production, overly permissive cloud storage buckets, verbose error messages that expose internal stack traces, unused services left running and exposed to the internet.
What happens when code security is neglected
It's easy to treat software security as an abstract concern, something that matters in principle, but that's unlikely to actually affect you. The problem is - if it does end up affecting you, you’ll find out why it was so important the hard way.
Up next, we’re going to give you a few examples of types of situations that can occur when you neglect your app’s code security.
Data breaches and the cost that follows
IBM's Cost of a Data Breach Report consistently puts the average cost of a breach in the millions of dollars: with costs including investigation, notification, regulatory fines, legal fees, remediation, and lost business. For startups and mid-sized companies, a breach of that magnitude isn't just expensive. It can be devastating.
The direct financial cost is only part of it. The reputational damage is often harder to quantify and longer-lasting.
When your customers' data is exposed because of a vulnerability in your product, the trust you've spent months or years building can evaporate in days. Worst of all, you’ll be known as that one company who had a data breach.
Regulatory and legal exposure
If your product handles personal data from users in the European Union, GDPR applies. If you handle payment data, PCI DSS requirements apply. If you're in healthcare, HIPAA is in scope. If you're building for enterprise customers, you're likely to face security questionnaires, audits, and contractual requirements around how you handle their data.
An insecure codebase isn't just a technical problem. It's a compliance problem, a contractual problem, and potentially a legal problem.
Regulators have shown an increasing willingness to levy significant fines for security failures: and they're particularly interested in whether companies took reasonable precautions in the first place.
Fixing it later is so expensive
One of the least-discussed costs of neglecting security is the cost of fixing it later. A vulnerability that takes an hour to fix during development might take weeks to remediate in a production system with thousands of users, complex deployment processes, and years of accumulated technical context to understand.
Security vulnerabilities tend to be among the most expensive defects to remediate, because they often require changes that touch many parts of the system. And the bigger the system, the bigger the updates needed.
Plus, the longer you wait to address security, the more expensive it becomes: in engineering time, in risk exposure, and in the compounding complexity of a codebase that wasn't designed with security in mind.
Customer and investor scrutiny
As software becomes more central to how businesses operate, security has become a much more prominent consideration in procurement and investment decisions.
Enterprise customers often require vendors to complete security assessments before signing contracts. Investors increasingly ask about security practices during due diligence. SOC 2 compliance (which includes security as a key component) is becoming a standard expectation for B2B SaaS companies.
A codebase with known security issues is a liability that shows up in these conversations. A codebase with a clear, documented approach to security (including regular reviews) is an asset you want to have.
How we approach a code security review
When we conduct a security review of an app, we're not running a script and generating a report. We're doing something closer to what a thoughtful adversary would do: reading the code, understanding the system, tracing the data flows, and looking for places where the app can be made to behave in ways it wasn't intended to.
Here's how that process actually works:
Phase 1: Understanding your software
Before looking at a single line of code, we need to understand what your app is supposed to do. It's the foundation of a meaningful security review: we gotta know what it’s supposed to do first, to be able to identify what can go wrong with it.
We look at the architecture: how the system is structured, what components it has, how they communicate, where the boundaries are between trusted and untrusted zones. We map the data flows: what data enters the system, where it comes from, where it goes, what transformations it undergoes, and where it exits.
We identify the trust model: who the actors are, what they're supposed to be able to do, and what they're explicitly not supposed to be able to do. In other words, which users should do what.
We also look at the technology stack in depth. The security properties of a Node.js application are different from a Django application, which is different from a Spring Boot application. Each framework and runtime has its own set of common pitfalls, known vulnerabilities, and security-relevant behaviors. Understanding what you're working with is essential.
This phase produces a threat model: a structured way of thinking about what the application needs to protect, what it needs to protect it from, and where the most significant risks are likely to be. The threat model drives the rest of the review.
Phase 2: Authentication and authorization analysis
Authentication (who you are) and authorization (what you're allowed to do) are the cornerstones of app security, and they're where we focus significant attention.
On the authentication side, we look at: how credentials are stored and validated, how sessions are created and managed, what the token lifetime and invalidation logic looks like, how password reset flows work end to end, whether multi-factor authentication is implemented correctly, and whether there are rate limits and lockout mechanisms that prevent brute force attacks.
On the authorization side, we look at: whether every endpoint and operation has an explicit authorization check, whether those checks are implemented consistently, whether the authorization logic correctly handles edge cases, and whether there are paths through the application that bypass authorization entirely.
We pay particular attention to API endpoints, because APIs are where authorization failures most frequently occur in modern apps. We look at every endpoint and ask: who is this endpoint supposed to be accessible to? Is that enforced? What happens if an authenticated but unauthorized user tries to access it? Can they get in?
We also look for privilege escalation paths: ways that a user with lower privileges might be able to perform actions that should be restricted to users with higher privileges. These are often subtle and almost always missed by automated tools.
Phase 3: Input validation and output encoding
Every piece of data that enters the system from the outside world is a potential attack vector. We have to trace any paths of untrusted input through the app, and put a stop to it.
We look at: every point where user input is received, what validation is applied to it, whether that validation is sufficient and correctly implemented, and what happens when input doesn't match expectations. We look at how input is used downstream: is it inserted into a database query? Is it rendered in an HTML response? Is it passed to a system command? Each of these creates different risks and requires different protections.
We look at output encoding: making sure that data displayed to users is correctly shown. The same string might need to be encoded differently when placed in an HTML attribute, a JavaScript string, a CSS value, or a URL parameter. Getting this wrong creates XSS vulnerabilities.
We also look at file uploads, which are a particularly rich source of security issues. What file types are accepted? Is the file type validated based on content or just the extension? Where are uploaded files stored? Are they served in a way that allows script execution?
Phase 4: Data handling and cryptography
We look at how sensitive data is stored, transmitted, and processed throughout the application.
Storage
Are passwords hashed using an appropriate algorithm, with appropriate parameters? Are sensitive fields encrypted in the database? Are backups encrypted? Are secrets (API keys, database credentials, service credentials) stored securely and not committed to version control?
Transmission
Is all communication over HTTPS? Are the TLS configurations correct and using appropriate cipher suites? Are sensitive values transmitted in request bodies rather than URLs (which may be logged)?
Cryptography
We look for homegrown implementations of cryptographic functions, which are almost always wrong. We look for use of deprecated or broken algorithms (MD5, SHA-1 for password hashing, DES). We look for issues with random number generation: using non-cryptographic random number generators for security-sensitive purposes is a surprisingly common mistake.
Phase 5: Business logic review
This is the phase that requires the most app-specific knowledge and the most human judgment: and it's the phase that automated tools are entirely unable to perform.
Business logic vulnerabilities are flaws in the app's behavior that follow from incorrect assumptions about how the system will be used. They can only be found by someone who understands both what the app is supposed to do and how it can be made to do something different.
We approach this by systematically thinking through the application's critical flows and asking: what assumptions does this flow make? What happens if those assumptions are violated? Can the flow be manipulated by changing the order of operations? Can it be manipulated by making requests out of sequence? Can parameters be changed in ways that alter the outcome in the attacker's favor?
Common patterns we look for:
- Workflow manipulation: Can a multi-step process be completed by skipping steps? Many checkout flows, for example, assume that certain steps will be completed in order. If the app doesn't enforce that order server-side, it may be possible to skip validation or payment steps.
- Mass assignment vulnerabilities: In many frameworks, it's possible to bind request parameters directly to model attributes. If the app doesn't explicitly whitelist which attributes can be modified, an attacker may be able to set attributes that aren't supposed to be user-controllable — like an is_admin flag or an account_balance.
- Time-of-check to time-of-use (TOCTOU) races: If a check (like an authorization check) and the operation it's supposed to protect are not atomic, it may be possible to exploit the gap between them.
- Discount and pricing manipulation: In e-commerce or billing flows, we look for ways that discounts, coupons, or pricing tiers can be applied inappropriately: applied multiple times, applied in ways they weren't intended, or applied retroactively.
- Account enumeration and information leakage through error messages: Apps often inadvertently reveal whether a user account exists through their error messages or response timing. This is lower severity but can be an important first step in a targeted attack.
- Insecure state management: Apps that store state in client-side cookies or local storage, or that use predictable identifiers for sensitive operations, are particularly susceptible to manipulation.
Phase 6: Reviewing your infrastructure and configuration
An app’s security doesn't stop at its code. The environment it runs in matters too. We look at: server and service configurations, network access controls, cloud IAM policies and permissions, database access controls, logging and monitoring configurations, deployment pipelines and access to them, and how secrets are managed across environments.
Common findings here include: overly permissive IAM roles that grant more access than necessary, storage buckets or databases accessible from the public internet, debug endpoints or admin interfaces not properly protected, excessive logging of sensitive information, and deployment pipelines with access to production credentials that aren't adequately secured.
Phase 7: Dependency analysis
Modern applications are built on top of a large number of third-party libraries and dependencies. Each of those dependencies is a potential source of vulnerabilities, which your app might inherit:
- We look at the dependency tree and cross-reference it against known vulnerability databases
- We look for outdated dependencies that have known CVEs (Common Vulnerabilities and Exposures)
- We look for dependencies that have been abandoned and haven't received security patches.
- We look for the update policy and practices of the development team: are dependencies regularly reviewed and updated?
This phase benefits from automated tooling more than any other: tools like Snyk, Dependabot, or npm audit can efficiently scan dependency trees against known vulnerability databases.
But the human element is in prioritizing findings, understanding which vulnerabilities actually affect the application given how dependencies are used, and making recommendations about the overall approach to dependency management.
Phase 8: Documenting what we found and planning on how to fix it
A security review is only valuable if it produces actionable output.
After completing the technical work, we document every finding with:
- A clear description of the vulnerability, including the specific code or configuration where it occurs.
- An explanation of the attack scenario: not just what the vulnerability is, but what an attacker could do with it and how.
- A severity rating based on the actual impact and exploitability in the context of this specific application.
- A concrete recommendation for remediation, including examples where helpful. A priority rating that helps the development team understand what to fix first.
We also provide a summary assessment of the overall security posture of the app: a picture of where it’s strong, where it's weak, and what the highest-priority areas for improvement are.
The goal isn't to produce an overwhelming list that paralyzes the team. It's to give you a clear, prioritized roadmap for making the app more secure.
A security review isn't a one-time event. Your codebase changes, new vulnerabilities are discovered in libraries you depend on, the threat landscape evolves, and your app grows in ways that create new risks.
For most growing SaaS products, a meaningful security review at least once a year is a reasonable baseline: with more frequent reviews when significant new features are shipped, when you're approaching a major milestone like a funding round or enterprise sales process, or when you've had a security incident or near-miss.
Our security reviews go beyond automated scanning. We read your code. We understand your app. We think like the people who might want to break it, and we give you a clear picture of what's there and what we can do about it.
Thinking about improving your app’s security? Let's talk. We'd be happy to walk through where your product stands and what a security review would involve.




