<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hossam Atef</title>
    <description>The latest articles on DEV Community by Hossam Atef (@hossam9k).</description>
    <link>https://dev.to/hossam9k</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4208339%2Fdb5248de-278c-4af2-9b04-d627bcc1e021.jpg</url>
      <title>DEV Community: Hossam Atef</title>
      <link>https://dev.to/hossam9k</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hossam9k"/>
    <language>en</language>
    <item>
      <title>Stop telling news apps to pin certificates: risk-calibrated mobile security scanning</title>
      <dc:creator>Hossam Atef</dc:creator>
      <pubDate>Tue, 27 Oct 2026 11:08:23 +0000</pubDate>
      <link>https://dev.to/hossam9k/stop-telling-news-apps-to-pin-certificates-risk-calibrated-mobile-security-scanning-e5f</link>
      <guid>https://dev.to/hossam9k/stop-telling-news-apps-to-pin-certificates-risk-calibrated-mobile-security-scanning-e5f</guid>
      <description>&lt;p&gt;If you've ever run a mobile security scanner on a news app, you know the report. Somewhere near the top, next to the real problems, it says: &lt;em&gt;No certificate pinning detected.&lt;/em&gt; Run the same scanner on a banking app and you get the same line, at the same severity.&lt;/p&gt;

&lt;p&gt;Those two apps should not get the same advice. For the banking app, missing pinning is a reasonable finding. For the news app, adding pinning brings outage risk (a rotated certificate or a missed backup pin can lock every user out) and very little security in return. A team that follows the scanner's advice literally ends up spending a sprint on something that makes their app more fragile.&lt;/p&gt;

&lt;p&gt;That's the problem I wanted to fix. I built &lt;a href="https://github.com/hossam9k/masaudit" rel="noopener noreferrer"&gt;masaudit&lt;/a&gt;, a free, open-source scanner that reads the source of Android, iOS, React Native (JavaScript, TypeScript, and Expo config included), Kotlin Multiplatform, and Flutter apps, plus their GitHub Actions workflows. It asks one question first: &lt;strong&gt;"What kind of app is this?"&lt;/strong&gt; Then it scales its advice to the answer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fhossam9k%2Fmasaudit%2Fmain%2Fdocs%2Fdemo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fhossam9k%2Fmasaudit%2Fmain%2Fdocs%2Fdemo.gif" alt="masaudit asks what kind of app it is, shows the summary for a banking app, then scans the same app as a news app" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with one-size-fits-all reports
&lt;/h2&gt;

&lt;p&gt;Most static scanners treat every rule as equally relevant to every app. That has two costs.&lt;/p&gt;

&lt;p&gt;First, noise. When "your release build is debuggable" sits next to "consider pinning," developers can't tell what blocks a release. They either fix everything (slow, sometimes harmful) or start ignoring the report.&lt;/p&gt;

&lt;p&gt;Second, missing context. "We don't pin because we're a content app" is a defensible position, but a report that never says &lt;em&gt;why&lt;/em&gt; a check doesn't apply leaves you to argue it out with a reviewer, with no source to point to.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea: map app types to OWASP MAS profiles
&lt;/h2&gt;

&lt;p&gt;The OWASP Mobile Application Security project (MASVS, MASTG, MASWE) already has a vocabulary for this. It describes security profiles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;L1&lt;/strong&gt;: the baseline every app should meet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L2&lt;/strong&gt;: defense in depth for apps handling sensitive data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;R&lt;/strong&gt;: resilience against reverse engineering and tampering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;P&lt;/strong&gt;: privacy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;masaudit offers 13 app types and maps each one to a set of these profiles. A banking app gets L1 + L2 + R + P. A news or content app gets L1 + P. Each of the 207 rules belongs to one or more profiles, and every finding lands in one list:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fix before release&lt;/strong&gt; and &lt;strong&gt;Fix soon&lt;/strong&gt;: the only two that can fail CI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check by hand&lt;/strong&gt;: rules that often misread code; confirm before fixing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hardening checklist&lt;/strong&gt;: protections your app type should add (pinning, root detection, App Attest), which are advice, not bugs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not needed for your app&lt;/strong&gt;: with the reason. A finding your app doesn't need isn't hidden.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notes&lt;/strong&gt;: findings in test, debug, demo, or unused code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The app type is your answer, not a guess. If clues in shipping code (a payment SDK, card-number fields) point to a riskier type, masaudit prints a "Check your app type" warning that names the evidence. It never switches the type on its own.&lt;/p&gt;

&lt;p&gt;Every rule links to a section of my free book, &lt;a href="https://github.com/hossam9k/mobile-app-security" rel="noopener noreferrer"&gt;&lt;em&gt;Mobile Application Security: A Study Book for Engineers&lt;/em&gt;&lt;/a&gt;, which records how each claim was verified.&lt;/p&gt;

