Back to skill

Security audit

Google Ads Campaigns as Code

Security checks for vulnerabilities and agentic risk

Overview

This skill is a clearly scoped Google Ads campaign planner/creator that defaults to offline review and only creates paused campaigns after explicit approval.

Install only if you are comfortable giving the apply path Google Ads OAuth credentials for the target account. Review the dry-run output before using --apply, confirm campaigns are created paused, and treat the budget-cap script as a separate live-account automation that a human must scope, install, and monitor.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (4)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill declares no explicit tool scope or permission boundary even though it describes capabilities involving environment-variable access and file reads on the mutating path. In an agent ecosystem, missing declarative scoping weakens defense-in-depth: the runtime may grant broader access than reviewers expect, making accidental secret exposure or misuse easier if the skill is invoked incorrectly or later modified.

Content

No source excerpt is available for this finding.

Excessive Permissions

Low
Category
Privilege Escalation
Confidence
80% confidence
Finding

Skill requests more permissions than appear necessary for its stated functionality. Review if elevated access is justified.

Content

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

md
| Invalid config | Missing cap, prefix, or sheet URL, a wrong type, or unreadable spend: the run **throws before any pause or write** and the script runner reports the failure. Never falls back to "every campaign" |
| Leaves the machine | Only on the mutating path: the campaign tree, to the ad network you authenticated against |
| Spend authority | None. Every campaign is created PAUSED. Going live is a human click in the vendor UI |
| Prerequisites (mutating path) | A dedicated virtualenv with `pip install "google-ads==33.0.0"` (the script refuses `--apply` on any other version). **No developer token** — sunset 2026-09-09. API access = the level of the **Google Cloud project** that issued the OAuth client: Test (test accounts only) · Explorer (production, 2,880 ops/day, no `KeywordPlanIdeaService`) · Basic (15,000 ops/day, requires the project's brand verification) · Standard (unlimited, ~10 business days review). Upgrade: Cloud console → Google Ads API overview → *Upgrade access level*. A new project is at Test and cannot write to production. OAuth **Desktop** client + refresh token generated elsewhere; an app left in *Testing* status issues refresh tokens that expire after 7 days. Manager (MCC) account only if you reach the account through one |
| Your obligations | Vendor ad policies, consumer-protection and advertising law apply to everything you publish. Regulated verticals (health, finance, legal, licensed trades) carry statutory constraints on top of vendor policy — clear the copy with the accountable human, not with this skill |

Setup for the mutating path only: `python3 -m venv .venv && .venv/bin/pip install "google-ads==33.0.0"`, then export `GOOGLE_ADS_CLIENT_ID`, `GOOGLE_ADS_CLIENT_SECRET`, `GOOGLE_ADS_REFRESH_TOKEN`, `GOOGLE_ADS_CUSTOMER_ID`, plus `GOOGLE_ADS_LOGIN_CUSTOMER_ID` **only** when access goes through a manager account. That is the whole list: the script declares no env var it does not consume. Conversion goals are an **account-level** setti
...[truncated 24 chars]

Excessive Permissions

Low
Category
Privilege Escalation
Confidence
80% confidence
Finding

Skill requests more permissions than appear necessary for its stated functionality. Review if elevated access is justified.

Content

Scanner excerpt · ads-search.py (reported line 36)May include surrounding context.

python
| Invalid config | Missing cap, prefix, or sheet URL, a wrong type, or unreadable spend: the run **throws before any pause or write** and the script runner reports the failure. Never falls back to "every campaign" |
| Leaves the machine | Only on the mutating path: the campaign tree, to the ad network you authenticated against |
| Spend authority | None. Every campaign is created PAUSED. Going live is a human click in the vendor UI |
| Prerequisites (mutating path) | A dedicated virtualenv with `pip install "google-ads==33.0.0"` (the script refuses `--apply` on any other version). **No developer token** — sunset 2026-09-09. API access = the level of the **Google Cloud project** that issued the OAuth client: Test (test accounts only) · Explorer (production, 2,880 ops/day, no `KeywordPlanIdeaService`) · Basic (15,000 ops/day, requires the project's brand verification) · Standard (unlimited, ~10 business days review). Upgrade: Cloud console → Google Ads API overview → *Upgrade access level*. A new project is at Test and cannot write to production. OAuth **Desktop** client + refresh token generated elsewhere; an app left in *Testing* status issues refresh tokens that expire after 7 days. Manager (MCC) account only if you reach the account through one |
| Your obligations | Vendor ad policies, consumer-protection and advertising law apply to everything you publish. Regulated verticals (health, finance, legal, licensed trades) carry statutory constraints on top of vendor policy — clear the copy with the accountable human, not with this skill |

Setup for the mutating path only: `python3 -m venv .venv && .venv/bin/pip install "google-ads==33.0.0"`, then export `GOOGLE_ADS_CLIENT_ID`, `GOOGLE_ADS_CLIENT_SECRET`, `GOOGLE_ADS_REFRESH_TOKEN`, `GOOGLE_ADS_CUSTOMER_ID`, plus `GOOGLE_ADS_LOGIN_CUSTOMER_ID` **only** when access goes through a manager account. That is the whole list: the script declares no env var it does not consume. Conversion goals are an **account-level** setti
...[truncated 24 chars]

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

This file is a JSON manifest/config, so only SQP-1 and SQP-3 apply. The language and locale fields could look like a forced locale at first glance, but the adjacent note explicitly says they are required spec fields with no default and should be set to the audience, which provides justification and avoids a natural-language policy violation.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.