taraonsecurity

Your Smart Contract Got Audited. Who Tested the Rest of the App?

Your Smart Contract Got Audited. Who Tested the Rest of the App?

Application security testing works best as a set of techniques layered across the development cycle. Threat modeling decides what to test. Static analysis, dependency scanning, and secret scanning check the code before it ships. Dynamic, API, and fuzz testing exercise the running application. Manual review and penetration testing confirm what is actually exploitable and catch the logic flaws that tools miss.

That is the textbook answer, and it holds. My own view is that crypto has a habit of treating the smart contract audit as the whole security plan. The audit is valuable, and it covers one layer. The website your users connect their wallets to, the API feeding it, the npm packages it pulls in, and the cloud account it runs on all sit outside that PDF, and some of the industry's most expensive incidents started in exactly those places.

A clean audit report is a lovely thing to post on X. Your users, unfortunately, spend their time on the frontend.

This piece walks through each testing technique in the order a team would usually adopt it, what each one can and cannot see, and where dApp teams tend to leave gaps. There is a checklist at the end to hold your own setup against, which is either reassuring or a very efficient way to ruin a Friday.

Why Application Security Testing Needs More Than One Technique

Each technique looks at the application from a different vantage point, and each has blind spots that another one covers. A static analyzer reads source code but never sees how the deployed app behaves behind a load balancer. A dynamic scanner sees the running app but has no idea which code paths it never reached.

Neither of them knows that a refund endpoint should only refund orders the caller owns. That kind of rule lives in the product spec and in people's heads, and no scanner reads either.

Standards bodies have described this split for years. NIST SP 800-115 separates review techniques, target identification, and vulnerability validation into distinct activities with different strengths, and the OWASP Web Security Testing Guide treats manual testing as necessary alongside tooling. In a dApp the split gets wider, because there are two codebases, one on-chain and one off-chain, with very different ways of failing.

Threat Modeling for Applications and dApps

Threat modeling is a structured way of asking what could go wrong with a system before any testing starts. It produces a map of assets, entry points, trust boundaries, and likely attacks, and that map tells every later technique where to look.

The Threat Modeling Manifesto frames the work around four questions: what are we working on, what can go wrong, what are we going to do about it, and did we do a good enough job. Teams often use STRIDE (spoofing, tampering, repudiation, information disclosure, denial of service, and elevation of privilege) as a prompt for the second question.

For a dApp, the trust boundaries are where the interesting answers sit: the wallet signature prompt, the RPC provider, price oracles, the frontend's hosting and DNS, any bridge, and whoever holds the admin keys or multisig. A model drawn at design time and revisited when the architecture changes, for example when you add a new chain or a new admin role, keeps the rest of the testing pointed at the right places.

If I could make one technique mandatory for crypto teams, it would be this one. Mostly because it forces someone to write down, in a shared document, who can upgrade the contract.

Static Application Security Testing (SAST)

SAST tools analyze source code, bytecode, or binaries without running the application. They trace how data moves from inputs to sensitive operations and flag patterns such as unsanitized input reaching a SQL query or a shell command.

Because SAST works on code, it fits early: in the IDE, on pull requests, or as a pipeline gate. Its weakness is context. A static tool often cannot tell whether a flagged path is reachable in production, so every result needs triage.

Teams that skip triage tend to stop reading the output altogether, at which point the scanner becomes a very diligent author of reports nobody opens. Tuning the rules to the frameworks you actually use keeps the signal readable, and so does switching off the rules for a language you stopped writing two years ago.

Software Composition Analysis and Secret Scanning

Software composition analysis (SCA) inventories the open-source libraries an application depends on and matches them against known vulnerability databases. Modern apps are built mostly from other people's packages, so a vulnerable dependency is often a shorter path in than a flaw in your own code.

Good SCA output tells you which version is affected, which fixed version exists, and whether your code actually calls the vulnerable function. Secret scanning sits alongside it and looks for API keys, private keys, and credentials committed to repositories, including in old commits everyone has politely forgotten about.

Crypto has a vivid example of why this layer deserves attention. In December 2023, an attacker phished a former Ledger employee, got into Ledger's npm account, and published malicious versions of the Ledger Connect Kit, a library many dApps used to connect wallets. Sites that loaded it from a CDN picked up the poisoned release automatically, users saw a fake wallet prompt wired to a drainer for around 5 hours, and roughly $600,000 was taken.

My takeaway is that SCA answers "are we running a known-bad version" very well, and it has no way of knowing that a brand new release is malicious. Pinning exact versions with integrity hashes, and treating every script your frontend loads from a CDN as code you ship, covers that gap.

Dynamic Application Security Testing (DAST)

DAST tests a running application from the outside, the way an attacker would. The tool crawls the app, sends crafted requests, and watches the responses for signs of injection, cross-site scripting, misconfigured headers, and similar issues.

