# Borg > Borg is autonomous application security: continuous pentesting that chains vulnerabilities and tests business logic the way a real attacker would, then pushes confirmed findings straight into Linear, Jira, and GitHub. Odin is the platform; Whitebox Pentest runs application pentests, Attack Surface Monitoring tracks exposed assets, and PR Security Reviews check pull requests. ## Product - [Platform](https://www.borghq.io/platform): How Odin, whitebox pentesting, attack surface monitoring, and PR security reviews discover assets, find vulnerabilities, and confirm them. - [Enterprise](https://www.borghq.io/enterprise): Odin's modular approach, the embedded research service, deployment and scale. ## Company - [About](https://www.borghq.io/about): Who Borg is and how the company got here. - [Contact](https://www.borghq.io/contact): Get in touch or book a demo. - [Privacy Policy](https://www.borghq.io/privacy): How Borg handles data. ## Blog - [Blog index](https://www.borghq.io/blog): All articles. - [Security Review for AI-Generated Code: What Changes Before Merge](https://www.borghq.io/blog/security-review-for-ai-generated-code-what-changes-before-merge): More of every pull request is machine-drafted and the bug classes have not changed. What security review must catch in the diff before merge, and what it cannot. - [Offensive Security Testing in Production: What to Agree First](https://www.borghq.io/blog/offensive-security-testing-in-production-what-to-agree-first): What to settle before offensive testing runs against a live application: scope, timing, rate, permitted actions, and where staging answers a different question. - [From Pentests to Continuous Security with Pixels](https://www.borghq.io/blog/from-pentests-to-continuous-security-pixels): How Pixels moved from scheduled pentests to continuous security with Borg, covering its live game, authentication, APIs, on-chain rewards, and open-source releases. - [How to Prioritise Security Findings That Have No CVE](https://www.borghq.io/blog/how-to-prioritise-security-findings-that-have-no-cve): EPSS and KEV cover published CVEs only, and a self-assigned CVSS score loses that shared context. How to rank exploitable findings in your own application code. - [AI Application Security Testing: The Authorisation Boundary](https://www.borghq.io/blog/ai-application-security-testing-the-authorisation-boundary): OWASP's LLM list names permission-aware retrieval as a requirement and leaves enforcement placement to you. Here is how to test whether the tenant boundary in an AI feature actually holds. - [Security Testing Every Release Without a Manual Bottleneck](https://www.borghq.io/blog/security-testing-every-release-without-a-manual-bottleneck): Security review is often the last release step that waits on a person. What can run per merge, what still needs a human, and how the two split. - [Two Years of Continuous Security with Banana Gun](https://www.borghq.io/blog/banana-gun-continuous-security): How Borg has worked alongside Banana Gun for more than two years, providing continuous offensive security as its infrastructure evolved. - [How Automatic Retests Verify a Fix When Your PR Merges](https://www.borghq.io/blog/how-automatic-retests-verify-a-fix-when-your-pr-merges): Link a pull request to a security finding and the merge triggers verification. How the linking works, what the review reads, the outcomes, and where it stops. - [How to Continuously Map Your External Attack Surface](https://www.borghq.io/blog/how-to-continuously-map-your-external-attack-surface): Mapping an external attack surface continuously: what counts as an asset, the discovery techniques and why they run on different clocks, and where it stops. - [Autonomous Pentesting vs Vulnerability Scanning: What Each Finds](https://www.borghq.io/blog/autonomous-pentesting-vs-vulnerability-scanning-what-each-finds): Autonomous pentesting and vulnerability scanning answer different questions. What each one reaches, where each stops, and when to reach for which. - [SAST and Business Logic Vulnerabilities: What Static Analysis Can Reach](https://www.borghq.io/blog/sast-and-business-logic-vulnerabilities-what-static-analysis-can-reach): Static analysis and business logic are a poor fit by construction. What SAST covers well, what the published accuracy research shows, and what covers the rest. - [Bug Bounty Alternatives: What AI Security Testing Replaces, and What It Doesn't](https://www.borghq.io/blog/bug-bounty-alternatives-what-ai-security-testing-replaces-and-what-it-doesn-t): Bug bounty alternatives compared for engineering teams: what continuous AI offensive testing genuinely replaces, and where a bounty is still the right instrument. - [Full Pentest vs PR Security Review: What Each One Actually Answers](https://www.borghq.io/blog/full-pentest-vs-pr-security-review-what-each-one-actually-answers): A full pentest and a PR security review answer different questions. Here is what each one covers, what neither sees, and why you need both. - [What Is Code-Aware Application Security Testing?](https://www.borghq.io/blog/what-is-code-aware-application-security-testing): Code-aware application security testing uses repository context to drive targeted offensive attacks, finding auth and logic bugs that traditional scanners miss. - [How to Test Business Logic Vulnerabilities in a SaaS Application](https://www.borghq.io/blog/how-to-test-business-logic-vulnerabilities-in-a-saas-application): Learn how to test business logic vulnerabilities in SaaS using a 5 step offensive methodology. Surface auth, billing, and isolation bugs that scanners miss. - [What Is Continuous Pentesting? A Definition for Engineering Teams](https://www.borghq.io/blog/what-is-continuous-pentesting-a-definition-for-engineering-teams): What is continuous pentesting? Learn how this offensive security practice replaces annual audits with real-time testing for auth flows, APIs, and business logic. - [XBOW Alternatives: Comparing Autonomous AI Pentesting Platforms](https://www.borghq.io/blog/xbow-alternatives-comparing-autonomous-ai-pentesting-platforms): XBOW alternatives compared on code context, pull request review, proof of exploitation and pricing: Borg, Hacktron, MindFort, RunSybil, Escape and Aikido. - [Why we dropped "Security" from our name](https://www.borghq.io/blog/why-we-dropped-security-from-our-name): The story of how bug bounties in a living room turned into Borg, and why everything we've built now lives under one name. - [Your scanner didn't find it. Mjolnir did.](https://www.borghq.io/blog/your-scanner-didnt-find-it-mjolnir-did): Scanners match known patterns. Mjolnir reads your code, tests the auth and business logic behind it, and returns findings with a working proof of concept. - [What startups get wrong about Application Security](https://www.borghq.io/blog/what-startups-get-wrong-about-application-security): Most startups treat security as a compliance checkbox. Here's why that approach fails and what a practical appsec strategy actually looks like when you're shipping fast. - [How Borg pushes findings to Linear, Jira, and GitHub automatically](https://www.borghq.io/blog/how-borg-pushes-findings-to-linear-jira-github): Pentest findings only get fixed once they are tickets. How to connect Linear, Jira or GitHub to Odin, set auto-ticketing, and verify the fix afterwards. - [Your Vulnerabilities exist right now. Odin finds them first.](https://www.borghq.io/blog/your-vulnerabilities-exist-right-now-odin-finds-them-first): Your attack surface changes with every deployment. Odin provides continuous discovery, automated testing, and daily reporting so nothing slips through the cracks. # Full article text --- ## Security Review for AI-Generated Code: What Changes Before Merge URL: https://www.borghq.io/blog/security-review-for-ai-generated-code-what-changes-before-merge Published: 2026-09-28 Author: Borg AI-assisted code is now a common part of software development, and the pull requests can still look much the same as they always did. That is the difficulty. The diff is well formed, the naming is consistent, the tests pass, and none of that tells a reviewer whether the change respects a rule that lives somewhere else in the codebase. This is not an argument that machine-drafted code is worse. That claim gets made a lot and we are not going to make it, because we have not measured it and neither have most of the people asserting it. What is worth examining is narrower and more useful: a few things about the review itself change when a larger share of changes are drafted this way, and most of what matters does not change at all. ## What actually changes **The volume arriving per reviewer goes up.** This is the least interesting change, but it can have a large effect. Review attention is finite. When the rate of incoming change rises and the number of people reading it does not, less attention is available per diff on average. Nothing about the code caused that. It is arithmetic, and it applies equally to a fast team that hired three engineers. **The author may have less context than the reviewer assumes.** A human who writes an authorisation check may know why the rule exists because they were involved in the decision that created it. When a change is drafted from surrounding code, that reasoning is only available to the assistant if it is included in the context it receives. Review conventionally leans on the author to explain intent, and that lean is worth re-examining rather than trusting by default. **Plausibility and correctness come apart more visibly.** Code that follows the surrounding style can read as correct while still containing a security or logic error. A reviewer scanning quickly is checking shape as well as substance, and familiar shape can therefore be a less informative signal than it appears. This one is mostly about the reviewer rather than the code. **A rule enforced elsewhere may not be visible at the point of change.** If a tenancy constraint lives in a middleware two directories away, a change that fits its immediate neighbourhood can still miss it. Human authors miss this too, routinely. What is different is that the assumption of shared institutional context, which carried some of that weight, is weaker. ## What does not change at all The underlying security failure modes are familiar. Broken access control between accounts, a workflow that can be entered out of order, an endpoint that trusts a value it should re-derive, and a permission check on the wrong object were important before, and they remain important now. Generating the code differently does not by itself create a new class of software vulnerability. Reachability still cannot be read off a diff. Whether a dangerous-looking path is actually reachable by a real request depends on the rest of the application, and that has never been visible in the changed lines alone. The tests still only check what somebody thought to check. A generated test suite can reflect the same assumptions as the generated implementation, which means a wrong assumption can produce a passing test that confirms it. That is true of human-written pairs too; it just may take longer to produce both. And the authorisation question is still a question about intent. Code analysis cannot, by itself, tell you whether an administrator ought to be able to read another organisation's records. That belongs to whoever defined the product rules, unless those rules have been explicitly encoded elsewhere. ## What this looks like in one change An example makes the gap concrete. Take an endpoint that lists projects for an organisation, and a pull request that adds a filter so callers can narrow the list by the team that owns each project. The change is small. It reads the team identifier from the query string, adds a clause to the database query, and returns the filtered set. The existing authorisation check above it is untouched, because the change did not appear to concern authorisation: it validates that the caller belongs to the organisation in the path, and it still does. Everything about this reads as careful work. The new parameter is validated for type. The query uses the existing helper rather than string construction. A test covers the filter and it passes. A reviewer reading the diff sees a routine filter added to a list endpoint, which does not immediately look like a security-sensitive change. The problem is that team identifiers are global rather than scoped to an organisation, and nothing in the changed lines says so. Pass a team belonging to another organisation and the clause happily filters to it. The organisation check above still passes, because the caller really is a member of the organisation in the path. The constraint that was supposed to hold, that a caller only ever sees their own organisation's projects, was never enforced by the check everyone assumed enforced it. It was enforced by the fact that no query had previously accepted a second identifier. Notice what would have caught it. Not a pattern scanner, because there is no dangerous pattern here: the code is ordinary. Not the test, which asserts that the filter filters. A reviewer who happened to know that team identifiers are global would catch it instantly, which is precisely the institutional context this article is arguing you should stop depending on. Reading the change against the schema and the callers catches it. Running the request as a member of one organisation against another organisation's team settles it beyond argument. This shape, where a change is safe in isolation and unsafe in combination with a fact stated elsewhere, is one that a review process based on the diff alone may fail to catch. ## The volume problem needs an answer, not more reading If attention per change is the thing that fell, then telling reviewers to read more carefully is not a plan. It is the same amount of attention, asked to stretch, which is how careful review of everything becomes shallow review of everything. The available move is routing. Not every change needs the same level of scrutiny, and security guidance already supports prioritising review according to risk. A copy change to a marketing page and a change to the function that resolves which records a request may read are not the same risk, and giving them the same fifteen minutes is a choice with a cost. Routing well means deciding in advance which paths are sensitive, which is a conversation about the product rather than about code. A useful starting list is whatever establishes who the caller is, whatever decides what they may reach, whatever moves money, and whatever a third party can reach without authenticating. A change touching any of those gets the slow read. Everything else gets the fast one, honestly and without guilt. The benefit of writing that list down is that it can then be enforced by something other than memory. A check that runs on every pull request touching those paths does not get tired at five o'clock on a Friday, and does not depend on the reviewer having been at the meeting where the rule was set. That is the argument for automating this particular layer: not that the automation is cleverer than the engineer, but that it is indifferent to how many changes arrived that day. ## Where each kind of review reaches | Review approach | What it reads | Catches well | Does not establish by itself | | --- | --- | --- | --- | | Human reviewer, diff only | The changed lines and the reviewer's memory | Style, obvious logic errors, changes that contradict something the reviewer knows | Whether a rule two files away still holds; anything outside what that person happens to remember | | Linter or pattern scanner | The syntax tree, against known patterns | Known-shape issues at scale, consistently, on every change | Whether the flagged path is reachable, and anything whose danger depends on application meaning | | Code-aware review with repository context | The diff plus the surrounding code it depends on | Changes that break a constraint enforced elsewhere; auth and permission handling read against its actual callers | Whether the finished behaviour is exploitable in the running system | | Testing the running application | The deployed system, using the identities, requests and conditions exercised | Whether a boundary actually holds end to end, with the request and response recorded | Code paths and configurations that were not exercised during the run | | Generated test suite | The behaviour somebody specified | Regression against the stated expectation | Anything the specification got wrong, which it will confirm rather than flag | No row here is sufficient. The rows are complementary, and the honest reading is that the second and third catch different things, while the fourth is the one that directly tests the behaviour of the running application that the others cannot observe. ## What Borg does at this point, and what it does not Gungnir reviews pull requests that touch authentication, access control, APIs, billing or permissions. Its scope is the diff plus repository context, which is the specific gap described above: a change that looks reasonable on its own can be read against the code that actually calls it. Findings arrive as inline comments with severity and a suggested fix, before the change reaches production. Mjolnir handles whole-application testing, exercising the running application to test what is actually reachable. Two things it does not do, worth being explicit about. It is described in terms of the change and its repository context, not the identity of the person or tool that produced it. The review is therefore the same in the case that matters here: a diff that alters an authorisation path deserves the same scrutiny regardless of how it was produced. Whether a system should use authorship as a signal is a separate design question, but it should not replace analysis of what the change actually does. And it cannot settle exploitability from the diff alone. Reading code well can establish that a constraint appears to be missing; it does not establish that an attacker can reach and exploit the path in the running system. That is why testing the running application sits alongside rather than underneath. ## A pre-merge checklist that is actually about the code Many published checklists are broad. These are the questions worth asking specifically of a change to a sensitive path, whoever drafted it. **Which identity does this code run as, and which identity does it act on?** For anything touching accounts, tenancy or permissions, those can be two different things, and the security boundary can fail in the gap. A handler that validates one and loads the other is one important shape of the problem. **Is the check in the request path, or only near it?** A validation that happens in a sibling function, a different middleware, or a caller that may not always be the caller is not a check on this path. Follow it, do not assume it. **What re-derives the value that matters?** If the change trusts an identifier, an amount or a role that arrived in the request, something upstream must have established it. Find that something. If nothing did, the change is the problem regardless of how it reads. **Does the test prove the constraint, or restate it?** A test that asserts a denied request is denied, using a fixture built from the same assumption as the implementation, proves that the two agree. It does not prove either is right. **What was this change supposed to do?** The description on the pull request is a useful signal, and it is worth reading before the diff rather than after. A change whose stated purpose does not obviously require touching a permission check is a change worth slowing down on. ## The limits of any of this Review catches what a reader can see. That is genuinely a lot, and it is not everything, and a page that pretends otherwise is selling something. A reviewer, a scanner and a code-aware tool are all reading. Whether an attacker reaches the code path, whether the data behind it matters, and whether a control in production already blocks it are questions that can be tested against the running system, and the results apply only to the environment that was exercised. Configuration differs between deployments, which means even a proven finding needs judgement about its reach. The realistic position is layered and slightly unsatisfying. Catch what is cheap to catch before merge, because the fix is cheapest there. Test the running application on a cadence that matches how often you ship, because that is where the question of reachability gets answered. Accept that some things will only surface in production, and make sure the path from there back to a fix is short. ## The part that is genuinely new If there is one thing worth carrying away, it is not about vulnerability classes. It is that many review processes were built around a development model in which writing code was slower, and that slowness limited how much arrived at once and often gave the author more time to think through a change before it was reviewed. That is what changed. The underlying vulnerability classes did not disappear. So the sensible adjustment is not a new tool category aimed at machine-written code. It is to stop relying on the author's context as the primary safeguard on changes to sensitive paths, and to put something systematic in that position instead. What that something is can reasonably vary by team. That it should not be an assumption about how carefully the author was thinking is the part we would argue for. --- ## Offensive Security Testing in Production: What to Agree First URL: https://www.borghq.io/blog/offensive-security-testing-in-production-what-to-agree-first Published: 2026-09-25 Author: Borg Every team evaluating continuous security testing arrives at the same question, usually late in the conversation and usually phrased carefully: are you going to run this against production? It is the right thing to worry about and the wrong shape of question. "Is it safe" invites a yes, and a yes is not something any testing vendor can honestly give you about your own application. The useful version is narrower. What can this thing actually do to my system, which of those things do I want to permit, and what happens if it goes wrong at two in the morning. That question has real answers. Many of them are about configuration, boundaries and controls rather than taking a vendor's assurances on trust. ## What actually creates the risk Offensive testing is not a scanner reading headers. The requests are real, and several properties of them matter operationally. **The traffic looks like an attack, because it is one.** Borg's own setup documentation puts this plainly in a warning on the scope step: only add URLs you own and are authorised to test, because Mjolnir will send real HTTP requests, including probes that may look like attack traffic, to everything in scope. Any vendor whose documentation does not say something equivalent is worth a question. **Some requests change state.** Testing an authorisation boundary often means attempting the action, not just looking at the endpoint. A test that proves an account in one organisation can reach another organisation's record is more convincing when it actually retrieves the record. Extend that to workflows, payment paths or admin functions and some tests may involve requests that create or modify state. **A realistic test often needs to be authenticated.** The classes that matter most in a multi-tenant application, broken access control, tenant isolation and privilege escalation, often require testing in the context of an authenticated user. So a realistic test may need to run as a user of your system, which means exercising the application inside the same identity and data model that customers use. **Volume meets your own defences.** Your WAF and rate limiter do not know the difference between a test and an attack. They will do what you configured them to do, which is usually to throttle or block. That is not a safety problem so much as a measurement one, and it is the most common reason a first run produces a thin report. None of that argues against testing production. It argues for deciding the boundaries deliberately rather than discovering them during the run. ## The four things worth agreeing before anything runs Scope, timing, rate and permitted actions. Each is a real decision with a real tradeoff. ### Scope, and proving you are allowed Scope is the list of URLs and repositories a run may touch, and everything absent from that list is out. The part teams underestimate is that scope is also a legal question. Testing a domain you do not control is not a configuration mistake, so a serious platform should make you demonstrate control before it sends anything. Borg requires domain verification before a run starts, once per domain, via DNS record, and its documentation notes that the verification requests only the records it needs rather than broader DNS or account permissions. Whatever vendor you are evaluating, the question is the same: what stops someone entering a domain they do not own. The narrower point about scope is that a smaller scope is not automatically a safer one. A scope that excludes your payment flow excludes the findings that would have mattered most. What you want is a scope you chose, not a scope that happened. ### Timing, which is weaker than it sounds The instinct is to run at low traffic. It is reasonable and it is worth doing, but it carries less protection than teams expect. A global product has no quiet hour that is quiet everywhere. A scheduled assessment that repeats monthly will eventually land on a release day. And the failure modes that actually hurt, a state change you did not anticipate or a boundary that turns out to be load bearing, do not care what time it is. Timing is worth setting. It is not the control that does the work. ### Rate, and the tradeoff nobody names This is where the honest answer is uncomfortable. If your WAF throttles the tester, some parts of the test may no longer be representative. If you allowlist the tester, you may bypass some of the WAF's protections for the duration of the run, and you are measuring more of the application's behaviour without those particular controls in the request path. Both readings are legitimate and they answer different questions. Testing through the WAF tells you what a casual attacker meets. Testing behind it tells you what your application would do if the WAF were bypassed, misconfigured or simply not in the request path, which is a scenario worth understanding rather than assuming away. Borg's setup runs a connectivity check per URL and reports one of three states: reachable, reachable with warnings where requests are getting through but being flagged or throttled, and unreachable. That middle state is the one to read carefully, because a run that completes against a throttling WAF can produce a clean report for the wrong reason. Decide which question you are asking. Do not let a rate limiter decide it for you by accident. ### Permitted actions The last one is the most specific to your application and the least standardised across the industry. Which actions may a test perform. May it create records. May it modify them. May it exercise a deletion path, a refund path, an invitation flow, an export. There is no universal right answer here, which is exactly why it belongs in the conversation before the first run rather than in an incident review afterwards. A team that has thought about it usually arrives with a short list of things that must never be touched, and that list is more useful than any general assurance a vendor can offer. One practical note that belongs here. Borg's documentation says Mjolnir generates its own user contexts as it explores rather than requiring you to supply test accounts. That is convenient at setup, and it is also a thing to factor in when you decide what a run may touch, because it means the test is operating as a user of your system rather than alongside it. ## Production, staging, or both The reflex answer is to point the first run at staging. It is a sensible instinct and it is often the right first move. It is also worth being clear that staging answers a different question rather than the same question more safely. Staging usually differs from production in the three ways that matter to an authorisation test. The data is synthetic, so a cross tenant read that would be serious in production may look trivial. The configuration drifts, so the auth provider, feature flags and middleware may not match. And the scale differs, so anything that depends on volume or timing behaves differently or not at all. The consequence is that a clean staging run is weaker evidence than it feels, and a finding on staging still needs to be assessed against the environment you actually care about before you assume the same condition exists there. | Approach | What it answers well | What it does not establish | Limitation to keep in mind | | --- | --- | --- | --- | | Production only | Whether the boundary holds in the system your customers use, with real configuration and real data shapes | Nothing about the paths you excluded to make it safe enough to run | Any state change lands on real records, so permitted actions have to be decided carefully rather than broadly | | Staging only | Whether the tested application paths behave as intended, at lower operational risk, with more freedom to test destructive paths where appropriate | Whether production shares that configuration, since auth settings and feature flags drift between environments | A clean result can reflect synthetic data rather than a sound boundary | | Staging first, then bounded production | Most of the logic on a forgiving target, then confirmation where it counts | Anything in the production paths still excluded at the second stage | Two runs and two configurations to maintain, and the environments can drift apart between them | | Continuous production testing on a schedule | Whether the boundary still holds after the last few weeks of changes | Whether a given release broke something, since the run is not tied to the change that caused it | Each repetition inherits the scope you set originally, so the configuration ages as the application moves | | Testing bound to a pull request | Whether this specific change introduces a reachable problem, before it ships | Whether the deployed system as a whole holds, since the scope is the diff | Scope is the change, so a weakness that predates the pull request is not what it is looking at | ## What to insist on, from anyone Four things are reasonable to require of any vendor running offensive tests against a system you are responsible for, and none of them are exotic. An abort control you can reach without opening a support ticket. Scope you define explicitly rather than one inferred from your domain. Proof of ownership before testing begins. And enough activity and coverage information to establish whether the test was involved at all, because the first thing you will want during an incident is to establish what the assessment was doing. That last one is worth pushing on. A platform that reports findings but not activity leaves you unable to answer the only question that matters at the moment something breaks. ## Where Borg sits, with its limits Being specific about our own product is more useful than a reassurance, so here is the shape of it, including the parts that constrain you. A run operates within the target URLs and repositories you configure, while Mjolnir uses the connected source code and reconnaissance to map and test the application's attack surface within that scope. Domain verification is required before a run starts. The connectivity check tells you up front whether your WAF is throttling the test rather than leaving you to infer it from a thin report. Runs can be started from the Mjolnir setup, and teams should revisit their configuration as the application and its testing requirements change. The limitations are real and worth stating plainly. A run cannot be paused once it has started: you can abort it, but the assessment then has to be restarted from the beginning, so aborting late is expensive. Only one run can be active at a time. A scheduled assessment keeps the config it was created from and does not pick up later setup changes, so a recurring assessment should be revisited as the application changes: a scope that was right in March may no longer cover everything you care about in July. And, as above, a run generates its own user contexts rather than using accounts you provision. None of that makes the operational question disappear. It makes it a configuration you can inspect rather than a promise you have to accept. ## When something does go wrong The preparation that pays is not the part that prevents incidents. It is the part that lets you identify one quickly. When an alert fires during a test window, the expensive version of that night is the one where nobody can establish whether the testing is involved. On-call starts debugging a production fault while a security tool is sending attack-shaped traffic at the same system, and the two investigations run in parallel for an hour before anyone connects them. That is a coordination failure rather than a tooling failure, and it is avoidable in about ten minutes of preparation. Three things make it avoidable. **Know how to recognise the traffic before you need to.** This is the underrated benefit of allowlisting, and it has nothing to do with getting through the WAF. Once you have added a known set of source addresses, you have a filter your on-call can apply to any log in seconds. The same is true of a custom header attached to the requests. Either way the question "is this us" becomes a query rather than a debate. **Decide the abort threshold in advance, and know what it costs.** Aborting is the control you actually have during a run, so it is worth knowing beforehand what would trigger it and who is allowed to make that call at three in the morning. It is also worth knowing the price. In Borg's case a run cannot be paused, so an abort means starting the assessment again from the beginning rather than resuming, which is a real consideration when a run is most of the way through. **Tell the people who will see it.** On-call, whoever watches the error budget, and anyone who receives automated alerts from the target system. The failure here is rarely dramatic. It is usually a support engineer spending an afternoon on anomalous account activity that a colleague authorised the week before and nobody wrote down. None of this is sophisticated. It is the difference between a test that produces findings and a test that produces findings plus an afternoon of confusion, and the second outcome is almost always down to preparation nobody thought was worth the ten minutes. ## What this does not cover Two honest limits. The first is that everything above is about bounding operational risk during a test, not about eliminating it. Offensive testing against a live system carries some, and a vendor claiming otherwise is telling you something that cannot be true of any tool that sends real requests. The right posture is a scope you chose, an abort you can reach and a team who knows the run is happening. The second is that this is a question about testing safely, not about what testing finds. A perfectly bounded run against a scope that excludes your most interesting paths is safe and not very useful. The scoping conversation and the coverage conversation pull against each other, and treating them as one conversation is how teams end up with a clean repo|rt that means very little. If you want the reasoning behind why reading the code changes which paths are worth attacking in the first place, that sits in [code-aware application security testing](). If the question is narrower, whether a specific change is safe rather than whether the system is, [testing bound to a pull request]() is the smaller version of this decision. --- ## From Pentests to Continuous Security with Pixels URL: https://www.borghq.io/blog/from-pentests-to-continuous-security-pixels Published: 2026-09-25 Author: Borg Pixels is a browser-based MMO and one of the largest web3 games by daily active users. The game runs on an authoritative Colyseus server, and carries a live on-chain economy: $PIXEL, land NFTs, a player marketplace, wallet withdrawals. Every trade and every item is settled server-side. ## Before Borg Pixels was not starting from zero. They ran pentests on a schedule and had operated a public bug bounty for years, which already put them ahead of most companies their size. The problem was fit rather than effort. A game that ships every week is a different product by the time a scheduled report is written up, and a bounty brings in whatever researchers happen to look at rather than what the team most needs checked. ## How the relationship started Pixels came to us looking for a security partner that could stay close to the product over time, rather than step in for a single assessment. At that point, Borg was still a five-person team. Pixels became one of our earliest clients, and the way the engagement developed ended up shaping the model we now call Forward Deployed Security. ## What we've worked on **The live game server and its API**, tested continuously rather than at scheduled intervals, because the build in production is rarely the build that was last assessed. **Stacked**, Pixels' own analytics, offerwall and shop platform. A webhook that grants items and currency into player accounts is a path to minting value, and it gets treated as one. **OtterAuth**, the authentication system Pixels built in-house. Taking auth on yourself means sessions and accounts sit behind code you own. That is a large thing to build and a large thing to have tested. **Pixels as an OAuth provider**. They wanted third-party apps to offer sign-in with a Pixels account, which turns Pixels into an identity provider that other applications trust on their users' behalf. **Pixel Dungeons**, A fast-paced game within the Pixels ecosystem, running on its own stack and paying out in the same $PIXEL the main economy depends on. **The open source review**, reading the client and server codebases in 2026 before Pixels published them under AGPL-3.0, so the code was gone over ahead of publication rather than after it. ## Looking Back As Pixels evolved, the security work evolved with it. That experience helped shape how we approach long-term security engagements today: staying close to the product and adapting the testing as the system changes. --- ## How to Prioritise Security Findings That Have No CVE URL: https://www.borghq.io/blog/how-to-prioritise-security-findings-that-have-no-cve Published: 2026-09-24 Author: Borg A continuous testing programme changes the shape of your security work. Instead of a report twice a year, you get a queue that never empties. The first question every team asks once that queue exists is which item to pick up first. Most teams reach for the scoring systems they already know. CVSS gives a number out of ten. EPSS gives a probability. CISA maintains a catalogue of what attackers are actually using. All three are good tools, widely adopted for sound reasons, but two of them are explicitly built around a published CVE: EPSS predicts exploitation for CVEs, and KEV catalogues exploited CVEs. CVSS is broader, but in practice its standardised scores are most commonly consumed through the CVE ecosystem. The findings that come out of testing your own application are usually not that object. A tenant boundary that holds on the read path and not the write path has no CVE. Neither does an approval step that can be replayed, or an endpoint that authorises against a stale copy of the user's role. Nobody issues an advisory for your billing logic. So EPSS and KEV return nothing for a large part of the queue, while CVSS can still be calculated but loses much of the shared context and comparability that make it useful across organisations. This is a practical guide to ranking findings that no external scoring system will score for you. ## What the three standard systems actually measure It is worth being precise about these, because the reason they do not apply is specific rather than general. **CVSS** is a severity framework maintained by FIRST. Its specification is explicit that the Base group "represents the intrinsic qualities of a vulnerability that are constant over time and across user environments", and that the Base Score "reflects the severity of a vulnerability according to its intrinsic characteristics". Constant across environments is the load-bearing phrase. Base scoring deliberately assumes the reasonable worst-case impact across different deployments; environment-specific factors are handled separately through the Environmental metrics. FIRST is also clear that a Base score on its own is not meant to be a risk decision. The specification tells consumers they "should enrich the Base metrics with Threat and Environmental metric values specific to their use of the vulnerable system", which yields "a more comprehensive input to risk assessment specific to their organization". It lists regulatory requirements, the number of customers affected and monetary losses as outside CVSS entirely. In practice most teams never see the enriched score: the specification notes that assessment providers such as product maintainers and the National Vulnerability Database "typically provide only the Base Scores". **EPSS**, also from FIRST, answers a different question. It is "a data-driven machine-learning model that estimates the probability that a published CVE will be exploited in the wild in the next 30 days", published as a probability between 0 and 1 and refreshed daily. It is genuinely useful for the problem it targets, which is a backlog of thousands of CVEs where severity alone cannot tell you where attention is best spent. **The CISA Known Exploited Vulnerabilities catalogue** takes the empirical route. CISA describes it as the authoritative source of vulnerabilities that have been exploited in the wild, and recommends feeding it into a prioritisation process. Every entry in the published catalogue is identified by a CVE. Note what all three have in common. CVSS scores are supplied at scale by NVD and by product maintainers, against CVEs. EPSS is defined, in FIRST's own wording, over a published CVE. KEV is indexed by CVE. The apparatus is built on a shared identifier for a vulnerability in distributed software, and it works well because that identifier lets thousands of organisations pool what they learn about the same bug. ## Why none of it reaches a bug in your own code A finding in your own application often does not have the same public vulnerability record as a vulnerability in widely distributed software. It may exist only in your codebase, with no published CVE, shared exploitation data or external advisory. That has three consequences. There may be no public CVE identifier, so there is often nothing to look up in the external vulnerability databases. You can still compute a CVSS vector for your own finding by hand, and some teams do, but you are then producing the score rather than consuming an externally maintained assessment. That can still be useful internally, but it loses much of the shared context and comparability that make the wider CVE ecosystem valuable. EPSS only scores published CVEs, so an application-specific finding with no CVE has no EPSS score. EPSS learns from signals associated with published vulnerabilities, including observed exploitation activity. A flaw that exists only in your application and has never been assigned a CVE therefore does not receive an EPSS probability. Its score is not low. It does not exist. And the worst-case assumption cuts the wrong way. CVSS Base deliberately assumes the worst reasonable deployment because the scorer cannot see yours. For a finding in your own application you can see yours, in detail, and the honest impact may be much smaller or much larger than a generic worst case would suggest. An IDOR on an endpoint returning a display name is not the same as an IDOR on an endpoint returning an export. CVSS can incorporate environment-specific factors, but it does not replace the application-specific context your own team has about the data, workflows and business impact involved. None of this is a criticism of the three systems. They are solving the pooled-knowledge problem, and they solve it well. They are simply not addressed to the class of finding that offensive testing against your own application produces. ## What to rank on instead The useful properties of an application finding are the ones you can observe directly, because you own both the code and the running system. Four of them do most of the work. **Whether the attempt actually succeeded.** A finding that records an attempt against the running application, with the steps to reproduce it, is a different object from a pattern match that suggests a problem might exist. This is one of the strongest ranking signals available, and it is available before any judgement of severity. If nobody has established that the path works, the first task is not to rank it, but to test it. **What the attacker needed to start.** An unauthenticated path is generally easier to reach than one requiring an ordinary user account, which is generally easier to reach than one requiring an elevated role or restricted internal access. This is the closest analogue to CVSS Exploitability, and unlike a generic Base vector you can answer the authentication question exactly, because you know your own authentication model. **What the successful path reaches.** Not the endpoint, the data or the action at the end of it. Another tenant's records, a payment path, a credential, an administrative function. This is where your own knowledge provides context that an external framework cannot supply on its own, because you know which data, actions and workflows actually matter to the application. **How much of the system shares the defect.** A missing authorisation check on one handler is one fix. The same missing check in a helper used by forty handlers is a different piece of work with a different urgency, even though the individual finding looks identical. A practical ranking can be built from those four properties without inventing a composite number. Findings where an attempt succeeded, from a starting position an attacker can plausibly reach, touching data or money, come first. Findings where the attempt succeeded but the starting position requires access an attacker is unlikely to have come next. Unverified suspicions get tested before they get argued about. ## Why "verified" is doing so much work here The first property deserves defending, because it is contested. HackerOne, announcing its own agentic testing product in January 2026, characterised the fully autonomous end of this market as producing "shallow, unverified results that create noise rather than insight". That is a fair warning and it is worth taking seriously rather than dismissing, because a queue of machine-generated maybes is genuinely worse than a shorter list of confirmed problems: it consumes the triage attention that prioritisation is supposed to save. The answer that survives contact is not a confidence rating. A confidence rating is another number someone has to learn to trust. The answer is an attempt that was made and observed, with the reproduction steps attached, so the person receiving it can run the thing themselves and stop debating. That is what makes verification a prioritisation signal rather than a quality badge: it converts an argument about likelihood into a question of fact, and questions of fact can be settled in the time it takes to follow the steps. It is also the property that most often reorders a queue. Under this approach, a finding described as medium severity that comes with a working reproduction can be placed ahead of a high-severity item nobody has confirmed, because one is a demonstrated problem and the other remains a hypothesis. ## Comparing the approaches | Approach | What it ranks well | Where it does not reach | Limitation to keep in mind | | --- | --- | --- | --- | | CVSS Base | Published CVEs, comparably across organisations | Findings with no CVE; anything where your environment changes the impact | Base is severity, not risk. FIRST recommends Threat and Environmental enrichment that most teams never apply | | EPSS | Large CVE backlogs where you need likelihood, not just severity | Anything without a published CVE, which includes most application-logic findings | Estimates probability of exploitation in the wild, so it says nothing about impact or about your specific controls | | CISA KEV | Known real-world exploitation, as a strong signal for vulnerability-management prioritisation | Bugs unique to one application, which will never appear in a public catalogue | Records exploitation that CISA has determined is occurring or has occurred in the wild. Absence from the catalogue is not evidence that a vulnerability is safe or unexploited. | | Manual severity triage | Context only your team has | Consistency, especially across reviewers and across months | Depends on who is in the room, and tends to drift toward whoever argues most confidently | | Exploit-verified ranking (what Borg produces) | Findings in your own application, ordered by what an attempt actually reached | Known CVEs in your dependencies and infrastructure, which this does not attempt to cover | Only ranks what has been tested. Coverage gaps are invisible to it, so it should not be read as a complete picture of risk | ## Where the CVE apparatus is the right tool, and this is not The case above is about a specific class of finding. It is not an argument that CVSS, EPSS and KEV are outdated, and a team that dropped them on the strength of this piece would be making a mistake. For dependency and infrastructure vulnerabilities, they remain important instruments, and nothing here removes the need for them. Your third-party packages, base images, operating-system components and many managed services can carry published CVEs, and for those you genuinely want the pooled knowledge: a severity framework that lets you compare across vendors, a probability model trained on exploitation observed across the whole internet, and a catalogue telling you what is being used against people right now. No amount of testing your own application tells you that a widely deployed library has just started being exploited. KEV can provide that external exploitation signal, which is exactly the kind of information application testing cannot supply. The two problems also fail differently. A missed dependency CVE is usually a patch you did not apply in time. A missed business-logic flaw is usually a design decision nobody revisited. They need different instruments because they are different work, and a team running only one of the two has a gap either way. The practical arrangement most teams end up with is two ranked lists that meet at the point of scheduling: CVE-scored work for what you consume, evidence-ranked work for what you build. What you should not do is force the second list through scoring built for the first, which is how a verified authorisation bypass ends up sitting below a dependency advisory that does not reach any code path you actually run. ## Making it work in practice Three things make the ordering hold up once it leaves the spreadsheet. Put the evidence where the fix happens. A rank that lives in a security tool and a fix that happens in an engineering tracker will drift apart within a week. Findings that arrive in Linear, Jira or GitHub with the reproduction attached keep the ranking and the work in the same place, which is also where the person who has to argue about priority already is. Let the ranking change when the evidence changes. Ranking on observed properties means new observation can reorder the list. A finding that looked contained until testing found the same helper reachable from an unauthenticated route should move, and a process that cannot move it is not really ranking on evidence. Close findings on verification rather than on a merged pull request. A fix marked done is a claim; a fix that has been re-examined against the change is a checked claim. We covered that mechanic in detail in [how automatic retests verify a fix when your PR merges](). ## What this does not solve Ranking is downstream of coverage, and that is the honest limit of everything above. An order over the findings you have says nothing about the findings you do not have. If testing has never reached a particular flow, that flow contributes nothing to your queue and it will look, from the queue, exactly like a flow with no problems in it. So the ranking is only as good as what has been tested, and the useful discipline is to keep asking what is absent from the list rather than only working down it. [Continuous asset discovery]() helps on the infrastructure side by telling you what exists. On the application side the equivalent question is which flows and roles have actually been exercised, and that is worth reviewing on the same cadence as the queue itself. The second limit is that none of this produces a single number, and some organisations need one for reporting. Four observed properties are more defensible than a composite score and considerably harder to put in a board slide. If you need the number, derive it from the properties and keep the properties visible underneath, so that the ordering can still be interrogated when somebody disagrees with it. --- ## AI Application Security Testing: The Authorisation Boundary URL: https://www.borghq.io/blog/ai-application-security-testing-the-authorisation-boundary Published: 2026-09-14 Author: Borg Many teams shipping AI features this year have added a retrieval path. A user asks a question, something resolves the documents that user is allowed to see, and a model answers from them. The feature may be new. The question underneath it is not: can this user reach that record. That question has an established answer in the rest of your application, enforced by code you wrote and can point at. In the retrieval path it is often enforced somewhere newer, thinner, and harder to see, because the thing doing the resolving was added in a hurry and the identity of the person asking has to travel further to reach it. The major risk lists address this problem, but they leave the application-specific implementation and testing to you. This is a guide to testing whether that boundary actually holds. ## What the LLM risk lists cover, and what they hand back OWASP publishes a Top 10 for LLM and generative-AI applications, currently the 2026 edition. It is worth reading properly rather than skimming, and it is worth being precise about its scope, because the scope is where the gap sits. Two of its ten entries touch the question above. LLM03:2026 Excessive Agency deals with systems granted the ability to call tools or interact with other systems, including the risk of using an over-privileged identity on behalf of a user. LLM09:2026 Vector and Embedding Weaknesses is the closer one for retrieval, covering risks including cross-tenant leakage when similarity search runs across shared data before access control is applied. The 2026 entry explicitly covers cross-tenant leakage through shared similarity search. OWASP describes a scenario where similarity search runs across a shared index before access control is applied, allowing an attacker to infer information about another tenant's data even when the documents themselves are not returned. The current guidance is more specific. OWASP recommends enforcing tenant scoping inside the index query rather than applying it after retrieval, validating that scope server-side, and using physically separated indexes for high-sensitivity workloads. Now notice what that guidance is and is not. It names a required property: retrieval should be permission-aware and partitioned. The current guidance goes further: OWASP recommends authorising before retrieval, enforcing document- and chunk-level authorisation inside the index query rather than after retrieval, and using separate indexes for high-sensitivity workloads. The exact authorisation model and implementation still depend on the application. It also says nothing about how the end user's identity should propagate through to the retrieval layer in the first place. That is not a criticism. A risk taxonomy is supposed to name the property and leave the implementation to the people who know the system. But it does mean the list can be worked through carefully and still leave one question open, and it is an important question for avoiding a cross-tenant read: in your application, at what point is the requesting user's identity applied to the retrieval, and does the answer hold on every path that reaches it. ## Why the boundary is easy to lose in this particular architecture Three things about a retrieval path make the authorisation decision drift, and none of them are about the model. **The identity has further to travel.** In a conventional request the handler that loads a record usually sits close to the authenticated session. In a retrieval path there is often a service between them: a query is embedded, a store is searched, results come back, and only then does the model see them. Each hop is a place where the user's identity can be dropped in favour of a service credential with broader access than the user should have. OWASP specifically identifies this use of generic, high-privileged identities as an Excessive Agency risk. **The filter is a parameter rather than a check.** Permission-aware retrieval can be implemented by passing a tenant ID, classification or set of allowed document IDs into the search, but the security property depends on that scope being enforced by the trusted retrieval layer rather than simply supplied by the caller. A filter supplied by the caller is a different security property from a check performed by the resource. It works only if every caller supplies the correct scope and the retrieval layer fails safely when that scope is missing or invalid; otherwise, an omitted filter can become a broader search. **Ingestion and retrieval are written by different people at different times.** Whatever labels the retrieval filter depends on were attached when the document was indexed. If a re-index, a migration or a new source populates that metadata differently, the filter still runs and still returns results, and nothing in the request looks wrong. The common shape of all three is that the failure is silent. A broken tenant boundary in a retrieval path does not throw. It answers. ## The choice OWASP declines to make for you Because the guidance names the property and not the placement, it is worth being explicit about the two ways teams actually satisfy it, since they fail differently and the choice determines what you have to test. **Filtering at query time** keeps one index and attaches a constraint to every search: this tenant, these document ids, this classification. It can avoid the operational overhead of maintaining separate indexes, but its security depends on the retrieval layer enforcing the constraint on every search. OWASP recommends enforcing tenant scoping inside the index query and using physically separated indexes for high-sensitivity workloads. Its weakness is that correctness lives in every call site. The index itself holds everything, so a caller that forgets the constraint, builds it from the wrong field, or hands over a service identity instead of the user's gets a broader search rather than an error. What you have to test is therefore every caller, not the store. **Separating the data** gives each tenant its own index, collection or namespace, so a query issued in the wrong context has nothing to match. The boundary depends less on each caller supplying the correct filtering parameter, which can reduce the risk and blast radius of application-layer filtering mistakes. The main trade-offs are operational, but the partitioning and routing themselves still need to be secured: more indexes have to be provisioned and migrated, shared data becomes harder to handle, and changes to the partition scheme can require re-indexing. Neither is wrong, and plenty of systems end up with both, physically separating the largest tenants and filtering within a shared index for the rest. The reason to decide it deliberately is that the two arrangements move the test surface: in the first, an authorisation test has to enumerate callers; in the second, it has to enumerate partitions and the routing between them. What is worth avoiding is the arrangement where nobody can say which one is in force. If the answer to "how is this partitioned" is that a filter is passed in most places, that is query-time filtering with unknown coverage, and the unknown coverage is the finding. ## How to test it The useful property of this bug class is that many instances can be tested from outside with accounts representing different access scopes, because the thing you are checking is an answer that should not have been possible. **Start from the invariant, not the endpoint.** Write down what must never happen in one sentence: a user in organisation A must never receive content derived from a document belonging to organisation B. That sentence is the test oracle, and it holds regardless of how the retrieval is implemented. **Ask as one tenant for something only the other has.** Seed a document in tenant B with a distinctive string that appears nowhere else. Then, as a fully legitimate user of tenant A, ask questions designed to surface it. Ask directly, ask obliquely, ask for a summary of everything available on the topic, ask it to list its sources. You are not trying to trick the model. You are checking whether the retrieval reached across the boundary, and the distinctive string is how you know unambiguously rather than by interpretation. **Test the paths that skip the front door.** The chat surface is usually the best-guarded route. The same retrieval often backs an API endpoint, a scheduled digest, an export, a webhook or an internal admin view, and those were added later with less scrutiny. Enumerate every caller of the retrieval service and ask which of them applies the user's identity and which supplies a filter of its own choosing. **Check what happens when the filter is absent or malformed.** If a tenant filter is a parameter, find out what the store does when it is missing, empty, null, or a value that matches nothing. If the system fails open, a missing or invalid scope can result in an unfiltered search; over a shared index, that can create a cross-tenant data exposure. **Test after ingestion changes, not only after code changes.** Because the filter depends on metadata written at index time, a change to how documents are ingested can break the boundary without touching the authorisation code at all. That is a case a code review alone may not catch, particularly when the metadata change is introduced through a separate ingestion or data pipeline. **Include the agentic paths if you have them.** Where the feature can take actions rather than only answer, the same question applies to each tool it can call: does that tool re-check the requesting user's authority, or does it inherit the agent's. This is the ground OWASP's LLM03:2026 Excessive Agency covers, and it is worth reading alongside your own tool definitions. ## Comparing the approaches | Approach | What it reaches | Where it does not | Limitation to keep in mind | | --- | --- | --- | --- | | Prompt-injection and jailbreak testing | Model-layer manipulation, unsafe output, instruction override | Whether the retrieval returned data this user was never entitled to | A model that refuses politely can still have been handed another tenant's document | | Model evaluations and guardrail suites | Output quality, refusal behaviour, regression on known prompts | Authorisation, because the harness usually runs as one identity | Passing an eval set says nothing about a boundary the eval never crossed | | Working through the LLM Top 10 as a checklist | A genuinely broad map of model-layer and supply-chain risk | Enforcement placement and identity propagation, which it leaves to you by design | Completing the list is not the same as having tested your own boundary | | SAST and dependency scanning | Known-pattern flaws and vulnerable packages in the surrounding code | A filter that is applied correctly in code but populated from wrong metadata | Cannot see whether the runtime data actually honours the intended partition | | Exercising the running application as two tenants (what Borg does) | Whether a cross-boundary answer is actually reachable, with the steps to reproduce | Dedicated model-layer and embedding-specific risks, which are outside the scope of this particular test | Only covers the paths that were exercised, so absence of a finding is not proof of a boundary | ## Where the model-layer work is the right thing to do instead The case above is about one question, and a team that read this and dropped their model-layer work would be making a mistake. Prompt injection is real, it is the first entry on OWASP's list for good reason, and nothing in an authorisation test addresses it. The same goes for improper output handling, where model output reaches a downstream component without sufficient validation, and for system prompt leakage. Those are distinct AI and application-security properties, and they need the appropriate testing and controls built for them. The honest division is that model-layer testing asks whether the system can be made to behave badly, and authorisation testing asks whether a boundary you rely on actually holds. Both are worth doing, they fail differently, and a team doing only one of them has a gap either way. What does not work is treating a completed LLM risk checklist as proof that the tenant boundary actually holds, because guidance about the required controls is not the same thing as testing those controls against your running application. ## Where this fits in a delivery process Two practical notes, because a test that runs once is a snapshot. The retrieval path changes more often than the authorisation code around it, so the trigger for re-testing should include relevant ingestion and indexing changes, rather than only changes to auth. A pull request that adds a document source is a pull request that can move the boundary, even though it touches nothing that looks like a permission check. And the code-level and running-system views answer different halves. Reading the diff tells you whether a new caller of the retrieval service applies the user's identity; that is the question [code-aware review on a pull request]() is shaped for. Whether the boundary actually holds in the deployed system, with the real index and the real metadata, is best established by exercising the system with different tenant identities and seeing what comes back. Neither substitutes for the other, and a finding from the second is the one that comes with proof. ## What this does not solve The limit of everything above is the same limit that applies to any testing: it covers the paths that were exercised. An authorisation test over the chat surface and two API routes says nothing about the export job nobody mentioned, and a boundary can hold on every path you tried and fail on the one you did not enumerate. So the enumeration is the load-bearing step, and it is worth redoing whenever a new consumer of the retrieval service ships. The second limit is that this is a confidentiality question and not the whole of AI application security. Nothing here addresses whether the model is accurate, whether it can be made to produce harmful output, or whether the data it was trained or fine-tuned on was handled correctly. Those matter, and they are separate work with separate instruments. --- ## Security Testing Every Release Without a Manual Bottleneck URL: https://www.borghq.io/blog/security-testing-every-release-without-a-manual-bottleneck Published: 2026-09-09 Author: Borg Security review is often the last step in a release pipeline that still waits for a person. Builds run themselves. Tests run themselves. Deployments run themselves. Then a change touching authentication, tenant boundaries or billing reaches a queue, and it sits there until someone with the right context has time to read it. That queue is usually described as a security problem. It is closer to a capacity problem, and the distinction matters because the two have different fixes. ## Where the bottleneck actually sits The size of the security team is one of the things that produces the queue. When the number of changes requiring review grows faster than the team's review capacity, work starts to wait. The consequences tend to arrive in one of two shapes. Either changes wait, and delivery slows in a way engineering feels every sprint, or the gate gets routed around: changes get classified as low risk to avoid the queue, and the ones that genuinely touch an authorisation boundary go out with the same scrutiny as a copy change. Neither shape is a security control. A gate that people learn to avoid is worse than a slower gate, because it produces the appearance of review without the substance of it. The useful question is therefore not how to review faster. It is which parts of the review genuinely need a person, and which parts are being done by a person only because nothing else was doing them. ## What the pipeline already covers A modern CI pipeline is not empty on security. Dependency scanning flags known vulnerable packages. Secret scanning looks for committed credentials and other sensitive information. Static analysis flags injection-shaped patterns, unsafe deserialisation and a long list of known-bad constructs. These are real controls and they run without anyone waiting. They also share a property worth naming: they compare your code against patterns known in advance. A dependency scanner knows a CVE exists. A static analyser knows what a SQL string concatenation looks like. What generic scanning does not reliably hold is your application's full intent. A tool may be able to recognise some authorisation checks, but it generally cannot infer from generic patterns that an endpoint is supposed to be reachable only by an account administrator, because that rule comes from the application's design and business requirements. The gap between pattern matching and exploitability is covered in more detail in [what static analysis can reach](). So the pipeline covers classes that can be described and checked automatically, while the queue remains for the cases where automation cannot provide enough confidence or context on its own. ## The two questions a release actually raises Once you separate them, a release raises two different security questions, and conflating them is part of why the queue never clears. The first is about the change itself. Does this diff introduce a way for one tenant to reach another tenant's data, for a lower-privileged role to perform a higher-privileged action, or for a billing path to be walked out of order? That question is scoped to what changed, and it can often be assessed from the diff plus enough repository context to understand what the surrounding code expects. The second is about the application as a whole. Given everything that has shipped, is there now a reachable path through the running system that nobody intended? That question is not answerable from a diff, because the interesting failures are often combinations: a route added in March, a role added in June, and a check that was correct when it was written. Borg splits these deliberately. Gungnir reviews the pull request diff with repository context and posts inline comments carrying severity and a suggested fix, before the change reaches production. Mjolnir tests the full application, reading source code, mapping the attack surface, generating test cases and executing them against the running system. The distinction, and when each one answers your question, is set out in [full pentest versus PR security review](). The reason this matters for the bottleneck is that the first question is the one naturally suited to a merge-time check. The second can run on its own schedule without making every merge wait for a full application test. ## What can run without a person on every merge The per-change review is the part that can move into the pipeline, because it has a defined scope and a defined trigger. Gungnir runs as a GitHub check on pull requests. It reads the diff together with repository context rather than the diff alone, which is what lets it reason about whether a changed authorisation check is consistent with how the rest of the application enforces that boundary. Findings come back as inline comments on the pull request itself, with severity and a suggested fix, so the feedback arrives where the change is being discussed rather than in a separate tool. The check can block a merge, which means the gate still exists; what changes is that the gate no longer waits on a person's calendar. That last point is the whole mechanism. The bottleneck was never the existence of a gate. It was that the gate's throughput was bounded by how many diffs the available security reviewers could assess in a day. The application-wide question runs on its own cadence. Mjolnir can be run manually or on a schedule, returning verified findings with reproduction steps rather than relying solely on a list of candidates for an engineer to investigate. Findings land in Odin and can flow into Linear, Jira or GitHub as tickets, which is covered in [how findings reach your tracker](). When a fix merges, [automatic retests]() review the merged change and update the finding's state, so the loop closes without a manual re-check. None of that removes the security engineer. It changes what reaches them. ## What still needs a person This is the part a vendor is tempted to skip, and skipping it is how you end up with a security programme that quietly stops working. Threat modelling for a genuinely new surface still benefits from human judgement. When your team introduces a new tenancy model, a new payment flow or a first-of-kind integration, the question of what an attacker would want and which boundaries now matter is a design question. Automated testing can probe a boundary once it exists; it is much weaker at deciding which boundary the design should have drawn. Judgement on severity in context still needs a person. A verified finding tells you a path is reachable. Whether that path matters more than the other twelve things on the roadmap this quarter depends on what the data is, who your customers are and what your obligations to them look like. That is a business decision wearing a technical costume. Anything genuinely novel still needs a person. Borg's own Embedded Research Service exists for cases where longer-term offensive-security expertise is needed, operating as an extension of the client's team rather than providing only routine automated coverage. And review of the automation itself still needs a person. A check that runs on every pull request is a control like any other, and controls drift. Someone should periodically ask whether it is still firing on the paths that matter. The realistic version of the pitch is not that the human disappears. It is that the human spends less time on diffs a machine can evaluate, and more time on the questions that require human judgement. ## Why "verified" is the load-bearing word The queue is not only made of reading time. A large part of it is triage: deciding which of the things a tool flagged are real. That is why the distinction between a candidate and a verified finding matters more here than it looks. A candidate says something in the code resembles a known-bad pattern, and a person has to establish whether it is reachable and whether it matters. In this context, a verified finding should mean that the system has attempted to validate the suspected weakness and observed evidence supporting the result, with enough detail to reproduce it. The first still requires an engineer to establish whether the issue is real and relevant. The second removes much of that investigative work, leaving the engineer primarily to assess impact and fix it. This is also the sharpest published objection to the whole autonomous category, and it is worth stating rather than dodging. HackerOne's own January 2026 announcement of agentic PTaaS argued that fully autonomous testing tends to deliver "shallow, unverified results that create noise rather than insight". That is a fair challenge, and the answer to it is not a confidence score. The answer to it is whether the output includes the attempted action and its result, with enough evidence for an engineer to reproduce or independently validate the finding. Judge any per-release security tooling on that basis. If its output is a list you still have to investigate, it may have moved the bottleneck rather than removed it: the review queue becomes a triage queue, and a substantial part of the human workload remains. If its output is a reachable path with reproduction steps, the engineer's next action is a fix rather than an investigation, and that is the step that actually returns capacity to the team. It is worth being clear about the limit of that claim. Verification tells you a path was reachable at the moment it was tested. It does not promise that everything unreported is safe, and no honest reading of a finding count supports that inference. ## Comparing the approaches | Approach | Runs without waiting | What it answers well | Where it falls short | | --- | --- | --- | --- | | Dependency and secret scanning | Yes, per commit | Known vulnerable packages, committed credentials | Says nothing about your own logic or authorisation rules | | Static analysis | Yes, per commit | Injection-shaped patterns and known-bad constructs | Has limited visibility into application intent, so some authorisation and business-logic failures remain outside generic rules and findings still require triage | | Manual AppSec review of each PR | No, bounded by team capacity | Deep context, novel designs, judgement on what matters | Throughput is constrained by reviewer capacity, which can create queues and incentives to route around the review | | Periodic penetration test | No, scheduled and point-in-time | Whole-application view, multi-step and creative attack paths | Point-in-time coverage becomes less representative as the application changes, and the report describes the system as it existed during the assessment | | Code-aware PR review (Gungnir) | Yes, per pull request | Whether this diff introduces a reachable authorisation, tenancy or logic failure | Scoped to the change plus repository context, so it is not a substitute for whole-application testing, and it needs the repository connected | | Continuous application testing (Mjolnir) | Yes, on a schedule | Reachable paths across the whole running application, with reproduction steps | Runs on its cadence rather than per merge, so it is not the thing that gates a single change | The table is worth reading as a set rather than a ranking. Every row has something the others do not, and the rows that run without waiting are not automatically the better ones. They are the ones that can be applied repeatedly and at a scale that would be difficult to match with manual review alone. ## Where to start if the queue is the constraint If the review queue is the thing actually slowing releases, the order that tends to help is narrower than a full programme. Start by identifying which changes are currently gated on a human and why. In many teams, the gate is triggered by a path or label rather than by an assessment of what the change actually does, which can put changes that never needed offensive review into the queue. Then put automated per-change review on the paths that genuinely carry authorisation, tenancy, billing or permissions logic. Connecting the repository is the prerequisite here; a diff-scoped review cannot reason about repository context it cannot read. Then let the whole-application testing run on a schedule rather than trying to attach it to a release. It answers a different question and attaching it to a merge gate would recreate the bottleneck in a new place. Finally, decide deliberately what the security engineer now owns. If that step is skipped, the freed capacity tends to be absorbed rather than redirected, and the programme ends up faster without being better. ## What this does not solve Moving suitable parts of offensive review into the pipeline can improve review throughput. It does not make your authorisation model correct, and it does not tell you which risks your business should accept. It also depends on the reviews being trustworthy enough to act on. A per-merge check that produces too much noise is likely to be muted or bypassed, which can undermine the control by teaching engineers to ignore its output. That is the standard by which any per-change security review should be judged: not how much it flags, but whether an engineer reading its output changes what they ship. --- ## Two Years of Continuous Security with Banana Gun URL: https://www.borghq.io/blog/banana-gun-continuous-security Published: 2026-09-08 Author: Borg [Banana Gun]() has been a major part of the onchain trading ecosystem for years, processing significant trading activity across its products. Banana Gun’s infrastructure has processed more than $16B+ in cumulative trading volume and 26M+ lifetime trades. Borg has been working alongside the Banana Gun team on security for more than two years. They were one of our earliest clients, and the relationship has since developed into what we now call Forward Deployed Security (FDS): continuous security work that evolves alongside the product. ## **Security at scale** When a platform is responsible for billions of dollars in trading activity, security cannot be treated as something that happens once a year. Banana Gun ships quickly. Over the years we have worked together, its products, infrastructure and overall attack surface have changed substantially. As software development has accelerated, the gap between what gets shipped and what has actually been security-tested has only become more important. Rather than testing a fixed application once and coming back twelve months later, Borg has continuously worked across Banana Gun's environment as new functionality has been introduced and existing systems have evolved. That has meant maintaining context on the platform over time and repeatedly approaching it from an offensive perspective. ## **A strong team on the other side** The model works best when security is taken seriously internally. The Banana Gun team has consistently been hands-on throughout our work together. When something requires attention, their engineers are quick to investigate, understand the underlying issue and implement changes. They also work closely with us when new functionality or infrastructure needs to be tested. Borg is not there to replace that ownership. We are there to provide an additional offensive security layer around an already highly capable engineering team. ## **Why continuous security?** Traditional penetration testing is valuable, but it fundamentally represents a point in time. That model becomes harder to rely on as software starts moving faster. Teams can now ship changes daily or even hourly, while security testing is still often performed quarterly or annually. Infrastructure moves as new integrations occur. Entire products launch, often long before the next scheduled security review. For a platform processing billions of dollars in trading volume, that gap matters. The attack surface does not stand still and security testing should not either. That is the idea behind Borg FDS: maintain the context of a long-term security team while continuously testing from an attacker's perspective as the environment evolves. With Banana Gun, we have now been operating that way for more than two years. ## **Growing together** Banana Gun was one of the earliest teams to work with Borg. Since then, Banana Gun has continued operating at a significant scale, while our approach to continuous offensive security has matured alongside the relationship. If the product keeps changing, the security testing should too. --- ## How Automatic Retests Verify a Fix When Your PR Merges URL: https://www.borghq.io/blog/how-automatic-retests-verify-a-fix-when-your-pr-merges Published: 2026-09-07 Author: Borg A fix is not finished when the pull request merges. Somebody still has to establish that the reported problem is actually gone, and that step is often a person re-reading a diff, or it is not performed at all. Automatic retests close that loop by reading the merged change. Link the pull request that fixes a finding, and when it merges, Odin reviews the code that changed and decides whether the finding is resolved. Here is how the linking works, what the review reads, what it can return, and where it stops. ## What the retest actually reads Start here, because everything else follows from it. Automatic verification reviews the merged code change. Not the running application, and not a replay of the original attempt against a live environment. That has one important consequence worth stating before the setup steps: automatic retesting depends on a linked pull request, because the retest uses the merged change as its input. If the fix never passed through a pull request, this automatic path is not available. It also bounds what a passing retest means, which is worth being precise rather than generous about. A verified result says the change addresses the root cause as far as a code review can establish. Automatic retests review the merged code rather than testing the running application. ## Three things have to be in place Connect GitHub. The Borg GitHub App needs to be installed so Odin can read the repository and receive pull request events. Turn on Automatic retests. It sits on the GitHub card under Management then Integrations, and it is off by default. With it off, merging a linked fix still updates that pull request's state on the finding, but no retest fires. The toggle only appears once retests are available to your organisation, so if the card does not show it at all, that is a thing to ask about rather than a misconfiguration at your end. Link the pull request that fixes the finding. The fix has to be connected to the finding for any of this to happen, and there are three ways that link gets made. ## How a pull request gets linked Every finding carries a human-readable identifier built from your organisation's prefix and a sequence number, for example BORG-12. It appears in the finding header and on every row of the findings list, and it is the token Odin looks for. With the GitHub integration connected, Odin can use the finding identifier in the pull request to link the fix to the finding. The identifier can be included in the branch name, title or body. | Route | What you do | When it links | Worth knowing | | --- | --- | --- | --- | | Branch name | Use Copy branch name on the finding, which gives you something like `fix/borg-12-sql-injection-in-login-form`, then branch and push | The moment the pull request opens | Set when you branch, so there is nothing to remember later | | Title or body | Open the pull request with `BORG-12: fix the login form query` in the title, or `Fixes BORG-12` in the body | On open, and again on any later edit | The route for a pull request you have already opened | | Manual | Choose Link a pull request from the finding's action menu | Immediately | Needs the integration plus permission to update the finding, and is the fallback when the fix mentions the identifier nowhere | One detail catches people out. Odin re-scans when a pull request is opened, edited, reopened or closed, and pushing new commits does not re-scan. That is consistent rather than arbitrary: the branch, title and body are the three things it reads, and a new commit changes none of them. So if you forgot the identifier, editing the title or body is what creates the link, not pushing again. Odin also keeps a single Linked Findings comment on the pull request, listing each linked finding with its severity, status and discovery date. It edits that one comment in place rather than adding a new one on every event. ## Why the branch name is the route worth adopting Of the three, putting the identifier in the branch name is the one that survives contact with a busy week, and the reason generalises beyond this tool. A link created by the act of doing the work does not depend on anyone remembering to record it afterwards. You copy the branch name off the finding because you need a branch name anyway, and the link then happens on its own when the pull request opens. A link that depends on typing a reference into a description is a link somebody eventually forgets, usually on the busy day when the fix matters most. That is also why the title and body routes are worth keeping rather than dismissing. They are the repair path. A pull request opened without the identifier is not lost, because editing the title or body re-runs the same detection and links it retroactively. ## What linking changes before anything merges Linked pull requests appear as chips on the finding, one per request, each following its state on GitHub as it moves from open to merged or closed. Hovering one gives the title, repository, author and when it was linked. The link also moves the finding. A pull request linked to a Reported finding transitions it to Mitigating, which signals that work is under way rather than making any claim about the result. A finding already further along keeps the status it had. What linking does not do is close anything. Merging a pull request does not by itself move a finding to Fixed & Retested, and that restraint matters in a develop to staging to production workflow, where merging to develop does not mean the fix is live anywhere a user can reach. ## What happens when the pull request merges Given a linked pull request, a finding in a retestable state (Reported, Acknowledged, Mitigating, Open for Retest or Needs Revision), Automatic retests enabled, and the organisation's retest automation available, the merge queues a retest when the finding is eligible. Other conditions can cause a retest to be skipped, such as an already-running retest or insufficient credits. Odin then checks out the repository at the merge commit, extracts the change the pull request introduced as the diff between its base and head, and reviews that change against the original finding. Two properties are worth stating plainly for anyone who has to approve this running against their repository. The retest reads a snapshot of the code at the merge commit and then discards it. It never writes to your repository: no commits, no comments, no pull requests. ## What the review is actually asking Four questions rather than one, and the difference between them is where most of the value sits. Is the root cause addressed. The review reads past the symptom, so a SQL injection finding is checked for parameterised queries rather than for one string having been escaped. A change that makes the reported request fail while leaving the same construction elsewhere is not the same as a fix. Is the fix complete. It reads the surrounding files rather than the diff in isolation, which is what it takes to see whether a check added on one path is missing on the three other routes into the same code. Can it be bypassed. A guard placed where it can be reached around is a common shape, and it looks correct in a diff read on its own. Does it introduce a regression. A fix is a change like any other, and the review treats it as one rather than assuming a security patch cannot break something. ## The five things a retest can come back as Three of them are verdicts. Two mean no verdict was reached at all, and a system that hid those behind a pass or a fail would be misrepresenting how often reading a diff is genuinely ambiguous. | Result | What it means | What happens to the finding | | --- | --- | --- | | Fix verified | The change addresses the root cause with confidence | Moves to Fixed & Retested | | Fix is incomplete | The change does not fully resolve the finding, or introduces a new problem | Moves to Needs Revision, with a comment on what is still wrong | | Inconclusive | The review could not confirm the fix either way | No change, the finding stays where it was | | Retest skipped | It never started | No change, with the reason on the retest detail | | Retest did not complete | It started and something went wrong part way | No change, with the reason on the retest detail | Inconclusive is the row worth reading twice. It leaves the finding open, so you can adjust the fix and merge again, or move it to Open for Retest and have it confirmed by hand instead. Fixed & Retested is the one status only Borg sets, which is what stops the loop closing on an assumption. ## Where the result shows up You do not have to watch for it. In the findings list a chip appears next to the title, reading Retesting while the review runs and then Verified or Retest failed once it finishes. On the finding itself a banner reports the same states, and a finding sitting in Open for Retest with nothing yet reads Not retested yet. View retest gives the reasoning rather than only the verdict. There is a timeline of how the retest ran, starting with the pull request that triggered it and ending with the code review step and its result. There is also a Spotted while retesting section listing anything else the review noticed in the changed code, each with a file, a line and a short description. That last part is worth using. A review that has just read the diff and its surrounding files is well placed to notice adjacent problems, and those observations are attached to the retest rather than raised as new findings, so they are easy to miss if nobody opens it. ## When it comes back as Needs Revision An incomplete result is more useful than a failed one, because it arrives with a comment saying what is still wrong rather than a red mark. The practical loop is short: read the comment, adjust the fix, merge again. A merge of another linked pull request can queue another retest, so the next attempt is verified the same way as the first. Nothing needs re-requesting by hand. Worth setting expectations with the team on this: Needs Revision is not a rejected fix. It can be a fix that closes the reported path and leaves an adjacent one open, which is the kind of issue the review is looking for and a person re-reading their own diff can easily miss. ## Where this stops Three limits are worth knowing before anyone treats a verified retest as the end of the question. It reads code, not behaviour. A verified result is a well-informed review of the merged code change, which is a weaker claim than an attempt against the running system that failed. The automatic retest path needs the fix to arrive as a linked, merged pull request. A change made outside the repository, such as a configuration, infrastructure rule or gateway policy that is managed separately, may leave no relevant diff for that review to read, so fixes of that shape still need verifying another way. And a merge is not a deploy. The retest tells you the change that merged addresses the finding. Whether that change is running anywhere a user can reach is a question your deployment pipeline answers, not this one. None of that makes the loop less worth closing. It makes the claim a precise one, which is the difference between a status you can act on and a status you have to go and check. The other half of this loop, where a finding's status and its ticket in [Linear, Jira or GitHub stay in step](), works alongside it: the tracker carries the work, and the retest decides when the work is done. If you want to see this running against your own repository, book a walkthrough with Borg. --- ## How to Continuously Map Your External Attack Surface URL: https://www.borghq.io/blog/how-to-continuously-map-your-external-attack-surface Published: 2026-08-31 Author: Borg An asset inventory is accurate on the day someone builds it. After that it drifts. A subdomain gets pointed at a new host for a demo and stays. A service moves behind a load balancer and the old address keeps answering. A wildcard certificate quietly covers something nobody meant to publish. None of it shows up in a spreadsheet, because a spreadsheet only knows what somebody typed into it. Mapping an external attack surface continuously is the practice of closing that gap: finding what is reachable from the internet, keeping the list current as infrastructure changes, and understanding how the pieces connect. Here is how that works, and where it stops being useful. ## What counts as an asset Three things, and the distinction matters more than it looks. For the purposes of an external attack-surface inventory, three identifiers are particularly useful: domains, subdomains and the addresses they resolve to. They form the core infrastructure layer of the inventory. HTTP routes and endpoints are a separate layer. In this inventory model, `api.example.com/v2/accounts` is treated as a property of the application exposed through the hostname rather than as an infrastructure asset in its own right. Keeping the two layers apart is what stops an inventory turning into an unusable list of every URL a crawler has ever seen. The reason to be strict about it is that the question you ask differs per layer. "Do we still own this?" is a domain question. "Is this port meant to be open?" is an address question. "Should this endpoint require a session?" is a route question, and that one needs testing rather than discovery. ## The four ways an asset arrives, and the two that matter An inventory fills from more than one direction. In Borg's platform, assets can be associated with how they were learned. The taxonomy is worth borrowing even if you build your own: - Manual, added by someone on the team. - Import, loaded in bulk from a CSV or JSON file. - Recon, found by continuous discovery. - Agent, found by Mjolnir during a pentest run, as a by-product of reconnaissance against a target. The first two usually represent assets that were already known to someone or recorded somewhere. They are worth recording and they are rarely where the problem is. The interesting half of a real attack surface is what arrived through the other two: the things nobody put on a list. That is the case for continuous discovery in one line. You are not trying to catalogue what you remember. You are trying to find what you have forgotten. ## Why the count is always higher than anyone guessed Three mechanics generate assets without anyone deciding to. Delegation and inheritance. A root domain someone wrote off years ago can still carry a live subdomain tree, because the records were never removed and nothing forced a review. Names that outlive their targets. A CNAME pointing at a third-party service stays valid after the account behind it lapses. The name still resolves. What it resolves to may no longer be yours, and the gap between owning a name and controlling what answers on it is worth finding before somebody else does. Short-lived infrastructure that is not short-lived. A host stood up for a demo, a migration or a load test tends to outlive its reason. It was never in the inventory because it was never meant to be permanent. None of that is exotic. It is the ordinary consequence of shipping, and it is why the first honest count of an external surface often surprises the team that owns it. ## The techniques, and why they belong on different clocks Discovery is several jobs wearing one name, and they do not cost the same. Light DNS enumeration across a handful of root domains is cheap and can run constantly. A full port scan across every address you own is not cheap and cannot. A sensible continuous-discovery programme can run several tiers at different frequencies: | Tier | Typical Frequency | What it does | | --- | --- | --- | | Frequent | Hourly or several times a day | Light DNS enumeration to pick up new domains and subdomains | | Periodic | Every few hours or daily | Deeper enumeration for subdomains the lighter pass can miss | | Comprehensive | Daily or less frequently, depending on estate size | Broader port scanning, address linking and service identification | Copy the shape rather than the specific numbers. DNS enumeration is generally cheaper than comprehensive service scanning, so the cheap check that catches fresh names can usually run more often than the expensive check that enumerates what is listening across the address space. Two techniques do the looking. Domain recon runs DNS enumeration, HTTP probing and TLS inspection against domains and subdomains. Network recon scans the port range on the address side and labels the services it finds. Both can be designed to minimise operational impact, and that constraint matters more than it sounds for something you intend to run repeatedly against production. Discovery that carries a plausible risk of disruption gets switched off the first time anyone blames it for an incident, and then you are back to the spreadsheet. ## What a technology profile is for Knowing a subdomain exists is thin. Knowing what is answering on it is what lets you act. Domain recon returns several things per asset that change how you read it. The web server or edge provider answering, which can indicate whether a name is being served through a content delivery or reverse-proxy layer. The TLS protocol version and cipher, which can highlight hosts still negotiating configurations you intended to retire. The certificate subject and issuer, which can reveal differences from the rest of the estate. And whether a web application firewall is detected in front, which can help identify services that appear to differ from the standard deployment pattern. None of those is a vulnerability. All of them are triage signal. An asset with no delivery network in front, an old TLS version and a certificate from a different authority to the rest of your estate is not necessarily broken, but the combination is a useful signal that it may have followed a different deployment or management path, and that can make it worth looking at first. ## What continuous actually buys you Run discovery once and you get an inventory. Run it on a schedule and you get something more useful: the difference between today and yesterday. Most of a mature attack surface is stable and uninteresting. The signal is in the delta. A subdomain that appeared overnight, a port that opened on a host that had none, a certificate that changed issuer, an address that started answering for a name it never served before. Those are the events worth a person looking at, and they are invisible to a process that produces a fresh list every quarter with no memory of the last one. This is also why the tiering matters beyond cost. A daily port scan gives you a daily port delta. An hourly name check gives you an hourly name delta. The frequency you choose per technique is the resolution you get on that kind of change, so it is worth choosing deliberately rather than inheriting whatever a tool defaults to. ## Four approaches, and what each one cannot tell you | Approach | What it answers well | Where it falls short | | --- | --- | --- | | Spreadsheet or CMDB maintained by hand | What the team believes it owns, with business context attached | Only as current as the last person to update it, and it cannot know about anything nobody added | | Periodic external scan, quarterly or annual | A point-in-time snapshot, useful as an audit artefact | Anything that appears and disappears between scans is invisible, and short-lived hosts are exactly that | | Certificate transparency monitoring on its own | New names shortly after a public certificate is issued | Sees names covered by certificates recorded in public Certificate Transparency logs, so address-only services and services using certificates outside those logs can be missed | | Continuous discovery with tiered scanning | A list that stays close to current, plus relationships and a technology profile per asset | Reports what exists and what is reachable, not whether any of it can be exploited, and it stays bounded to what is externally visible | That last row is the honest limit and it is better stated than buried. Discovery is an inventory practice. It answers what is out there. It does not answer whether someone can get in through it. Those are different questions needing different instruments, and a programme that confuses them ends up with a very tidy list and no clear idea what to fix first. ## Relationships are the half most teams skip A flat list is a start. What makes it useful is knowing how the entries connect, because that is what tells you the blast radius of any one of them. Three relationships are worth deriving. Domain hierarchy. Root domain, parent, children. Walking it can help build out the subdomain tree beneath a name you own and reveal descendants that were not already recorded. Shared certificates. When two assets present the same TLS certificate, identified by its SHA-256 fingerprint, they are linked whether or not anybody documented it. That is routine with wildcard and multi-domain certificates, and it has three uses: spotting assets that may share infrastructure or certificate-management paths, seeing how far a wildcard actually reaches, and knowing which assets a single certificate expiry would affect together. Shared addresses. Every domain and subdomain resolving to one address, with the ports found on it. Shared hosting is not a defect, but it is a fact worth having before deciding that one of those names is low risk. Borg renders these as edges on an attack surface map with assets as nodes, so related things cluster and concentrations become visible rather than inferred. The value is not the picture. It is that "what else is on this host" and "what else does this certificate cover" become questions you look up instead of reconstruct. ## Classification is what makes an inventory triageable An inventory that treats every entry alike forces you to read all of it. Classifying by what an asset appears to be lets you triage instead. The useful classifications are behavioural rather than nominal. A root domain or subdomain is a name in the hierarchy. A server is an address serving web traffic. A likely database or data-store exposure can be identified from service fingerprints and commonly associated ports. Borg's classifier, for example, considers ports including 3306, 5432, 27017, 1433 and 6379 as signals that an address may be exposing a data service. An auth gateway is an address or subdomain running an authentication service. Those last two are the point. "We have 400 subdomains" is not actionable. "One of these addresses has 27017 reachable from the internet" is actionable immediately, and it should not be sitting in the same undifferentiated list as a marketing microsite. The same goes for anything classified as handling authentication: an authentication surface nobody knew was public deserves a different level of scrutiny from a forgotten redirect, and a list that cannot tell them apart makes you find that out by reading. ## Turning an inventory into work Discovery produces a list, and a list changes nothing by itself. This is where attack surface programmes usually stall. Two connections make it operational. The first is findings attached to assets, so the inventory stops being a catalogue and starts being a worklist. Borg rings nodes on the map by the highest severity attached to them, which sounds cosmetic and changes how the map gets read. The second is routing into wherever engineering work already lives. A discovered asset with a real problem on it needs to become a ticket in the tracker the team already uses, not a row in a security tool nobody opens. That is worth setting up before discovery is switched on rather than after, because the first full run can be especially noisy: it establishes a baseline from which later changes can be compared, and [pushing findings into Linear, Jira or GitHub]() can help stop that first run being ignored wholesale by moving the work into the systems engineering teams already use. ## Where external discovery stops Being clear about the boundary is what keeps the practice credible. External discovery sees what is reachable from the internet. It does not see what is not exposed, so it is not a substitute for knowing your internal estate. It reports what is present, not what is exploitable. An open port is a fact. Whether an attacker can do anything with it is a question for [testing that attempts the action and observes the outcome](). And it says close to nothing about the class of problem that lives behind a login. Tenant boundaries, ownership checks, payment and entitlement logic: none of that has an external signature, and enumeration will not surface it. That is [work for authenticated, application-aware testing](). The honest framing is that discovery sets the scope for everything else. It tells you what the target is. It does not tell you whether the target holds. ## Starting from nothing If there is no inventory today, the order that wastes least effort is unglamorous. Begin with the root domains, which somebody in the organisation definitely knows. Let enumeration find the subdomain tree beneath them, and expect a higher count than anyone predicted. Then read the classifications before the raw list, because anything resembling a database or an authentication surface deserves an answer today whatever else is queued. Then set the cadence, cheap checks on a short clock and expensive ones on a long one. The first pass is an audit. The value arrives on the second and every one after, when the list stops being a discovery exercise and becomes a change feed: this is new since yesterday, and nobody recognises it. That is also the point at which discovery starts feeding the rest of the programme rather than sitting beside it. A current inventory is what lets [continuous testing]() be scoped to what you run today instead of what you ran when someone last wrote the list down. If you want to see what this returns against your own domains, book a walkthrough with Borg. --- ## Autonomous Pentesting vs Vulnerability Scanning: What Each Finds URL: https://www.borghq.io/blog/autonomous-pentesting-vs-vulnerability-scanning-what-each-finds Published: 2026-08-28 Author: Borg Teams usually arrive at this comparison trying to work out whether one of these replaces the other. They do not, and the more useful question is which one answers which question. A vulnerability scanner asks whether something recognisably dangerous is present. A published advisory against a declared dependency. An injectable sink reachable from user input. A credential committed to a configuration file. A storage bucket left readable. These are properties that can be expressed as rules and evaluated without anyone reasoning about what your particular application is supposed to do. Autonomous pentesting asks whether security weaknesses can actually be exploited against the running application. For example, rather than asking whether a pattern resembling an access-control bug is present, it can test whether this account can in fact read that record. Those two questions can have different answers on the same codebase, and success in one does not establish success in the other. A clean scan can coexist with a broken authorisation boundary, because that boundary may never have been expressed as a pattern for a scanner to match. What follows is what each approach reaches, where each stops, and when each is the better use of the next unit of effort. ## Why a clean scan and a real vulnerability can coexist The reason is that the two are measuring different things, and only one of them is written down anywhere. Many vulnerability scanners work from a catalogue of known weaknesses or detectable conditions. Somebody has described a dangerous construct, vulnerable component, or insecure configuration in a way the tool can evaluate: this call on untrusted input, this dependency at this version, this header absent, this permission set to public. When the relevant condition is present, the tool can flag it. The catalogue is general because it has to be, and that generality is what makes it possible to apply the same checks across many codebases. An application's own rules are not in that catalogue. That only an account with a billing role may change a plan. That a support agent may read a ticket but not the card on file. That an order belongs to exactly one organisation. These are specific to your product, and in most codebases they are enforced by code rather than declared anywhere a tool can read. A violation of one of them often looks like ordinary working code, because there is no dangerous construct present. Something that should have been present is absent. That is the whole of the gap, and it is not a criticism of scanning. A missing check has no signature, so an instrument built on signatures has little to match. Reaching that class means executing the action and observing what happened, which is a different kind of tool for a different kind of work. ## What a vulnerability scanner reaches Start with the case for scanning, because it is a strong one and it tends to get undersold in comparisons like this. Scanners cover the classes where a weakness can be described once and then checked everywhere. Software composition analysis reads your declared dependencies and matches them against published advisories. Static analysis examines source without executing it and flags constructs associated with known weaknesses, such as an unparameterised query, deserialisation of untrusted input, or a secret in a configuration file. Cloud posture scanning checks resources against a baseline. Dynamic scanning exercises the running application with generic payloads and reports what came back. Three things follow from that shape, and they are advantages rather than consolations. Placement is often cheap. Many scanners can be integrated early in development and run repeatedly as the codebase changes, with feedback landing close to the change that caused it. Breadth is genuinely wide. A team with a large estate of services, hosts and repositories can establish baseline coverage across all of it without manually deciding where to look first. That breadth is one of scanning's strongest advantages. Advisory tracking is work you should not be doing by hand. A dependency that was fine last month can acquire a serious advisory this month with no change to your code at all. That is a monitoring problem, and automation solves it properly. The limits are real too, and worth knowing in the specific rather than the abstract. Reachability is the first. A matched pattern is not the same as an exploitable path. Some analysers can say whether a match is reachable from an entry point at all, and that distinction tends to do more for a triage queue than almost anything on a datasheet. Accuracy varies more than a single figure would suggest. NIST's SATE V report (NIST SP 500-326) evaluated static-analysis tools across both synthetic and real-world Java code and reported substantially different results depending on the dataset and metric used. On the synthetic Java test cases, the evaluated tools achieved precision ranging from 78 to 94 percent, averaging 85 percent. On the real-world Java track, NIST's stricter useful-precision measure ranged from 22 to 79 percent across the tools evaluated, averaging 61 percent. NIST cautions that synthetic cases are smaller and less complex than production software, so results from those tests should not be treated as a direct prediction of what a team will see on its own code. The OWASP Benchmark illustrates the same trade from another angle: on the Java v1.2 benchmark, FindSecBugs v1.4.6 reported a 96.84 percent true positive rate and a 57.74 percent false positive rate. These figures illustrate why coverage and false-positive rates need to be considered together, and where a given tool sits on that trade depends on the tool, configuration and workload. Application-specific rules are the third limit, and the one this comparison turns on. Some analysers let you author custom rules, and encoding the invariants you can express cleanly is worth doing. That approach still has limits, because many application-specific invariants are difficult to express completely as static patterns in the first place. There is more on that boundary in [SAST and business logic vulnerabilities](). ## What autonomous pentesting reaches Autonomous pentesting works from the other end. Rather than matching a construct in source, it operates against the running application, attempts actions that should not succeed, and reports the ones that did. The mechanics matter because they determine the coverage. Reading the repository indicates where the interesting paths are: which endpoints resolve a record from a caller-supplied identifier, where a tenant boundary is enforced, which transitions a workflow permits. Executing against the running application is what turns a candidate into an answer. A finding is the record of an attempt that worked, which is why it can carry the steps needed to reproduce it. Chaining is where this diverges most from a scan, and the mechanics are easier to follow hypothetically. Suppose an endpoint discloses an internal identifier that was not meant to be visible. On its own that is low severity, and a reasonable team might defer it. If a second endpoint accepts that identifier as sufficient proof of ownership, the pair can become an authorisation bypass or, depending on what the second action permits, a much more serious compromise. Neither half necessarily matches a dangerous pattern on its own. The severity lives in the relationship between them, and finding it means having tried the second request with the output of the first. Retesting is the other structural difference. Re-running the same attempt against the deployed change provides a direct way to establish whether the attempted exploit path is still working, rather than relying solely on reasoning about the diff. The limits belong in the same section, and they are not small. Context bounds coverage. Reasoning from the repository and the running application means anything represented in neither can fall outside the view: configuration that exists only in a cloud console or a third-party dashboard, the behaviour of an external service, a rule that lives only as an undocumented assumption. Those may still need human or specialised testing. Access bounds coverage too. Testing a tenant boundary means holding credentials for more than one tenant. Testing a billing transition means an environment where that transition can be attempted safely. Coverage tends to track how much of the application the testing configuration can actually reach, which is a setup question as much as a capability one. And it does not substitute for a researcher reasoning about an unusual architecture. The systematic half is the part automation handles well: enumerating every endpoint that resolves a record by identifier, checking which of them carry an ownership check, and re-running that after each change is patient work. Someone looking at a strange design and forming a hypothesis nobody catalogued is a different exercise. ## Where each approach sits The comparison is more useful between categories of approach than between named products, because most teams past a certain size run several of these at once. | Approach | What it answers well | Where it stops | | --- | --- | --- | | Dependency and configuration scanning | Whether a declared component carries a published advisory, or a resource sits outside a known-good baseline | Says less about whether the vulnerable path is reachable in your application, so a matched advisory still needs triage | | Static analysis | Whether a known-dangerous construct is present in source, cheaply enough to run on every commit | Analyses source rather than exercising the application, so application-specific authorisation and workflow invariants that are not expressed as detectable conditions can remain outside its coverage | | Dynamic scanning | How the running application responds to generic payloads, with no need for source access | Coverage depends on how much of the application the configuration reaches, so routes, states and multi-step flows outside it stay untested | | Custom static rules | Invariants you can express as a pattern, checked continuously once written | Covers only the invariants somebody wrote down, which is usually a minority of the ones that matter | | Autonomous pentesting with code context | Whether a prohibited action succeeds against the running application, including cross-tenant and multi-step cases | Reasons from the repository and what is reachable at runtime, so anything configured outside those sources, or needing credentials it does not hold, can fall outside its view | None of those rows is a criticism of the others. They answer different questions, and a team running only one of them has a gap shaped like whichever ones are missing. Where the feedback lands also differs, which is the subject of [full pentest vs PR security review](). ## The classes that sit on one side of the line Three classes come up repeatedly in multi-tenant software, and they share a property. The code runs as written, and what was written was incomplete. Object-level authorisation. A handler resolves a record from an identifier the caller supplied. The query is parameterised, the input is validated, the session is real. If the handler never confirms that the requesting account owns that record, the endpoint returns somebody else's data. At the level a pattern can inspect, the request is hard to distinguish from a legitimate one, because the only thing wrong is a relationship between a user and a record that was never checked. Tenant isolation. Whether organisation A can reach organisation B's records depends on how your particular tenancy model is enforced across your particular service boundaries. There is no general rule for it because there is no general tenancy model. Testing it means holding two tenants and trying. Workflow and state abuse. Whether a checkout can reach a paid state without a payment event, whether an invitation can be accepted twice, whether a cancelled subscription still authorises an export. These depend on your state machine, and a tool with no model of that machine has little basis for calling any single transition wrong. The methodology for finding these by hand is a separate exercise, covered in [how to test business logic vulnerabilities](). ## When a scanner is the better use of the next unit of effort There are situations where scanning wins that comparison, and a page that cannot name them is not much use. If your estate is wide and shallow, scanning wins on coverage. Many services, hosts and repositories with no baseline across them is a breadth problem, and breadth is what scanning is for. If nothing is currently automated, dependency and advisory monitoring can be a high-value place to start. A published advisory against a dependency you actually ship is a concrete issue with a known remediation path, and finding those by hand does not scale. If most of your risk sits in components you did not write, the same holds. An application that is mainly integration work has a different risk profile from one carrying a large custom authorisation layer, and the instrument should follow the profile. If there is nowhere a test can safely attempt a state change, that is worth sorting out before buying something that needs one. Testing by attempting the action needs somewhere to act. ## Where Borg fits Borg's Mjolnir tests the running application. It reads the repository to decide which paths are worth attacking, executes against the deployed system, reports findings with reproduction steps, and retests once a fix lands. Gungnir applies a narrower version of the same approach to a pull request diff, before the change reaches production. Huginn tracks the external attack surface as it changes. Findings from all three collect in Odin, the dashboard, and route into the tracker your engineers already use. There is a fuller description in [what continuous pentesting means](), and the routing is covered in [how Borg pushes findings to Linear, Jira and GitHub](). The limits stated earlier apply to all of it. Code-aware testing is bounded by the context available to it, so configuration held outside the repository and the running application, and behaviour belonging to external services, may still need human or specialised testing. It does not remove the case for keeping a scanner running, and it does not stand in for a researcher reasoning about an unusual architecture. ## Conclusion The search behind this comparison usually assumes one of these is the modern version of the other. They are two instruments answering two questions, and a team running only one has a gap in the shape of the other. Keep the scanner. It covers the classes that can be described once and checked everywhere, and it covers them cheaply. Add something that attempts the action when the risk that would hurt most sits in your own authorisation, tenancy or workflow logic, because that is the class a catalogue cannot describe. If you are unsure which of those two describes your application, a walkthrough against your own system tends to be the shortest route to the answer. --- ## SAST and Business Logic Vulnerabilities: What Static Analysis Can Reach URL: https://www.borghq.io/blog/sast-and-business-logic-vulnerabilities-what-static-analysis-can-reach Published: 2026-08-24 Author: Borg If you are looking for a static analyser that catches business logic vulnerabilities, it is worth knowing up front that this is a fit problem rather than a shortlist problem. Static analysis is built to recognise patterns in source code. Most business logic bugs are not patterns. They are an application behaving exactly as written, where what was written was incomplete. That does not make static analysis the wrong purchase. It makes it the wrong instrument for this particular class of bug. So the useful version of this page is not a ranked list of products. It is which part of the problem a static analyser can reasonably be expected to cover, what the published accuracy research suggests you should expect from it, and what tends to cover the rest. Everything below compares categories of tooling rather than named products. ## What static analysis genuinely covers Start with the case for it, because it is a real one and it tends to get undersold by people selling something adjacent. A static analyser examines software artefacts without executing the application, looking for code patterns, data flows, configurations, or other properties associated with known weaknesses. A credential committed to a configuration file. User input reaching a query without parameterisation. A deserialisation call on untrusted input. A declared dependency with a known advisory against it. These are the kinds of weaknesses static analysis is well suited to finding because they can be expressed in rules that a tool can evaluate automatically. It is also cheap to place. Static analysis can be integrated into CI/CD so that scans run automatically on commits or pull requests, allowing many of the weaknesses it covers to be identified close to when they are introduced. That is worth a lot, and offensive testing does not replace it. The useful framing is that static analysis is particularly effective when a vulnerability can be expressed in terms of recognisable code properties, data flows, control flows, or other rules that can be evaluated from the software being analysed. Business logic asks a different question, and the rest of this page is about why that difference matters more than it sounds. ## Why business logic sits outside the pattern Business logic vulnerabilities often arise because an application's implementation does not correctly enforce an intended rule or invariant. Sometimes that means a missing check; in other cases it is an incorrect condition, state transition, calculation, or authorisation decision. The code can behave exactly as implemented while still violating a rule the application is supposed to enforce. Take a handler that fetches an order by its identifier. The query is parameterised, the input is validated, there is no injection anywhere near it. If the handler never confirms that the requesting account owns that order, the endpoint returns another customer's data. There may be no generic code pattern that tells an analyser that the missing ownership check is a vulnerability. A tool can detect this kind of issue when the relevant authorisation invariant and application structure are sufficiently explicit for its rules or analysis to reason about them. The same shape recurs across the classes that tend to matter most in multi-tenant software. Whether organisation A can reach organisation B's records depends on how your particular tenancy model is enforced across your particular service boundaries. Whether a checkout can reach a paid state without a payment event depends on your particular state machine. A static analyser can reason about application-specific invariants when those invariants are represented in code, configuration, annotations, specifications, or custom rules it understands. In many applications, however, important business rules are only partially or implicitly represented in those forms. Some analysers do better here than others. Tools that let you author custom rules can encode some of your own invariants, and that is worth doing for the ones you can express cleanly. That approach tends not to scale to the full set. The methodology for finding the rest is a separate exercise, covered in [how to test business logic vulnerabilities](). ## What the published accuracy research suggests Alert fatigue gets asserted often and measured rarely. A few third-party figures are worth knowing, and they are more useful than any vendor's own numbers. NIST's SATE V evaluation found substantial variation in static-analysis accuracy. In its synthetic Java track, the tools evaluated had precision ranging from 78% to 94%, with an average precision of 85%. NIST also noted that higher recall can come with lower precision, illustrating the trade-off between finding more potential weaknesses and producing more incorrect warnings. The OWASP Benchmark measures true-positive and false-positive rates separately and publishes results for individual tools. On the Java v1.2 benchmark, FindSecBugs v1.4.6 reported a 96.84% true-positive rate and a 57.74% false-positive rate. Historical commercial-tool results published for the Java v1.1 benchmark show six anonymous SAST tools with true-positive rates ranging from 28.96% to 85.02% and false-positive rates ranging from 12.22% to 52.09%. These figures illustrate why benchmark results should not be treated as universal production accuracy figures: the relationship between recall and false positives varies substantially by tool and benchmark. Read together, those results suggest that the practical constraint is often not how many issue types a scanner can detect, but how many findings a team can realistically investigate. For a small team, triage capacity can matter more than scanner count. The same results suggest a selection criterion that is more useful than a feature list. The number worth asking a vendor about is not how many issue types are detected, but how many findings a developer reads before reaching one that is real in your codebase. ## Where each approach sits | Approach | What it covers well | Where it stops | | --- | --- | --- | | Static analysis | Known-dangerous constructs in source, cheap enough to run on every commit | Reads code without executing it, so a missing check is hard to distinguish from a check that was never needed | | Software composition analysis | Known advisories against declared dependencies | Says less about whether the vulnerable path is reachable in your application | | Dynamic scanning | Behaviour of the running application against generic payloads | Coverage depends heavily on how the application is discovered and exercised; routes, states, authentication contexts, and multi-step workflows that are not reached by the test configuration may remain untested | | Custom static rules | Invariants you can express as a pattern | Covers only the invariants somebody wrote down | | Offensive testing with code context | Attempting the action and observing the result, including cross-tenant and multi-step cases | Reasons from the repository and what is reachable at runtime, so anything configured outside those sources can fall outside its view | None of those rows is a criticism of the others, and most teams past a certain size run several. They answer different questions. Where the feedback lands also differs, which is the subject of [full pentest vs PR security review](). ## Choosing a static analyser well If a static analyser is what you are buying, the questions that separate them are narrower than the feature lists suggest. **How much noise reaches a developer?** Ask for a false-positive rate on a codebase resembling yours rather than on a benchmark suite. The figures above suggest the honest answer varies considerably. **Can it express your own rules?** The invariants you can encode as a pattern are the slice of your business logic a static tool can help with. If custom rules are awkward to author, that slice is smaller in practice than on paper. **Does it distinguish reachable from unreachable?** Some analysers can say whether a matched pattern is reachable from an entry point at all. That distinction tends to do more for triage load than almost anything else on a datasheet. **Where do findings land?** A finding in a portal nobody opens is not much better than a report nobody reads. None of those questions is about business logic, which is rather the point. Choose the static analyser on what static analysis is for, and treat the logic layer as a separate line item. ## What tends to cover the rest The part of the logic problem that automation takes over is the systematic part. Enumerating every endpoint that resolves a record by a caller-supplied identifier, checking which of them carry an ownership check, and re-running that after each change is patient work rather than creative work. That is where Borg's Mjolnir fits. It reads the repository to decide which paths are worth attacking, executes against the running application, reports findings with reproduction steps, and retests once a fix lands. Gungnir applies a narrower version of the same approach to a pull request diff, before the change reaches production. Huginn tracks the external attack surface as it changes. Findings collect in Odin, the dashboard where Mjolnir, Gungnir and Huginn report their results. There is a fuller description in [what continuous pentesting means](). The limit belongs in the same paragraph. Code-aware testing is constrained by the context available to it. Configuration that exists only in a cloud console or third-party dashboard, external services whose behaviour is not represented in the repository, and business rules that exist only as undocumented assumptions may require additional human or specialised testing. It also does not stand in for a researcher reasoning about an unusual architecture. ## A sequence that tends to work If the question behind the search is really what to run and in what order, the sequencing below is a reasonable default. It is ordered by how much you get for the effort rather than by how much ground each one covers. **Dependency advisories first.** Software composition analysis is relatively cheap to automate, and a published advisory provides a concrete issue to investigate. Whether that vulnerability is actually reachable or exploitable in your application can still require additional analysis, but the triage cost is low compared with almost anything else. **Then static analysis, tuned rather than maximal.** Turn on the rule classes with a low false-positive rate for your stack and leave the rest off until someone has capacity to triage them. A scanner configured for maximum coverage can create a large triage burden for a small team. In practice, a narrower configuration that produces findings developers can consistently review may be more useful than one that generates more alerts than the team can process. **Then write down your invariants, whether or not a tool can check them.** The list of things that should never be possible in your application is useful even as a document. It tells a reviewer what to look for, it tells a new engineer what the rules are, and where an invariant happens to be expressible as a pattern you can hand it to a custom rule. **Then cover the logic layer with something that attempts the action.** For many logic flaws, source analysis alone does not answer the final question: whether the prohibited action actually succeeds in the deployed application. What that ordering deliberately avoids is buying breadth before triage capacity. If a new security layer produces more findings than the team can realistically triage, adding still more tooling is unlikely to solve the underlying capacity problem. ## Conclusion The search that brings people to this question usually assumes the answer is a product name. The more useful answer is that static analysis and business logic are a poor fit by construction, and noticing that early can save a procurement cycle. Run a static analyser, choose it on the things it is genuinely good at, and treat the logic layer as a separate problem with its own instrument. Borg tests that layer, auth flows, APIs, access control, tenant isolation and billing logic, continuously and with the repository as context, and delivers findings with reproduction steps into the tracker your engineers already use. Book a walkthrough to see what it turns up against your own application. --- ## Bug Bounty Alternatives: What AI Security Testing Replaces, and What It Doesn't URL: https://www.borghq.io/blog/bug-bounty-alternatives-what-ai-security-testing-replaces-and-what-it-doesn-t Published: 2026-08-23 Author: Borg A bug bounty tells you what somebody happened to find. It does not tell you what nobody looked at. That distinction is the whole question behind this page. Teams shipping several times a week increasingly ask what an AI-driven alternative to a bounty programme looks like, and the honest answer is that the two are not substitutes in most of the places people assume they are. A bounty is an open invitation to researchers within a defined scope, without requiring them to follow a fixed testing schedule. Continuous offensive testing is a bounded, repeatable exercise that runs whether or not anyone finds your application interesting this week. Each is good at something the other is structurally unsuited to. What follows is what each model actually covers, where the boundaries fall, and what an evaluator should ask before replacing one with the other. ## What a bug bounty is genuinely good at Start with the case for keeping one, because it is stronger than most replacement pitches admit. A bounty buys access to attacker diversity: people with different backgrounds, tooling and instincts can investigate the same application. They are not constrained by the assumptions of the team that built it. That matters, because your threat model encodes your assumptions, and the interesting bugs tend to live exactly where an assumption was wrong. An internal team, or a tool configured by that internal team, can inherit some of the same assumptions the application was built with. An external researcher brings a different set of assumptions and incentives. It also pays for outcomes rather than effort. You are not buying testing hours, you are buying findings, and a quiet month costs nothing. For an application with a large, mature, public surface, that is hard economics to argue with. And it does not normally require a scheduled testing window for every researcher. Someone may look at your password-reset flow on a Tuesday in March because they decided to investigate it. None of that should be assumed to disappear just because automation gets better. A vendor claiming that an automated system completely replaces external attacker diversity should be able to demonstrate that claim, not just assert it. The question is not whether a bounty finds real bugs. It is whether a bounty is the right instrument for the parts of your application you actually need assurance on. ## Where the model's boundaries are These are properties of the model, not failures of any platform running it. **Coverage is chosen by the researcher, not by you.** A programme defines a scope, but within that scope nobody is obliged to look anywhere in particular. Researchers have to allocate their limited time somewhere, so effort naturally follows the expected value of a finding relative to the work required to discover it. That can leave functionality that is expensive to understand or validate with less researcher attention than a simpler, more immediately testable surface. Billing logic and permission models can fall into that second category because understanding them often requires more application context than testing a single endpoint. That is where [business logic vulnerabilities]() can arise. **There is no completed state.** A point-in-time engagement can give you a defined record of what was tested during a defined period. A conventional bounty, by contrast, does not inherently tell you that a particular flow was systematically checked after your latest change. Silence remains ambiguous: either nobody found a vulnerability, or nobody investigated that path deeply enough to find one. **Duplicate handling shapes what gets reported.** HackerOne's own documentation describes closing a report as a duplicate of an earlier one, with the second reporter shown only high-level metadata unless the programme explicitly adds them to the original. Bounty eligibility for duplicates depends on the programme's rules. In many programmes the first valid report receives the primary reward, but HackerOne's current guidance also recognises cases where a later report adds meaningful attack paths, evidence or remediation insight and should receive additional compensation. Where duplicate rewards are limited, researchers have an economic reason to prioritise areas where they believe they can establish a distinct finding rather than simply reproduce something another researcher has already reported. Your well-trodden critical paths are the ground everyone else is standing on. **Multi-tenant testing needs an arrangement, not a discovery.** Establishing whether organisation A can reach organisation B's records requires multiple identities with known permission levels. Bounty programmes such as HackerOne and Bugcrowd can provision these accounts, but the available authenticated context and test infrastructure varies by programme. ## The models side by side | Model | What it is good at | Where it stops | | --- | --- | --- | | Vulnerability disclosure programme | Giving outsiders a safe, legal route to report what they already found | No bounty incentive, so researcher participation and resulting coverage are less directly driven by financial rewards | | Bug bounty | Externally generated findings, with rewards generally tied to validated vulnerabilities | Coverage is chosen by the researcher, and there is no completed state to point at | | Point-in-time pentest | Depth, a defined scope and testing period, plus a formal report suitable for customer or assurance requirements | Describes the application and environment as assessed during the testing period | | Pentest as a service | Repeatable pentesting delivered through a platform and/or recurring service model, potentially with more frequent testing and workflow integration than a traditional engagement | Human-led PTaaS remains constrained by researcher capacity and scheduling, although newer PTaaS models increasingly combine human testing with automation | | Continuous offensive testing | Repeatable testing that can be integrated into a team's development and deployment cadence, with the ability to retest | Only reasons about what it can read and reach. A rule nobody wrote down stays invisible | None of those rows is a criticism of the others. They describe different purchases. [Continuous pentesting]() sits in the last row, and it is the only one whose value depends on being wired into how you ship. ## The bounty platforms are shipping this too It would be convenient to frame this as crowd against machine. That framing is already out of date. On 26 January 2026 HackerOne announced Agentic Pentest as a Service, which it describes as combining "autonomous agent execution with elite human expertise", with agents covering reconnaissance, setup, exploitation and validation across a large attack surface while human experts provide validation. In the same announcement it takes a clear position on the fully autonomous end of the market, saying such tools promise speed but often deliver "shallow, unverified results that create noise rather than insight". Synack describes a comparable pairing of an agentic engine with its own researcher pool on its blog. That criticism deserves a straight answer rather than a rebuttal, because it is the right question to put to any autonomous platform: what makes a finding trustworthy when no human has checked it? For us, the useful distinction is not the confidence score. It is whether the system attempted the relevant action and observed what happened. A finding that says a pattern matched is a candidate. A finding that says this request, sent as this user, returned another organisation's record, is an observation, and it arrives with the steps to reproduce it. The engineer receiving it does not have to trust the tool. They have to run four lines and watch. Where a suspected weakness cannot be exercised against the running application, it should be reported as exactly that, rather than promoted to the same confidence as one that was. So the useful evaluation question for anything in this category, Borg included, is not how autonomous it is. It is: what did reading the code contribute to the exploit you are showing me, and what did the running system contribute? ## What an automated platform actually takes over The parts of a bounty's output that continuous offensive testing genuinely displaces are the systematic ones. Enumerating every endpoint that resolves a record by an identifier supplied by the caller, then checking which of those carry an ownership check, is patient work rather than creative work. So is re-checking the same authorisation paths after every change. That is where Borg's Mjolnir runs. It reads the repository to decide which paths are worth attacking, executes against the running application, reports findings with reproduction steps, and retests once a fix lands. Gungnir applies a narrower version of that approach to pull requests, reviewing security-sensitive changes before they reach production. Huginn handles continuous asset discovery and monitoring of the external attack surface as it changes. Findings collect in Odin, which is the dashboard the other three report into. What does not transfer is the part that made the bounty worth having. An unfamiliar mind with no stake in your assumptions, chaining two harmless behaviours into one that is not harmless, is not a scheduling problem. The important question is therefore how far each platform has actually solved it, and what evidence it provides for that claim. One further limit worth stating plainly: code-aware testing can only reason from what is represented in the code and the context supplied to the system. A permission set configured only in a cloud console, a third-party service configured through its own dashboard, or an invariant that exists only in someone's head may be outside what repository analysis can tell you. ## What to ask before you switch Five questions that separate the platforms in this category from each other, and that a vendor should be able to answer without reaching for a case study. **What did reading the code contribute to this finding?** Ask it about a specific finding, not in general. The useful answer names something the repository revealed that crawling would not have: an unlisted route, an authorisation helper applied to nine handlers out of eleven, a version of the API still in service. If the answer is that code access improves coverage generally, code access is not doing much. **What happens to a suspected issue that cannot be reproduced?** You want it reported as unproven, or dropped, rather than shipped at the same severity as one that was reproduced against the running system. A platform that presents both with equal confidence has moved the triage work back onto you and called it coverage. **How do you test something that needs two accounts?** Tenant isolation, role escalation and cross-organisation access all need more than one identity at known permission levels. If the platform cannot describe how it establishes that, it is not testing the boundary that does the most damage when it fails. **What happens after we fix it?** A finding is not closed because a ticket moved. Ask whether the fix is retested automatically, and what state the finding lands in if the retest fails. **Where does the output arrive?** A finding in a portal your engineers do not open is not much better than a PDF. Ask which trackers it writes to, what triggers a ticket, and who can change a finding's state from the tracker side. None of these is a trick question, and any vendor you are seriously evaluating should be able to answer all five directly. Vague answers are particularly revealing when they concern evidence, authentication, reproduction and retesting, which are the parts of the workflow that decide whether a finding is actionable at all. ## Choosing between them The question is not which model is better. It is which failure you cannot live with. If the failure you fear is a boundary you rely on breaking quietly, tenant isolation, an authorisation check on a refactored route, or a billing state reachable out of order, then the control that fits is systematic testing of those paths on a cadence you set. If the failure you fear is an attack path your team did not anticipate, external researchers provide a form of attacker diversity that automated testing does not automatically reproduce. That remains a strong reason to keep a bounty alongside automation. If what you need is a formal assessment tied to a defined scope and testing period that you can hand to a customer or auditor, a point-in-time engagement may still be the most appropriate instrument. Many organisations use more than one of these models because they address different assurance needs. A sensible approach is often to cover the paths you can name first, because those are the ones you can test systematically, and use external researchers to challenge the assumptions you cannot fully enumerate. Once findings exist, where they land matters as much as what they say, which is why the [route from finding to ticket and verified fix]() is worth deciding before you pick the model that generates them. ## Conclusion A bug bounty and a continuous offensive platform answer two different questions. A bounty tells you what researchers reported. A continuous offensive platform can give you repeatable evidence about the paths it was configured and able to test at a particular point in time. Teams often start looking for a complementary control when they realise they have been using a bounty to answer a coverage question it was never designed to answer systematically. Borg tests the exploitability layer, auth flows, APIs, access control, tenant isolation and business logic, continuously and with the repository as context, and delivers findings with reproduction steps into the tracker your engineers already use. Book a walkthrough to see what that turns up against your own application. --- ## Full Pentest vs PR Security Review: What Each One Actually Answers URL: https://www.borghq.io/blog/full-pentest-vs-pr-security-review-what-each-one-actually-answers Published: 2026-08-20 Author: Borg A full pentest and a PR security review get compared as if one is a lighter version of the other. They are not. They answer different questions, and a team that buys one expecting the other ends up with a blind spot it cannot see. ## The question each one asks A full pentest asks what an attacker can do to this system as it stands. The scope is the agreed target: the application, APIs, authenticated surfaces, infrastructure, or other assets included in the engagement. Its findings are properties of the system as tested. A PR security review asks something much narrower. Does this change introduce a reachable weakness? The scope is the diff plus whatever the diff touches. Its findings are properties of a change. Every other difference follows from that one. | | Full pentest | PR security review | | --- | --- | --- | | Question it answers | What can an attacker do to this system as it stands? | Does this change introduce a reachable weakness? | | Scope | The agreed application, APIs and infrastructure in scope | The diff, plus the paths the diff touches | | What triggers it | A date, a release or a milestone | A pull request opening | | Time between a change and it being tested | Weeks to months | Minutes to hours | | Cross-service flaws and anything outside the repository | In scope, including parts no recent change has touched | May be missed when the relevant context falls outside the change | | Where the finding lands | A report, read later by whoever owns the service now | A comment on the pull request, read by its author | | What it cannot tell you | Anything about the code you merged after it finished | Whether the system as a whole is exploitable | ## What a pentest sees that a PR review cannot Some flaws do not exist in any single diff. Service A starts trusting a field that service B quietly stopped validating a quarter earlier. Both changes may have been correct in isolation. A narrowly scoped PR review can miss the interaction. Testing the running system across those service boundaries gives you a way to uncover that class of bug. The same goes for weaknesses that exist outside the repository. A staging environment left reachable, an old API version still answering, a subdomain pointing at infrastructure nobody owns any more. No diff introduced those, so no diff review will surface them. And some attack paths need sustained effort. Enumerating identifiers across two tenants for a day, chaining a weak redirect into a token leak into privilege escalation. That is deliberate work against a live system, not a judgement about a changeset. ## What a PR review sees that a pentest cannot Everything you ship after the pentest is outside the scope of that specific assessment. If you deploy daily and assess annually, the version tested by the pentest may have been live for only a small fraction of the year. Every subsequent change creates an opportunity for a regression or a new vulnerability that the original assessment could not have evaluated. This is the gap behind most of [what startups get wrong about application security](). Regressions on fixes, too. A permission check gets restored during remediation, then refactored back out six weeks later by someone tidying a controller. The original finding is marked resolved. The vulnerability is back. Then there is cost. Caught on the diff, the fix can often happen before the code ships. Caught in an assessment later, the same issue may require a ticket, a new release, and a retest. ## Why it matters where the feedback lands A finding on an open pull request reaches the person who wrote the line, while they still remember why they wrote it that way. The same finding in a report months later reaches whoever owns that service now, with no context and a backlog already full. The technical content is identical. The odds of it being fixed properly are not. That is the argument for treating security findings as engineering work rather than a document, and for [pushing them into Linear, Jira or GitHub]() where the rest of the work already lives. ## Neither one is a scanner Automated scanners are useful for finding potential weaknesses at scale. SAST identifies insecure code patterns and data flows, dependency scanners flag known vulnerable components, and DAST probes running applications for common weaknesses. But a potential weakness is not the same as an exploitable one. A missing authorisation check, for example, is different from proving that one user can actually access another user's data. That is where offensive testing adds value. Pentests test the running system from an attacker's perspective, while code-aware PR reviews use the context of a change to focus testing on exploitability before it reaches production. That is [why a scanner can pass a codebase that an actual assessment does not](). ## How they actually fit together Depth and cadence are different axes, and you need both. A deep periodic assessment reasons about architecture, chains attack paths, and looks at the parts of your system no changeset has touched in a year. A change-triggered review keeps the gap between deploying code and testing it down to hours rather than months. That is the split in how Borg is built. Mjolnir runs continuous application pentesting against the running system. Gungnir does code-aware review on changes, running targeted micropentests against the diff and surfacing findings in GitHub. They are separate jobs because they answer separate questions. ## The failure mode to avoid Do not use either one as a reason to skip the other. Shipping PR review and dropping deep assessment leaves you blind to everything cross-service and everything outside the repository. Running the annual assessment and nothing else leaves you blind for eleven months of merges. Both are real gaps. They are just different gaps. The useful question to ask a vendor is not which one they do. It is what happens to your coverage in the weeks between. If you want to see what that looks like against your own application and your own pull requests, book a walkthrough with Borg. --- ## What Is Code-Aware Application Security Testing? URL: https://www.borghq.io/blog/what-is-code-aware-application-security-testing Published: 2026-08-18 Author: Borg Code-aware application security testing is a methodology in which the testing engine uses an application's source code to understand routes, authorisation logic and data flows, then performs targeted security tests against the running system. The repository provides context about what is worth testing; the running application provides the evidence needed to determine whether a suspected weakness is actually exploitable. The problem it addresses is familiar to anyone who has triaged security tooling. Static analysis examines code without directly observing whether a suspected weakness is exploitable in the deployed application. Dynamic scanning tests the running application but has limited visibility into the code and logic behind the interfaces it can reach. Used independently, neither provides the same combination of repository context and direct runtime verification. ## Definition: What Code-Aware Application Security Testing Means Code-aware application security testing is a methodology that uses repository metadata to inform dynamic offensive attacks against a running application. The system analyses source code to discover unlisted routes, authorisation decorators, and complex multi-step flows that a blind scanner would never find. The goal is to move beyond pattern matching and start reasoning about how an application actually handles data and permissions. Most traditional tools fall into two camps. Static Application Security Testing (SAST) analyses code without directly observing how a suspected weakness behaves in the deployed application. Dynamic Application Security Testing (DAST) tests the running application but has limited visibility into the internal code and logic behind the interfaces it can reach. Code-aware testing merges these by using the code to build a comprehensive map of the attack surface. It looks for specific patterns: which endpoints require authentication, which parameters control tenant IDs, and which routes handle sensitive billing logic. Once it has this map, it launches targeted attacks to see if those protections actually hold up. The repository tells the system what to attack, and the running application decides what actually gets reported as a verified finding. This approach is aimed squarely at [business logic vulnerabilities](), the class that needs application context to recognise at all. When an attacker exploits an Insecure Direct Object Reference (IDOR) bug, they are not using a generic exploit string. They are exploiting a flaw in how the application verifies whether a requester is authorised to access or modify a particular object. A code-aware system can identify a controller that appears to lack the required object-level authorisation check and then attempt cross-user or cross-tenant access to determine whether the weakness is exploitable. This is not just scanning for known CVEs. It is an automated search for flaws in the logic your team wrote last Tuesday. ## How It Works: Repository Context Meets Runtime Verification The process begins with repository access. The testing engine clones the code and builds an internal representation of the application structure. Discovering an internal route does not necessarily mean it can be tested dynamically; runtime verification still depends on the testing environment being able to reach it. It builds a map of application entry points visible from the repository, including REST API endpoints, GraphQL operations, and internal webhooks that may be invisible to an external crawler. By reading the code, the system can infer security-relevant properties of each route, including authentication middleware, role checks, access-control helpers, and data-access patterns. It sees which decorators are used for Role-Based Access Control (RBAC) and which controllers handle data persistence. This allows the system to prioritise high-risk areas like password resets, payment processing, and administrative dashboards. Next comes runtime verification. After mapping the routes, the system executes offensive payloads against a live environment (staging or production). If repository context indicates that a route should require authentication or authorisation, and runtime testing demonstrates that the protected action can be performed without the required access, the system can report a verified finding. If a suspected weakness cannot be exercised in the running application, it can be deprioritised rather than presented with the same confidence as a runtime-verified issue. Combining the two views is what lets a team spend its attention on paths that were actually taken. Verification is the key differentiator. Code-aware testing provides that context by linking code patterns to execution results. Borg uses this dual-context approach to find broken tenant isolation. The system identifies potential flaws in tenant isolation and verifies these findings during a dynamic session. If the application returns data from a different organisation, the bug is verified and the remediation steps are clear, because the system provides verified findings with reproduction steps. ## How It Compares to SAST, DAST, and IAST SAST is good at what it does: analysing source code for potential weaknesses such as unsafe data flows, insecure API usage, and hardcoded secrets, cheaply and on every build. What static analysis alone does not provide is direct evidence that a suspected weakness is exploitable in the deployed application, which can leave teams triaging findings that are unreachable, non-exploitable, or irrelevant in the actual runtime environment. Code-aware testing does not replace that scan. It adds the step of attempting to reproduce the suspected weakness against the running application before presenting it with the confidence of a verified finding. DAST has the opposite constraint. A DAST scanner discovers an application by crawling it, so an endpoint with no link in the frontend is one it may never reach. That matters more than it used to, because much of a modern single-page or mobile backend is only addressable as JSON. Reading the repository surfaces routes that crawling would miss. It does not guarantee complete coverage of the API surface, and a route that exists only in generated configuration can still be missed. Interactive Application Security Testing (IAST) is the closest relative. It instruments the application from the inside with an agent, which gives it genuine execution visibility that neither of the other two has. The trade is deployment: an agent has to run in the runtime, which is more work in serverless or heavily distributed setups. Code-aware offensive testing takes a different route to a similar place, using the repository for context and external probes for the attempt, so nothing has to be installed alongside the application. | | What it reads | What it establishes | Main limit | | --- | --- | --- | --- | | SAST | Source code | That a pattern is present | Cannot tell you whether the match is reachable | | DAST | The running application | That an input produced a given response | Only tests what it can find by crawling | | IAST | The running application from inside, via an agent | That a payload reached a sensitive sink | Needs an agent deployed in the runtime | | Code-aware offensive testing | Source code, then the running application | That a suspected weakness can be reproduced against the running application | Blind to behaviour decided outside the repository | ## What Code Awareness Actually Unlocks (and What It Doesn't) Code awareness makes it possible to automate the discovery of complex authorisation bugs. Traditional scanners cannot tell if a specific user should be able to delete a resource, because they do not understand the relationship between the user and the data. A code-aware system sees that the 'DeleteResource' function expects a 'resource_id' and a 'user_id' but only checks if the 'user_id' is logged in. The system then automatically attempts to delete a resource belonging to a different user. This level of logic testing is usually reserved for manual penetration tests, but code-aware systems can perform it continuously. That said, code access still leaves gaps worth being honest about. Code-aware testing is limited by what is defined in the repository. If your application relies on a third-party managed service that is misconfigured in the cloud console rather than in your Infrastructure-as-Code (IaC), the testing engine might miss it. It also cannot find vulnerabilities in closed-source binary blobs or legacy systems where code access is restricted. It is an application-level strategy, not a substitute for full network or infrastructure security. Another limitation is the reliance on accurate code parsing. If your team uses a highly custom, homegrown framework that the tool does not recognise, the initial mapping might be incomplete. Despite these gaps, the increase in signal over traditional DAST is large. By focusing on the code your engineers actually write, you catch the [exploitable bugs]() that scanners miss and that attackers prioritise. ## Code-Aware Testing at the PR Level vs Full Application Testing One of the most powerful applications of code-aware testing is at the Pull Request (PR) level. Instead of waiting for a weekly scan or an annual pentest, the system can analyse every code change as it is proposed. When a developer modifies a sensitive file, such as an auth middleware or a billing controller, the system performs a security review of the changes. This allows security teams to catch broken access control before the code ever reaches production. This is the core of Borg's Gungnir feature, which focuses on the risk introduced by new code changes. Full application testing is about the cumulative risk of the entire system. Even if every PR looks safe in isolation, the interaction between different services can create new vulnerabilities. A change in the identity provider might break an assumption in a downstream microservice. Continuous offensive testing at the application level catches these systemic issues. Combining PR-level reviews and full application testing creates a safety net that traditional security models lack. Most teams rely on a point-in-time pentest that is obsolete the moment the next PR is merged. Code-aware testing means the security posture evolves alongside the codebase. If a new route is added in a PR, it is known about immediately and tested for auth bypasses on the diff rather than whenever the next assessment happens to run. Security becomes a continuous engineering process rather than a biannual hurdle. ## Related Terms Worth Knowing Understanding code-aware testing requires a basic grasp of several adjacent technologies. The first is Insecure Direct Object Reference (IDOR), a type of access control vulnerability where an application provides direct access to objects based on user-supplied input. Code-aware testing is built to find these by identifying ID-based parameters in the code and attempting to manipulate them during runtime. Another term is Attack Surface Management (ASM). While ASM focuses on discovering exposed assets like subdomains and IPs, code-aware testing focuses on the depth of the application surface once those assets are found. Continuous Penetration Testing is the broader category that code-aware testing belongs to. This refers to the practice of performing offensive security assessments on a recurring basis rather than as a single event. You can read more about this in our guide on [what is continuous pentesting](). Finally, there is the concept of Tenant Isolation. In multi-tenant SaaS environments, ensuring that one customer cannot see another customer's data is the highest priority. Code-aware testing checks that isolation by reading how the code separates data at the database and API layers, then attempting to cross the boundary against the running system. Linking these concepts helps security engineers build a more resilient defence. The goal is to move away from generic scanning and toward a model that understands the specific business logic of the application. Whether you are using Mjolnir for full application runs or Gungnir for PR reviews, the underlying principle is the same: use the code to inform the attack, and use the attack to verify the code. ## Conclusion Code-aware testing addresses a specific gap. Repository analysis alone cannot establish how a suspected weakness behaves in the deployed system, while black-box runtime testing has limited visibility into code paths and application logic it cannot discover. Using repository context to form targeted attack hypotheses and runtime testing to validate them combines the strengths of both approaches. That combination is how Borg is built. Gungnir reviews pull requests touching auth, access control, APIs and billing. Mjolnir runs against the full application. Findings arrive with reproduction steps rather than as a list of matches. Book a walkthrough with Borg to see what that turns up on your own codebase. --- ## How to Test Business Logic Vulnerabilities in a SaaS Application URL: https://www.borghq.io/blog/how-to-test-business-logic-vulnerabilities-in-a-saas-application Published: 2026-08-11 Author: Borg Automated security tools are good at finding classes of vulnerabilities they know how to recognise: vulnerable dependencies, injection patterns, exposed services and malformed input. They struggle when the vulnerability is a violation of a rule specific to your application. A generic scanner does not inherently know that a user should not be able to downgrade from the Enterprise plan while keeping Enterprise-only features. The API response can look completely valid while the business rule has been broken. That gap is where some of the most consequential SaaS vulnerabilities live, and it is one of the hardest areas to test systematically. To effectively test business logic vulnerabilities, you must move beyond generic input validation checks. You need a methodology that maps the specific invariants of your application and systematically attempts to break them. This guide provides a step-by-step offensive testing framework designed for modern SaaS environments. It is designed for senior security engineers who need to verify auth flows, billing logic, and tenant isolation in modern B2B SaaS environments. Everything below assumes you are testing an application your organisation owns or is authorised to test. Before you begin, set up a dedicated staging environment and at least two test accounts at different permission levels, in two different organisations, so object-level authorisation can be checked properly. ## Step 1: Map Your Application's Business Rules and Invariants You cannot test business logic vulnerabilities if you do not understand the underlying state machine. Every SaaS application has a set of unwritten rules that govern how data moves and who can trigger specific actions. This mapping process represents a critical business logic data validation phase. Your first task is to translate these rules into a map of invariants: things that should never be possible regardless of the input. Start by documenting the primary workflows. In a B2B SaaS platform, these usually include seat management, API key generation, and data export. For each workflow, identify the requirements. If a rule says a user needs the Admin role to export data, that is an invariant. If the rule says a user can only invite five teammates on the Pro plan, that is another. Write these down. Do not rely on your product documentation because it often describes how the app is supposed to work, not how the API actually behaves under pressure. Use an intercepting proxy to observe how the application handles these rules at the network level. Look for the specific parameters that enforce these logic gates. If a seat limit is enforced by a client-side check, the API might still accept the request. This is why mapping is the most technical part of the process. You are looking for the discrepancy between what the UI shows and what the backend allows. Focus on the transitions between states. A transition from an Unpaid state to a Paid state is a high-value target. If you can move a resource into a Paid state without a corresponding success signal from a payment processor, you have found a business logic flaw. Most teams fail here because they treat the app as a collection of endpoints rather than a sequenced state machine. Mapping the sequence is the only way to find where the logic breaks. ## Step 2: Identify the High-Risk Surfaces (Auth, Billing, Tenant Isolation, Approvals) Not all logic is created equal. A bug in the profile picture upload logic is annoying. A bug in the tenant isolation layer is catastrophic. You must prioritise your testing efforts on the four surfaces that carry the most risk for B2B SaaS companies: authentication flows, billing and subscription management, multi-tenant data isolation, and administrative approval workflows. Authentication flows are more than just login screens. They include password resets, multi-factor authentication (MFA) enrolment, and Single Sign-On (SSO) handshakes. A common logic flaw involves the MFA skip: an attacker triggers a password reset, changes the password, and the system automatically logs them in without asking for the MFA token that was previously active. This bypasses the security intent of the MFA feature entirely. Billing logic is the most overlooked area in traditional pentesting. Scanners rarely have the context to test if a user can manipulate the quantity of items in a cart to a negative number or if they can apply a 100% discount code multiple times. In a SaaS context, look for the credit system. If your app uses a credit-based model for AI tokens or API calls, test whether the balance check happens before or after the resource-intensive action is performed. If it happens after, an attacker can spin up thousands of concurrent requests to drain resources before the balance hits zero. Tenant isolation is the core requirement of SaaS. You must verify that a request intended for Organisation A cannot access resources belonging to Organisation B. This is often implemented via a tenant ID in the header or the URL. Test this by capturing a valid request from User A, then replacing the session token with User B's token while keeping User A's tenant ID. If the request succeeds, your isolation logic is broken. This is a classic Broken Object Level Authorization (BOLA) issue, which remains a primary concern for modern API security. ## Step 3: Build Abuse Cases From Your Invariants Once you have mapped your invariants and identified high-risk surfaces, you must build specific abuse cases. An abuse case is a structured plan to break a business rule. It differs from a standard test case because it ignores the intended user path entirely. If the intended path is A to B to C, the abuse case asks what happens if you go from A directly to C, or if you repeat B ten thousand times in a second. Create a matrix of your invariants. For an invariant like "A user cannot view another user's private data", the abuse cases should include: 1. Changing the ID in the URL. 2. Changing the ID in the JSON body. 3. Accessing the resource via a public API endpoint that lacks authorisation checks. 4. Accessing the resource via a legacy version of the API (v1 instead of v2). This systematic approach ensures you do not just check the obvious paths. If a SaaS platform offers a 14-day trial, an invariant might be: “An account cannot retain paid functionality after its trial entitlement expires.” Test the different paths that affect that entitlement: cancelling and restarting the trial, creating additional workspaces, accepting invitations into another tenant, changing plans near expiration, replaying trial-creation requests, and sending requests directly to paid-only endpoints after the UI reports the trial as expired. The interesting bugs usually appear where two legitimate workflows interact in a way nobody intended. Use your understanding of the tech stack to inform these cases. If approvals are processed asynchronously, test whether the backend still validates the actor, resource and expected state when the approval is eventually processed. Security checks performed only when the workflow is initiated can become ineffective if a queued or delayed action is later executed against state that has changed. ## Step 4: Test Workflow State and Request Sequence Modern SaaS applications are essentially large state machines. A user moves from a draft state to a pending state to a published state. Vulnerabilities occur when these transitions are not strictly enforced. A critical area of concern involves the potential circumvention of established workflows. You must test if steps can be skipped, reordered, or replayed to achieve an unauthorised outcome. The most common workflow attack is the forced browsing or step-skipping attack. In a multi-step registration flow, Step 1 might be email verification, Step 2 might be payment, and Step 3 might be account creation. If an attacker can navigate directly to the Step 3 URL or POST to the Step 3 endpoint without a valid session token from Step 2, the billing logic is bypassed. You test this by completing the workflow legitimately, capturing the final request, and then attempting to replay that request in a fresh, unauthenticated session. Another critical area is the replay attack on one-time actions. If an application allows a user to claim a reward or use a coupon, that action should be idempotent or strictly limited. Test this by sending multiple identical requests in a very short window (a race condition) to see if the system processes the action more than once before the database updates the state. If you can use a single-use coupon code five times because the validation and the consumption are not an atomic operation, you have a logic flaw. Parameter pollution and integrity checks also fall under sequence testing. In some cases, a parameter from an earlier step is cached and used in a later step. If you can inject a different parameter in a later request that overrides the initial validated value, you can break the logic. In a fintech app, if the interest rate is calculated in Step 1 and passed as a hidden field to Step 2, you must attempt to modify that field before the final submission. If the backend does not re-calculate or verify the signature of that data, the logic is vulnerable. ## Step 5: Test Multi-Tenant Isolation and Object-Level Authorisation In a B2B SaaS environment, the most critical business logic is the code that ensures Tenant A cannot see Tenant B's data. This is not just about authentication. It is about authorisation at the object level. Every single API request that takes an ID as a parameter is a potential failure point. You must test these by attempting to access, modify, or delete objects belonging to a different organisation using a valid session from your own. Start with the most obvious IDs: those in the URL. If the endpoint is `/api/v1/org/123/projects/456`, change `123` to the ID of another tenant. Then try changing `456` to the ID of a project belonging to that tenant. If the API returns the data, the authorisation check is likely only happening at the Org level and not the Project level. This is a common shortcut developers take to save on database queries, but it creates a massive security hole. Move beyond the URL and check the request body and headers. Many modern APIs use custom headers like `X-Org-ID` or `X-Tenant-ID` to route requests. If the backend trusts this header without verifying it against the user's session token, an attacker can simply swap the header value to impersonate another organisation. This is the class of change Borg's Gungnir is pointed at: it reviews pull requests touching auth, access control and isolation, so a header the backend trusts too readily gets raised on the diff rather than after release. Finally, test for vertical privilege escalation within a tenant. If a standard user can access the `/api/v1/admin/settings` endpoint by simply knowing the URL, the business logic governing roles is failing. A common mistake is to hide the Admin button in the UI while leaving the API endpoint completely unprotected. You must verify that every administrative action requires a specific permission check on the backend. A missing link in the UI does not mean the endpoint is secure. Offensive testing requires you to act as if the UI does not exist. ## Why Scanners Miss Business Logic Bugs: and What to Use Instead Traditional security tools are designed to find known patterns. A logic bug is, by definition, a unique flaw in the way your specific application was built. Static analysis (SAST) tools see the code but lack the execution context to know if a logic gate is correct. Dynamic analysis (DAST) tools see the execution but lack the business context to know if a 200 OK response is actually a security failure. [Your scanner didn't find it]() because it wasn't looking for the violation of a business rule. | Approach | Good at | Where it stops | | --- | --- | --- | | SAST | Known-bad patterns in source, cheap enough to run on every build | Determining whether application-specific behaviour violates the intended business rules | | DAST | Crawling and fuzzing a running application | Workflows that require product-specific context, complex state or relationships between multiple users and tenants | | Manual testing against invariants | Reasoning about what should be impossible | Expensive to repeat continuously and constrained by the time and scope of each engagement | | Code-informed offensive testing | Using the repository to decide which paths are worth attacking, then attempting them | Still depends on the invariants being right. Nothing finds a rule nobody wrote down | Instead of relying on annual PDFs from a point-in-time pentest, engineering teams need to integrate offensive testing into their daily workflow. A vulnerability in your auth flow is a high-priority bug that should be treated like a breaking production error. Borg reports findings with reproduction steps attached, so a developer sees the path that was actually taken rather than a pattern that matched. Proving the path before reporting it is what changes the shape of the queue. Shifting to a continuous offensive model closes the gap between code changes and security verification. Findings then collect in [Odin](), where they are tracked and retested once a fix lands. ## Conclusion Business logic vulnerabilities sit in an awkward place. They are not pattern matches, so signature-based tooling does not see them, and they are not exotic, so they turn up in ordinary features. A broken tenant boundary or a bypass in a billing workflow lives in the space between what your features do and what you assumed they would do. Testing them requires a methodical approach that prioritises invariants over generic inputs. A point-in-time pentest can tell you where the application breaks today. It cannot continuously verify every auth change, billing feature or new API route you ship afterwards. If your application changes every week, some part of your security testing should move at the same speed. Book a walkthrough with Borg to see how continuous offensive testing works against your own auth flows, APIs and tenant boundaries, with validated findings delivered into the same workflow your engineers already use. --- ## What Is Continuous Pentesting? A Definition for Engineering Teams URL: https://www.borghq.io/blog/what-is-continuous-pentesting-a-definition-for-engineering-teams Published: 2026-08-06 Author: Borg Continuous pentesting is an offensive security practice that identifies exploitable vulnerabilities in applications and infrastructure by running security tests constantly rather than once a year. It integrates into the development lifecycle to catch auth, API, and business logic flaws as code changes. Most engineering teams treat security like a graduation ceremony. You build for six months, you ship, and then you pay a consultant to find bugs in a system that has already changed ten times since the engagement started. This point-in-time approach creates a dangerous gap. Between annual audits, every pull request that touches your billing logic or authentication flow is a blind spot. Static analysis tools (SAST) often miss these issues because they cannot reason about how an attacker would actually chain vulnerabilities together. Continuous offensive security solves this by making the attacker perspective a permanent part of your CI/CD pipeline. Instead of a static PDF delivered once a year, you get a stream of verified findings. Your security posture keeps pace with your deployment frequency. This is not just another noisy scanner. It is a systematic way to verify that your most sensitive logic remains secure every time you push code. ## What continuous pentesting means (the short answer) Continuous pentesting is the process of performing offensive security assessments on a recurring, automated, or trigger-based schedule. Traditional penetration testing relies on a consultant spending two weeks manually poking at your app once a year. Continuous testing runs in the background of your daily operations instead. It specifically targets the gaps left by standard security scanners. Automated scanners are good at finding outdated libraries or missing headers, but they are notoriously bad at identifying broken access control or multi-tenant isolation flaws. [Borg](https://www.borghq.io) focuses on these complex, exploitable bugs that require an understanding of how the application actually works. This practice involves three main components: asset discovery, vulnerability identification, and remediation verification. You first need to know what you are running, then you test those assets for flaws, and finally you prove the fix worked. Because the testing is ongoing, the feedback loop is significantly shorter than with traditional point-in-time testing. When a senior engineer modifies a permission check in a high-traffic API, continuous pentesting identifies if that change accidentally introduced an access control vulnerability before the next point-in-time review. Security-minded CTOs are moving toward this model because it aligns with how modern software is built. If you ship code daily, your security testing must happen daily. A point-in-time test is an autopsy of a version of your software that no longer exists in production. Continuous offensive security provides a living view of your attack surface. It turns security from a periodic hurdle into a constant stream of verified data. ## What changes when testing is tied to change, not the calendar The primary shift is moving from a calendar-based schedule to a change-based trigger. In a traditional setup, you schedule a pentest because it is October and your SOC 2 audit is due. In a continuous setup, you trigger a pentest because you just merged a pull request that modifies your authentication flow. This is the difference between reactive and proactive security. When testing is tied to code changes, you catch bugs at the most cost-effective moment: before they are exploited. Borg uses a feature called Gungnir to provide PR-level security reviews for risky changes. If a developer touches billing, permissions, or API endpoints, the platform performs an offensive review immediately. This prevents the security team from becoming a bottleneck. Instead of stopping all work for a two-week manual audit, the team receives verified findings directly in tools like Linear or Jira. Security issues get treated as engineering bugs rather than compliance checkboxes. [What startups get wrong about application security](https://www.borghq.io/blog/what-startups-get-wrong-about-application-security) is often the belief that a single annual test is sufficient for a fast-moving codebase. This shift also changes how developers interact with security. When a pentest report arrives six months after a feature was shipped, the original context is lost. The engineer who wrote the code might have left the company or moved to a different squad. Continuous testing provides immediate feedback while the context is still fresh in the developer's mind. It creates a culture where security is part of the definition of done, not an after-the-fact correction. You are no longer managing a massive backlog of ancient vulnerabilities. You are managing a real-time stream of exploitable risks. ## What continuous pentesting does not mean Continuous pentesting is not a replacement for SAST (Static Application Security Testing) or DAST (Dynamic Application Security Testing) scanners. It is an evolution of them. SAST looks at code patterns. DAST looks at a running app for common web flaws like SQL injection. Continuous pentesting goes further by reasoning about the application's business logic. It checks if User A can see User B's data, or if an API call can bypass the billing gate. A standard scanner will almost always miss these issues because it does not understand your business rules. It also does not mean you have a human being manually testing your app 24 hours a day. That is physically impossible and prohibitively expensive. Platforms like Borg use autonomous engines to perform the testing instead. These engines are designed to deliver ongoing offensive testing against live applications. The aim is to report what was actually demonstrated rather than what merely matched a pattern, so a finding arrives with reproduction steps attached. No tool removes triage entirely, but proving a path before reporting it changes what lands in the queue. Finally, continuous pentesting does not mean your app is 100% secure at all times. No tool can promise that. It means your attack surface is being monitored and tested more frequently than it would be otherwise. It reduces the time to discovery. An attacker only needs one window of opportunity. By closing the gap between a code change and a security test, you reduce that window considerably. It is about risk reduction and speed, not a magical shield that eliminates all threats. ## Where a periodic assessment still fits The traditional deep-dive assessment still has a place in a mature security program. High-stakes events, such as a major architectural migration or an M&A (mergers and acquisitions) due diligence process, often require a bespoke manual review. A human pentester can spend a week thinking about a highly specific, novel attack vector that an automated system might not yet be programmed to detect. These deep dives are best used for architectural review rather than catching common regression bugs. Compliance is another area where periodic assessments remain relevant. Frameworks like SOC 2 or ISO 27001 typically involve assessments by external auditors for attestation or certification to satisfy specific control requirements. A continuous platform makes these audits considerably easier. When the auditor asks what testing happens between engagements, a continuous record is easier to evidence than a single PDF from twelve months ago. Whether that satisfies a particular control is a question for your auditor rather than your vendor, but you can at least show a history of [findings that were raised and retested](https://www.borghq.io/blog/your-vulnerabilities-exist-right-now-odin-finds-them-first) instead of one snapshot. Think of continuous pentesting as your daily workout and the periodic assessment as your annual physical. You need the daily work to stay in shape and catch issues early, but the annual physical is a useful benchmark to confirm nothing has been missed. The mistake most companies make is doing the physical but skipping the daily exercise. Combining both gives you a defensive layer that covers the common, high-frequency risks and the rare, deep architectural flaws. ## How a team would actually adopt it Adopting continuous pentesting does not require a complete overhaul of your development process. The most effective way to start is by integrating with the tools your team already uses. Connect your version control system and project management software first. Borg, for instance, [pushes findings directly to Linear, Jira and GitHub](https://www.borghq.io/blog/how-borg-pushes-findings-to-linear-jira-github), so security bugs live where the work happens. This avoids the PDF problem where security reports go to die in a shared drive. The next step is to define what constitutes a risky change. You do not need a full pentest for a CSS change or a documentation update. Trigger offensive reviews for changes to auth flows, sensitive data access, and API logic. Once the platform is integrated, it begins discovering your assets to map your attack surface. This visibility is often eye-opening for CTOs who may not realise how many staging environments or old API versions are currently exposed to the internet. Finally, build a loop for remediation retesting. When a developer marks a security finding as fixed, the system should automatically retest that specific vulnerability to verify the fix. This prevents regressions and confirms the bug is actually dead. The goal is to move from manual spreadsheets and long email threads to an automated workflow where security is just another part of the CI/CD pipeline. Start small with a scoped pentest or a PR review pilot, then expand coverage as the team becomes comfortable with the workflow. ## Conclusion The annual pentest is a relic of a slower era of software development. In a world where AI companies and fintechs ship multiple times a day, relying on a point-in-time assessment is a liability. Continuous pentesting fills the gap between your automated scanners and your yearly audits by focusing on the exploitable logic flaws that actually lead to breaches. It provides verified findings, cuts the noise of traditional scanners, and integrates directly into your engineering workflow. Borg was built for teams that cannot afford to wait months for security feedback. It provides the offensive security you need to protect your auth flows, APIs, and business logic on every single PR. If you are ready to see what is actually happening in your app between audits, book a walkthrough at borghq.io. See what Borg finds in your app and stop guessing about your security posture. --- ## XBOW Alternatives: Comparing Autonomous AI Pentesting Platforms URL: https://www.borghq.io/blog/xbow-alternatives-comparing-autonomous-ai-pentesting-platforms Published: 2026-08-05 Author: Borg XBOW was one of the earliest companies to demonstrate autonomous penetration testing at scale, and it earned a prominent position in the category. It was the first autonomous pentester to reach the top of the HackerOne US leaderboard, and it reports discovering a critical vulnerability in Microsoft software without human assistance. It is also a very specific type of product. XBOW performs autonomous testing against running applications using the targets and context you provide, such as credentials, API specifications, documentation, other environment details, and code artifacts you upload. For many teams, that's exactly what's needed. Others evaluating XBOW are looking for something slightly different: source-code awareness during testing, security feedback before code is merged, continuous external attack surface discovery, or a workflow that follows findings all the way through remediation and retesting. This page compares Borg, Hacktron, MindFort, RunSybil, Escape and Aikido from that perspective. Every capability below comes from each vendor's own public product, pricing or documentation pages, checked on **3 August 2026**. Where a vendor makes no public claim about a capability, this page says so instead of guessing. Self-reported benchmarks and false-positive figures are labelled as self-reported rather than presented as independent comparisons. ## What XBOW does well XBOW approaches penetration testing as an autonomous reasoning problem. According to its public documentation, the platform builds an understanding of the application's attack surface from the target and the context customers provide, then autonomously prioritises and executes testing across that surface. The feature worth paying attention to is validation. XBOW emphasises validating findings before reporting them. Rather than simply identifying a potentially vulnerable endpoint, it attempts to confirm exploitability using reproducible evidence. For example, its documentation describes validating cross-site scripting using a browser, generating reproducible exploit material, and supporting customer-specific canaries where appropriate. For teams that have experienced AI tools confidently reporting vulnerabilities that don't actually exist, this focus on validation is one of XBOW's strongest differentiators. Cadence is not really the differentiator either. XBOW states that it continuously tests applications as they change, with Lightspeed engagements returning initial findings within hours or days and full reports within five business days. It also states that its reports satisfy penetration testing requirements for frameworks including SOC 2 and ISO 27001. ## Where the platforms differ The biggest differences between today's AI pentesting platforms are architectural rather than qualitative. Some products primarily test deployed applications. Others combine testing of deployed applications with repository-aware analysis, pull-request review and developer workflows. Those choices affect **when** developers receive security feedback as much as **what** gets tested. XBOW's public documentation focuses on testing deployed applications, using the application plus whatever context you supply, including code artifacts you upload, and it recommends triggering tests around high-risk code changes. By comparison, Borg, Hacktron and MindFort describe a standing repository connection and pull-request workflows, so the feedback arrives on the diff rather than after the change is deployed. Likewise, pricing models reflect different purchasing models. XBOW publishes pricing from **$4,000 per pentest**, while several competitors sell on a per-developer or platform subscription basis. Neither approach is inherently better. It depends whether you are buying penetration tests or embedding offensive security into everyday software development. ## The dimensions that actually separate these platforms Nine things distinguish these products once you get past the word *autonomous*. - Primary scope - Source code context - Pull request review - Proof of exploitation - External attack surface discovery - Continuous or on-demand execution - Workflow integrations - Retest after fix - Published pricing ## XBOW compared with six alternatives *Every cell reflects what each vendor publicly describes as of 3 August 2026, with XBOW's row re-checked on 6 August 2026. "Not stated publicly" means we could not find a public claim either way, not that the capability is absent.* | Dimension | XBOW | Borg | Hacktron | MindFort | RunSybil | Escape | Aikido | | --- | --- | --- | --- | --- | --- | --- | --- | | Primary scope | Web apps and APIs | Apps, APIs, external attack surface | Code and applications | Apps, APIs, infrastructure | Code, APIs, cloud, infrastructure | APIs, web apps, exposure | Broad AppSec platform | | Source code context | No persistent repository connection described; accepts code artifacts such as uploaded route files as assessment input | Yes, reads the repo to map routes, auth flows, middleware | Yes, indexes the codebase, taint tracing, call graph | Yes, static and dynamic code review | Implied by PR feedback; no repo connection detail published | Remediation suggestions only, no static analysis claimed | Yes, SAST and code audit | | Pull request review | Not stated publicly | Yes, every PR, with a GitHub check that can block merges | Yes, every pull and merge request | Yes, posts a review summary comment | States feedback on every pull request | Not stated publicly | Yes, AutoFix opens reviewable PRs | | Proof of exploitation | Separate validator agents, reproducible exploit scripts, optional customer canaries | Working proof of concept plus a suggested code fix per finding | States it does not alert without demonstrated impact | States findings are proven with a working exploit | Pre-validated findings; positions itself as the validation phase of CTEM | Screenshots, execution logs, attack path validation | States autonomous pentesting validates exploitability | | Attack surface discovery | Mapped within a test | Yes, continuous, passive plus cloud-integrated | Not stated publicly as a standalone product | States it maps the reachable attack surface | Yes, a dedicated discovery agent | Yes, a dedicated ASM product | Cloud posture and attack paths | | Continuous or on demand | Continuous, retested as the app changes | PR review every PR; full pentest manual or scheduled | PR review continuous; whitebox pentest on demand | Continuous on a schedule, tunable depth | Continuous, evaluates what changed | Continuous, scans on every push | Continuous, plus on demand pentests | | Workflow integrations | Public REST API with webhooks, plus Microsoft Security Copilot and Sentinel; its own guidance names Jira, GitHub, GitLab and ServiceNow as handoff targets | GitHub, Jira, Linear, Slack | GitHub, GitLab, Bitbucket, Jira, Linear, Slack, MCP and REST API | Slack, Jira, git repositories | Not named publicly | Wiz, Cursor, Claude Code, API, CLI, MCP | GitHub, GitLab, Bitbucket, Jira, Azure Pipelines, Vanta, Drata, Teams | | Retest after fix | Continuous retesting as the app changes | Explicit lifecycle: reported, mitigating, ready for retest, fixed and retested | Not stated publicly | States the validated fix is retested | Not stated publicly | Regression testing, reproduces ingested findings | States it retests the fix | | Published pricing | From $4,000 per test | Not published, demo only | $40 per developer per month, 14 day free trial | Not published | Not published | Not published | Free tier, no card required | One of the clearest distinctions in the market is whether a platform operates primarily from the running application or whether it also incorporates the source repository into testing. XBOW and Escape focus primarily on testing deployed applications. Borg, Hacktron, MindFort and Aikido publicly describe repository-aware capabilities alongside testing running software. Neither approach is universally better. They solve different problems. Black-box testing reflects what an attacker can discover from the outside. Repository-aware testing can reason about routes, authorisation logic, middleware and code paths that may never appear in a crawler or sitemap. Many engineering teams ultimately choose to combine both perspectives. ## The six alternatives in detail ### Borg A continuous offensive security platform designed around the software development lifecycle. Mjolnir reads the repository to understand routes, authorisation flows, middleware and business logic before running targeted exploitation against the deployed application. Findings include a working proof of concept together with suggested remediation. Gungnir reviews every pull request, not as a traditional SAST scanner but by analysing the actual code changes, and can publish inline comments or enforce GitHub checks before merge. Huginn continuously discovers external attack surface, including cloud-connected assets developers may not realise are publicly exposed. Everything flows into Odin, which tracks findings from discovery through remediation and verified retesting. Customers include Aave, Gnosis Pay, Dfns, Privy, Biconomy and Squads. DFNS has also published its own write-up describing a month-long black-box engagement using Borg. **Best for:** engineering teams wanting repository-aware testing, autonomous exploitation, attack surface discovery and remediation workflows in one platform. **What it isn't:** a posture management or compliance platform. ### Hacktron Hacktron is another repository-aware autonomous security platform with a strong emphasis on developer workflows. It indexes the codebase, performs taint analysis, builds call graphs and reviews every pull or merge request. Its public positioning around false positives is straightforward: if it cannot demonstrate meaningful impact, it does not generate an alert. White-box pentests run on demand, while human triage continuously improves its threat models. Hacktron is also among the most transparent vendors on pricing. **Best for:** teams primarily concerned with code entering production today. **What it isn't:** its public documentation does not describe continuous external attack surface discovery. ### MindFort MindFort positions itself as a replacement for traditional point-in-time penetration testing across applications, APIs and infrastructure. Its platform combines testing deployed applications with repository-aware review and states that findings include working exploits together with ready-to-merge fixes that are subsequently retested. MindFort publishes self-reported benchmark results and false-positive figures. Since those measurements are vendor-produced rather than independently benchmarked, they should be interpreted as product claims rather than industry comparisons. **Best for:** teams wanting autonomous remediation alongside autonomous testing. **What it isn't:** transparent on pricing. ### RunSybil RunSybil focuses on continuous offensive testing across code, APIs, cloud infrastructure and attack-path validation. Its platform combines asset discovery with autonomous attack execution and positions itself as validating which findings from other security tools are actually exploitable. It also promises pull-request feedback, although its public documentation provides relatively little detail about repository integration compared with some competitors. **Best for:** organisations trying to prioritise genuine risk among large volumes of scanner findings. ### Escape Escape focuses primarily on API security. Its platform combines attack surface discovery, business-logic-aware DAST and autonomous penetration testing aimed at APIs and modern web applications. Findings include screenshots, execution logs and attack-path validation. Escape also integrates particularly well with API-first development environments. **Best for:** API-first organisations or teams already invested in Wiz. **What it isn't:** its public documentation focuses on runtime testing rather than repository-aware analysis. ### Aikido Aikido approaches the problem from the opposite direction. Rather than being primarily an offensive security platform, it provides a broad application security suite including SAST, dependency scanning, secrets detection, container security, cloud posture management and autonomous pentesting. Its offensive testing capabilities include exploit validation, automated remediation and retesting, alongside AutoFix and AutoTriage features. **Best for:** organisations looking to consolidate multiple AppSec tools into a single platform. **What it isn't:** a platform focused exclusively on offensive security. ## Matching the platform to your situation If you're primarily looking for autonomous penetration testing against deployed applications, XBOW remains one of the strongest and most established products in the category. If your priority is securing software **before deployment**, repository-aware platforms narrow the field considerably. Borg, Hacktron, MindFort and Aikido all publicly describe pull-request or repository-integrated workflows. If APIs are your primary concern, Escape deserves close consideration. If your biggest challenge is separating real risk from scanner noise, RunSybil's validation-first positioning is aimed directly at that problem. If you want offensive security integrated throughout the engineering lifecycle, from repository to deployment to attack surface discovery to remediation and verified retesting, Borg combines those capabilities within a single workflow. ## The short version XBOW helped define the autonomous pentesting category and remains one of the most recognisable products in the space. The decision between XBOW and newer platforms is usually less about which AI is "better" and more about **where autonomous security fits into your engineering workflow**. If your priority is autonomous testing of deployed applications, XBOW is a compelling choice. If you also want repository-aware analysis, pull-request review, continuous attack surface discovery and integrated remediation workflows, platforms like Borg take a broader software development lifecycle approach. Ultimately, the right platform depends less on marketing claims about AI models and more on where your team wants security feedback to appear: after deployment, before merge, or continuously across both. --- ## Why we dropped "Security" from our name URL: https://www.borghq.io/blog/why-we-dropped-security-from-our-name Published: 2026-06-09 Author: Hans If you found this post through a redirect from borgsecurity.io, borgresearch.io, or watchdog.app, this is the right place. All of those are now Borg, and this is the story of how we got here. ## It started as a side project A little over two years ago, a few of us were doing bug bounties in web3 projects on the side. We'd find vulnerabilities, submit them through the designated channels, and collect the rewards. What surprised us wasn't just how well it paid, but how responsive and collaborative these teams were. For most of us, this was something we genuinely looked forward to doing after clocking out of our regular jobs. Then something unexpected happened. A major web3 gaming platform, one of the biggest by daily active users, had been pentested multiple times on a scheduled basis and had maintained a public bug bounty program for years. We found things others had missed. They asked if we'd be available for something more long-term. That question changed the trajectory for all of us. If a project of that scale, with that level of existing security coverage, still had gaps we could find, maybe this could be more than a hobby. ![stue_alex_2024.jpeg](https://pub-57d7227ec8204f33bc07c9792aff21f4.r2.dev/outreach/114a53be33b416c6a07eb79fa864f172e86e4471ffd3530e5df417c9d08e60ee.jpg) ## The living room era Summer 2024. Five of us, me, Alexander, Theodor, Jonas and Herman, were working, living and eating out of Alexander's living room. Most of us had been working together on smaller and bigger projects through high school and the years that followed, but we all knew this was something different. Something more serious. We had incorporated Borg Research as the parent entity, and under it we ran Borg Security: the team that jumped into your webapp with no prior knowledge of your codebase and found the vulnerabilities that earlier pentesting teams had missed. That reputation is what put us on the map. ## The thesis Borg Security was founded to secure the gap between web2 and web3. A huge number of web3 platforms aren't fully decentralized. They operate with smart contracts, tokens and wallets, but they still depend on web2 infrastructure underneath. That opens the door to classic web vulnerabilities: XSS, IDOR, broken access controls, the full list. The difference is what happens when those vulnerabilities get exploited. An XSS on a traditional web app is bad. An XSS on a platform where users have connected crypto wallets can mean stolen funds with no way to recover them. The decentralized layer that makes web3 powerful also makes it unforgiving. We called these "web2.5" platforms, and securing them required people who understood both worlds. That thesis held up. The work kept coming. ## Getting serious In August 2024, we got our own office. We were actually renting in the same building where me, Theodor and Alexander had rented a space back in high school, but this time we got a proper setup. At the time it felt enormous for how few of us there were. ![borg_adressa_artikkel.png](https://pub-57d7227ec8204f33bc07c9792aff21f4.r2.dev/outreach/3a2e7c3220277a6d62fbb533213998fe969c01cccdf1fa26c488490eaf1ab87a.png) We painted the walls, put up Borg signs (the very same logo you see on borghq.io today; it's actually older than most people think), and things accelerated. New people joined. More work came in. We were delivering what's now known as Borg's Embedded Research Service, long-term offensive security partnerships where we operate as an extension of the client's team. Things moved so fast that by May 2025, barely nine months later, we had to move offices again. We got lucky and stayed in the same building, just closer to the river, with a much bigger space all to ourselves. We're sitting in that office right now, and we're already starting to outgrow it. ![IMG_4958.jpeg](https://pub-57d7227ec8204f33bc07c9792aff21f4.r2.dev/outreach/415594aa35c2ec8976067c37c8ecfd3fa57b94a20b696f1067d33f513c962504.jpg) ## Watchdog and the path to Odin While Borg Security was growing, we had another project running under Borg Research: Watchdog. Watchdog came from a pattern we kept hearing from clients, both in Norway and internationally. Teams didn't really know what assets they had exposed. Subdomains, related domains, IP ranges, open ports. The basics that every assessment should start with, but that most teams had no continuous visibility into. We decided to build our own framework from the ground up to identify these things with precision, and it worked. But we quickly realized the real opportunity was in combining what Watchdog could do with the offensive expertise we'd built through manual work. Automated discovery feeding into real attack capabilities, not just a list of assets but an understanding of what's actually exploitable. ## January 2, 2026 I think it was literally the first day back in the office after New Year's. We made the call: everything is now Borg. Not Borg Security. Not Borg Research. Not Watchdog. Just Borg. The reasoning was simple. We'd outgrown every name we'd given ourselves. "Borg Security" described a pentest team. We were building a platform. "Watchdog" described a monitoring tool. We were building something that discovers, attacks, reports, and retests. None of the old names fit what we were becoming. Shortly after, the Odin concept came together. Odin is the platform that brings everything under one roof: the discovery capabilities from Watchdog, the offensive depth from Borg Security, and new layers of automation, integration and continuous testing on top. Four verticals: Discover, Attack, Report & Confirm, Mitigate & Retest. One platform. ## What Borg is now We still secure the gap between web2 and web3. That hasn't changed. But we've grown well beyond it. Today we work with companies in fintech, AI, infrastructure and high-traffic consumer apps. The common thread isn't the industry. It's teams that ship fast, operate at scale, and need security that keeps pace with their development. The entire idea behind Borg is that security should be automated and embedded into developer workflows, not something that lives in reports in a drawer, managed once a year by someone three levels removed from the codebase. With the pace code ships today, annual pentests are a losing game. You can't catch up. Odin and everything we're building at Borg exists to close that gap. And there's a lot more coming, both in depth and in breadth. ## If you're here from an old link borgsecurity.io, borgresearch.io, and watchdog.app all redirect here. The work we did under those names is the foundation of what Borg is today. If you worked with us under any of those names, you already know what we're about. Now you know where to find us. Welcome to Borg. We're just getting started. ### Frequently asked questions **Why did Borg Security change its name to Borg?** On 2 January 2026 everything became simply Borg — not Borg Security, not Borg Research, not Watchdog. The old names had been outgrown: "Borg Security" described a pentest team and "Watchdog" described a monitoring tool, while the company was building a platform that discovers, attacks, reports, and retests. **Do borgsecurity.io, borgresearch.io, and watchdog.app still work?** Yes. All three redirect to borghq.io. The work done under those names is the foundation of what Borg is today, so old links keep resolving rather than breaking. **What happened to Watchdog?** Watchdog was a continuous asset discovery framework built in-house after clients kept saying they had no ongoing visibility into their subdomains, related domains, IP ranges, and open ports. Its discovery capabilities are now part of Odin, combined with the offensive expertise built through manual pentesting work. **What is Odin?** Odin is the platform that brings everything under one roof: the discovery capabilities from Watchdog, the offensive depth from Borg Security, and new layers of automation, integration, and continuous testing on top. It spans four verticals — Discover, Attack, Report & Confirm, and Mitigate & Retest. --- ## Your scanner didn't find it. Mjolnir did. URL: https://www.borghq.io/blog/your-scanner-didnt-find-it-mjolnir-did Published: 2026-06-08 Author: Hans Most security teams rely on automated scanners to catch vulnerabilities before they go live. The problem is that scanners are pattern matchers. They check for known signatures, run through a list of published CVEs, and call it a day. They do not reason about state. They do not chain findings together. And they do not test your business logic. That is the gap Mjolnir was built to fill. ## What scanners are actually good at Start with the fair version, because the straw man helps nobody. Scanners are good at breadth and repetition. Dependency scanning catches the vulnerable package you pulled in six months ago. Secret detection catches the key someone committed on a Friday afternoon. Configuration checks catch the missing security header and the storage bucket that was never meant to be readable. Signature-based detection catches the known injection point in a known framework version. That work matters, it is cheap, and it should run on every commit. Nobody serious argues otherwise. ## What they structurally cannot do The limit is not effort or tuning. It is that most high-impact application vulnerabilities are not patterns. They are consequences of your specific rules. Consider a multi-step approval flow. The first request validates that the caller belongs to the tenant. The second request completes the action, and because step one already ran, it trusts the record identifier that arrives in the body. Send the second request on its own, carrying a record identifier that belongs to another customer, and the check that was supposed to protect the boundary never runs. Every individual request in that sequence is well formed. There is no vulnerable dependency, no published CVE, no malformed input, nothing to match against. The vulnerability lives in the assumption that step two only ever follows step one, and that assumption appears nowhere in the code as a string a scanner could find. The same shape shows up across the surfaces that actually hurt. An IDOR that only appears when a workflow is driven out of order. A role check applied at the route but never on the underlying record. A billing endpoint that accepts a quantity the pricing logic later trusts. A token scope quietly widened by a refactor. In custody and signing systems the equivalent is sharper still: a boundary between an off-chain instruction and an on-chain action where authorisation is verified once and then reused. Finding those requires two things a scanner does not have. An understanding of what the application is supposed to allow, and a willingness to attempt the thing that should not work. ## How Mjolnir works differently Mjolnir provides ongoing offensive testing against live applications to identify real, exploitable security issues. From there it plans and executes. It generates targeted test cases against the functionality that carries real risk, producing verified findings with reproduction steps. Each run provides ongoing offensive testing against live applications, resulting in verified findings. The output is the part that matters to an engineering team. Findings arrive with reproduction steps, so the first question a developer asks is how to fix it rather than whether it is real. Not a checklist. An actual assessment. ## Catching it before it merges A pentest that runs after deployment still leaves a window. Gungnir closes it from the other side, reviewing pull requests with greater code awareness than a standard static scan. It provides security review for risky pull requests before they reach production. That is the practical difference between finding a broken tenant boundary in review and finding it in production. ## Continuous, not periodic Traditional pentests typically occur on a scheduled basis. Between those engagements your attack surface changes with every deployment, and the report ages into a description of an application that no longer exists. Mjolnir runs manually or on a schedule, so regressions and new issues surface as the code evolves. Findings land in Odin and move through a workflow where they are tracked and automatically retested once fixed. That last state is the one teams usually lack. A remediation marked done without verification is a finding you have stopped looking at, not a finding you have closed. The same applies to where the work lands, which is why findings are integrated into existing development tools rather than into a PDF. ## Where this leaves your scanner Keep it. Dependency, secret and configuration scanning solve real problems cheaply, and they are not competing for this job. Run them on every commit, and stop expecting them to answer a question they were never designed to answer. Your scanner gives you the known patterns. Mjolnir gives you the findings your scanner was never built to catch, with the proof attached. ### Frequently asked questions **How is autonomous pentesting different from a vulnerability scanner?** A scanner matches known signatures against your code and dependencies — known CVEs, outdated packages, missing headers, basic misconfigurations. Autonomous pentesting reasons about the application instead: it explores attack paths, chains low-severity issues into high-impact exploits, and tests the business logic that makes your product unique. That reaches classes of vulnerability pattern matching cannot see. **What kinds of vulnerabilities do scanners typically miss?** The ones that actually get exploited are rarely simple pattern matches. An IDOR that only surfaces when you manipulate a multi-step workflow. An SSRF buried behind an AI-powered feature. A broken access control issue that requires authentication context to even discover. These are the findings that show up in real pentest reports, not in scanner output. **Does the whitebox pentest need access to our source code?** The whitebox pentest connects directly to your codebase via GitHub so it understands your application's structure, then runs targeted attacks against real functionality rather than working from a generic checklist. The code context is what lets it reason about which attack paths are worth pursuing. **How often does the whitebox pentest run against our application?** Continuously. Traditional pentests happen once or twice a year, and between those engagements your attack surface changes with every deployment. The whitebox pentest runs on an ongoing basis, catching regressions and new vulnerabilities as your code evolves. --- ## What startups get wrong about Application Security URL: https://www.borghq.io/blog/what-startups-get-wrong-about-application-security Published: 2026-06-03 Author: Theodor Startups move fast. Security tends to show up only when a customer asks for a SOC 2 report or an investor wants to know about your security posture. By then, you're playing catch-up. The problem isn't that startups don't care about security. It's that the default playbook doesn't fit how they actually build software. ## Mistake 1: Treating pentests as a yearly event The annual pentest made sense when release cycles were quarterly. If you're deploying multiple times a day, a point-in-time assessment tells you what was vulnerable three months ago. It says nothing about what shipped last Tuesday. Continuous testing that runs alongside your CI/CD pipeline catches vulnerabilities when they're introduced, not months later. ## Mistake 2: Over-investing in scanners, under-investing in logic testing SAST and DAST tools are useful. They're also limited to pattern matching. The vulnerabilities that matter most to startups, things like broken authorisation, payment logic flaws, and privilege escalation, require understanding how your application actually works. Scanners don't have that context. A $200/month scanner plus a real pentest of your critical flows beats a $2,000/month scanner suite that only finds the easy stuff. ## Mistake 3: No visibility into your external attack surface You probably have more subdomains, exposed services, and forgotten staging environments than you think. If you haven't mapped your external attack surface recently, attackers already know more about your infrastructure than you do. Asset discovery isn't a nice-to-have. It's the foundation everything else builds on. ## What actually works Start with visibility. Know what you're exposing. Layer in continuous testing that covers business logic, not just known CVE patterns. Push findings into your engineering workflow so they get fixed in the same sprint they're found. And retest to confirm fixes actually hold. Security isn't a phase. It's a feedback loop. ### Frequently asked questions **Is an annual penetration test enough if we deploy every day?** No. The annual pentest made sense when release cycles were quarterly. If you deploy multiple times a day, a point-in-time assessment tells you what was vulnerable three months ago and says nothing about what shipped last Tuesday. Continuous testing that runs alongside your CI/CD pipeline catches vulnerabilities when they are introduced. **Are SAST and DAST scanners enough on their own?** They are useful, but limited to pattern matching. The vulnerabilities that matter most to startups — broken authorization, payment logic flaws, privilege escalation — require understanding how your application actually works, and scanners do not have that context. A cheap scanner plus a real pentest of your critical flows beats an expensive scanner suite that only finds the easy issues. **Why does external attack surface discovery matter for a small team?** Most teams have more subdomains, exposed services, and forgotten staging environments than they think. If you have not mapped your external attack surface recently, attackers already know more about your infrastructure than you do. Asset discovery is the foundation everything else builds on, not a nice-to-have. **When should a startup start doing application security work?** Before a customer asks for a SOC 2 report or an investor asks about your security posture — by then you are playing catch-up. Start with visibility into what you are exposing, layer in continuous testing that covers business logic, push findings into your engineering workflow so they get fixed in the sprint they are found, and retest to confirm the fixes hold. --- ## How Borg pushes findings to Linear, Jira, and GitHub automatically URL: https://www.borghq.io/blog/how-borg-pushes-findings-to-linear-jira-github Published: 2026-06-01 Author: Hans Here's what usually happens after a pentest: someone sends a PDF. That PDF gets forwarded around, maybe uploaded to a shared drive. A few findings get turned into tickets manually. The rest sit unread until the next audit asks about them. We built the findings workflow to kill that. ## Findings belong where work happens Mjolnir tests the application and returns verified findings with reproduction steps. Odin is where those findings live: severity, affected asset, proof of exploitation, suggested fix. But a finding sitting in a security dashboard is still a finding nobody is working on. So Odin pushes them into the tracker your engineers already have open. Linear, Jira, and GitHub Issues. The issue arrives carrying the finding title, a severity label, the description, reproduction steps and the suggested fix. No copy-pasting from reports. No context lost in translation. ## How to enable issue sync Every tracker connects from the same place: **Management > Integrations** in the Odin sidebar. Each one has its own connect flow. ### GitHub 1. Click **Connect** next to GitHub and authorise the Borg Security GitHub App. 2. Select the GitHub organisation or account that owns your repositories. 3. Select the repository where issues should be created. 4. Turn on **Issue tracking** on the GitHub card. Step four matters. Repository access and ticketing are separate switches: without Issue tracking enabled, GitHub is connected for code context only, for example Mjolnir whitebox testing. You can change which repositories the app can reach at any time from your GitHub organisation settings. ### Linear 1. Click **Connect** next to Linear and authorise the Odin app through Linear's OAuth flow. 2. Choose the Linear team that should receive issues. Under **Issue customization** you can then place every synced issue into a specific Linear project, and set the workflow state new issues open in. Leave that unset to use your team's default. ### Jira 1. Click **Connect** next to Jira. You are redirected to Atlassian to authorise the Odin app. 2. Choose the Jira Cloud site where issues should be created. 3. Select the Jira project that should receive security findings. Issue type and component are set afterwards under **Issue customization**, so findings can land as a custom type such as Vulnerability rather than the project default. Jira Cloud only. Jira Server and Jira Data Center are not currently supported. ### What to set after connecting Three settings decide how much lands without anyone touching it: - **Auto-create tickets.** On, and an issue opens as soon as a finding is reported. Off, and you push findings by hand from the finding view. - **Minimum severity.** Only findings at or above this level trigger automatic creation. It defaults to Medium. Raise it to High or Critical if you want the board to stay short. - **Default assignee, labels and team mapping.** Map your Borg users to tracker users and the assignee follows the finding across. Linear and Jira also expose a priority mapping table, so Critical, High, Medium and Low become the priorities your team already sorts by. Already sitting on findings from before you connected? Use the **Sync now** banner on the integration card to backfill them. Individual findings also have a **Push to** button, and the Findings page has a bulk action toolbar for pushing several at once. Trackers are not exclusive. Connect Jira alongside Linear and Odin keeps findings and status changes aligned across both. ## Status stays in sync both ways Move a linked issue to In Progress in your tracker and the finding moves to Mitigating in Odin. Change the finding in Odin and the issue moves with it. Matching runs on the state name first, falling back to the state's underlying type where your workflow is customised. One deliberate exception: **Fixed & Retested** is the one status a tracker can never set. Dragging an issue to Done signals that your fix is ready for Borg to verify, and the finding moves to Open for Retest rather than closing. Closing the loop is a retest, not a checkbox in your sprint board. ## Close the loop When a fix is ready, Mjolnir retests it and confirms whether the issue is actually gone. On GitHub, merging the linked pull request starts that on its own: the retest inspects the merged change and reports whether the fix held. That is the difference between a ticket somebody dragged to Done and remediation you can evidence. Security findings that live in PDFs don't get fixed. Findings that show up in your sprint board, with a retest behind them, do. ### Frequently asked questions **Which issue trackers does Borg integrate with?** Linear, Jira, and GitHub Issues. You connect your project management tool to Odin in a few clicks, and confirmed findings are created as tickets in the tools your engineering team already works in. **What information is included in an automatically created ticket?** Each pushed finding carries the full technical context: severity, the affected endpoint and parameters, reproduction steps, proof of exploitation, and remediation guidance with a suggested fix. Engineers get everything they need to remediate without switching to a separate security tool. **Can we control which findings create tickets?** Yes. You configure which severity levels auto-create tickets, which projects they land in, and what metadata gets attached. Odin handles the rest. **Does Borg verify that a fix actually worked?** When your team marks a finding as resolved, Odin can automatically retest to confirm the fix holds. You get verified remediation status tracked over time with a full audit trail, instead of findings marked done on trust. --- ## Your Vulnerabilities exist right now. Odin finds them first. URL: https://www.borghq.io/blog/your-vulnerabilities-exist-right-now-odin-finds-them-first Published: 2026-05-27 Author: Jonas Every time your team pushes code, your attack surface changes. New endpoints go live. Old services get reconfigured. Staging environments stay exposed longer than anyone intended. The gap between what you think is deployed and what's actually reachable from the internet grows with every sprint. Borg closes that gap. ## Continuous discovery Huginn, Borg's asset discovery engine, maps your external attack surface automatically, identifying subdomains, services, open ports, and exposed assets on an ongoing basis. Not once a quarter. Continuously. When something new appears, you know about it before an attacker does. This isn't just DNS enumeration. Huginn builds a graph of your infrastructure, showing how assets relate to each other and where shared dependencies create blast radius risk. ## Automated testing, real findings Discovery is step one. Mjolnir, the autonomous pentesting engine, goes further by running security assessments against discovered assets, testing for real vulnerabilities with real exploit attempts. Findings are confirmed, not theoretical. Each one comes with proof of exploitation and clear remediation steps. Results feed directly into your existing workflow through integrations with Linear, Jira, and GitHub. No more PDF reports gathering dust. ## Daily reporting Every day, Odin generates a status report showing new findings, remediation progress, and changes to your attack surface. Odin is the dashboard the other engines report into, so you get one picture of your security posture rather than three. The reports include pass/fail metrics, confirmation status, and trend data so you can track improvement over time, not just react to individual findings. ## Why it matters Attackers don't wait for your next scheduled pentest. They probe continuously, looking for the one thing you missed. Borg matches that pace, giving your team the same always-on visibility that attackers already have, just pointed in the right direction. ### Frequently asked questions **What does continuous attack surface discovery actually find?** Odin maps your external attack surface on an ongoing basis, identifying subdomains, services, open ports, and exposed assets. When something new appears — a new endpoint, a reconfigured service, a staging environment left up longer than intended — you know about it before an attacker does. **How is this different from DNS enumeration or a subdomain scanner?** Odin builds a graph of your infrastructure showing how assets relate to each other and where shared dependencies create blast radius risk, rather than returning a flat list of hostnames. It then runs automated security assessments against the assets it discovers, with real exploit attempts. **Are the findings confirmed, or just potential issues?** Confirmed. Odin tests discovered assets with real exploit attempts, so each finding arrives with proof of exploitation and clear remediation steps rather than a theoretical severity rating you have to triage yourself. **How do we see results day to day?** Odin generates a daily status report covering new findings, remediation progress, and changes to your attack surface, including pass/fail metrics, confirmation status, and trend data so you can track improvement over time. Findings also feed straight into Linear, Jira, and GitHub, so you do not have to log into another dashboard.