The package tells you things the traffic never will
An intercepting proxy records one session: the routes that session took, the fields that session sent, the responses it happened to receive. A decompiled APK or IPA is not a record of a session at all. It is the instruction set — every endpoint the build names, every credential compiled into it, the pinning logic as written, and the exact shape of what the client was told to send. Some of that is reachable no other way. The most useful part of it is not a statement about the application.
Yash K · · What we test · about 12 min
The gap
Traffic is a record of one afternoon
Everything a proxy shows you is something the application decided to do while somebody was holding it. That is a narrower set than it looks.
Run a mobile application through an intercepting proxy and you get an honest, useful and strictly partial account: the routes exercised by the paths somebody happened to walk, as the account they walked them with, on the build installed that day, under whatever the server returned that morning. Nothing about it is wrong. What it does not contain is the rest — the administrative route the build still knows the address of, the second environment the client can be pointed at, the key that only travels when one particular flow is triggered, the parameter no screen renders and the parser still reads. None of that is hidden anywhere clever. It is compiled in, and the compiled artefact is the one thing an attacker is guaranteed to have, because a store handed it to them.
What comes out
Five things a package carries that a session does not
Each of these is recovered from the binary as published — signed, obfuscated, and with nothing switched off for the benefit of the test.
| What is recovered | What the package shows | Why traffic does not |
|---|---|---|
| Secrets compiled in | Keys, tokens and cryptographic material placed into the build at compile time, together with the class that reads them and the call that uses them. CWE-798 is use of hard-coded credentials; CWE-321 is use of a hard-coded cryptographic key. | A secret that is never transmitted cannot be intercepted. One used to sign or encrypt locally never crosses the wire in a form anybody could pick out of a capture. |
| Endpoints nothing called | Host names, path templates and route constants the binary names but the exercised flows never reach — a retired administrative route, a second environment, an older version of the interface the client can still speak. | A proxy can only show a request that was made. A route nobody walked produces no traffic at all, and its absence from a capture is not evidence of anything. |
| Certificate pinning, as written | The pin set, the place the check sits, what the failure path does, and whether every code path that opens a connection reaches the check at all. | From outside, pinning presents as a refused connection, which is one bit of information. The code says which hosts are pinned, which are not, and what happens when a pin does not match. |
| Debug and logging paths | Verbose logging still compiled into a release build, diagnostic screens behind a flag whose default is the only thing holding them shut, and test scaffolding that survived the build configuration meant to exclude it. | These fire on a condition ordinary use does not meet — a flag, a build variant, a device property, an argument nobody passes by accident — so a trace of ordinary use records none of them. |
| What the client was told to send | Serialisation classes, request builders, header construction and the parameters assembled before the first byte leaves the device, including every field the interface never exposes. | Traffic shows the part of the contract the user interface chose to exercise. The builder shows the contract. |
| This is the row the second half of this article turns on. | ||
Secrets compiled in
- What the package shows
- Keys, tokens and cryptographic material placed into the build at compile time, together with the class that reads them and the call that uses them. CWE-798 is use of hard-coded credentials; CWE-321 is use of a hard-coded cryptographic key.
- Why traffic does not
- A secret that is never transmitted cannot be intercepted. One used to sign or encrypt locally never crosses the wire in a form anybody could pick out of a capture.
Endpoints nothing called
- What the package shows
- Host names, path templates and route constants the binary names but the exercised flows never reach — a retired administrative route, a second environment, an older version of the interface the client can still speak.
- Why traffic does not
- A proxy can only show a request that was made. A route nobody walked produces no traffic at all, and its absence from a capture is not evidence of anything.
Certificate pinning, as written
- What the package shows
- The pin set, the place the check sits, what the failure path does, and whether every code path that opens a connection reaches the check at all.
- Why traffic does not
- From outside, pinning presents as a refused connection, which is one bit of information. The code says which hosts are pinned, which are not, and what happens when a pin does not match.
Debug and logging paths
- What the package shows
- Verbose logging still compiled into a release build, diagnostic screens behind a flag whose default is the only thing holding them shut, and test scaffolding that survived the build configuration meant to exclude it.
- Why traffic does not
- These fire on a condition ordinary use does not meet — a flag, a build variant, a device property, an argument nobody passes by accident — so a trace of ordinary use records none of them.
What the client was told to send
- What the package shows
- Serialisation classes, request builders, header construction and the parameters assembled before the first byte leaves the device, including every field the interface never exposes.
- Why traffic does not
- Traffic shows the part of the contract the user interface chose to exercise. The builder shows the contract.
This is the row the second half of this article turns on.
One finding, read three ways
A hard-coded key is three different findings
The same recovered constant means one thing to the engineer who shipped it, another to whoever is testing it, and a third to whoever owns the service at the other end.
In the build
- What is there
- A constant and the class that consumes it, recovered from a signed release build with no source supplied and no unobfuscated variant requested.
- What it is worth on its own
- Not much. A compiled-in constant can be inert, and a finding that stops here is a code-quality observation wearing a severity rating.
In the run
- What is tested
- Whether the constant authenticates anything. It is used the way the application uses it, against the service the application uses it against, and what comes back is recorded.
- What is produced
- The request as sent, the response as returned, the steps that reproduce it, a CVSS v4.0 vector and a CWE — the artefact every B-52 finding carries, in every coverage class and in all three delivery models.
On the server
- What it says about the backend
- A key that still works says the service accepts a credential distributed to every installation of the application. That is a finding about an interface, not about a handset.
- Where it gets settled
- Against the interface itself, on its own routes, with a credential for each role — which is a separate scope, and the reason the last section of this article exists.
The contract
What the client was told to send is how you learn what the server accepts
A request builder inside a package is a specification written by the same team that wrote the server.
Parameters no screen renders
A request object with members nothing populates is a member the server still parses. Sending it is the test. The builder is where you find out it exists to be sent.
Values the client is not meant to choose
Role names, tier identifiers and entitlement flags assembled on the device tell you what the server distinguishes between. Whether the server verifies the distinction, rather than trusting the client to make it, is the next request.
Interface versions the build still speaks
A client able to construct a request against an older route tells you that route was live when the build was cut. Whether it is still live, and still as permissive, is one request away.
Computed signatures, and how they are computed
Where a request carries a signature or an integrity header, the routine that produces it is in the package. Reproducing that routine is what makes the rest of the interface testable at all.
The other base address
A pre-production host compiled in beside the production one is ordinary. Whether the one that is not production is defended like production is a question the package raises and only a request answers.
Whatever the client checks before sending
Length caps, allowed character sets and range checks performed on the device are a written list of the assumptions the server may be making about its input. Each one is a test case for the server.
Release builds
Debug paths do not always leave with the debug build
This category survives for procedural reasons rather than technical ones.
Verbose logging is switched by a flag, the flag has a default, and the default lives in a file that is not the file anybody opens before a release. Diagnostic scaffolding is excluded by a build configuration, and that configuration stays correct right up until somebody adds a dependency that quietly pulls it back in. None of this shows in a session trace, because none of it fires under ordinary use — the condition is a flag, a build variant, a device property or an argument nobody passes by accident. All of it shows immediately in the package, because code is either in the binary or it is not, and that is a question with an answer rather than a probability. It is also where the two halves of a mobile assessment stop being separable: a diagnostic route reachable from a shipped client is not a property of the client. It is a route on somebody’s server, reachable by every copy of the application that was ever downloaded.
Coverage in MASVS terms
Eight categories, and where each answer actually lives
OWASP MASVS v2.1.0 carries eight categories and 24 controls, read on 8 September 2026. Verification levels went at v2.0.0 and became the MAS Testing Profiles, so coverage is stated by category. What a category list does not tell you is which of them a package can settle on its own.
| Category | Where the answer lives | What that means in practice |
|---|---|---|
| MASVS-STORAGE | Package, then device | What is written, where it is written and what protects it is decided by code in the build, and confirmed by what is actually sitting on the device after a session. |
| MASVS-CRYPTO | Mostly the package | Algorithm, mode and the origin of key material are all visible statically. A compiled-in key is identified here — and then followed to whatever still honours it. |
| MASVS-AUTH | Both halves | The client’s side of an authentication or authorisation flow is in the binary. Whether the server enforces the decision the client displays is not in the binary, and cannot be. |
| MASVS-NETWORK | Package, then run | Which hosts are pinned, where the check sits and what the failure path does are read from the code, and then exercised against the live connection. |
| MASVS-PLATFORM | Package, then run | Exported components, inter-process entry points and what the application will accept from another application on the same device — declared in the build, confirmed by sending it. |
| MASVS-CODE | The package | Dependencies, their versions and the data handling around them. A static question with a static answer, and one of the few here that needs nothing else. |
| MASVS-RESILIENCE | The run | Resistance to reverse engineering and tampering is measured by working against it rather than by reading a setting. The build arrives hardened and stays hardened for the test. |
| MASVS-PRIVACY | Both halves | Added at v2.1.0. What the client collects is in the package. What happens to it after transmission is a question for the interface that received it. |
MASVS-STORAGE
- Where the answer lives
- Package, then device
- What that means in practice
- What is written, where it is written and what protects it is decided by code in the build, and confirmed by what is actually sitting on the device after a session.
MASVS-CRYPTO
- Where the answer lives
- Mostly the package
- What that means in practice
- Algorithm, mode and the origin of key material are all visible statically. A compiled-in key is identified here — and then followed to whatever still honours it.
MASVS-AUTH
- Where the answer lives
- Both halves
- What that means in practice
- The client’s side of an authentication or authorisation flow is in the binary. Whether the server enforces the decision the client displays is not in the binary, and cannot be.
MASVS-NETWORK
- Where the answer lives
- Package, then run
- What that means in practice
- Which hosts are pinned, where the check sits and what the failure path does are read from the code, and then exercised against the live connection.
MASVS-PLATFORM
- Where the answer lives
- Package, then run
- What that means in practice
- Exported components, inter-process entry points and what the application will accept from another application on the same device — declared in the build, confirmed by sending it.
MASVS-CODE
- Where the answer lives
- The package
- What that means in practice
- Dependencies, their versions and the data handling around them. A static question with a static answer, and one of the few here that needs nothing else.
MASVS-RESILIENCE
- Where the answer lives
- The run
- What that means in practice
- Resistance to reverse engineering and tampering is measured by working against it rather than by reading a setting. The build arrives hardened and stays hardened for the test.
MASVS-PRIVACY
- Where the answer lives
- Both halves
- What that means in practice
- Added at v2.1.0. What the client collects is in the package. What happens to it after transmission is a question for the interface that received it.
Where this comes from
What sits behind the two weakness classes named above
The two weakness classes, the framework versions behind the category table, and the artefact every finding arrives with.
Sourcing As of 2026-09-15
- CWE-798, use of hard-coded credentials, and CWE-321, use of a hard-coded cryptographic key, are named here because each is recovered from a package rather than intercepted from traffic. That property is the argument of this article.
- Framework versions — OWASP MASVS v2.1.0 with eight categories and 24 controls, and OWASP MASTG v2.0.0 for the test procedures behind them — were read on 8 September 2026 and carry that date wherever they appear on this site.
- Every B-52 finding, in every coverage class and in all three delivery models, arrives with the request as sent, the response as returned, the steps that reproduce it, a CVSS v4.0 vector and a CWE.
- Security Brigade has been running assessments since 2006, and every one of them was worked inside Lemon. That record is what the platform’s models were trained on.
Deliberately excluded
- Physical, hardware and wireless testing, which is out of scope for B-52 in every coverage class.
- Turnaround beyond one model: an observed median of one to three business days applies to the fully autonomous model. No equivalent figure exists for expert-verified or human-led work, so none is stated.
Scoping
The four scopes a mobile question splits into
One scan is one application or one target, so these are separate scopes worked to answer one question. Each is a coverage class in its own right.
The Android and iOS build you publish
Decompiled and analysed as shipped, with no source supplied and no softened build requested. That page sets out what happens to each defence the build carries.
The backendThe interface the client calls
Worked against its own routes with a credential for each role. Every question the package raises about what the server accepts is settled here, and nowhere else.
The hostsWhatever answers on the internet
The addresses and names the application reaches — including the ones the binary knows about and the asset list does not.
The sourceThe repository the build came from
Reading the code a package was compiled from is its own class, and it answers a different question: how the secret reached the build, rather than whether it is in it.
The payoff
The binary half and the API half are one engagement
A mobile test that stops at the package misses what the package tells you about the server.
Every item in this article points the same direction. A compiled-in key matters when a service still honours it. A route the running application never calls matters when the host still answers it. A parameter no screen renders matters when the parser still reads it. A signature routine matters because reproducing it is what makes the rest of the interface reachable. In each case the package supplies the question and the interface supplies the answer, and a report that delivers only the first half hands over a list of properties of a file. That is why these are scoped separately and worked together: one scan is one application or one target, and a client and the service behind it are two. B-52 covers both — along with the hosts that serve them and the source the build was compiled from — in all three delivery models. Fully autonomous, where signing off the scope is the last thing anybody is asked to do. Autonomous expert verified, where a senior Security Brigade auditor verifies every finding before it reaches you. Human led, where that auditor runs the engagement with the platform underneath. The coverage is identical in all three. What changes is whose signature the report carries.