It needs a deployed environment, so it usually runs against staging. For the issues it can see, it produces fewer false positives than SAST, because it is observing real behavior.

Its coverage depends on how far it can get into the app. Authenticated areas, multi-step flows, and single-page apps need careful setup, or large parts of the application go untested without anyone noticing. A DAST tool that cannot log in will report, with complete confidence, that your login page is very secure.

dApps add a wrinkle. Much of the interesting behavior happens after a wallet signature, and many scanners have no built-in way to complete one, such as a Sign-In with Ethereum message. Unless someone scripts that step, the authenticated half of the dApp stays invisible to the scan.

Interactive Application Security Testing (IAST)

IAST places an agent inside the running application, usually during functional or QA testing. The agent watches code execute in real time and reports when untrusted data reaches a dangerous operation.

That gives IAST the code-level detail of SAST with the runtime confirmation of DAST, and it can point to the exact line responsible. The catch is that it only reports on code paths that actually run, so its value tracks the quality of your existing test suite. If the QA suite covers the happy path and calls it a day, IAST will faithfully confirm that the happy path is fine.

Language and framework support also varies between tools, which is worth checking before anyone signs a contract.

API Security Testing

API security testing examines the endpoints that mobile apps, single-page frontends, and partner integrations talk to. APIs usually expose more functionality than the interface shows, and scanners built for web pages can miss them entirely.

The OWASP API Security Top 10 lists broken object level authorization as its first risk: a user changes an ID in a request and receives someone else's data. Testing for it means working from the API spec or captured traffic, authenticating as at least two users with different roles, and checking that each one can only reach its own objects. Rate limiting, mass assignment, and old API versions that never got switched off belong in the same pass.

Plenty of dApps run a conventional backend next to the contracts: an indexer, a portfolio API, a points or referral system, sometimes a KYC service. Those hold wallet-linked personal data, and in my view they get a fraction of the attention the contracts do. If I had to bet on where a dApp's next data leak comes from, I would put my money on the points API.

Fuzz Testing

Fuzz testing sends large volumes of malformed, unexpected, or random input to an application and watches for crashes, hangs, or strange behavior. It is especially effective against parsers, file upload handlers, protocol implementations, and anything written in a memory-unsafe language.

Coverage-guided fuzzers learn which inputs reach new code and mutate them to go deeper, so they find edge cases a person would rarely think to try. Picture handing your parser to a very patient toddler who never gets bored and keeps detailed notes.

Fuzzing works best as a long-running job pointed at the components that handle untrusted input most directly. Contract teams have a close cousin of it, covered further down, that fuzzes sequences of function calls instead of bytes.

Manual Secure Code Review

Manual code review is a person reading the code with security in mind, guided by the threat model. It catches issues that depend on intent, such as an authorization check that exists but compares the wrong field, or a cryptographic routine that uses a sound algorithm with a reused nonce.

Reading an entire codebase line by line is rarely practical, so reviewers prioritize authentication, authorization, session handling, cryptography, payment logic, and anything the threat model marks as high risk. In a dApp backend, add signature verification and nonce handling to that list, since a replayed signature is a classic way to spend someone's approval twice.

Review pairs well with SAST. The tool surfaces candidates, and the reviewer decides which are real and looks for the problems no rule could express.

Penetration Testing

A penetration test is an authorized, time-boxed attempt to exploit an application the way a real attacker would, followed by a report of what was proven. A good test chains findings together, for example combining an information leak with a weak password reset flow to take over an account, and demonstrates the impact with evidence.

This is the technique best suited to business logic flaws, authorization gaps across roles, and multi-step attack paths, because finding those requires understanding what the application is supposed to do. Testing usually follows a methodology such as the OWASP WSTG, with findings mapped to a verification standard like the OWASP ASVS. A useful report includes reproduction steps for every finding, and a retest after fixes confirms each issue is actually closed.

The format is changing quickly. Agentic platforms such as CredShields One now run AI agents that authenticate as different roles, map who can reach which objects, attempt the exploit, and report only what they reproduce. My opinion is that this is the most useful shift in the field in years, mostly because it moves penetration testing from a once-a-year event toward something that can run on every significant release.

Configuration and Infrastructure Review

A secure application can still be exposed by its environment. Configuration review checks the layers around the code: cloud permissions, storage bucket policies, container images, TLS settings, security headers, CORS rules, and debug features left on in production. For a dApp, add DNS records, the domain registrar account, and the CDN or edge platform serving the frontend.

The best-known case is BadgerDAO. In late 2021, an attacker used a compromised Cloudflare API key to inject a script into the project's website, and that script asked users to approve token spending for the attacker's address. Roughly $120 million left users' wallets, and the smart contracts were never exploited. To make it worse, Nexus Mutual signaled that its cover would not pay out, because the policy excluded frontend attacks, which is the kind of fine print nobody reads until the worst possible day.

