Back to skill

Security audit

EvoMap Dashboard

Security checks for vulnerabilities and agentic risk

Overview

This dashboard mostly behaves like an EvoMap viewer, but it ships hardcoded credentials, exposes an unauthenticated local proxy, and includes an unrelated publishing script that can write to EvoMap.

Install only if you are prepared to audit and modify it first: remove and rotate the embedded EvoMap secret, delete or isolate publish.py, bind the server to localhost, restrict CORS, require explicit user-provided credentials, and treat any Node Secret entered into the dashboard as sensitive account authority.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
evomap_main.py:13
Finding
Hardcoded EvoMap Credentials Used as Default Authentication<![CDATA[ ## Vulnerability Details **File Location**: `evomap_main.py:13-14, 48-64`; additional credential copy in `publish.py:9-10` **Vulnerability Type**: Hardcoded secret and insecure default authentication **Risk Level**: Critical ### Vulnerable Code ```python DEFAULT_NODE_ID = "node_ea73e34385b44413" DEFAULT_NODE_SECRET = "8daa0c462caedcf506c103a77bb1d3c495f00f6869df1bd47a4d77f0353333ce" ``` The proxy endpoints automatically use this secret when an Authorization header is absent: ```python @app.get("/proxy/node") async def proxy_node(x_node_id: str = Header(default=DEFAULT_NODE_ID), authorization: str = Header(default="")): node_secret = authorization.replace("Bearer ", "") if authorization else DEFAULT_NODE_SECRET return await fetch_async(f"/a2a/nodes/{x_node_id}", x_node_id, node_secret) @app.get("/proxy/my_tasks") async def proxy_my_tasks(x_node_id: str = Header(default=DEFAULT_NODE_ID), authorization: str = Header(default="")): node_secret = authorization.replace("Bearer ", "") if authorization else DEFAULT_NODE_SECRET return await fetch_async(f"/a2a/task/my?node_id={x_node_id}", x_node_id, node_secret) @app.get("/proxy/assets") async def proxy_assets(x_node_id: str = Header(default=DEFAULT_NODE_ID), authorization: str = Header(default="")): node_secret = authorization.replace("Bearer ", "") if authorization else DEFAULT_NODE_SECRET return await fetch_async("/a2a/assets?limit=20", x_node_id, node_secret) ``` The same credential is duplicated in the publishing utility: ```python NODE_ID = "node_ea73e34385b44413" NODE_SECRET = "8daa0c462caedcf506c103a77bb1d3c495f00f6869df1bd47a4d77f0353333ce" HUB = "https://evomap.ai" ``` ### Technical Analysis A live-looking node secret is committed directly to source control. Every person or system with access to the package can recover and reuse it independently of the dashboard. The backend makes the exposure more ...[truncated 1360 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Revoke and rotate the exposed node secret immediately. 2. Remove the credential from every source file, release artifact, and repository history. 3. Read credentials from an explicit runtime secret source, such as environment variables or an operating-system credential manager. 4. Reject requests when credentials are absent; never fall back to a privileged identity. 5. Validate the Authorization scheme strictly rather than using unrestricted string replacement. 6. Use separate, least-privileged credentials for read-only dashboard access and publishing. 7. Add secret scanning to CI and pre-commit checks. 8. Audit EvoMap activity associated with the exposed node for unauthorized access or publishing. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
evomap_main.py:19
Finding
Network-Exposed Unauthenticated Proxy with Wildcard CORS<![CDATA[ ## Vulnerability Details **File Location**: `evomap_main.py:19-24, 48-68` **Vulnerability Type**: Unauthenticated network service and excessive cross-origin access **Risk Level**: Critical ### Vulnerable Code ```python app = FastAPI() app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) ``` ```python @app.get("/proxy/node") async def proxy_node(x_node_id: str = Header(default=DEFAULT_NODE_ID), authorization: str = Header(default="")): node_secret = authorization.replace("Bearer ", "") if authorization else DEFAULT_NODE_SECRET return await fetch_async(f"/a2a/nodes/{x_node_id}", x_node_id, node_secret) @app.get("/proxy/my_tasks") async def proxy_my_tasks(x_node_id: str = Header(default=DEFAULT_NODE_ID), authorization: str = Header(default="")): node_secret = authorization.replace("Bearer ", "") if authorization else DEFAULT_NODE_SECRET return await fetch_async(f"/a2a/task/my?node_id={x_node_id}", x_node_id, node_secret) @app.get("/proxy/assets") async def proxy_assets(x_node_id: str = Header(default=DEFAULT_NODE_ID), authorization: str = Header(default="")): node_secret = authorization.replace("Bearer ", "") if authorization else DEFAULT_NODE_SECRET return await fetch_async("/a2a/assets?limit=20", x_node_id, node_secret) if __name__ == "__main__": print("EvoMap Dashboard → http://localhost:8766") uvicorn.run(app, host="0.0.0.0", port=8766) ``` ### Technical Analysis The application is described as a local dashboard but binds to `0.0.0.0`, exposing it on every available network interface. The proxy endpoints have no application-level authentication and permit arbitrary callers to supply node credentials. When no credentials are supplied, the endpoints use the hardcoded secret. Wildcard CORS allows scripts from any website origin to interact with the proxy. Although browser behavior ...[truncated 1424 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Bind the service to `127.0.0.1` or `::1` by default. 2. Remove the embedded default credential and fail closed when authentication is absent. 3. Restrict CORS to the exact dashboard origin, or disable CORS because the frontend is served from the same origin. 4. Reject requests with unexpected `Origin` or `Host` headers. 5. Add a random per-launch session token and require it for every proxy request. 6. Apply CSRF protections if browser-based state-changing endpoints are added. 7. Validate `X-Node-Id` against an expected format and strictly parse Bearer authentication. 8. Document any intentional non-loopback mode and require an explicit opt-in with TLS and authentication. 9. Add host-firewall guidance as defense in depth rather than relying on it as the primary control. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
evomap_dashboard.html:248
Finding
Remote-Content DOM XSS Can Expose the Node Secret<![CDATA[ ## Vulnerability Details **File Location**: `evomap_dashboard.html:173-185, 248-253, 276-284, 296-306` **Vulnerability Type**: DOM-based cross-site scripting through unsafe HTML construction **Risk Level**: High ### Vulnerable Code The secret is stored in script-readable browser storage: ```javascript let creds = { nodeId: '', nodeSecret: '' }; function loadCreds() { creds.nodeId = sessionStorage.getItem('evo_node_id') || ''; creds.nodeSecret = sessionStorage.getItem('evo_node_secret') || ''; } function saveCreds() { sessionStorage.setItem('evo_node_id', creds.nodeId); sessionStorage.setItem('evo_node_secret', creds.nodeSecret); } ``` Remote node fields are inserted with `innerHTML`: ```javascript document.getElementById('cards').innerHTML = cards.map(([v,l,cls])=> '<div class="card"><div class="v'+(cls?' '+cls:'')+'">'+v+'</div><div class="l">'+l+'</div></div>' ).join(''); ``` Remote task fields are also concatenated into HTML: ```javascript document.getElementById('tasksBody').innerHTML = tasks.map(t=> '<tr><td style="max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">'+ts(t.created_at||'')+'</td>'+ '<td>'+statusBadge(t.status)+'</td>'+ '<td style="font-size:0.7rem;color:#6b7280;max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">'+(t.asset_id||'-')+'</td></tr>' ).join(''); ``` Remote asset fields are handled in the same way: ```javascript document.getElementById('assetsBody').innerHTML = items.map(a=> '<tr>'+ '<td>'+b('b', a.asset_type||'-')+'</td>'+ '<td style="max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">'+(a.signal||a.trigger_text||a.domain||'-')+'</td>'+ '<td>'+((a.confidence||0)*100).toFixed(1)+'%</td>'+ '<td>'+statusBadge(a.status)+'</td>'+ '<td class="t">'+ts(a.created_at)+'</td></tr>' ).join(''); ``` ### Technical Analysis The dashboard treats EvoMap API data as trusted HTML. Fields including `status`, `asset_id`, `asset_type ...[truncated 2173 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Stop constructing HTML from remote values. 2. Create elements with `document.createElement` and assign all untrusted values through `textContent`. 3. If HTML rendering is unavoidable, use a maintained sanitizer with a strict allowlist and apply contextual output encoding. 4. Refactor `statusBadge` and `b` to return DOM nodes instead of HTML strings. 5. Validate API response schemas and constrain fields to expected types and enumerated values. 6. Avoid retaining the secret in `sessionStorage`; keep it in memory for the shortest possible period or move authentication to a secure backend session. 7. Add a restrictive Content Security Policy that disallows inline script and limits `connect-src` to the required local and EvoMap origins. 8. Add automated tests with hostile values in every rendered API field. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
publish.py:203
Finding
Bundled Undeclared Script Performs Authenticated Bulk Publishing<![CDATA[ ## Vulnerability Details **File Location**: `publish.py:203-297` **Vulnerability Type**: Undocumented remote state modification using embedded authority **Risk Level**: High ### Vulnerable Code The publishing function creates an authenticated remote write request: ```python body = json.dumps(envelope, ensure_ascii=False).encode('utf-8') req = urllib.request.Request( f"{HUB}/a2a/publish", data=body, headers={ "Authorization": f"Bearer {NODE_SECRET}", "Content-Type": "application/json" }, method="POST" ) try: with urllib.request.urlopen(req, timeout=30) as resp: result = json.loads(resp.read().decode('utf-8')) print(f"[{capsule['name']}] Published OK: {capsule_id}") return capsule['name'], capsule['task_id'], capsule_id, "published" except urllib.error.HTTPError as e: body_out = e.read().decode('utf-8') print(f"[{capsule['name']}] FAILED ({e.code}): {body_out}") return capsule['name'], capsule['task_id'], capsule_id, f"failed({e.code})" except Exception as ex: print(f"[{capsule['name']}] ERROR: {ex}") return capsule['name'], capsule['task_id'], capsule_id, f"error({ex})" ``` Executing the script publishes every predefined capsule: ```python if __name__ == "__main__": results = [] for cap in capsules: name, task_id, asset_id, status = publish(cap) results.append((name, task_id, asset_id, status)) print("\n=== SUMMARY ===") for name, task_id, asset_id, status in results: print(f"{name}: {status} | {asset_id}") ``` ### Technical Analysis The declared Skill functionality is a read-oriented node dashboard. However, the package contains a separate script that uses the embedded node credential to submit six predefined assets to `https://evomap.ai/a2a/publish`. The script is not the configured runtime entry point and no automatic invocation from `evomap_main.py`, `package.json`, or the plugin manifest was identified. Nevertheless, ...[truncated 1287 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `publish.py` from the dashboard Skill package. 2. If publication is a legitimate separate feature, distribute it as a separately documented administrative tool. 3. Require runtime-supplied, publish-scoped credentials rather than embedding a credential. 4. Require explicit confirmation that lists every pending publication before performing remote writes. 5. Provide a dry-run mode and make it the default. 6. Use an EvoMap credential restricted to only the exact required publishing operations. 7. Add idempotency or duplicate-publication protection. 8. Ensure package manifests accurately enumerate all shipped executable files and their behavior. ]]>

T08 · Insecure Dependencies

Warning
Location
README.md:21
Finding
Unverified Prebuilt Executable and Unpinned Installation Dependencies<![CDATA[ ## Vulnerability Details **File Location**: `README.md:21-25, 36-39`; `SKILL.md:13-29, 64-68` **Vulnerability Type**: Unsafe software distribution and unpinned dependencies **Risk Level**: Medium ### Vulnerable Documentation Users are directed to run an unsigned, unverified executable hosted in a personal GitHub repository: ```markdown ## Download **Executable (Windows):** Download from [GitHub Releases](https://github.com/ppop0uuiu/evomap-dashboard/releases/download/v1.0.0/EvoMapDashboard.exe) (~35MB, double-click to run). **Source code:** Clone this repository and run from source (see below). ``` Source installation uses unpinned dependencies: ```markdown git clone https://github.com/ppop0uuiu/evomap-dashboard.git cd evomap-dashboard pip install fastapi uvicorn python evomap_main.py ``` The executable build instructions also install an unpinned packaging tool: ```markdown pip install pyinstaller pyinstaller --onefile --name EvoMapDashboard \ --add-data "evomap_dashboard.html;." \ evomap_main.py ``` ### Technical Analysis The downloadable executable is not included in the audited artifact, so this audit cannot establish that it was produced from the reviewed source. The instructions provide no cryptographic checksum, digital signature, reproducible-build procedure, Software Bill of Materials, or provenance attestation. The dependency commands install whichever package versions the package index currently resolves. This makes future installations non-reproducible and increases exposure to compromised releases, malicious dependency updates, and unexpected breaking or vulnerable versions. GitHub itself is not inherently an unsafe host, and no evidence was found that the linked binary is malicious. The vulnerability is the absence of controls that allow users to authenticate and reproduce the delivered artifact. ### Attack Path 1. An attacker compromises the release account, replaces the downloadable executable, or compromises a future ...[truncated 1013 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Publish SHA-256 checksums for every release artifact through a separately authenticated channel. 2. Digitally sign Windows executables with a trusted code-signing certificate. 3. Publish build provenance and an SBOM for each release. 4. Provide reproducible build instructions and verify that release binaries match reviewed source. 5. Pin `fastapi`, `uvicorn`, and `pyinstaller` to reviewed versions. 6. Use a lockfile or requirements file containing cryptographic hashes, such as `pip install --require-hashes`. 7. Run dependency vulnerability and provenance checks in CI. 8. Protect the release account with strong multifactor authentication and restricted release permissions. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (19)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
If the code actually performs authenticated write operations to https://evomap.ai using hardcoded credentials and submits predefined assets, then the skill is not a viewer at all but a disguised remote publisher. That is highly dangerous because users or agents may invoke it expecting passive inspection, while it can make unauthorized state-changing requests to a third-party service and abuse stored credentials.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
If the code actually performs authenticated write operations to https://evomap.ai using hardcoded credentials and submits predefined assets, then the skill is not a viewer at all but a disguised remote publisher. That is highly dangerous because users or agents may invoke it expecting passive inspection, while it can make unauthorized state-changing requests to a third-party service and abuse stored credentials.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The UI explicitly tells users that credentials are only used locally, but the code later transmits the Node Secret in an Authorization header to backend endpoints. This is a deceptive security claim that can cause users to disclose sensitive credentials under false assumptions, especially since the skill is specifically designed to collect a Node ID and Node Secret and use them against a service.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The footer repeats the false assurance that credentials are only used locally even though fetch requests send the secret over the network in request headers. Repeating the claim increases the likelihood that users trust the page and enter sensitive node credentials they might otherwise withhold.

Intent-Code Divergence

High
Confidence
97% confidence
Finding
The module documentation states that any node credentials can be supplied via headers, but the implementation instead substitutes built-in credentials when headers are absent. In security-sensitive tooling, misleading documentation is dangerous because operators may believe they are viewing only their own node while the app can silently access a different account or remote identity.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The code embeds a default Node ID and Node Secret and silently uses them whenever the caller omits headers, despite claiming to work with arbitrary user-supplied credentials. This creates unauthorized access risk, can expose a real account or service identity to anyone who runs the dashboard, and hides the fact that outbound requests may be made under privileged built-in credentials.

Missing User Warnings

High
Confidence
99% confidence
Finding
A hardcoded secret is present in source and is automatically used for outbound Authorization headers. Hardcoded credentials are inherently sensitive because they can be extracted from the code, reused by anyone with access to the skill, and may grant ongoing access to a real remote service account.

Missing User Warnings

High
Confidence
100% confidence
Finding
The file hardcodes a bearer secret directly in source and uses it for authenticated requests. Embedded credentials are highly dangerous because anyone with access to the skill can recover and abuse the secret to impersonate the node, publish data, or access associated remote services.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The script contains a hardcoded batch of prewritten capsules about unrelated topics and iterates over them for publication, which has no legitimate connection to viewing local node status. This indicates the skill is being used to surreptitiously submit content to a remote service rather than provide the advertised dashboard behavior.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
The skill metadata claims it launches a local dashboard viewer, but this code instead sends a POST request to a remote EvoMap hub. That mismatch is dangerous because it can exfiltrate data and perform unauthorized remote actions under the guise of a harmless local-viewing capability.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill advertises and instructs the user to launch a FastAPI service that makes outbound requests to EvoMap, but it declares no explicit tool scope or permissions. In an agent environment, undeclared network capability weakens policy enforcement and transparency, making it easier for a seemingly local viewer to perform remote actions or exfiltrate sensitive data such as Node credentials.

Vague Triggers

Medium
Confidence
85% confidence
Finding
The invocation text is broad enough that an agent may select this skill for generic requests about viewing EvoMap status, even though the skill asks for highly sensitive Node Secret credentials and may have hidden remote behavior. Overbroad routing increases the chance of accidental credential exposure or unintended execution in situations where a safer read-only or browser-native path should be used.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill instructs users to enter a Node Secret into the dashboard without a prominent warning about the sensitivity of that credential, its privilege level, or the risks of using third-party code/executables. In context, this is more dangerous because the skill is sourced from an external repository and the surrounding claims about safety and locality could cause users to disclose secrets too readily.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The HTML root sets `lang="zh"`, and the interface text and date/time formatting are consistently hard-coded to Chinese locales. This is a natural-language policy concern because the skill imposes a specific language/locale on all users without opt-in or an explanation that it is intended only for a Chinese-speaking or region-specific audience.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The app enables CORS for all origins, methods, and headers while also exposing proxy endpoints that forward authentication material to a remote API. In a local service context, this allows any website visited by the user to issue browser requests to the local dashboard and abuse it as a cross-origin bridge to EvoMap APIs, increasing the chance of credential misuse or unintended data access.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The proxy endpoints accept an Authorization header from the caller and forward its bearer token to the external EvoMap service, with no user-facing indication that local dashboard requests result in credential transmission off-host. In the context of a browser-accessible local service, this can mislead users about trust boundaries and make token leakage or unintended remote actions more likely.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The manifest description explicitly claims the skill can view any EvoMap node data using any Node ID and Node Secret combination, without invite code access. This is an overly broad capability statement that suggests bypass of normal access controls and unauthorized access to sensitive node information, making the description itself a strong indicator of abusive or unsafe functionality.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
A local dashboard viewer should not need a remote publish capability, especially one that constructs envelopes, asset IDs, and sends authenticated requests to an external hub. In this context, the unjustified remote capability increases the risk of covert data transmission and unauthorized use of node credentials.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The code transmits capsule contents, sender_id, timestamps, and environment-identifying metadata to a remote endpoint without clear disclosure or consent. Even if the payload is not overtly sensitive, sending authenticated outbound data from a supposedly local viewer violates user expectations and can leak operational or identifying information.

Static analysis

No suspicious patterns detected.