Back to skill

Security audit

Meta Ads CLI - control your ad campaigns

Security checks for vulnerabilities and agentic risk

Overview

This Meta Ads skill is coherent, but it needs review because it can perform spend-affecting ad changes using a weak, reusable approval check.

Review before installing. Use read-only Meta tokens whenever possible, avoid setting `META_ADS_AGENT_APPROVED`, require fresh approval text for each write, and do not run budget, activation, or destructive commands unless you have verified the exact account, object ID, and values. For production use, install dependencies from a locked and hash-verified source.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/meta_ads_agent.py:238
Finding
Mutation approval is not cryptographically or semantically bound to the requested operation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/meta_ads_agent.py:238-260`, with affected call sites at `scripts/meta_ads_agent.py:329-338` and `scripts/meta_ads_agent.py:414-423` **Vulnerability Type**: Weak authorization validation and reusable approval **Risk Level**: High ### Vulnerable Code ```python def approval_is_specific(text: Optional[str]) -> bool: if not text: return False compact = " ".join(text.strip().split()) return len(compact) >= 25 and compact.lower() not in GENERIC_APPROVALS def safety_problem(cls: Dict[str, Any], approved: Optional[str], allow_active: bool, allow_budget: bool, allow_destructive: bool, allow_unknown: bool) -> Optional[str]: if cls.get("secret_arg_signals"): return "Do not pass access tokens or secrets as CLI arguments. Put credentials in environment variables or official auth storage." if cls["risk"] == "non_meta": return "Refusing to run non-`meta ads` command through this guard." if cls["risk"] == "unknown" and not allow_unknown: return "Unknown CLI action. Check `meta ads <resource> <action> --help`; use --allow-unknown only after review." if cls["requires_approval"] and not approval_is_specific(approved): return "Write or unknown-risk command requires --approved with specific user approval, not a generic yes/ok." if cls["requires_allow_active"] and not allow_active: return "Activation requires --allow-active plus specific approval." if cls["requires_allow_budget"] and not allow_budget: return "Budget/bid/spend change requires --allow-budget plus specific approval." if cls["requires_allow_destructive"] and not allow_destructive: return "Destructive command requires --allow-destructive plus specific approval." return None ``` The direct command execution path accepts either command-line or reusable environment-based approval: ```python def cmd_run(args: argparse.Namespace) -> int: raw = strip ...[truncated 4246 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace free-form approval validation with a structured approval object containing: - Ad account ID - Resource type and action - Exact object ID - Exact fields and proposed values - Risk class - Issuance and expiration times - A unique nonce 2. Normalize the exact command and calculate a digest over its security-relevant arguments. Require the approved command digest to match the command being executed. 3. Make approvals single-use. Record consumed nonces or approval identifiers and reject replay attempts. 4. Remove or disable `META_ADS_AGENT_APPROVED` for write operations. If environment-based approval must remain, require a short expiration, one command digest, and one account scope. 5. Validate high-risk details explicitly. For example, budget approval should match the exact campaign ID, account ID, budget type, currency or minor-unit interpretation, and new amount. 6. Bind plan approval to the complete immutable plan digest. Do not apply one unstructured sentence to changed or additional plan steps. 7. Add tests proving that approval for one account, resource, object, value, or action cannot authorize a different operation. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements-meta-ads-cli.txt:1
Finding
Recorded package hashes are not enforced during dependency installation<![CDATA[ ## Vulnerability Details **File Location**: `requirements-meta-ads-cli.txt:1-4`; related installation instructions at `SKILL.md:39-45` and recorded but unenforced hashes at `resources/provenance.json:25-36` **Vulnerability Type**: Unenforced dependency integrity and incomplete supply-chain locking **Risk Level**: Medium ### Vulnerable Configuration ```text # Direct dependency for Meta's official Ads CLI. # Pinned for reproducibility and supply-chain review. Verify resources/provenance.json # against the current PyPI JSON before production installs. meta-ads==1.0.1 ``` The documented installation command is: ```bash python3.12 -m pip install -r requirements-meta-ads-cli.txt meta --help meta ads --help meta auth status ``` The project separately records wheel hashes, for example: ```json "wheel_hashes_sha256": [ {"filename":"meta_ads-1.0.1-cp312-cp312-macosx_15_0_arm64.whl","sha256":"a4ad47c845fd6c7c7434e5aab1a6ab7f702e51f421e9b005ba275d0e85cf2c47"}, {"filename":"meta_ads-1.0.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl","sha256":"8242dc9da9096e96b20484d61928bf698427faae7fc1cff85b2f0c174a19bb00"}, {"filename":"meta_ads-1.0.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_aarch64.whl","sha256":"4521a6fd07dd32110ed3b284fcf8e52e5a5f7eb59d4ae4e9559c3591810e9995"} ] ``` The hashes are metadata only and are not referenced by the requirements file or enforced by the documented `pip` command. ### Technical Analysis Pinning `meta-ads==1.0.1` constrains the selected version but does not authenticate the downloaded artifact. The installation does not use pip's `--require-hashes` mode, and the requirement entry has no `--hash=sha256:...` clauses. The project also records transitive requirements using minimum-version constraints: ```json "requires_dist": [ "click>=8.1", "facebook-business>=20.0", "python-dotenv>=1.0", "rich>=13.0" ] ``` Those transitive packages are neither ful ...[truncated 1801 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate a fully resolved lock file containing exact versions and SHA-256 hashes for the direct package and every transitive dependency. 2. Install using enforced hash verification: ```bash python3.12 -m pip install --require-hashes -r requirements-meta-ads-cli.txt ``` 3. Add all supported wheel hashes directly to requirement entries with `--hash=sha256:<digest>` rather than storing them only as informational provenance. 4. Maintain platform-specific lock files where dependency artifacts differ by Python version, operating system, or architecture. 5. Use a trusted internal package mirror or artifact repository and admit packages only after provenance, malware, and signature review. 6. Verify the lock file in continuous integration and fail builds when dependency resolution or hashes drift. 7. Review and update dependencies through a controlled process that checks release provenance, vulnerability advisories, package ownership changes, and artifact hashes before deployment. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (30)

Credential Access

High
Category
Privilege Escalation
Content
optional_env_vars:
  - BUSINESS_ID
  - META_ADS_AGENT_LOG
primary_credential: Meta Marketing API access token supplied through ACCESS_TOKEN
capability_signals:
  - requires-sensitive-meta-credentials
  - can-affect-ad-spend
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
optional_env_vars:
  - BUSINESS_ID
  - META_ADS_AGENT_LOG
primary_credential: Meta Marketing API access token supplied through ACCESS_TOKEN
capability_signals:
  - requires-sensitive-meta-credentials
  - can-affect-ad-spend
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
optional_env_vars:
  - BUSINESS_ID
  - META_ADS_AGENT_LOG
primary_credential: Meta Marketing API access token supplied through ACCESS_TOKEN
capability_signals:
  - requires-sensitive-meta-credentials
  - can-affect-ad-spend
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Ae1

High
Category
analysis-evasion
Content
primary_cli":"meta ads","official_cli_package":"meta-ads==1.0.1","wrapped_cli":"scripts/meta_ads_agent.py","risk_model":"read-plan-approve-write-verify","approv
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
primary_cli":"meta ads","official_cli_package":"meta-ads==1.0.1","wrapped_cli":"scripts/meta_ads_agent.py","risk_model":"read-plan-approve-write-verify","approv
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
primary_cli":"meta ads","official_cli_package":"meta-ads==1.0.1","wrapped_cli":"scripts/meta_ads_agent.py","risk_model":"read-plan-approve-write-verify","approv
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
primary_cli":"meta ads","official_cli_package":"meta-ads==1.0.1","wrapped_cli":"scripts/meta_ads_agent.py","risk_model":"read-plan-approve-write-verify","approv
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
primary_cli":"meta ads","official_cli_package":"meta-ads==1.0.1","wrapped_cli":"scripts/meta_ads_agent.py","risk_model":"read-plan-approve-write-verify","approv
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
primary_cli":"meta ads","official_cli_package":"meta-ads==1.0.1","wrapped_cli":"scripts/meta_ads_agent.py","risk_model":"read-plan-approve-write-verify","approv
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
primary_cli":"meta ads","official_cli_package":"meta-ads==1.0.1","wrapped_cli":"scripts/meta_ads_agent.py","risk_model":"read-plan-approve-write-verify","approv
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
primary_cli":"meta ads","official_cli_package":"meta-ads==1.0.1","wrapped_cli":"scripts/meta_ads_agent.py","risk_model":"read-plan-approve-write-verify","approv
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
primary_cli":"meta ads","official_cli_package":"meta-ads==1.0.1","wrapped_cli":"scripts/meta_ads_agent.py","risk_model":"read-plan-approve-write-verify","approv
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
primary_cli":"meta ads","official_cli_package":"meta-ads==1.0.1","wrapped_cli":"scripts/meta_ads_agent.py","risk_model":"read-plan-approve-write-verify","approv
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
primary_cli":"meta ads","official_cli_package":"meta-ads==1.0.1","wrapped_cli":"scripts/meta_ads_agent.py","risk_model":"read-plan-approve-write-verify","approv
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
primary_cli":"meta ads","official_cli_package":"meta-ads==1.0.1","wrapped_cli":"scripts/meta_ads_agent.py","risk_model":"read-plan-approve-write-verify","approv
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
primary_cli":"meta ads","official_cli_package":"meta-ads==1.0.1","wrapped_cli":"scripts/meta_ads_agent.py","risk_model":"read-plan-approve-write-verify","approv
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
primary_cli":"meta ads","official_cli_package":"meta-ads==1.0.1","wrapped_cli":"scripts/meta_ads_agent.py","risk_model":"read-plan-approve-write-verify","approv
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
primary_cli":"meta ads","official_cli_package":"meta-ads==1.0.1","wrapped_cli":"scripts/meta_ads_agent.py","risk_model":"read-plan-approve-write-verify","approv
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
primary_cli":"meta ads","official_cli_package":"meta-ads==1.0.1","wrapped_cli":"scripts/meta_ads_agent.py","risk_model":"read-plan-approve-write-verify","approv
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
primary_cli":"meta ads","official_cli_package":"meta-ads==1.0.1","wrapped_cli":"scripts/meta_ads_agent.py","risk_model":"read-plan-approve-write-verify","approv
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
primary_cli":"meta ads","official_cli_package":"meta-ads==1.0.1","wrapped_cli":"scripts/meta_ads_agent.py","risk_model":"read-plan-approve-write-verify","approv
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
primary_cli":"meta ads","official_cli_package":"meta-ads==1.0.1","wrapped_cli":"scripts/meta_ads_agent.py","risk_model":"read-plan-approve-write-verify","approv
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
primary_cli":"meta ads","official_cli_package":"meta-ads==1.0.1","wrapped_cli":"scripts/meta_ads_agent.py","risk_model":"read-plan-approve-write-verify","approv
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Env Variable Harvesting

High
Category
Data Exfiltration
Content
def known_secret_values() -> List[str]:
    out: List[str] = []
    for key, value in os.environ.items():
        if value and len(value) >= 8 and any(marker in key.upper() for marker in SENSITIVE_ENV_MARKERS):
            out.append(value)
    return out
Confidence
70% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
def known_secret_values() -> List[str]:
    out: List[str] = []
    for key, value in os.environ.items():
        if value and len(value) >= 8 and any(marker in key.upper() for marker in SENSITIVE_ENV_MARKERS):
            out.append(value)
    return out
Confidence
70% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Static analysis

No suspicious patterns detected.