Infrastructure-as-code scanning catches many of these problems before deployment, and a periodic review of the live environment catches drift after it. Overly broad cloud roles and long-lived API keys deserve particular attention, because they turn one leaked credential into control of everything else in the account.

Testing Smart Contracts and dApps

Smart contract code is often immutable once deployed and holds value directly, so testing has to happen before launch. A missed bug can be very expensive to recover from, assuming recovery is possible at all.

Contract teams usually combine several techniques. Static analysis tuned for contract languages catches known bug classes early; SolidityScan, which CredShields built for Solidity, is one example. Invariant tests assert properties that must always hold, such as total supply never exceeding a cap, and fuzzers hit contract functions with random sequences of calls to try to break them. Manual review then focuses on access control, reentrancy, oracle and price manipulation, and upgradeability, with formal verification reserved for the highest-value contracts.

Everything earlier in this article still applies to the rest of the dApp. The frontend, the backend APIs, the wallet integration, and the infrastructure serving them need web and API testing like any other application, and the Ledger and BadgerDAO cases show how costly that layer can be. If your project's security page lists one audit and nothing else, the frontend is probably wondering when its turn comes.

How the Application Security Testing Techniques Fit Together

Each technique answers a different question at a different point in the cycle. The summary below shows where each one runs and where it runs out of road, so you can see which questions your current program answers and which it leaves open.

  • Threat modeling runs at design and finds missing controls and risky architecture, though it is only as good as the team's knowledge of the system.

  • SAST runs on code and pull requests and finds injection patterns and unsafe functions, with false positives and no runtime context as the trade-off.

  • SCA and secret scanning run at build and find vulnerable dependencies and leaked credentials, limited to issues that are already known.

  • DAST runs against staging and finds injection, XSS, and header issues, with coverage that depends on crawl and login setup.

  • IAST runs during QA and finds code-level flaws, but only on paths the test suite exercises.

  • API testing runs against staging and finds object-level authorization and mass assignment issues, provided you have specs and multiple test accounts.

  • Fuzzing runs at build and on an ongoing basis and finds crashes and parser bugs, while staying weak on logic flaws.

  • Manual code review happens before release and finds logic errors and intent mismatches, at the cost of time and careful prioritization.

  • Penetration testing happens before major releases, and increasingly on every significant one, and finds chained exploits and business logic flaws with reproduced impact.

  • Configuration review happens at deployment and on an ongoing basis and finds cloud, container, DNS, and TLS misconfigurations, which requires access to the environment.

  • Smart contract testing happens before deployment and finds contract-level bugs through static analysis, invariant tests, call-sequence fuzzing, and review.

Application Security Testing Checklist

The checklist below turns everything above into questions about your own program. A "no" on any line points to the gap worth closing next, and a "not sure" usually means the same thing.

  • A threat model exists for each major application and was updated after the last architecture change.

  • SAST runs on every pull request, and someone triages its findings on a set schedule.

  • Dependencies are scanned on every build, with a defined window for patching critical issues.

  • Frontend packages and CDN scripts are pinned to exact versions with integrity hashes.

  • Secret scanning covers repository history as well as new commits.

  • DAST runs authenticated against staging and reaches the main user flows, including those behind a wallet signature.

  • API tests run with at least two roles and check object-level authorization on every endpoint.

  • Components that parse untrusted input are fuzzed.

  • High-risk code (authentication, payments, signature handling, and cryptography) gets a manual review before release.

  • Penetration testing covers every major release, with a retest after fixes.

  • Cloud, CDN, DNS, and registrar accounts are reviewed against a written baseline, with MFA on every one and no unexplained API keys.

  • For smart contracts, invariant tests and manual review are complete before deployment.

Where to Start This Week

Take the checklist above and mark each line yes or no for your main application, including the parts that are not the contract. Pick the first "no" your team can close in a sprint, usually dependency pinning or secret scanning, and schedule it before moving on to the next one.

Most of us run on a few comfortable assumptions: the audit covered it, the scanner would have flagged it, nobody can reach that API without a wallet, and the CDN script hasn't changed since launch. I'd love for all of those to be true. The catch is that they go a little stale every time you ship something new.

If you want to replace those assumptions with evidence, CredShields One is what my team at CredShields built for exactly this. It runs pentesting agents against your frontend and APIs whenever you need it, tells you which vulnerabilities are actually exploitable, and hands you the proof, so the conversation with your developers is a short one. Reach out to us.

Did you enjoy this article?


Tara reads security
Tara reads security

Cybersecurity researcher


taraonsecurity
taraonsecurity

Security researcher writing about web application, cloud, and smart contract security. Notes on vulnerabilities I find interesting and how they get fixed.

Publish0x Publish0x

Reward the author with $0.01 in crypto, and earn yourself as you read!

20% to author / 80% to me.
Rewards are FREE. Publish0x pays them, not you.

Page not displaying correctly?