Ungoverned Inbound External Agent (MCP / Action Fabric)
This page describes the
nowisor-ai-inbound-agent-ungovernedcheck from the nowisor Instance Scan Pack v1.2.0 (ai-agent-securitygroup, spec AIA-008). The check flags inbound external agents reaching the instance over MCP Server or Action Fabric that are absent from AI Control Tower or any sanctioned catalog.
What this finding means
Your instance exposes an inbound agent surface, and one or more registered entries on it are not in your sanctioned catalog.
Action Fabric and MCP Server let an agent built anywhere - Claude, Copilot, or something homegrown - act inside the instance headlessly, under an OAuth client, against role-scoped tool packages. That is the system of action reachable by a caller no ITSM control was designed around.
The check keeps three states distinct:
| Instance state | Verdict |
|---|---|
| no inbound surface at all | not_applicable - out of scope, not a pass |
| inbound surface, no catalog to compare | INVESTIGATE |
| inbound surface + populated catalog + misses | CRITICAL |
The middle row matters: reporting "N ungoverned agents" on an instance that simply has no Control Tower would be an artifact of our own detection rather than a customer defect. The missing catalog is the governance-configuration check's finding, not this one's.
Why it matters for your compliance
NIS2 Article 21(2)(d) - supply chain security. An external agent acting inside your instance is a supplier with write access. If it is not in your catalog, it is not in your supplier risk assessment either.
NIS2 Article 21(2)(i) asset management and DORA Article 9(4)(c) approved-function access both require an inventory that this finding proves you do not have.
ISO 27001 A.8.2 applies because MCP tool packages are privileged access grants in everything but name.
OWASP LLM03:2025 (Supply Chain) and MITRE ATLAS AML.T0103 (Deploy AI Agent) / AML.T0108 (AI Agent) are the industry framings.
The attack path
Why this is newer than most check libraries. Action Fabric and MCP Server ship inside Now Assist entitlements. The inbound surface can therefore appear on your instance as a consequence of a licensing change rather than a deliberate project - nobody ran a design review, because from the platform's perspective nothing was installed.
The grant shape is the problem. An inbound agent authenticates as an OAuth client and acts through a tool package scoped by role. Two failure modes follow: the tool package is broader than the integration needs (write tools where read-only would serve), and the OAuth client has no expiry, so the access outlives the project that justified it.
No lifecycle, no revocation. A registered agent has an owner and a review date. An unregistered one has neither: nothing reviews its scope and nothing owns turning it off. That is standing privileged access to your system of action, held by a caller outside your instance.
See the MCP exposure-scope check for the complementary question - not who is connected, but how much the connection can do.
How to fix it
Step 1 - identify the client and its reach. For each ungoverned entry the evidence names the surface table and the entry. Establish which OAuth client authenticates it and which tool package it can reach.
Step 2 - register or revoke. Legitimate agents get a catalog entry with a named owner and a review date. Unrecognised ones get deactivated and their OAuth client revoked.
Step 3 - minimise the tool package. Prefer read-only tools wherever they satisfy the use case. A summarisation agent does not need write tools.
Step 4 - set expiry and rotation on the OAuth clients that remain.
Note on matching. Registration is compared by normalised name, so an agent catalogued under a different label will appear here. Confirm before revoking - the evidence carries match_basis for exactly this reason.
How this check stays honest
Every check in the ai-agent-security group is enumeration-driven: table and
field names are resolved from sys_db_object and sys_dictionary on your
instance, never asserted from a hardcoded list. The sn_aia namespace is
paid-SKU gated and cannot be verified against a ServiceNow developer instance, so
asserting its identifiers from documentation alone would risk exactly the
fabricated-identifier problem this codebase audits for.
Two consequences you will see in the output:
- An absent plugin produces
not_applicable, never a pass. The absence of Now Assist is not a security win; it means the control is out of scope. A check that silently passed would let a customer believe they were assessed on something that was never examined. - An unresolvable field produces
not_applicable, not a finding. Where the release does not expose the column a check needs, the check says so and names it. Firing anyway would flag healthy instances on releases we simply cannot read.
All detection is read-only. No record contents are inspected or transmitted.