AID-D-004.004: Model Source & Namespace Drift Detection
Pillars: infra, app
Lifecycle: validation, operation
Scope boundary
Owns high-signal findings for external model-repository lifecycle and namespace changes during curation, plus unexpected production traffic to public model hubs. It does not perform broad enterprise AI-service inventory discovery.
AID-M-001.004: AID-D-004.004 detects model-source or namespace drift and production contact with public model hubs; AID-M-001.004 discovers broad use of externally hosted AI services and embedded SaaS AI across enterprise telemetry. A model supply-chain drift signal is narrower than enterprise-wide shadow-AI service discovery.
Implementation guidance
- AID-D-004.004-G001: Alert on 404 or 3xx status codes when validating external model URLs during CI curation.
- AID-D-004.004-G002: Monitor for runtime DNS queries or egress traffic from production pods to public model hubs.
AID-D-004.004-G001
CI namespace-state probe
Probe only model references from a signed, provider-allowlisted dependency inventory. Do not follow redirects: a redirect is itself evidence of namespace movement. Bind the result to DNS, TLS, provider, namespace, revision, and URL evidence, and revalidate the immutable revision at the separate promotion gate to address time-of-check/time-of-use.
from __future__ import annotations
# File: detection/model_reference_probe.py
import hashlib
from urllib.parse import urlsplit
import httpx
def probe_model_reference(reference: dict, policy: dict) -> dict:
required_reference = {
"reference_id", "provider", "namespace", "revision", "url",
}
if set(reference) != required_reference:
raise ValueError("model reference schema mismatch")
host = urlsplit(reference["url"]).hostname
provider = policy["providers"].get(reference["provider"])
if (
host is None
or provider is None
or host not in provider["allowed_hosts"]
or not reference["revision"]
):
raise ValueError("reference is outside signed provider policy")
headers = {
"Accept": "application/octet-stream",
"Range": "bytes=0-0",
"User-Agent": "aidefend-model-reference-probe/1",
}
try:
with httpx.Client(
follow_redirects=False,
timeout=httpx.Timeout(
float(policy["read_timeout_seconds"]),
connect=float(policy["connect_timeout_seconds"]),
),
verify=True,
) as client:
with client.stream("GET", reference["url"], headers=headers) as response:
status = int(response.status_code)
location = response.headers.get("location")
finding_reasons = []
if 300 <= status < 400:
finding_reasons.append("namespace_redirect")
if status == 404:
finding_reasons.append("namespace_not_found")
if status not in set(provider["accepted_status_codes"]):
finding_reasons.append("unexpected_status")
return {
"schema_version": "aidefend.model_reference_probe.v1",
"control_status": "PASS",
"finding_status": "FINDING" if finding_reasons else "NO_FINDING",
"reference_id": reference["reference_id"],
"provider": reference["provider"],
"namespace": reference["namespace"],
"revision": reference["revision"],
"url_sha256": hashlib.sha256(reference["url"].encode()).hexdigest(),
"status_code": status,
"redirect_location_sha256": (
hashlib.sha256(location.encode()).hexdigest() if location else None
),
"finding_reasons": sorted(set(finding_reasons)),
"policy_version": policy["policy_version"],
}
except (httpx.HTTPError, OSError) as exc:
return {
"control_status": "ERROR",
"finding_status": "UNKNOWN",
"reference_id": reference["reference_id"],
"reason": type(exc).__name__,
"policy_version": policy["policy_version"],
}Verification: Replay stable, 404, single-hop and chained redirect, unauthorized, rate-limited, TLS failure, DNS failure, disallowed host, missing revision, timeout, and provider-error fixtures through a controlled test server. Confirm redirect bodies are never followed or trusted.
Action: Probe every signed external reference during curation and emit namespace-state findings without mutating the reference. The promotion or deployment control must separately resolve and verify the immutable revision again.
AID-D-004.004-G002
Concept:
If your hardening policies are correctly implemented, your production AI services should have no reason to ever contact a public model hub like `huggingface.co`. Any attempt to do so is a high-confidence indicator of a misconfiguration, a policy bypass, or malicious code that has slipped through the supply chain.
Note: For the `fd.sip.name` field to be effective, the environment must allow Falco to perform or receive DNS resolutions. If this is not feasible, an alternative is to monitor DNS logs directly or use a CNI-level tool like Cilium Hubble.
Implement a Runtime Egress Detection Rule
This safer Falco pattern explicitly checks for connection events and uses Falco's documented field for resolved domain names.
# File: falco_rules/ai_egress_violation.yaml
- rule: Prod AI Pod Egress to Public Model Hub
desc: Egress from prod AI namespaces to public model hubs (HF domains)
condition: >
evt.type=connect and fd.l4proto in (tcp, udp) and
k8s.ns.name in (ai-prod, ai-inference) and
fd.sip.name in (huggingface.co, hf.co)
output: >
Disallowed egress to public model hub (ns=%k8s.ns.name pod=%k8s.pod.name
user=%user.name cmd=%proc.cmdline dst=%fd.name)
priority: CRITICAL
tags: [network, supply_chain, aidefend]Verify safely
Store inputs, results, and readback receipts in append-only or WORM storage outside monitored identities. An independent verifier must query DNS, network-flow, proxy, and workload inventories independently, replay approved and disallowed canary destinations, reconcile every expected workload and event ID, read the evidence back, verify signatures and digests, and compare the canonical result digest.
Open-source tools
- Falco
- Cilium Tetragon
- Zeek (DNS, TLS, and HTTP egress evidence for unexpected model-source destinations)
- OpenSearch
- curl (CI URL status probe; not continuous monitoring)
Commercial / hosted tools
- Splunk Enterprise Security
- Microsoft Sentinel
- Google Security Operations
- Amazon VPC Flow Logs
- CrowdStrike Falcon Insight XDR
- SentinelOne Singularity
- Microsoft Defender for Endpoint
Threat mappings
MITRE ATLAS
- AML.T0018 Manipulate AI Model
- AML.T0115 Publish Poisoned AI Artifacts
- AML.T0010 AI Supply Chain Compromise
- AML.T0010.003 AI Supply Chain Compromise: Model
- AML.T0074 Masquerading
- AML.T0115.001 Publish Poisoned AI Artifacts: Models (source deletion or redirect can expose namespace replacement)
- AML.T0018.002 Manipulate AI Model: Embed Malware (namespace takeover can substitute a malware-bearing model)
- AML.T0060 Publish Hallucinated Entities (source validation exposes nonexistent or replaced model namespaces)
- AML.T0120 AI Artifact Repository (unexpected production DNS or egress to public model hubs surfaces repository dead-drop polling)
MAESTRO
- Supply Chain Attacks (Cross-Layer)
OWASP LLM Top 10 2026
- LLM04:2026 Supply Chain
OWASP ML Top 10 2023
- ML06:2023 AI Supply Chain Attacks
- ML07:2023 Transfer Learning Attack (source monitoring detects compromised pre-trained models)
- ML10:2023 Model Poisoning (source monitoring detects poisoned models)
OWASP Top 10 for Agentic Applications 2026
- ASI04:2026 Agentic Supply Chain Vulnerabilities
NIST Adversarial Machine Learning 2025
- NISTAML.051 Model Poisoning (Supply Chain)
Cisco Integrated AI Security and Safety Framework
- AISubtech-9.3.3 Dependency Replacement / Rug Pull (deleted or redirected original model namespaces and production hub access)
- AITech-9.3 Dependency / Plugin Compromise (deleted or redirected original model namespaces and production hub access)
Google Secure AI Framework 2.0 - Risks
- MST: Model Source Tampering
- MDT: Model Deployment Tampering (namespace takeover enables deployment tampering)
Databricks AI Security Framework 3.0
- Model 7.3: ML Supply chain vulnerabilities
- Model 7.1: Backdoor machine learning / Trojaned model (namespace takeover can substitute a trojaned model)