Back to skill

Security audit

Apple Search Ads

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a coherent Apple Ads management guide, but its provided mutation scripts can change live ad spend without enforcing the confirmation gate the skill promises.

Review before installing if you plan to use API automation. Treat the provided mutation scripts as unsafe until they add explicit confirmations, JSON-safe payload construction, input validation, and safer defaults such as creating campaigns paused. Keep Apple private keys, client secrets, and access tokens in a secure secret store, not in the skill workspace, shell history, logs, or source control.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts.md:114
Finding

Campaign Mutation Scripts Bypass the Promised Confirmation Gate

Content
View full analysis
}" ADAM_ID="${2:?Missing adam_id}" COUNTRY="${3:?Missing country}" DAILY_BUDGET="${4:?Missing daily_budget}" curl -s -X POST "https://api.searchads.apple.com/api/v5/campaigns" \ -H "Authorization: Bearer $ASA_ACCESS_TOKEN" \ -H "X-AP-Context: orgId=$ASA_ORG_ID" \ -H "Content-Type: application/json" \ -d '{ "name": "'"$NAME"'", "adamId": '"$ADAM_ID"', "countriesOrRegions": ["'"$COUNTRY"'"], "budgetAmount": {"amount": "10000", "currency": "USD"}, "dailyBudgetAmount": {"amount": "'"$DAILY_BUDGET"'", "currency": "USD"}, "supplySources": ["APPSTORE_SEARCH_RESULTS"], "billingEvent": "TAPS", "status": "ENABLED" }' | jq ``` Equivalent immediate-mutation behavior also appears in the pause, add-keyword, and add-negative scripts at `scripts.md:153-168`, `scripts.md:181-203`, and `scripts.md:216-235`. ### Technical Analysis The Skill states that every mutation script respects `confirm_before_push`, and `SKILL.md` defines that setting as the approval gate for changes affecting bids, budgets, campaign status, and keywords. However, the mutation scripts neither read `config.yaml` nor inspect `confirm_before_push`. They also do not display a proposed change or prompt the operator before sending the authenticated request. The campaign creation example is particularly consequential because it creates the campaign with `"status": "ENABLED"`. Once invoked with valid credentials, the script immediately sends the request to Ap ...[truncated 1288 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts.md:122
Finding

Untrusted Arguments Are Concatenated Directly into JSON Mutation Payloads

Content
View full analysis
}" ADAM_ID="${2:?Missing adam_id}" COUNTRY="${3:?Missing country}" DAILY_BUDGET="${4:?Missing daily_budget}" curl -s -X POST "https://api.searchads.apple.com/api/v5/campaigns" \ -H "Authorization: Bearer $ASA_ACCESS_TOKEN" \ -H "X-AP-Context: orgId=$ASA_ORG_ID" \ -H "Content-Type: application/json" \ -d '{ "name": "'"$NAME"'", "adamId": '"$ADAM_ID"', "countriesOrRegions": ["'"$COUNTRY"'"], "budgetAmount": {"amount": "10000", "currency": "USD"}, "dailyBudgetAmount": {"amount": "'"$DAILY_BUDGET"'", "currency": "USD"}, "supplySources": ["APPSTORE_SEARCH_RESULTS"], "billingEvent": "TAPS", "status": "ENABLED" }' | jq ``` The same construction pattern is used for keyword input at `scripts.md:189-203` and `scripts.md:224-235`: ```bash -d '[{ "text": "'"$KEYWORD"'", "matchType": "'"$MATCH_TYPE"'", "bidAmount": {"amount": "'"$BID"'", "currency": "USD"}, "status": "ACTIVE" }]' | jq ``` ### Technical Analysis Values supplied through command-line arguments are inserted into JSON through shell string concatenation. The script does not apply JSON encoding and does not validate expected types or formats. A value containing quotation marks, backslashes, arrays, objects, or other JSON syntax can terminate its intended string or scalar context and alter the surrounding request body. For example, a crafted campaign name can inject additional JSON properties. Numeric fields such as `ADAM_ID` are inserted without quotation marks, making them especially sensitive to malformed or attacker-controlled JSON fragments. This is JSON injection rather than shell command injection: shell metacharacters introduced through ordinary variable expansion are not automatically reparsed as s ...[truncated 1458 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
ios-integration.md:397
Finding

