Pass 07: Egress Analysis (EGR)
| Property | Value |
|---|---|
| Pass number | 07 |
| Key | egr |
| Pass name in output | egress |
| Signal prefix | EGR |
| Type | Deterministic (rule engine), no LLM |
| Default | On |
| Assessment types | application; llm (inside each leg). Skipped for service (no egress graph without source code). Not run for landing-zone-catalog. |
| Tier | Not tier-gated (Community and above) |
What it is for
A workload is only as sovereign as the services it calls. Moving an application to a sovereign landing zone does not help if it still sends data to a US-based API. Pass 07 answers: "Which external services outside the target jurisdiction does this application send data to, and which of them block a data residency requirement?"
It finds outbound destinations from known client packages and from domain names in the source code, records their jurisdiction and whether standard contractual clauses (SCC) are in place, and marks destinations that block DE-only or EU-only residency. For JVM services it also lists every hard-coded host in Java code and Spring configuration (HTTP base URLs, JDBC URLs, Kafka bootstrap servers), so endpoints outside the catalogues are visible too.
What you need to provide
- The application source tree:
package.jsonand JavaScript or TypeScript files, or JVM sources (.java,.kt,.kts,.groovy,.scala) with Spring configuration (application*.yml|yaml|properties,bootstrap*, any.properties). Files are listed through the central source scope, so build output (target/,build/,node_modules/) is never read and the test-code setting applies.
No credentials, network access or LLM are needed.
How it works
Package check.
dependenciesanddevDependenciesin the rootpackage.jsonare compared with three known client packages:Package Destination alphavantageapi.alphavantage.coyahoo-finance2query1.finance.yahoo.com@aws-sdk/client-comprehendmedicalcomprehendmedical.amazonaws.comSource scan. All
.ts,.js,.mjs,.cjs,.java,.kt,.kts,.groovyand.scalafiles and the Spring configuration files are searched for the seven catalogued domain names:Domain Jurisdiction SCC Blocks DE-only / EU-only api.alphavantage.coUS no yes query1.finance.yahoo.comUS no yes query2.finance.yahoo.comUS no yes api.rapidapi.comUS no yes comprehendmedical.amazonaws.comUS no yes s3.amazonaws.comUS (region-dependent) no no api.coingecko.comuncertain no no A domain found both ways is reported once, with the package and the source file as evidence.
Allowlist guard. If the folder
apps/api/src/services/data-provider/exists, the pass checksdata-provider.service.tsfor an allowlist or blocklist (allowlist,allowedDomains,sovereignProviders,blocklist). A missing guard is reported, because new providers could then be added without sovereignty control.Sovereign service availability. The pass reads
controls/sovereign-service-catalogue.yaml(31 SaaS and cloud services, each with aportability_verdictofavailable,partialorunavailable, asovereign_evidenceclass, averdict_confidenceofverifiedorunverified, the publicsourcesthe verdict rests on with their review dates, and ablocks_migrationflag). The catalogue header defines one verdict rule for every entry: a vendor whose parent is a US company (US CLOUD Act exposure) is neveravailable; with a documented EU data residency option it ispartial, without oneunavailable.blocks_migrationis set exactly when a verdict isunavailableandverified. A service counts as used when one of its client packages appears in apackage.json,requirements*.txt,pyproject.tomlorPipfileanywhere in the source tree (up to four folder levels;node_modules,dist,build,target,vendorand virtual environments are skipped), or when one of its API host names appears in a JavaScript or TypeScript file. Examples:@sentry/*orsentry.io(Sentry),stripe(Stripe),firebaseorfirebaseio.com(Firebase),@anthropic-ai/sdkorapi.anthropic.com(Anthropic API),@slack/*orhooks.slack.com(Slack),@aws-sdk/client-cognito-identity-provider(AWS Cognito). The mapping from packages and host names to catalogue entries is fixed in the pass (SOVEREIGN_SERVICE_DETECTION); the verdicts come from the catalogue. Host names are searched in the same files as step 2, so a host in Java code orapplication.ymlcounts.Hard-coded endpoints in JVM source and Spring configuration (#3422). JVM source files, Spring configuration and
.propertiesfiles are read by the shared hard-coded endpoint scanner (Design 117 section 3): network URLs (http(s), JDBC, LDAP, AMQP, Redis, Kafka and the other recognised schemes) and IPv4 literals. Two forms without://are added by the pass: Kafka bootstrap lists (spring.kafka.bootstrap-servers: host1:9092,host2:9092,bootstrap.servers=...,ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "host:9092", also as a YAML list) and Oracle thin JDBC URLs (jdbc:oracle:thin:@host:1521:SID,@//host:1521/service). Only reportable hosts count:publicandprivate(internal names such askafka.internal, single-label service names, RFC 1918 addresses); loopback, documentation names, XML namespaces and placeholders (${DB_HOST}) are not reported. Each location records the HTTP client it sits in (RestTemplate,WebClient,RestClient,Feign,OkHttp,JDK HttpClient,java.net,JDBC; the line itself, or the line above when the hit continues a builder chain or an open argument list) or, in configuration, its dotted key (spring.datasource.url). Hosts the egress or sovereign service catalogues already report are skipped, so a service is never reported twice.
How it is measured
| Signal | Emitted when | Severity | Meaning |
|---|---|---|---|
EGR-01 onwards | One per destination found | critical (blocks DE-only or EU-only residency), medium (jurisdiction uncertain), high (other) | Destination, jurisdiction, SCC status, detection method; blocking destinations state blocks_migration: true for DE-only and EU-only residency |
| Next number | Data-provider folder exists without allowlist or blocklist | high | No provider-sovereignty allowlist or geographic routing guard |
| Next numbers | One per sovereign service catalogue entry found (step 4) that is not already an egress destination above | verified verdicts: critical (unavailable and blocks_migration: true), high (unavailable), medium (partial), low (available); unverified verdicts: at most medium and never blocking; confidence high when found by package, medium when found by host name | Service, vendor, catalogue verdict and its confidence, evidence class, review date and blocks_migration, with a reminder to confirm the configured region and contract; implies: sovereign_service:<id> |
| Next numbers | JVM endpoint scan (step 5) found public and/or private hosts; one signal per class | public: medium; private: low; a class whose hosts are all in test code only: informational, confidence low | Hosts with channel (http, database, messaging, cache, directory, mail, network) and client or key, production and test-only hosts listed separately, up to 10 file:line locations with production first; public hosts have an unknown jurisdiction to confirm, private hosts are internal dependencies the target landing zone must reach |
EGR-01 (fallback) | Nothing found | informational, confidence low | No known egress domains or packages detected; manual review recommended |
The assessment block records:
egress_destinations: one entry per destination withdomain,jurisdiction,scc,blocks_migration,detected_via(packageorsource_scan) andsignal_ref; ornote: none_detectedmigration_blockers: number of destinations that block DE-only or EU-only residencysovereignty_claim_verified: alwaysfalse(static analysis cannot verify contractual or routing claims)sovereign_services: one entry per catalogue service found withservice_id,name,category,portability_verdict,verdict_confidence,sovereign_evidence,blocks_migration(the effective value: false for an unverified verdict),detected_via,last_reviewedandsignal_ref;merged_with_egress_destinationnames the egress domain when the service was reported in that destination's signalsovereign_service_blockers: number of services found whose effectiveblocks_migrationis true (counted separately frommigration_blockers)sovereign_services_merged_with_egress: number of services reported in an egress destination's signal instead of their ownsovereign_service_check:matched,no_match, orskipped: <reason>when the catalogue is missing or invalidhardcoded_endpoints(present only when step 5 found a host):files_scannedand per hosthost,classification(publicorprivate),channels,via,total,production,test,unclassifiedand up to 20locations(file, line, path class, client or key), production first; these hosts do not count towardsegress_destinationsormigration_blockers
How results feed later passes. EGR signals are listed explicitly in the prompt of Pass 09 (SYNTH). Its portability_score is defined as the share of egress destinations with a sovereign-cloud equivalent: (available + 0.5 × partial) / total egress, with 0.70 as the sovereign-ready threshold. EGR signals are also evidence for data transfer and third-country controls in Pass 11 (COMP), for example under GDPR when active in assessment.regimes_active. Critical signals feed the derived findings, where critical severity marks a finding as blocking migration.
Third-party modules and tools
None. The catalogue of egress domains and packages is built into the pass; the sovereign service verdicts are read from controls/sovereign-service-catalogue.yaml, which ships with SWAO. Domain names are matched case-insensitively as whole host names through the shared catalogue matcher (bucket.s3.amazonaws.com matches s3.amazonaws.com, mys3.amazonaws.com does not); generated or minified files, base64 runs and data: URIs are skipped.
Outputs
wsp/runs/<ts>/passes/07-egr.yamlwith header (id: 7,name: egress), signals andassessment.
Configuration and limitations
Configuration
--passes egrruns the pass alone. There are no pass-specific settings.
Limitations and known gaps
- The egress catalogue is small and fixed in code: seven domains and three packages. Beyond it, only the 31 services of the sovereign service catalogue are recognised (step 4); any other external service (for example a SaaS API, a telemetry endpoint or an LLM API) is not detected. Treat a clean result as "nothing from the known list", not as "no egress".
- Catalogued host names are searched in JavaScript, TypeScript and JVM source files and in Spring configuration; other languages, environment templates and generic configuration files are not. Sovereign service packages are read from npm and Python manifests only (Maven and Gradle coordinates are not mapped).
- The hard-coded endpoint scan (step 5) covers JVM sources and Spring configuration only. A host built at run time, read from an environment variable or a placeholder default without a literal is not seen. Multi-host JDBC URLs of MySQL, MariaDB and PostgreSQL (
jdbc:mysql://h1:3306,h2:3306/db, alsojdbc:mysql:replication://andjdbc:mysql:loadbalance://) report every host (#3485); the MySQLaddress=(host=...)form and failover lists of other engines (for example the SQL ServerfailoverPartnerproperty) are not split. An IPv4 host in such a list keeps the channelnetworkof the shared scanner hit. - A sovereign service signal reflects the catalogue verdict, not the configuration of the application: a service with an EU region option (for example Sentry or Datadog) is reported with its catalogue verdict whether or not the EU region is used. Check
last_reviewedin the signal; verdicts are only as current as the catalogue. - When an egress destination and a sovereign service catalogue entry name the same service (for example AWS Comprehend Medical), the pass emits one signal: the egress signal carries the catalogue verdict, and a verified verdict sets its severity and
blocks_migration. Both kinds of signal carryimplies: sovereign_service:<id>, and derive-plan keeps one security finding and one risk per service even for runs written before this rule (#3296). - The blocking flag is derived from the catalogue entry; the pass does not read the residency requirement of the application.
- The allowlist check only applies to one specific folder layout (
apps/api/src/services/data-provider/). - Runtime traffic is not observed. Use Pass 10 (dynamic analysis) and context documents for a fuller picture.