&lt;h2&gt;
  
  
  How calibration works: the pinning example
&lt;/h2&gt;

&lt;p&gt;Pinning is the clearest case. The book's §8.8, "The decision, by application type," argues that you should reason from the threat, and lands roughly here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Banking, fintech, trading, health:&lt;/strong&gt; pin the SPKI of an intermediate, keep a backup pin, and monitor Certificate Transparency. The value of the data justifies the operational weight.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;E-commerce:&lt;/strong&gt; depends on whether the app handles card numbers itself or hands payment to a provider's SDK.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Marketplaces, ride-sharing, delivery:&lt;/strong&gt; strict TLS, CT monitoring, and backend fraud detection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Social, messaging, content:&lt;/strong&gt; strict platform defaults. Pinning a news feed adds risk and no security.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise:&lt;/strong&gt; trust your private CA explicitly, and pin only if you control both ends.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also repeats a rule from OWASP's pinning cheat sheet: "if you can't update the pinset securely, don't pin."&lt;/p&gt;

&lt;p&gt;In masaudit, the "no certificate pinning" rules (&lt;code&gt;AND-007&lt;/code&gt; for Android, &lt;code&gt;IOS-007&lt;/code&gt; for iOS) belong to MAS-L2 and apply only to banking, identity, health, and e-commerce apps whose own code handles card numbers. For a banking app, it goes on the Hardening checklist. Scan a content app and it shows up like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;No certificate pinning (needed for high-risk apps)&lt;/strong&gt; (&lt;code&gt;AND-007&lt;/code&gt;). Pinning is for apps that handle money, health data, identity credentials, or card numbers. For other apps the book recommends strict TLS plus Certificate Transparency monitoring: pinning adds outage risk for little gain, and if you can't update the pins securely, don't pin (§8.8).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Calibration only goes one way, though. L1 rules, like a debuggable release build or a WebView that calls &lt;code&gt;handler.proceed()&lt;/code&gt; on a TLS error, are "fix before release" for every app type.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo: same code, two app types
&lt;/h2&gt;

&lt;p&gt;The repository includes a deliberately insecure demo app, &lt;em&gt;Acme Wallet&lt;/em&gt;, with Android, iOS, and GitHub Actions code. Here's the same code scanned twice:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;As a &lt;strong&gt;banking&lt;/strong&gt; app&lt;/th&gt;
&lt;th&gt;As a &lt;strong&gt;news&lt;/strong&gt; app&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fix before release&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fix soon&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check by hand&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hardening checklist&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Not needed for your app&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The banking app's 14 hardening items become the news app's "not needed" list: pinning, root and debugger detection, App Attest, Play Integrity, screenshot blocking, and more, each with its reason.&lt;/p&gt;

&lt;p&gt;The top three fixes are the same either way: a debuggable release build (&lt;code&gt;AND-002&lt;/code&gt;), a WebView that ignores TLS errors (&lt;code&gt;AND-004&lt;/code&gt;), and a &lt;code&gt;pull_request_target&lt;/code&gt; workflow that checks out the pull request's code (&lt;code&gt;CI-003&lt;/code&gt;). Calibration removes noise without hiding what matters.&lt;/p&gt;

&lt;p&gt;Read both reports: &lt;a href="https://github.com/hossam9k/masaudit/blob/main/docs/sample-report-banking.md" rel="noopener noreferrer"&gt;banking&lt;/a&gt; and &lt;a href="https://github.com/hossam9k/masaudit/blob/main/docs/sample-report-news.md" rel="noopener noreferrer"&gt;news&lt;/a&gt;. Each finding has why it matters, how to fix it (often with paste-ready code), how to verify the fix, an effort estimate, its OWASP IDs, and the book section behind it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dogfooding: one real finding
&lt;/h2&gt;

&lt;p&gt;Before releasing, I ran masaudit on the production Android app I work on. It found one real issue.&lt;/p&gt;

&lt;p&gt;The app used Tink's &lt;code&gt;AndroidKeysetManager&lt;/code&gt; without checking &lt;code&gt;isUsingKeystore()&lt;/code&gt; after building it. That matters because when the Android Keystore fails, Tink can fall back to storing the keyset unencrypted and only log a warning. Your "encrypted" data is then protected by a key that anyone with file access can read. The book warns about this exact fallback, and it's the kind of thing that's easy to miss in code review because nothing looks wrong. The fix is small: check &lt;code&gt;isUsingKeystore&lt;/code&gt; and record the result, so you know when the fallback happens.&lt;/p&gt;

&lt;p&gt;One finding isn't a benchmark. But it was a real issue that a well-maintained codebase had missed, which is the kind of result I wanted.&lt;/p&gt;