Debugging Example Logs a Substantial Portion of a Live Attribution Token

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
api-reference.md:41
Finding

Authentication Guidance Recommends Storing a Long-Lived Client-Secret JWT Without Storage Protections

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (51)

Credential Access

High
Category
Privilege Escalation
Confidence
75% confidence
Finding

The guidance says the client secret JWT may be generated once and stored for up to 180 days, but it does not pair that recommendation with strong storage constraints. Long-lived bearer-style authentication material materially increases blast radius if copied from disk, backups, CI logs, or developer machines, especially in automation-heavy ad operations.

Content

Scanner excerpt · api-reference.md (reported line 58)May include surrounding context.

}

text

Access tokens are valid 1 hour — cache and reuse across calls; minting a fresh token per request is the usual cause of auth throttling. The client secret JWT may set `exp` up to 180 days out: generate it once and store it, not per run.

## Base URL & Headers

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · api-reference.md (reported line 200)May include surrounding context.

Delete Campaign

bash
DELETE /campaigns/{campaignId}

Ad Groups

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · memory-template.md (reported line 15)May include surrounding context.

md
## Apps

<!-- Add each app being advertised -->
### App Name
- Adam ID: 123456789
- Category: Health & Fitness

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · memory-template.md (reported line 40)May include surrounding context.

md
## Learnings

<!-- What's working, what's not -->
- Brand keywords: CPA consistently under $4
- Generic keywords: Only "meditation app" profitable
- Competitor keywords: Not working, paused

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 164)May include surrounding context.

get-token.sh

Generate OAuth access token.

bash
#!/usr/bin/env bash

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 · scripts.md (reported line 18)May include surrounding context.

get-token.sh

Generate OAuth access token.

bash
#!/usr/bin/env bash

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 · scripts.md (reported line 49)May include surrounding context.

get-token.sh

Generate OAuth access token.

bash
#!/usr/bin/env bash

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 · troubleshooting.md (reported line 67)May include surrounding context.

md
| Error | Causes in order | Fix |
|---|---|---|
| 401 UNAUTHORIZED | Access token >1h old · client secret JWT malformed (wrong `aud`, missing `kid`, not ES256) · JWT `exp` passed | Mint a new token; regenerate the client secret only if the token exchange itself fails (`api-reference.md` → Authentication) |
| 403 FORBIDDEN | `X-AP-Context: orgId=` missing or pointing at an org this user can't touch (multi-org accounts) · API access not granted to this user/key in account settings · resource belongs to another org | `GET /acls` lists the orgs and roles your credentials actually hold — compare against the orgId you are sending |
| 404 NOT_FOUND | Wrong campaign/adgroup/keyword ID, or right ID under the wrong org context | Re-list the parent to confirm the ID exists under this orgId |
| INVALID_FIELD | Payload shape drift — commonly money objects (`{"amount":"5.00","currency":"USD"}` with amount as string) or a phased-out field | Compare against the request bodies in `api-reference.md` |

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
91% confidence
Finding

The skill allows confirm_before_push to be set to false, which permits spend-changing API mutations to execute without an interactive confirmation. In an advertising tool, that can directly alter bids, budgets, statuses, and keywords, creating financial loss or large unintended campaign changes if the agent misinterprets instructions or operates on stale data.

Content

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

md
| report_timezone | UTC \| ORTZ | UTC | Passed as `timeZone` in every report request — one value everywhere, never mixed (Traps) |
| ltv_divisor | number (3-5) | 4 | Target CPA = LTV / `ltv_divisor`; 3 = aggressive payback, 5 = conservative (`strategy.md`) |
| mmp | none \| appsflyer \| adjust \| singular \| kochava \| branch | none | With an MMP set, it owns AdServices and SKAN integration and cross-channel truth (`ios-integration.md`, `measurement.md`) |
| confirm_before_push | bool | true | Every API mutation (bids, budgets, status, keywords) is listed and confirmed before pushing; false = push and log without asking. Setting it to false means real ad spend changes without a prompt: only do it for campaigns you own and can afford to have moved unattended; campaign DELETE stays confirmed either way |
| naming_pattern | text | App - Country - Intent | Template for every campaign name in create payloads, scripts, and memory logs; parsing reports assumes this pattern |

