Back to skill

Security audit

nomos-decision-hub

Security checks for vulnerabilities and agentic risk

Overview

This skill appears to be a decision-analysis service wrapper, but it asks for sensitive deployment and approval authority without enough audited runtime code or user-control safeguards.

Install only after reviewing the missing second_perspective runtime and deployment code. Do not grant database credentials, API keys, OIDC variables, public network binding, or approval-writing access unless you explicitly need the API deployment and can enforce network isolation, per-user identity, scoped database permissions, HTTPS domain control, and confirmation before approval actions. Treat the included Docker and bearer-key examples as pilot-only, not enterprise-safe production guidance.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (3)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
SKILL.md:9
Finding
Excessive Access to Credentials, Database Resources, Network Egress, and Public Port Binding<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 9-24 **Vulnerability Type**: Excessive capability declaration and violation of least privilege **Risk Level**: High ### Vulnerable Code ```yaml # Capability disclosure (NVIDIA MCP Least-Privilege): declared vs. actual. # The skill's runtime engine (second_perspective) requires the following # beyond `python3`. Loaders must grant these explicitly, not implicitly. capabilities: network_egress: - purpose: "OIDC discovery & JWKS fetch (HTTPS only, issuer-controlled host)" scope: "outbound to SP_OIDC_ISSUER and its jwks_uri only" env_read: - SP_API_KEY - SP_DATABASE_DSN # contains PostgreSQL credentials - SP_OIDC_ISSUER - SP_OIDC_CLIENT_ID - SP_OIDC_AUDIENCE - SP_PUBLIC_BASE_URL database: "PostgreSQL connection via SP_DATABASE_DSN" docker_deploy: true binds_port: "0.0.0.0:8000 (configurable; requires external network isolation)" ``` ### Technical Analysis The Skill asks its loader to grant access to an API key, PostgreSQL credentials, identity-provider configuration, database connectivity, outbound network access, Docker deployment, and a listener on all network interfaces. These capabilities materially exceed what is needed by the executable code included in the audited artifact. The only supplied Python program, `scripts/export_openapi.py`, reads `SP_PUBLIC_BASE_URL` and writes an OpenAPI document. No included implementation uses the API key, database DSN, OIDC variables, database access, or OIDC network egress. The capability request also conflicts with the artifact's documented implementation status. `BRANCH_MANIFEST.md`, lines 49-55, identifies durable databases, OIDC, delegated authority, RBAC/ABAC, and other production controls as deferred. Moreover, the actual `second_perspective` runtime source is absent, so it is impossible to verify that the requested secrets and egress are safely handled. Granting sensitive environment access ...[truncated 1587 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove every capability that the included executable code does not require. 2. Restrict the OpenAPI exporter to `SP_PUBLIC_BASE_URL`; do not expose API keys, database credentials, or OIDC configuration to it. 3. Separate API deployment from the documentation/export Skill so that each component receives an independent capability profile. 4. Include and audit the complete `second_perspective` runtime before granting it credentials or network access. 5. Pin the runtime package by exact version and cryptographic hash. 6. Grant database access through a narrowly scoped account with only the required tables and operations. 7. Use a secret manager or short-lived credentials rather than broadly inherited environment variables. 8. Restrict OIDC egress at the network layer to a validated HTTPS issuer and its validated JWKS endpoint. 9. Bind to `127.0.0.1` by default. Require an explicit deployment option to listen on `0.0.0.0`. 10. Add automated tests that fail when declared capabilities exceed capabilities exercised by the audited source. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/export_openapi.py:41
Finding
Unvalidated OpenAPI Server URL Can Redirect Bearer Tokens and Sensitive Decision Data<![CDATA[ ## Vulnerability Details **File Location**: `scripts/export_openapi.py`, lines 41-43; generated result at `openapi-action.yaml`, lines 1866-1870 **Vulnerability Type**: Untrusted configuration used as an authenticated API destination **Risk Level**: High ### Vulnerable Code ```python base_url = os.getenv("SP_PUBLIC_BASE_URL", "https://YOUR-DOMAIN.example.com") destination = Path(args.output) destination.write_text( yaml.safe_dump( action_schema(base_url), sort_keys=False, allow_unicode=True, width=100, ), encoding="utf-8", ) ``` The environment value is inserted into the schema without validation: ```python def action_schema(public_base_url: str) -> dict[str, Any]: schema = app.openapi() schema["servers"] = [{"url": public_base_url.rstrip("/")}] ``` The generated schema combines that destination with bearer authentication: ```yaml securitySchemes: bearerAuth: type: http scheme: bearer servers: - url: https://YOUR-DOMAIN.example.com ``` The exporter also applies bearer authentication to every non-health operation: ```python for path, operations in schema.get("paths", {}).items(): if path == "/health": continue for operation in operations.values(): if not isinstance(operation, dict) or "operationId" not in operation: continue operation["security"] = [{"bearerAuth": []}] ``` ### Technical Analysis `SP_PUBLIC_BASE_URL` is treated as trusted even though environment variables can be influenced by CI jobs, shell profiles, container configuration, deployment manifests, or a compromised build environment. The exporter performs no URL parsing, HTTPS enforcement, hostname allowlisting, local-address rejection, or integrity check. The generated OpenAPI document describes authenticated endpoints that accept or return decision requests, evidence, reports, histories, owner information, authorization references, and approvals. An Action client importing ...[truncated 1769 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse `SP_PUBLIC_BASE_URL` with a standards-compliant URL parser. 2. Require the `https` scheme and reject plaintext HTTP. 3. Reject embedded user information, fragments, unexpected paths, and malformed hostnames. 4. Enforce an explicit allowlist of production API domains rather than accepting an arbitrary host. 5. Reject loopback, link-local, private, multicast, and metadata-service addresses after DNS resolution where they are not expressly required. 6. Require an explicit command-line confirmation when exporting for a domain that differs from the approved production domain. 7. Print the resolved server URL prominently and fail generation if it remains the placeholder value. 8. Cryptographically sign or hash approved OpenAPI artifacts and verify them before publication or import. 9. Use short-lived, audience-bound credentials so a token captured by another host cannot be replayed against the legitimate API. 10. Add tests covering malicious values such as `http://attacker.example`, URLs containing user information, local IP addresses, and invalid schemes. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:70
Finding
Enterprise Deployment Example Publicly Exposes a Sensitive API Using a Shared Bearer Secret<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 70-76 **Related Locations**: `README.md`, lines 140-166 and 179-191; `docs/DECISION_FOUNDATION_V0_2.md`, lines 172-174 **Vulnerability Type**: Insecure production deployment guidance and insufficient authentication boundary **Risk Level**: Medium ### Vulnerable Code ```bash # Docker部署 docker build -t nomos-hub . docker run -p 8000:8000 \ -e SP_ENV=production \ -e SP_API_KEY=your-secret-key \ -e SP_DATABASE_DSN=postgresql://user:pass@db:5432/nomos \ nomos-hub ``` The corresponding README instructs operators to expose the application on all interfaces: ```bash export SP_ENV=production export SP_API_KEY="replace-with-a-strong-secret" uvicorn second_perspective.api.main:app --host 0.0.0.0 --port 8000 ``` It then directs clients to use a single bearer key: ```text Protected clients send `Authorization: Bearer <SP_API_KEY>`. ``` The documented API includes sensitive read and write operations: ```text - `POST /v1/hub/analyze` - `GET /v1/hub/reports/{hub_run_id}` - `POST /v1/decisions/evaluate` - `GET /v1/decisions/{decision_id}` - `GET /v1/decisions/{decision_id}/history` - `POST /v1/decisions/{decision_id}/approval` ``` The project documentation itself acknowledges the limitation: ```text The v0.2 Bearer API key is only suitable for development and controlled pilot deployments and cannot prove a real person's identity. Production approval must integrate an organizational identity provider and verify the authorization chain. ``` ### Technical Analysis The enterprise deployment example maps port 8000 to the host and describes the service as binding to `0.0.0.0`. This can expose the API to every network from which the host is reachable. Authentication relies on a shared bearer secret rather than an individual identity, delegated authority, or per-resource authorization model. A shared bearer token cannot reliably attribute approval operations to a person. Anyone possessing the ...[truncated 2116 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not label the shared-key deployment as enterprise-ready. 2. Bind the service to `127.0.0.1` by default and require an explicit configuration change for external exposure. 3. Place the service behind a TLS-terminating reverse proxy or API gateway. 4. Replace the shared bearer secret with OIDC access tokens tied to individual users or service identities. 5. Validate issuer, audience, signature, expiration, and authorization claims for every request. 6. Implement resource-level authorization and tenant isolation for report, history, and approval endpoints. 7. Require stronger, separately scoped authorization for approval operations. 8. Derive approver identity from the authenticated principal rather than accepting identity solely from request content. 9. Add rate limiting, request-size limits, audit logging, and alerting. 10. Supply secrets through a secret manager or Docker secrets rather than inline `-e` values. 11. Use a restricted database account and avoid examples that place plaintext credentials directly in command lines. 12. Include the Dockerfile, API security source, and security tests in the auditable artifact before recommending production deployment. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (12)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description presents an analytical/decisioning system with domain-specific capabilities such as causal analysis, audit, stress testing, and root-cause investigation. The actual code chunk does none of those things. It is an API documentation/export helper for generating an Action-ready OpenAPI specification. Its behavior is limited to schema transformation and file output, plus reading an environment variable for the public base URL. This is a materially different primary purpose, so the description does not accurately represent the code.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill exposes powerful capabilities in metadata and examples, including reading sensitive environment variables, database access, outbound network access, port binding, and Docker-based deployment, but it does not define a formal tool-permission scope such as `permissions` or `allowed-tools`. In a loader or agent platform that relies on explicit tool scoping, this gap can lead to over-broad implicit grants and unintended access to secrets or infrastructure.

Rp1

Medium
Category
MCP Rug Pull
Confidence
79% confidence
Finding
The deployment example uses an unpinned local image reference (`nomos-hub`) and does not encourage running a specifically tagged or digested image. In practice, this weakens supply-chain integrity because operators may rebuild or pull a different image under the same name, making deployments non-reproducible and increasing the chance of accidental or malicious image substitution.

Vague Triggers

Medium
Confidence
89% confidence
Finding
The auto-trigger conditions are broad and loosely scoped, covering generic topics like decision engine design, compliance audit, stress testing, and governance. This can cause the skill to activate in contexts where it was not intended, increasing the chance of inappropriate tool routing, over-collection of sensitive decision data, or unreviewed influence on high-stakes business or governance workflows.

Rp1

Medium
Category
MCP Rug Pull
Confidence
75% confidence
Finding
Docker image references without a specific tag (:latest is implicit) or digest (@sha256:...) can be silently replaced by a malicious image.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
The entire skill documentation is written in Chinese, with no indication that users may choose another language or that the skill is intentionally limited to a Chinese-language or China-specific operating context. Under the policy, language constraints should be optional or clearly justified rather than implicitly enforced.

Vague Triggers

Medium
Confidence
89% confidence
Finding
The /v1/hub/analyze action can trigger high-impact remote analysis over rich decision, evidence, and scenario inputs, but the spec provides no explicit conditions limiting when the agent should call it. In an agent setting, lack of trigger-scope constraints increases the risk of over-broad autonomous invocation on sensitive enterprise data or on ambiguous user requests.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The hub analysis endpoint processes decision payloads plus scenario stress-test data, which may reveal strategic plans, assumptions, evidence inventories, and governance details, yet there is no explicit disclosure of remote processing. In an enterprise decision-support context, this omission is more dangerous because the data is likely sensitive and users may not expect it to leave the local conversation context.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The decision evaluation endpoint accepts detailed objectives, ownership, alternatives, assumptions, evidence, metadata, and responsibility references, but the spec gives no user-facing disclosure that this information is sent to an external service. This creates a meaningful data-handling risk because agents may forward sensitive business, compliance, or personnel information without the user's informed awareness.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The approval-recording endpoint changes decision state and creates an approval record, yet the spec does not state when the action must be withheld or require an explicit user authorization checkpoint. For agent-integrated tools, ambiguous approval semantics can let the model record approvals prematurely, incorrectly, or without proper human intent, undermining governance and audit integrity.

Tainted flow: 'base_url' from os.getenv (line 41, credential/environment) → pathlib.Path.write_text (file write)

Medium
Category
Data Flow
Content
base_url = os.getenv("SP_PUBLIC_BASE_URL", "https://YOUR-DOMAIN.example.com")
    destination = Path(args.output)
    destination.write_text(
        yaml.safe_dump(
            action_schema(base_url),
            sort_keys=False,
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
This JSON manifest includes a natural-language locale setting of "en" in metadata, which fixes the skill to English. Under the policy rules, forcing a specific language without user opt-in or a documented regional justification is a natural-language policy concern.

Static analysis

No suspicious patterns detected.