Case Study Supply Chain · npm Ecosystem
When Your Trust Tree Gets Poisoned: Lessons from the Latest npm Compromise
In the past year a series of malicious npm packages spread through stolen maintainer tokens, lookalike package names, and worm-like post-install scripts. Thousands of unrelated projects pulled the bad code without anyone touching a keyboard. Here is what we learned helping clients clean up—and the simple habits that would have prevented most of it.
TL;DR — A package you never installed can still ship code into your build by hiding inside a transitive dependency. The fix is not paranoia. It is a few small habits: lockfiles, pinned versions, install-time scanning, and a rule that build agents cannot touch production secrets.
What actually happened
Attackers used three repeating tricks. First, they phished or stole publish tokens from maintainers of popular packages, then pushed a new version that ran a small post-install script. Second, they registered packages with names that looked like real ones (one letter off, or a different casing) and waited for tired developers to typo. Third, some payloads were worm-like: once installed on a developer's machine, they searched for other npm tokens and re-published malicious updates from those accounts too.
The result was a chain reaction. A single compromised maintainer could indirectly ship code into projects that never imported their package at all.
Why it spread so far
- Implicit trust in transitive packages. Most teams only review direct dependencies. The package that ate the secrets was three levels deep.
- Build agents with broad access. CI runners often hold AWS keys, npm tokens, signing keys, and database credentials in the same environment that runs
npm install.
- Auto-updates and floating versions. Caret ranges like
^1.2.3 mean tonight's build can pick up code that did not exist yesterday.
- Post-install hooks treated as harmless. Lifecycle scripts can run arbitrary shell on the build machine. Most teams have never read what they do.
Five checks every team should run this week
- Commit a lockfile and treat it as source. Make sure
package-lock.json or pnpm-lock.yaml is committed, reviewed in PRs, and used by CI with npm ci or equivalent. No floating versions for releases.
- Turn off install scripts where you can. For most apps,
npm install --ignore-scripts in CI is safe and stops the most common payload path. Whitelist the few packages that genuinely need post-install builds.
- Strip credentials from the install stage. Split your CI into a lightweight install/build job and a separate deploy job. Production secrets should never be available where
npm install runs.
- Audit transitively. Run
npm audit --omit=dev, plus a tool like Socket, Snyk, or your own SCA on every PR. Trend the number of new transitive dependencies over time. A sudden spike is a signal.
- Rotate publish tokens and require 2FA. Every maintainer account that can publish should require hardware-key 2FA. Rotate tokens on a schedule and never share them between humans and CI.
What we did in our investigations
For each affected client we ran the same playbook: snapshot every machine that had run a recent npm install, look at outbound network logs for unusual hosts, rotate every credential that lived on those machines, and rebuild from a clean lockfile that pre-dated the bad version. The clients who already had this list as a runbook were done in hours. Those who did not took weeks.
If you want us to run this drill against your own build pipeline, the contact form is two scrolls away. Twenty minutes of conversation usually saves a long weekend later.
Mobile Security · iOS · Android
10 Mobile App Mistakes We See in Every Pen Test
We pen-test a lot of mobile apps. Different industries, different stacks—but the same ten findings show up almost every time. Here they are in plain English, with the one-line fix beside each.
- API keys hardcoded in the binary. Move them to a server-side proxy that signs requests for you.
- No SSL pinning. Easy to add with modern frameworks; stops most "free Wi-Fi" attacks instantly.
- Sensitive screens not blanked in app switcher. Set the secure-screen flag on Android, hide content on background on iOS.
- Logs leaking tokens. Audit your logging library. Anything labelled "debug" still ships in many release builds.
- Local storage with no encryption. Use Keychain on iOS, EncryptedSharedPreferences or Keystore on Android. Never plain SharedPreferences for secrets.
- Deep links that trust their parameters. Validate every deep link parameter the same way you validate a web form.
- Insecure WebViews. JavaScript bridges and file access on WebViews are the easiest way to escape sandboxing.
- Backend that trusts the client. Server-side authorization beats client-side checks every time. Assume the app is hostile.
- Anti-tamper checks that are easy to flip. If a single string comparison decides whether the app runs on a rooted device, it will be patched in ten minutes.
- Third-party SDKs with too many permissions. Audit the SDKs at every release. A library you added two years ago for analytics may now phone home with more than analytics.
None of these require exotic tooling to spot. A few hours with a proxy, a rooted test device, and a careful eye is enough. Fixing them takes a week. Ignoring them is what keeps incident response teams in business.
Cloud Security · AWS · IAM
The Silent Cloud Bills: Open Buckets and Leaky Defaults
Most of the cloud incidents we are called into are not the result of advanced attackers. They are misconfigurations: a bucket that was set to public for a one-time test and never closed, an IAM role with * on actions because someone wanted the demo to "just work", a forgotten lambda with a credential printed in plaintext.
The three patterns we see most
- Public S3 buckets with internal data. Often discovered by competitors before defenders find them. Search engines and bucket-listing services index them quickly.
- IAM roles that can assume each other. A low-risk role used by a marketing tool ends up with a chain that reaches production. Map the graph; it always surprises.
- Forgotten services left in default config. Old EC2 instances, unused RDS snapshots, container registries open to the world. Inventory beats firewalls.
Ten-minute audit you can do today
- List every bucket in your account and confirm none have
public-read or open bucket policies.
- Pull every IAM policy that contains
"*" on Action or Resource. If there are more than five, you have homework.
- Check for access keys older than 90 days. Rotate or retire them.
- Run CloudTrail event lookup for
AssumeRole calls from unexpected principals in the last 30 days.
- Confirm MFA is enforced on the root and on every human user. Yes, even the "just for testing" ones.
Cloud security is mostly hygiene. The teams that do well are the ones that schedule the boring review—not the ones with the fanciest tools.
AI Threats • RAG Security
Prompt Injection in RAG Pipelines Is the New Supply Chain Risk
RAG systems introduce a second supply chain: the document corpus. If the corpus is untrusted or easily poisoned, an attacker can insert instructions that override your system prompt and trigger unsafe actions. This is not a theoretical edge case; we see it in customer deployments across internal wikis, PDFs, and shared knowledge bases.
What changes in RAG? The model now treats retrieved content as authoritative context. Without strict boundaries, it will follow malicious instructions hidden inside trusted documents.
We recommend treating every retrieved document as untrusted input, even if it came from “internal” sources. Attackers only need one weak link — a stale document, an unreviewed upload, or a compromised wiki page — to poison the context.
Defensive controls that matter
- Assign trust levels to sources and keep low-trust data out of high-risk actions.
- Run retrieval filtering to remove prompt-like strings and suspicious instruction patterns.
- Use a separate safety layer that enforces hard rules, independent of model output.
- Log and review model decisions that reference external documents.
Most teams already do some of this, but the key is consistency. Make guardrails part of the RAG pipeline, not an afterthought in the UI layer.
Agentic Security • Tooling
Agentic Tool Abuse: Hardening MCP and Plugin Workflows
Agentic systems amplify risk because a single prompt can trigger multiple tool calls. When tools are overly permissive, the model can be tricked into taking real-world actions such as reading sensitive files, making unintended API calls, or leaking data through indirect channels.
We’ve seen successful prompt injection attacks where the model was tricked into “helpfully” exporting data, granting extra permissions, or executing actions outside of the user’s intent.
Hardening checklist
- Apply least-privilege scopes to each tool and require explicit user intent for high-risk actions.
- Validate tool outputs before the model can act on them.
- Limit tool chaining depth and enforce rate limits for sensitive operations.
- Implement policy-based guards that reject ambiguous or risky instructions.
Well-designed guardrails reduce the attack surface without sacrificing the workflow benefits of agentic systems. The goal is not to block agents — it’s to ensure every action is accountable and intentional.
Data Protection • LLM Hygiene
LLM Data Leakage: Logging, Redaction, and Secrets Hygiene
LLM systems often collect telemetry for observability. The problem: prompt and completion logs can accidentally store secrets, credentials, or sensitive business data. Once it hits a log or a trace, it’s very difficult to control downstream exposure.
We’ve seen incidents where secrets were leaked into monitoring dashboards, customer support tools, or long-term storage simply because logs were never scrubbed.
Practical steps
- Redact secrets at ingestion using deterministic patterns and contextual scanning.
- Separate security telemetry from developer analytics to reduce exposure.
- Expire and rotate embedding stores that include user-generated text.
- Define retention policies for prompts and completions, and enforce them.
Security and observability can coexist, but only with strict data boundaries and enforcement. Treat LLM logs like sensitive production data — because they usually are.