What a Deep Security Audit of InfraScout Found — and What We Fixed

July 11, 2026 · InfraScout Team

InfraScout runs commands against your infrastructure and holds credentials for your Microsoft Cloud tenants. That makes the platform itself worth attacking, so we periodically audit it as if we were the attacker. We recently finished the deepest such pass we have run, and this post is the honest account of it: what we looked at, what we found, and what changed as a result.

We are publishing this because a security tool that never reports finding anything in itself is not reassuring. It is the opposite.

What we audited

The audit covered the full critical surface: the transport agents use to reach the server, the tool surface exposed to AI clients, the web API behind the portal, the boundaries that keep tenants apart, the sign-in flow, the agent binary that runs on your hosts, and the web portal itself.

It was deliberately adversarial and multi-angle rather than a checklist pass — the goal was to find the bug a checklist misses, so each surface was probed by working backward from what an attacker would want out of it.

Two results are worth stating plainly, because they are the ones that matter most. The audit found no unauthenticated remote code execution anywhere in the platform, and no break in authentication. Tenant isolation, the sign-in flow, mutual TLS at both ends of the agent connection, and certificate renewal were all examined closely and verified sound.

It did find real problems. Here are the ones that affect you — each with an honest account of how reachable it actually was, because "we found a bug" and "you were exposed" are not the same statement, and we would rather give you the detail to tell them apart than have you guess.

Tenant isolation, hardened

The most significant finding was in an internal cache used for Microsoft Cloud connections. It was keyed by the connection's identifier alone. Because those identifiers are only guaranteed unique within a tenant, a request that asked for a connection identifier which happened to be sitting in the cache under a different tenant could have been served that other tenant's connection.

That is a cross-tenant boundary failure, which is the most serious class of bug this platform can have, and we are not going to describe it as anything less. But describing only the impact would be its own kind of dishonesty, so here is what actually reaching it would have required.

You would have had to be an authenticated, authorized user of InfraScout to begin with. Then you would have had to name another tenant's connection identifier — a randomly generated UUID, held only inside the tenant that owns it, never listed or exposed across tenants, and with no enumeration path. There is no way to discover one; you would be guessing a 128-bit random value. And a guess alone would not have been enough: the identifier also had to be live in the cache at that moment, warmed by the other tenant's own recent activity, because a cache miss fell through to a lookup that was already correctly tenant-scoped and would have refused you. In practice that means guessing an unguessable value and winning a timing race against it.

So the realistic exposure was very close to nil. We are still treating it as the most serious thing on this list, because "unreachable in practice" is a property of the identifier being random — not of the boundary being enforced, which is where a boundary should be enforced. The cache is now keyed by tenant and connection, with the tenant taken from the resolved tenant context and never from anything a caller can supply. A regression test runs two tenants against one cache and fails if a request ever crosses. Tenant isolation elsewhere in the platform was reviewed in the same pass and held up.

The false "certificate expiring" badge — and the gap behind it

If you have seen a healthy, connected agent in the portal wearing a "certificate expires in 3 days" badge, this is the explanation, and it is fixed.

Each agent automatically renews its client certificate about 30 days before it expires and reconnects using the new one. The server, however, was only ever recording the certificate an agent presented at enrollment. Renewals were never written down. The agent moved on; the server's record did not.

That had two consequences. The visible one was cosmetic but corrosive: the portal computed the expiry badge from a certificate the agent had long since replaced, so it counted an agent down to "expiring," then to "expired," while that agent went on working normally for months. The badge was wrong, and it never corrected itself.

The consequence underneath it was not cosmetic. Because the server had the superseded certificate on file, revoking or deleting an agent revoked that certificate — not the one the agent is actually connecting with. Against any agent that had renewed, revocation was not doing what it said it did.

Reachability here is worth being precise about, because it cuts the opposite way from the finding above. This was never an entry point: nobody could use it to get into anything. To be harmed by it you would already need to hold a copy of that agent's superseded private key — which means the host was already compromised. What the bug did was ensure that the control you reach for after a compromise did not work: you would revoke the agent, believe you had cut the attacker off, and the certificate they actually held would keep working until it expired on its own. Low likelihood, but the failure lands at precisely the worst moment, which is why it was treated as the priority it was.

Both are fixed. The server now records an agent's certificate every time it connects, so its record always reflects the certificate in live use — and revocation now targets the credential that actually works. When a renewal completes, the previous certificate is revoked as well, which closes the window in which two valid certificates existed for one agent.

