T09 · Insecure Skill Coding Practices
- Location
traffic_source_report.py:317- Finding
Unrestricted Webhook Destination Can Disclose Google Analytics Data
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This GA4 skill largely matches its stated purpose, but it needs Review because it can send analytics reports to an arbitrary webhook and its credential-storage guidance is unsafe.
Install only if you are comfortable granting the skill read access to GA4 data through a narrowly scoped Viewer service account. Keep the service-account JSON outside the skill or add your own ignore/secret-scanning protections, and do not set DINGTALK_WEBHOOK unless you have verified the exact destination and are willing to send the generated analytics report there.
traffic_source_report.py:317Unrestricted Webhook Destination Can Disclose Google Analytics Data
SKILL.md:37Credential Storage Instructions Rely on a Missing Git Ignore Rule
requirements.txt:2Unbounded Dependency Versions Prevent Reproducible and Auditable Installation
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
},
}
response = requests.post(url, headers=headers, json=data, timeout=30)
try:
result = response.json()
except Exception:
The script automatically falls back to a local ga-credentials.json file if present, enabling authenticated access without an explicit credential path from the user. In a skill environment, implicit credential discovery increases the risk of unintended privilege use and unauthorized querying of analytics data if the skill is triggered by an untrusted prompt or caller.
if credentials:
cmd.extend(["--credentials", credentials])
else:
default_creds = os.path.join(SCRIPT_DIR, "ga-credentials.json")
if os.path.exists(default_creds):
cmd.extend(["--credentials", default_creds])
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
creds_path = os.path.join(SCRIPT_DIR, "ga-credentials.json")
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
print(f"Credentials path: {creds_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
creds_path = os.path.join(SCRIPT_DIR, "ga-credentials.json")
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
print(f"Credentials path: {creds_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
creds_path = os.path.join(SCRIPT_DIR, "ga-credentials.json")
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
print(f"Credentials path: {creds_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
creds_path = os.path.join(SCRIPT_DIR, "ga-credentials.json")
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
print(f"Credentials path: {creds_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
creds_path = os.path.join(SCRIPT_DIR, "ga-credentials.json")
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
print(f"Credentials path: {creds_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
creds_path = os.path.join(SCRIPT_DIR, "ga-credentials.json")
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
print(f"Credentials path: {creds_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
creds_path = os.path.join(SCRIPT_DIR, "ga-credentials.json")
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
print(f"Credentials path: {creds_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
creds_path = os.path.join(SCRIPT_DIR, "ga-credentials.json")
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
print(f"Credentials path: {creds_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
creds_path = os.path.join(SCRIPT_DIR, "ga-credentials.json")
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
print(f"Credentials path: {creds_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
creds_path = os.path.join(SCRIPT_DIR, "ga-credentials.json")
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
print(f"Credentials path: {creds_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
creds_path = os.path.join(SCRIPT_DIR, "ga-credentials.json")
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
print(f"Credentials path: {creds_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
creds_path = os.path.join(SCRIPT_DIR, "ga-credentials.json")
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
print(f"Credentials path: {creds_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
creds_path = os.path.join(SCRIPT_DIR, "ga-credentials.json")
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
print(f"Credentials path: {creds_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
creds_path = os.path.join(SCRIPT_DIR, "ga-credentials.json")
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
print(f"Credentials path: {creds_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
creds_path = os.path.join(SCRIPT_DIR, "ga-credentials.json")
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
print(f"Credentials path: {creds_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
creds_path = os.path.join(SCRIPT_DIR, "ga-credentials.json")
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
print(f"Credentials path: {creds_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
creds_path = os.path.join(SCRIPT_DIR, "ga-credentials.json")
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
print(f"Credentials path: {creds_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
creds_path = os.path.join(SCRIPT_DIR, "ga-credentials.json")
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
print(f"Credentials path: {creds_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
creds_path = os.path.join(SCRIPT_DIR, "ga-credentials.json")
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
print(f"Credentials path: {creds_path}")
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
### 3. Grant GA4 access
1. Open [Google Analytics](https://analytics.google.com/).
2. Select the property.
3. **Admin** (gear) → **Property access management**.
4. **Add users** → enter the service account email (`…@….iam.gserviceaccount.com`).
The skill documentation discloses optional DingTalk webhook notifications and a traffic source reporting capability that are not described in the main capability list, which creates a transparency gap around data flows. In a security-sensitive analytics skill, undocumented outbound integrations can lead users to unintentionally transmit analytics-derived data to third-party endpoints without realizing the feature exists.
Mentioning DingTalk webhook support without a clear warning about outbound transmission understates the privacy and data-handling implications of the skill. Analytics reports can contain sensitive business intelligence or user-segmentation data, so silent or poorly disclosed export paths increase the risk of unintended data leakage to external services.
This Python file creates a GA4 client and sends multiple report requests to Google's Analytics API. Although the script is described as a manual integration test, it does not explicitly warn the user that running it will transmit account-scoped identifiers and query live analytics data over the network.
No suspicious patterns detected.