The scanner now checks your code against what you said it would do
- +Six actions in the app could fail without telling you. Saving notification preferences said “Saved!” whatever the server replied — including when your session had quietly expired, which is the likeliest thing to happen to a settings page left open in a tab. Revoking an API key, revoking a session and deleting a saved questionnaire response each did nothing visible when they failed, after you had confirmed an action described as irreversible; the key or session was still live and you had no way to know. Renaming a device showed the new name even when the save failed, and it stayed until you reloaded. Creating a checklist share link failed silently, which is worse than it sounds — nothing was copied, so the next thing you pasted to a customer was whatever was already on your clipboard. All six now say what happened, and the device rename puts the old name back.
- +Claiming a free Trust Page after signing up no longer ends on an error screen. The last step of the signup flow tried to clear its own cookie while rendering, which Next.js does not permit — so the page was claimed and then the request failed, showing an error for something that had actually worked, and leaving a stale cookie that made the next page load look like a second, failed claim. The claim now happens in a request handler that can clear the cookie properly. Separately, when a Trust Page has already been claimed by a different account we now say that, instead of telling you the link was missing or expired and sending you back to your inbox to re-click a link that would fail the same way.
- +Deleting your account now deletes all of it. Twelve tables keyed to a user were never cleared, and the consequential one was your Trust Page: it resolves by its public slug with no check that the owner still exists, so a deleted customer’s branded page stayed online and stayed indexed by search engines — with its security sections blank, because the scan data behind them had been deleted. The same applied to a claimed free Trust Page, whose record also held the email address it was created with. Also cleared now: saved questionnaires and their per-prospect responses, custom domain verifications, project descriptions, your AI-analysis preference, dismissed prompts, review reminders and feedback. A connected GitHub App installation is unlinked rather than deleted, because the App still exists on GitHub — it simply stops being associated with the closed account.
- +A saved scan kept only the first 100 findings, and not the most important ones. The summary counted every finding — so a scan could report 18 critical issues on the dashboard while the scan page could only ever show four of them, because the hundred that happened to be kept were whichever the scanner emitted first rather than the most severe. Saved scans now keep the most severe findings first, and up to 500 rather than 100, which covers every scan size we have seen. Where a scan still has more than that, the page says so instead of showing two numbers that disagree. Findings dropped by older saved scans cannot be recovered — re-run the scan to see them all.
- +New: an intent check. Every other thing a scanner tells you is about code. This one is about the gap between your code and your own description of it. If your README says "customers can only see their own orders" and an endpoint returns orders without checking who is asking, you now get that stated as a sentence: what you wrote, where you wrote it, and the endpoint that disagrees. It reads README.md, CLAUDE.md, AGENTS.md, .cursorrules, and a .xploitscan-intent file if you keep one.
- +Our own benchmark number was stale on six pages, and understated. Several comparison and blog pages still said XploitScan caught 15 of 16 on the held-out third-party set; it has caught all 16 since the broken-access-control rule landed. One of those pages went further and said the sixteenth was a case no scanner catches, ours included — that stopped being true and is now corrected. The benchmark page itself was always right; the copy around it had drifted.
- +The benchmark page now states which rule set produced our own score. It has always said Semgrep and Bearer are measured with their free rules; it said nothing about ours, which made a comparison look like-for-like when it was not. Our held-out score uses the full catalogue — what the MCP server, the VS Code extension and a paid CLI run — and the free CLI would score lower. The number did not change; what you are told about it did.
- +The intent check now reads the file your AI tool already wrote. Beyond README.md and CLAUDE.md it picks up .cursorrules, .cursor/rules, .windsurfrules, .github/copilot-instructions.md, .bolt/prompt, and saved chat transcripts under .specstory/history — where it reads your turns and deliberately ignores the assistant's, because the model's summary of what it built is not a specification of what you asked for.
- +And if your project says nothing about what it is for, the scanner asks. One question, once, saved to a plain .xploitscan-intent file you can edit. It only asks in a real terminal — never in CI, never when output is piped, and never inside a git hook, so it cannot interrupt a commit. `xploitscan intent` does the same thing deliberately, and `--intent "..."` passes a sentence for a single run. We measured why this matters: across 19 real AI-built projects, 13 had nothing on disk that said what the app was supposed to do.
- +The intent check ignores code that does not count toward your grade. Findings in vendored, generated or bundled example code are held out of your score already, and they are now held out of the intent check too — an example app bundled inside a repo should not be evidence that your own API is unauthenticated. It also reads the exceptions a sentence states about itself: "all API routes except /api/health require authentication" no longer treats a correctly-public health check as a contradiction.
- +It only ever reports a disagreement it can prove. Each line points at a finding the scan already produced, with a file and a line — there is no "your app might not do this" verdict, and nothing is inferred from the sentence alone. A claim the scan cannot dispute is left alone rather than dressed up as a pass. Run a scan and it appears on its own; there is no flag to find.
- +Why this is worth having if you build with AI tools: you described what you wanted in the prompt, and the generator produced something plausible. Nobody has been checking the second thing against the first. It currently understands six kinds of claim — who owns which rows, what needs a login, what is admin-only, what is private, which webhooks are verified, and what is rate limited.
- +If you are on a paid plan, your Pro rules were out of date and are now current. The rule set delivered to the CLI had been serving an older catalogue than the one we publish: 128 of the paid rules instead of 184. Every paid rule added since April was missing from it, including this week's authentication work. Your next scan runs the full set. If a scan now reports findings it did not report last week, those findings were always in your code — we simply were not running the rules that catch them.
- +Stripe webhooks written the modern way are now checked. The payment-webhook check required the word "Stripe" to appear inside the file, so it never examined the most common shape AI builders produce: a Next.js route at app/api/webhooks/stripe/route.ts that reads the event and acts on it without naming Stripe anywhere in the body. The folder said what the file was; the check was only reading the code. If that describes your app, your next scan may report a critical finding that was always there — anyone who knew that URL could send a fake payment-succeeded event and be granted whatever a real one grants. The check now also reads the arrow form of the handler, and stays quiet when signature verification lives in a helper rather than inline.
- +The MCP server and the VS Code extension were reporting some findings twice. A duplicate of the same issue, on the same line, with the same identifier. The finding was real both times; the count was wrong. Fixed in MCP server 1.3.0. The VS Code extension carries the same fix and ships with its next Marketplace update. The CLI and the web scanner were never affected.
- +The MCP server could start and then say nothing at all. Depending on where it was installed, xploitscan-mcp would launch, print no error, and never answer your editor — so the tools showed as unavailable with nothing in any log to explain it. It affected installs whose path runs through a symbolic link, which is the ordinary case for npx on macOS and for anything installed with pnpm. Fixed in MCP server 1.4.1.
- +Three checks were firing on code that is correct, and have stopped. Routes that are meant to be reached without logging in — Open Graph image endpoints, sitemaps, robots, manifests and status badges — are no longer reported as missing authentication, because being fetched anonymously is what they are for. Neither are magic-link routes: share links, unsubscribe links, invite and claim URLs, where the unguessable token in the link is the credential and the route already turns away anyone without it. And two cookie checks: a constant that merely names a cookie is no longer mistaken for code that sets one, and a Secure flag switched on by environment now counts as set — a Secure cookie is not sent over plain http, so guarding it that way is the normal way to write it, not a missing flag.
- +A saved project description could be checked against the wrong project. If you told the scanner what one project was supposed to do and then scanned a different one, that sentence came along with it, and the report could say the second project contradicted something you had said about the first. It is now kept per project. Anything saved before today is no longer applied to anything — the scanner will simply ask again the next time it needs it.
- +The dashboard’s promotional cards can be dismissed, and stop calling themselves new. They had no close button and carried a “New” badge indefinitely, including for people who had been using the product for months.
- +“Last scanned” showed negative times. A scan from an hour ago could read as “-232m ago” on the dashboard, the reports tab and the scan table. Recent scans were the only ones affected, which is why it was easy to miss — the error is invisible on anything scanned days earlier.
- +The Launch Checklist has a way in. It reads your latest scan, tracks what is left before you ship, lets you mark items done or not applicable, add your own, export a PDF and share a link — and it feeds the completion percentage on your Trust Page. It also had no link anywhere in the product, so you could only reach it by typing the address. It is now in the Scan menu, on your dashboard, and on both scan result views.
- +Deep analysis, the optional AI pass on the CLI, now runs on a newer model. It had been pinned to a version two releases behind. It still runs on your own API key, still at fixed sampling so a second scan of unchanged code returns the same grade.
- +Rule counts in a few places were wrong and are now correct: the Docker image described itself as 158 rules, the VS Code Marketplace listing as 210, and the CLI README as covering more categories than exist. The real figures are 214 rules across 14 categories.
- +Indie plans now get on the command line what the pricing page always said they get. Indie is sold the full rule set, terminal output and the GitHub Action, and all three run through the CLI — but the CLI authenticates with an API key, and Indie accounts were not allowed to create one, nor to download the paid rule catalogue even holding one. Scans from an Indie account’s CLI quietly ran the 30 free rules instead of all 214, with nothing to say why. Web scans were unaffected and always ran the full set, which is why this went unnoticed. If you are on Indie, generate a key in Settings → API Keys and your next CLI scan runs the full catalogue.
- +If your card fails, you keep everything you pay for while Stripe retries. That was the intent and the billing banner already said so — “your plan is still active while we retry” — but six places decided access separately and none of them had been told. A past-due Pro or Team account was quietly dropped to the free tier: scans fell to the free daily cap, Trust Page editing started refusing, GitHub pull-request scans ran 30 rules instead of 214, API keys stopped resolving, and every member of a team lost the same things at once, while the CLI carried on working normally. Access now follows one rule in one place, for the whole retry window. Nothing changes when a subscription is genuinely over.
- +All Scans on the Reports tab is paged. It rendered every scan the page had loaded in a single table — up to 500 rows, each with its own PDF, Markdown and CSV buttons — so an established account got a very long table and no way to reach an older scan except scrolling. It now shows 25 at a time and tells you where you are: “Showing 26–30 of 137 scans”. If you are at the 500-scan ceiling it says so, rather than leaving you to assume that is everything.
- +The compliance page now measures coverage against the actual standards. It reported “100% coverage” for SOC 2, ISO 27001 and CWE, but that percentage was calculated against our own list of mapped controls rather than the standard itself — so it could only ever come out at 100%, whatever we mapped. It now tells you how much of each framework the mapping reaches: 11 of SOC 2’s 33 common criteria, 13 of ISO 27001’s 93 Annex A controls, and the OWASP Top 10 in full, which genuinely is all ten. Each framework also says plainly which parts a code scanner cannot speak to — SOC 2’s control-environment, risk-assessment and monitoring criteria are assessed by an auditor reading policies and interviewing people, not by reading source. The mapping itself is unchanged and no control moved; what the page claims about it has.
- +Export My Data now includes the parts you would actually want. The button promises every row we hold for your account, and it was returning 13 of 25 tables — leaving out your Trust Page configuration, the security questionnaires you filled in and every per-prospect copy of them, your custom domain verifications, what you wrote about your projects, and your AI-analysis preference. Those are the work product, which is the part worth taking with you. Keys and claim tokens are still deliberately excluded.
- +Checklist items stopped claiming they were completed a moment ago. Anything the scanner passed for you showed “Completed just now” and kept saying it — the scan time is stored without a timezone and was being read as local, which put it slightly in the future and made every item look like it had just happened. Times are now real on both your checklist and the shared version you send to customers.
- +Text across the signed-in pages is readable now. Labels, descriptions and helper text on the dashboard, reports, settings, checklist and scan pages were rendering in greys too faint against the dark background to meet the accessibility standard — in the worst case a clean result showed "0 Critical" at a contrast so low it was effectively invisible, which is exactly the number you want to see. Anything you are meant to read is a legible tone; the fainter greys stay where they belong, on separators and bullets.
- +The AI analysis opt-out is now enforced everywhere your code is scanned. Turning it off in Settings stopped scans from the web app sending code excerpts to Anthropic for false-positive filtering, but the setting was not consulted when the GitHub App scanned a pull request, and a browser that could not load your setting fell back to leaving analysis on. Both paths now read the stored preference on our servers before a scan runs, and a preference we cannot read is treated as "off" rather than "on". If the filter is skipped for this reason the pull request check says so instead of quietly reporting a smaller plan.
- +The dashboard trend chart and the weekly digest email were each missing a day. Both asked for a window — "last 30 days", "this week" — in a date format the database sorts differently from the one it stores, so the oldest day of every window was excluded outright, including scans that ran well inside it. The chart drew fine and the email sent fine; they were simply reporting less than had happened. A window now means the window.
- +Failures reading or saving your scans now say so. If the database was briefly unreachable, the dashboard showed the screen a brand-new account sees — "Welcome to XploitScan, pick how you want to scan" — to people with years of history, and Reports said "No Scans Yet"; nothing indicated anything had gone wrong. A scan that failed to save behaved the same way: the results looked exactly like a saved scan, while no history entry, email or webhook was ever created. Both now report the failure plainly, and the checklist no longer tells someone with hundreds of scans to go and run one.
- +The scan limit notice now matches the plan you are on. An Indie subscriber who reached the 500-scan monthly cap was shown the free plan's wording — "You've used all 500 of your 5 free scans today, your limit resets at midnight UTC" — which was wrong about the plan, wrong about when the cap resets, and offered them an upgrade to the plan they were already paying for. The notice now says what the server already knew: which cap was reached, and when it actually resets. Scanning a public GitHub repository also reports a failed save the same way every other scan does, instead of appearing to save and quietly not.
- +The subscription confirmation page no longer claims a free trial you did not get. The 7-day trial applies to your first Pro or Team subscription; if you upgraded from another plan, or subscribed again after cancelling, you are charged straight away — but the confirmation screen said a trial had started regardless, because it guessed from the plan name. It now reports what actually happened to your subscription.
- +The plan comparison in Settings no longer marks scan history as a paid feature. Free accounts have always had their full scan history — the dashboard is available to anyone signed in — and the public pricing page says so, but the table inside Settings showed an X in the Free column, presenting it as something a $9 upgrade unlocks. The two tables now agree, and both quote the same rule count instead of differing by four.
- +Team scan history is actually readable now. Switching the dashboard to Team view listed every teammate's scans, but opening one said "Scan not found — this scan may have been deleted or you don't have access to it", because only the person who ran a scan could open it. Teammates on an active Team plan can now open each other's scans, which is what shared scan history was meant to be. Deleting stays with the person who ran the scan: the delete button no longer appears on a colleague's row, where it previously did nothing at all while appearing to work.
- +Team seats are counted now. The Team plan has always been sold as five seats included, with extra seats at $15/month each, but nothing in the product counted them — a team could add a sixth, twentieth or hundredth member, each getting full access, none of them billed. The invite form now shows how many of your five seats are in use, and an invitation past the fifth is declined with a note about adding more. Existing teams keep every seat they already have.
- +The CLI now tells you when a scan did not reach your dashboard. Running a scan while signed in saves the result to your account, and when that upload failed the CLI said nothing at all — the report printed exactly as usual and the scan was simply missing from your dashboard afterwards, with no way to tell the two apart. It now prints one line when the upload does not land: "Scan saved locally only — couldn't reach your dashboard." Everything else is deliberately unchanged: the full report still prints, and a failed upload never fails the command or changes its exit code, so a scan running in CI does not go red because saving it did not work.