&lt;h2&gt;
  
  
  How accurate is it?
&lt;/h2&gt;

&lt;p&gt;A scanner with false alarms gets ignored. A scanner that misses things gives false comfort. So I measured both.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Precision: are the findings real?&lt;/strong&gt; I scanned 19 open-source apps, including Signal, Bitwarden, Wikipedia, Nextcloud, DuckDuckGo, and Thunderbird, with every rule on. Reviewers who didn't write the rules opened every finding in shipping code (CI findings were sampled). Each false alarm was fixed, with a test. 79 rules fired, and 78 are at 90% precision or better. The one below, &lt;code&gt;IOS-012&lt;/code&gt; at 89%, is marked &lt;em&gt;Check by hand&lt;/em&gt;. &lt;code&gt;scripts/corpus.py&lt;/code&gt; reproduces the corpus at fixed commits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recall: what does it miss?&lt;/strong&gt; For that I used four apps whose authors list the weaknesses they planted: OWASP's MASTG demos, DIVA, InsecureBankv2, and DVIA-v2. Of the 133 weaknesses a source scanner can see in them, version 0.4.3 found 70. That's 53%.&lt;/p&gt;

&lt;p&gt;That wasn't good enough, so I went through every miss. Some were weaknesses no rule covered. Others were rules that missed a variant, such as accept-all trust code after a &lt;code&gt;guard let&lt;/code&gt; unwrap. Version 0.5.0 adds 15 rules, fixes 20 others, and finds 126 of the 133, or 95%.&lt;/p&gt;

&lt;p&gt;Read that 95% with care. I wrote the 0.5.0 rules from the 0.4.3 misses on these same apps, so it's recall on apps masaudit was tuned against, not a prediction for your code. The 7 remaining misses, and why, are &lt;a href="https://github.com/hossam9k/masaudit/blob/main/docs/precision.md#recall-on-apps-with-known-weaknesses" rel="noopener noreferrer"&gt;published with the method&lt;/a&gt;. Some are on purpose: the keyboard-caching rules skip name and email fields, because flagging every name field would bury the real ones.&lt;/p&gt;

&lt;p&gt;Version 0.6.0 added React Native's JavaScript and TypeScript, with the same method: 10 open-source React Native apps (Mattermost, Bluesky, Expensify, MetaMask, and others), every new finding judged by reviewers who didn't write the rules (57 of 63 real), and 21 of 28 planted weaknesses found in three deliberately vulnerable React Native apps, up from 6. There's also a &lt;a href="https://github.com/hossam9k/masaudit/blob/main/docs/compare-rnsec.md" rel="noopener noreferrer"&gt;rule-by-rule comparison with rnsec&lt;/a&gt;, the other free React Native scanner.&lt;/p&gt;

&lt;p&gt;The new rules also went through the precision check. On the 19 real apps they added 3 findings, all real, and removed none.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On apps it has never seen, it's noisier, and I published that too.&lt;/strong&gt; Everything above was measured on apps the rules were tuned on. So in five rounds I scanned 87 open-source apps no rule was written for, alongside the other free scanners (mobsfscan, Semgrep's free rules, and rnsec) on the same commits, and judged random samples of every tool's findings blind, with tool names removed.&lt;/p&gt;

&lt;p&gt;The honest result: &lt;strong&gt;about 1 in 3 findings in masaudit's fix lists on unseen code is a false alarm.&lt;/strong&gt; In the latest round (0.7.0, 16 apps), 29% of the sampled app-code findings in &lt;em&gt;Fix before release&lt;/em&gt; and &lt;em&gt;Fix soon&lt;/em&gt; were false, against 1 in 10 on the tuned apps. CI workflow findings held up: 1 of 30 false. The other tools did worse on the same apps: 90% of mobsfscan's sampled findings were false, 80% of rnsec's.&lt;/p&gt;

&lt;p&gt;Each round changed the tool. Missing protections moved to the Hardening checklist, the rules most often wrong moved to Check by hand, and 0.7 reads the Gradle module graph, Xcode targets, and call sites, so findings in demo apps, test modules, and functions nothing calls stop failing CI. One caveat: the judges were AI reviewers that wrote no rule, not human experts. Every verdict and reason is published so you can check them: &lt;a href="https://github.com/hossam9k/masaudit/blob/main/docs/benchmark.md" rel="noopener noreferrer"&gt;Benchmark on unseen apps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Speed:&lt;/strong&gt; DuckDuckGo for Android, about 10,000 files, scans in about 5 seconds on an Apple M5 laptop. The parsers are fuzzed in CI, and crafted inputs scan in linear time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it with an AI assistant (optional)
&lt;/h2&gt;

