Back to skill

Security audit

GCP Keyless

Security checks for vulnerabilities and agentic risk

Overview

This skill is a coherent GCP keyless-access helper with disclosed cloud-auth diagnostics and a read-only workflow template, though users should review its credential check and installer guidance before use.

Install only if you are comfortable letting the helper inspect local gh and gcloud state and, when gcloud is installed, briefly generate an access token to validate credentials without printing it. Treat the workflow validator as a convenience check, not a security guarantee, and pin or verify the installer in higher-assurance environments.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (3)

T08 · Insecure Dependencies

Warning
Location
README.md:24
Finding

Unpinned npm CLI Execution Creates a Supply-Chain Risk

Content
View full analysis

Vulnerability Details

File Location: README.md:24-28
Vulnerability Type: Unpinned third-party package execution
Risk Level: Medium

Vulnerable Code

markdown
## Get started

```bash
npx skills add AntreasAntoniou/gcp-keyless
text

### Technical Analysis

The documented installation command invokes `npx skills` without specifying an exact package version or verifying package integrity. If the package is not already installed locally, `npx` can retrieve and execute the package currently published under that name.

Consequently, the executable installer is not cryptographically tied to the version reviewed in this audit. Compromise of the npm publisher account, registry package, or a future malicious release could cause the command to execute code different from the audited repository. The GitHub repository argument does not constrain the implementation of the `skills` npm CLI itself.

### Attack Path

1. An attacker compromises the npm package or its publisher account, or publishes a malicious future version through another applicable supply-chain failure.
2. The attacker adds malicious behavior to the CLI or its installation lifecycle.
3. A user follows the README and runs the unversioned `npx skills add ...` command.
4. `npx` retrieves the current package version and executes it.
5. The malicious package runs with the operating-system privileges and environment access of the user performing the installation.

### Impact Assessment

Successful exploitation could permit arbitrary local code execution under the installing user's account. Depending on that account's permissions and environment, exposed resources could include repository contents, environment variables, local credentials, SSH configuration, cloud CLI credentials, and writable user files. The project itself does not demonstrate exploitation of this path; the risk arises from the unpinned executable dependency.
Remediation
View remediation

Remediation Suggestions

  • Pin the installer to a specifically reviewed version, such as npx skills@<exact-version>, rather than resolving the latest release.
  • Publish and document the expected npm package publisher, package provenance, and verified release version.
  • Use npm provenance attestations and integrity verification where available.
  • Prefer a locked installation process with a committed lockfile when the surrounding installation environment supports it.
  • In higher-assurance environments, download and verify a known release artifact before executing the installer.
  • Establish a release process that reviews both the repository content and the exact installer version referenced by the documentation.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/doctor.py:54
Finding

Credential Diagnostic Generates and Captures a Live GCP Access Token

Content
View full analysis

Vulnerability Details

File Location: scripts/doctor.py:54-66
Vulnerability Type: Unnecessary handling of bearer-token material
Risk Level: Low

Vulnerable Code

python
def gcloud_state() -> tuple[str | None, bool]:
    if not shutil.which("gcloud"):
        return None, False
    account = _run([
        "gcloud", "auth", "list", "--filter=status:ACTIVE",
        "--format=value(account)", "--limit=1",
    ])
    active = (account.stdout.strip().splitlines()[0]
              if account and account.returncode == 0 and account.stdout.strip() else None)
    # The token is captured in memory only and never returned or printed.
    probe = _run(["gcloud", "auth", "print-access-token", "--quiet"])
    valid = bool(probe and probe.returncode == 0 and probe.stdout.strip())
    return active, valid

The subprocess helper captures command output:

python
return subprocess.run(
    argv,
    check=False,
    capture_output=True,
    text=True,
    timeout=timeout,
    env={**os.environ, "CLOUDSDK_CORE_DISABLE_PROMPTS": "1"},
)

Technical Analysis

The diagnostic executes gcloud auth print-access-token, causing gcloud to mint or retrieve a live bearer token and write it to subprocess standard output. Because _run uses capture_output=True, the token remains in the returned CompletedProcess.stdout value until that object is released.

The current implementation does not print or transmit the token, and no direct exfiltration path was found. Nevertheless, generating complete credential material merely to determine whether credentials are valid increases secret exposure unnecessarily. The token may be visible to process instrumentation, debuggers, memory inspection, crash diagnostics, or future logging added around _run.

This behavior is narrower than obtaining additional permissions: it uses the authority already held by the configured gcloud account. Ho ...[truncated 1284 chars]

Remediation
View remediation

Remediation Suggestions

  • Avoid gcloud auth print-access-token for a diagnostic that only needs to select an authentication route.
  • If configuration discovery is sufficient, report that an active account is configured without claiming that its credentials have been validated.
  • If live validation is required, use a narrowly scoped, non-mutating API request through a client that does not return raw token material to the diagnostic process.
  • Separate credential probing into an explicit opt-in mode and document that it performs an authenticated request.
  • If raw-token generation is unavoidable, minimize object lifetime, prevent logging of subprocess output, and ensure crash or telemetry systems cannot collect it.
  • Add tests confirming that normal diagnostic execution never invokes a command that prints access tokens.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/validate_workflow.py:18
Finding

Regex-Based Workflow Validator Can Produce False Security Approval

Content
View full analysis

Vulnerability Details

File Location: scripts/validate_workflow.py:18-49
Vulnerability Type: Incomplete security validation of GitHub Actions workflows
Risk Level: Medium

Vulnerable Code

python
def validate(text: str) -> list[str]:
    failures: list[str] = []
    lowered = text.lower()
    if not re.search(r"(?m)^\s*id-token:\s*write\s*$", text):
        failures.append("permissions.id-token must be write")
    if not re.search(r"(?m)^\s*contents:\s*read\s*$", text):
        failures.append("permissions.contents must be read")
    extra_writes = [name for name in re.findall(
        r"(?m)^\s*([a-z][a-z0-9-]*):\s*write\s*$", text) if name != "id-token"]
    if extra_writes:
        failures.append(f"unexpected write permissions: {sorted(set(extra_writes))}")
    if not re.search(r"(?ms)^\s*operation:\s*.*?type:\s*choice\s*$", text):
        failures.append("workflow_dispatch.operation must be a choice input")
    if re.search(r"(?mi)^\s*(command|cmd|script|shell):\s*$", text):
        failures.append("arbitrary command-like workflow input is forbidden")
    for action, pin in PINS.items():
        uses = re.findall(rf"(?m)^\s*-?\s*uses:\s*{re.escape(action)}@([^\s#]+)", text)
        if uses != [pin]:
            failures.append(f"{action} must appear once at exact pin {pin}")
    for action, ref in re.findall(r"(?m)^\s*-?\s*uses:\s*([^@\s]+)@([^\s#]+)", text):
        if not re.fullmatch(r"[0-9a-f]{40}", ref):
            failures.append(f"action is not pinned to a full commit SHA: {action}@{ref}")
    forbidden = ("credentials_json", "service_account_key", "google_credentials")
    for marker in forbidden:
        if marker in lowered:
            failures.append(f"forbidden credential pattern: {marker}")
    run_blocks = re.findall(r"(?ms)^\s*run:\s*\|\s*\n(.*?)(?=^\s{0,8}[A-Za-z_-]+:|\Z)", text)
    if any(re.search(r"\$\{\{\s*inputs\.[^}]+\}\}", block) for block i
...[truncated 3066 chars]
Remediation
View remediation

Remediation Suggestions

  • Parse the workflow with a YAML parser instead of validating raw text with regular expressions.
  • Reject duplicate mapping keys and handle YAML aliases and scalar forms explicitly.
  • Validate the complete effective permission model at both workflow and job levels.
  • Explicitly reject permissions: write-all and any permission not present in the approved allowlist.
  • Define and enforce a strict schema for allowed triggers, inputs, environments, jobs, steps, actions, and action parameters.
  • Reject every unknown or additional action and shell step rather than merely requiring expected actions to be present.
  • Validate exact reviewed command structures for the three supported operations.
  • Consider generating the workflow from a trusted canonical template and comparing its parsed structure against that template.
  • Add negative regression tests for write-all, job-level permission overrides, extra actions, extra run steps, duplicate keys, YAML aliases, folded scalars, and alternate interpolation paths.
  • Continue treating validation as a local linting aid rather than proof of live IAM, WIF, or repository security.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (9)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SECURITY.md (reported line 3)May include surrounding context.

md
# Security

Please report vulnerabilities privately through GitHub's security advisory interface. Do not include credentials, private documents, access tokens, internal hostnames, or personal paths in a public issue.

This project provides agent instructions and local helpers. It is not a sandbox, an authorization system, or a guarantee that an external service is secure.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 34)May include surrounding context.

md
# Security

Please report vulnerabilities privately through GitHub's security advisory interface. Do not include credentials, private documents, access tokens, internal hostnames, or personal paths in a public issue.

This project provides agent instructions and local helpers. It is not a sandbox, an authorization system, or a guarantee that an external service is secure.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description is about operating Google Cloud using keyless authentication patterns. The actual code chunk does not implement or exercise GCP access, OAuth refresh handling, Workload Identity Federation, managed service accounts, or credential acquisition. Instead, it reads local files and asserts absence of certain strings and workflow fields. That is a materially different primary purpose and set of capabilities from the declared skill behavior.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/doctor.py (reported line 22)May include surrounding context.

python
capture_output=True,
            text=True,
            timeout=timeout,
            env={**os.environ, "CLOUDSDK_CORE_DISABLE_PROMPTS": "1"},
        )
    except (OSError, subprocess.SubprocessError):
        return None

Cloud Metadata Access

High
Category
Server-Side Request Forgery
Confidence
90% confidence
Finding

Code accesses a cloud instance metadata endpoint (e.g. 169.254.169.254). A single request can return temporary IAM credentials, making this a high-value SSRF target for credential theft.

Content

Scanner excerpt · scripts/doctor.py (reported line 30)May include surrounding context.

python
def metadata_identity() -> str | None:
    request = urllib.request.Request(
        "http://metadata.google.internal/computeMetadata/v1/instance/"
        "service-accounts/default/email",
        headers={"Metadata-Flavor": "Google"},
    )

Rp1

Medium
Category
MCP Rug Pull
Confidence
88% confidence
Finding

The README instructs users to run npx skills add AntreasAntoniou/gcp-keyless without pinning the package version or exact artifact source. Because npx resolves and executes remote package code at install time, a future malicious or compromised release of the referenced tool or dependency chain could execute unintended code on the user's machine.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
78% confidence
Finding

The skill describes use of shell, network, file reads, and environment-dependent operations but does not declare any explicit tool scope or allowed-tools boundary. In an agent setting, this can lead to over-broad execution privileges and make it easier for a downstream prompt or user request to trigger unintended commands or data access.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/doctor.py (reported line 16)May include surrounding context.

python
def _run(argv: list[str], timeout: float = 8.0) -> subprocess.CompletedProcess[str] | None:
    try:
        return subprocess.run(
            argv,
            check=False,
            capture_output=True,

Unbounded Resource Access

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.

Content

Scanner excerpt · scripts/doctor.py (reported line 35)May include surrounding context.

python
headers={"Metadata-Flavor": "Google"},
    )
    try:
        with urllib.request.urlopen(request, timeout=0.4) as response:
            if response.headers.get("Metadata-Flavor") != "Google":
                return None
            email = response.read(512).decode("utf-8", "replace").strip()

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
scripts/test_gcp_keyless.py:12