
Research
/Security News
737 Chrome VPN Extensions Linked to Brand Impersonation and Browser Traffic Redirection
The campaign amassed more than 75,000 installs by targeting Russian-speaking users seeking access to blocked services.
lna-readiness
Advanced tools
Static CI scanner that finds requests Chrome 142 Local Network Access will silently break — before they ship. CLI + ESLint plugin + GitHub Action.
Find the requests Chrome 142 will silently break — before your users do.
$ npx lna-readiness src
src/print.ts
12:3 error fetch() to a local address (http://localhost:9100/print) will be gated by
Chrome 142 LNA and fail silently if the user denies the prompt.
lna/fetch-private-target
fix: fetch("http://localhost:9100/print", { targetAddressSpace: "local" })
1 error, 0 warnings across 214 files
Chrome 142 shipped the Local Network Access permission to the stable channel in late 2025. Any request from a web page to a more-private address space now goes behind a one-time prompt:
localhost, 127.0.0.0/8, ::1, 0.0.0.010.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16100.64.0.0/10 (Tailscale and friends)169.254.0.0/16, IPv6 fe80::/10fc00::/7*.local and *.localhost (mDNS)If the user denies or dismisses the prompt, the request fails silently — no console error you can catch reliably, no rejected promise you'd expect. The LocalNetworkAccessRestrictionsTemporaryOptOut enterprise escape hatch is removed after M152, so the gate hardens through 2026.
This breaks a specific, real set of apps: POS and label-printer bridges (the QZ Tray pattern), Office/Figma/Photoshop add-ins talking to a desktop companion over localhost, IoT and router config UIs served from 192.168.x.x, camera snapshot <img> tags, and iframe embeds that reach a private-IP backend.
The fix exists in docs — a targetAddressSpace hint on fetch, an allow="local-network-access" attribute on iframes, a delegated permission from the top frame. What's been missing is a way to find which requests in a codebase need it without shipping to production and waiting for the bug reports. That's this tool.
npm i -D lna-readiness
# or run it once, no install:
npx lna-readiness
lna-readiness # scan the source tree from the current dir
lna-readiness src app pages # scan specific dirs/globs
lna-readiness --json # machine-readable, for tooling
lna-readiness --markdown # a table for PR comments / CI summaries
lna-readiness --fail-on-warning # warnings fail the build too
lna-readiness --max-warnings 5 # allow up to 5 warnings
Exit code is 1 when there are errors (or warnings over your threshold), 0 otherwise — drop it into any CI step.
| Rule | What it catches |
|---|---|
lna/fetch-private-target | fetch() to a private/local host with no targetAddressSpace hint |
lna/xhr-private-target | XMLHttpRequest.open() to a private/local host |
lna/axios-private-target | axios.*() to a private/local host |
lna/websocket-private-target | new WebSocket() to a private/local host |
lna/eventsource-private-target | new EventSource() to a private/local host |
lna/iframe-missing-allow | <iframe> at a private/local URL missing allow="local-network-access" |
lna/{img,script,source,...}-private-src | sub-resource loaded from a private/local host |
It scans .js/.jsx/.ts/.tsx/.mjs/.cjs, .vue, .svelte, and .html. Requests with a URL it can't resolve statically (a variable, a template with an interpolated host) are left alone — no guessing, so no noise.
Same rules, live in your editor and existing lint run (flat config):
// eslint.config.js
import lna from 'lna-readiness/eslint-plugin';
export default [
{
plugins: { lna },
rules: { 'lna/no-unguarded-local-network-request': 'error' },
},
];
# .github/workflows/lna.yml
name: LNA Readiness
on: [pull_request]
jobs:
lna:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: fernforge/lna-readiness@v1
with:
# paths: 'src' # optional, defaults to the whole repo
# fail-on-warning: true # optional
# max-warnings: 0 # optional
The action writes a Markdown table to the job summary and exposes errors / warnings outputs.
import { scan, isPrivateNetworkUrl, targetSpaceOf } from 'lna-readiness';
const { findings } = scan(process.cwd());
isPrivateNetworkUrl('http://192.168.1.20/label'); // true
targetSpaceOf('ws://printer.local:8181/'); // 'local'
targetSpaceOf('https://api.stripe.com'); // 'public'
It's static analysis. It catches requests whose target is a literal string in your source. It can't see a host that's only known at runtime (from config, an env var, or user input) — for those, test with the prompt denied and handle the failure explicitly. It also doesn't check that you actually hold the granted permission; a passing scan means "these calls carry the right hints/attributes," not "these calls will succeed."
MIT
FAQs
Static CI scanner that finds requests Chrome 142 Local Network Access will silently break — before they ship. CLI + ESLint plugin + GitHub Action.
We found that lna-readiness demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
The campaign amassed more than 75,000 installs by targeting Russian-speaking users seeking access to blocked services.

Company News
Open source maintainers are under more pressure than ever. We're raising our open source program from the Team plan to the Business plan, free.

Security News
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.