&lt;p&gt;The scanner has no AI in it and needs no account. If you use Claude Code, there's a plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/plugin marketplace add hossam9k/masaudit
/plugin install masaudit@masaudit
/masaudit:scan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same skill works in Codex, Copilot, Cursor, Gemini CLI, and other Agent Skills tools, and any chat assistant can use the fix plan at the end of every report. The assistant confirms each finding in your code and fixes one at a time. It asks before any change that alters behavior and never commits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits
&lt;/h2&gt;

&lt;p&gt;masaudit reads source and configuration. It can't see your server, runtime behavior, or business logic, which is where many real breaches happen. Every report has a &lt;em&gt;Residual risk&lt;/em&gt; section that says so. It doesn't read APK or IPA files or do dynamic testing; MobSF does both, so use it alongside masaudit.&lt;/p&gt;

&lt;p&gt;The app-type mapping is a judgment call grounded in the book and in OWASP MAS. If your app doesn't fit a type cleanly, pick the riskier one, or tell me where the mapping is wrong.&lt;/p&gt;

&lt;p&gt;On safety: it never builds or runs the project, makes no network calls (a test enforces it), sends no telemetry, and masks secrets in every output, so it's safe to run on code you don't trust. The project has the OpenSSF Best Practices passing badge and an OpenSSF Scorecard, and releases ship with signed build provenance and &lt;code&gt;SHA256SUMS&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it in 60 seconds
&lt;/h2&gt;

&lt;p&gt;You need Python 3.9 or newer. No other dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvx masaudit &lt;span class="nb"&gt;.&lt;/span&gt;                           &lt;span class="c"&gt;# or: pipx run masaudit .&lt;/span&gt;
uvx masaudit &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--app-type&lt;/span&gt; news           &lt;span class="c"&gt;# skip the question&lt;/span&gt;
uvx masaudit &lt;span class="nt"&gt;--explain&lt;/span&gt; AND-024           &lt;span class="c"&gt;# what a rule checks, offline&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No installer? Download &lt;code&gt;masaudit.pyz&lt;/code&gt; from the &lt;a href="https://github.com/hossam9k/masaudit/releases/latest" rel="noopener noreferrer"&gt;latest release&lt;/a&gt; and run &lt;code&gt;python3 ~/Downloads/masaudit.pyz .&lt;/code&gt; (Windows: &lt;code&gt;py $HOME\Downloads\masaudit.pyz .&lt;/code&gt;). It writes &lt;code&gt;SECURITY-AUDIT.md&lt;/code&gt; with the top fixes first.&lt;/p&gt;

&lt;p&gt;For teams: a GitHub Action that uploads SARIF to code scanning, writes a job summary with the verdict and top fixes, and takes a baseline so CI fails only on new findings; copy-paste setups for Bitrise, GitLab CI/CD, CircleCI, Travis CI, and Docker; a pre-commit hook; and &lt;code&gt;--import-sarif&lt;/code&gt; to sort results from Android Lint, mobsfscan, Semgrep, and zizmor by the same app type.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Repository: &lt;a href="https://github.com/hossam9k/masaudit" rel="noopener noreferrer"&gt;https://github.com/hossam9k/masaudit&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Getting started (about ten minutes, on the demo app): &lt;a href="https://github.com/hossam9k/masaudit/blob/main/docs/getting-started.md" rel="noopener noreferrer"&gt;https://github.com/hossam9k/masaudit/blob/main/docs/getting-started.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Accuracy and speed: &lt;a href="https://github.com/hossam9k/masaudit/blob/main/docs/precision.md" rel="noopener noreferrer"&gt;https://github.com/hossam9k/masaudit/blob/main/docs/precision.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Rules (207: 70 Android, 47 iOS, 20 React Native, 48 cross-platform, 8 Flutter, 8 CI and supply chain, 6 KMP; they cover 52 of the 78 weaknesses in OWASP's MASWE catalog): &lt;a href="https://github.com/hossam9k/masaudit/blob/main/docs/rules.md" rel="noopener noreferrer"&gt;https://github.com/hossam9k/masaudit/blob/main/docs/rules.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The book: &lt;a href="https://github.com/hossam9k/mobile-app-security" rel="noopener noreferrer"&gt;https://github.com/hossam9k/mobile-app-security&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;masaudit is free: code under Apache-2.0, fix snippets under MIT-0, explanations under CC BY-SA 4.0. It's not an OWASP project, and OWASP doesn't endorse tools. If a rule fires where it shouldn't, please open an issue. False-positive reports are the most useful contribution, because a rule that raises false alarms gets ignored.&lt;/p&gt;

</description>
      <category>security</category>
      <category>android</category>
      <category>ios</category>
      <category>kotlin</category>
    </item>
  </channel>
</rss>