Preference areas to record as the user reveals them:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This markdown file includes examples for reading a private key from disk, accessing credential environment variables, generating a long-lived client secret, and exchanging it over HTTP, but it does not warn users that these values are sensitive secrets that must be protected. Because SQP-2 applies to markdown files, documentation describing secret use and transmission should disclose privacy and system-integrity implications.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · api-reference.md (reported line 41)May include surrounding context.

Token Exchange

bash
curl -X POST "https://appleid.apple.com/auth/oauth2/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=${ASA_CLIENT_ID}" \

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

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

All endpoints below are relative to the base URL.

text
Base URL: https://api.searchads.apple.com/api/v5
Headers:
  Authorization: Bearer {ACCESS_TOKEN}
  X-AP-Context: orgId={ORG_ID}

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · api-reference.md (reported line 65)May include surrounding context.

All endpoints below are relative to the base URL.

text
Base URL: https://api.searchads.apple.com/api/v5
Headers:
  Authorization: Bearer {ACCESS_TOKEN}
  X-AP-Context: orgId={ORG_ID}

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts.md (reported line 85)May include surrounding context.

All endpoints below are relative to the base URL.

text
Base URL: https://api.searchads.apple.com/api/v5
Headers:
  Authorization: Bearer {ACCESS_TOKEN}
  X-AP-Context: orgId={ORG_ID}

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts.md (reported line 92)May include surrounding context.

All endpoints below are relative to the base URL.

text
Base URL: https://api.searchads.apple.com/api/v5
Headers:
  Authorization: Bearer {ACCESS_TOKEN}
  X-AP-Context: orgId={ORG_ID}

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts.md (reported line 115)May include surrounding context.

All endpoints below are relative to the base URL.

text
Base URL: https://api.searchads.apple.com/api/v5
Headers:
  Authorization: Bearer {ACCESS_TOKEN}
  X-AP-Context: orgId={ORG_ID}

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts.md (reported line 154)May include surrounding context.

All endpoints below are relative to the base URL.

text
Base URL: https://api.searchads.apple.com/api/v5
Headers:
  Authorization: Bearer {ACCESS_TOKEN}
  X-AP-Context: orgId={ORG_ID}

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts.md (reported line 163)May include surrounding context.

All endpoints below are relative to the base URL.

text
Base URL: https://api.searchads.apple.com/api/v5
Headers:
  Authorization: Bearer {ACCESS_TOKEN}
  X-AP-Context: orgId={ORG_ID}

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts.md (reported line 182)May include surrounding context.

All endpoints below are relative to the base URL.

text
Base URL: https://api.searchads.apple.com/api/v5
Headers:
  Authorization: Bearer {ACCESS_TOKEN}
  X-AP-Context: orgId={ORG_ID}

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts.md (reported line 217)May include surrounding context.

All endpoints below are relative to the base URL.

text
Base URL: https://api.searchads.apple.com/api/v5
Headers:
  Authorization: Bearer {ACCESS_TOKEN}
  X-AP-Context: orgId={ORG_ID}

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts.md (reported line 250)May include surrounding context.

All endpoints below are relative to the base URL.

text
Base URL: https://api.searchads.apple.com/api/v5
Headers:
  Authorization: Bearer {ACCESS_TOKEN}
  X-AP-Context: orgId={ORG_ID}

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts.md (reported line 266)May include surrounding context.

All endpoints below are relative to the base URL.

text
Base URL: https://api.searchads.apple.com/api/v5
Headers:
  Authorization: Bearer {ACCESS_TOKEN}
  X-AP-Context: orgId={ORG_ID}

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts.md (reported line 305)May include surrounding context.

All endpoints below are relative to the base URL.

text
Base URL: https://api.searchads.apple.com/api/v5
Headers:
  Authorization: Bearer {ACCESS_TOKEN}
  X-AP-Context: orgId={ORG_ID}

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts.md (reported line 324)May include surrounding context.

All endpoints below are relative to the base URL.

text
Base URL: https://api.searchads.apple.com/api/v5
Headers:
  Authorization: Bearer {ACCESS_TOKEN}
  X-AP-Context: orgId={ORG_ID}

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts.md (reported line 360)May include surrounding context.

All endpoints below are relative to the base URL.

text
Base URL: https://api.searchads.apple.com/api/v5
Headers:
  Authorization: Bearer {ACCESS_TOKEN}
  X-AP-Context: orgId={ORG_ID}

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
api-reference.md:17