Self-healing — no action needed

Every agent rewrites its own record the next time it reconnects. False expiry badges clear across your fleet on their own as agents check in. There is nothing to migrate, reinstall, or re-enroll.

You can watch this settle on the agents view. For how enrollment and renewal work end to end, see the agents overview.

Browser security headers, now enforced

The portal shipped without browser security headers. The concrete risk that created was clickjacking: an attacker who could lure a signed-in admin to a page they controlled could invisibly frame the portal over their own page and trick that admin into clicking privileged buttons they could not see. It is a real attack, though it is not a quiet one — it requires an admin with a live session, successfully phished onto a hostile page, and clicking where the attacker wants. That is now blocked outright, at the browser, and cannot be re-enabled by anything on the page.

The portal's content security policy has also moved from monitoring to enforcing. This one is a backstop rather than a fix for a known hole: it means that if a bug were ever introduced — in our code or in a dependency — that let attacker-controlled markup onto a page, the browser would refuse to execute it rather than run it happily. We know of no such bug. The point of a backstop is to be in place before you need it.

We staged the flip rather than switching it on and hoping. The policy ran in report-only mode for weeks, and we then reviewed the breakage surface by hand instead of trusting an absence of complaints. That review found exactly one genuine incompatibility, in the hidden frame the portal uses to renew your session token silently. Left alone, enforcement would have broken silent renewal and bounced you to the sign-in screen every time a token expired — with nothing in the console to explain why. We fixed it before the flip, so you should notice nothing at all.

Auditing that survives a disconnect

If you closed a browser tab in the middle of an AI turn, that interrupted turn could drop out of the conversation transcript — including tool calls that had already executed against your hosts. Worse, the turn's cost was recorded even when its content was not, so a conversation could show spend against a turn with no visible record of what that turn did.

Nobody outside your tenant could exploit this — it is not an entry point, and it needs no attacker at all, since an ordinary tab close would do it. What it means is that the transcript could not be relied on to be complete: someone with legitimate access could run commands against your hosts and, by disconnecting mid-turn, leave no record of them. For a platform whose entire value rests on being able to show what ran where, an audit trail with a gap that anyone can trigger is a security finding, not a UI glitch.

Interrupted turns are now always persisted. What ran against your infrastructure is recorded whether or not you stayed on the page, and it is there in the chat audit trail afterward.

The agent on your hosts

Two findings were in the agent binary itself, which matters more than most because it runs on your machines rather than ours.

At enrollment, the agent wrote its private key and configuration to disk before applying its own file protections, which only arrive when it first starts. On Windows the permissions it used in the meantime do not actually restrict anything — so between enrollment and first start, those files were readable by unprivileged local users on that host. Reaching it required already having a local account on the machine in question and catching that window, so this was never remotely exploitable; it is a local privilege boundary, not a way in. It is closed regardless: the agent now applies owner-only protection at the moment it writes, which every other key path already did. Enrollment was the one gap.

Separately, the agent logged the full text of shell commands it was asked to run. A command can carry a secret inline, and a log file is a much less careful place for one than a credential store. It now logs the command's length, keeping the body behind debug-level logging, which is how the PowerShell path already behaved.

Faster where it was slow

The audit also turned up several places doing far more work than the job required — most of which only became noticeable at fleet scale. Visibility filtering, inventory change queries, and usage aggregation were all reworked. The practical effect: the agents list and the inventory change views stay fast as your fleet grows, rather than getting heavier with every host you add.

Why we publish this

Every finding above came out of our own audit. None of it came from an incident, and none of it was reported to us by someone who had run into it.

That shapes how you should read the list. Most of these were reachable only by someone who had already gotten somewhere they should not be — with a local account on your host, or with an agent's private key in hand — or, in the case of the cross-tenant cache, by guessing a random identifier that is never exposed and then winning a race against it. We are telling you about them at that level of detail precisely so you can judge them yourself, rather than taking either "critical vulnerability" or "nothing to see here" from us on faith.

We fixed all of them anyway, and at the boundary rather than at the odds. A control that holds only because an identifier is hard to guess is not a control; it is a coincidence you have not been punished for yet. That is the standard the audit was run against, and it is the standard the fixes were built to.

If you have questions about any of this — or you run InfraScout somewhere that makes you want the specifics — reach us at info@infrascout.cloud.