T09 · Insecure Skill Coding Practices
- Location
scripts/afkl_http.mjs:37- Finding
AFKL Credentials Can Be Forwarded to an Arbitrary Network Destination
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill mostly matches its flight-tracking purpose, but it should be reviewed because it handles API credentials with overly broad network scope and under-discloses a third-party aircraft lookup.
Review before installing. Use a dedicated, low-privilege AFKL API key, keep the credential state directory private, and consider hardening the HTTP helper to only send credentials to https://api.airfranceklm.com. Be aware that aircraft registration values may be sent to Planespotters and cached locally; remove or disable that enrichment if you want AFKL-only network traffic.
scripts/afkl_http.mjs:37AFKL Credentials Can Be Forwarded to an Arbitrary Network Destination
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
- Preferred: env vars `AFKL_API_KEY` (and optional `AFKL_API_SECRET`)
- Or files in your state dir (`CLAWDBOT_STATE_DIR` or `./state`):
- `afkl_api_key.txt` (chmod 600)
- `afkl_api_secret.txt` (chmod 600, optional)
2) Query flight status:
- Run: `node skills/airfrance-afkl/scripts/afkl_flightstatus_query.mjs --carrier AF --flight 7 --origin JFK --dep-date 2026-01-29`
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
- Preferred: env vars `AFKL_API_KEY` (and optional `AFKL_API_SECRET`)
- Or files in your state dir (`CLAWDBOT_STATE_DIR` or `./state`):
- `afkl_api_key.txt` (chmod 600)
- `afkl_api_secret.txt` (chmod 600, optional)
2) Query flight status:
- Run: `node skills/airfrance-afkl/scripts/afkl_flightstatus_query.mjs --carrier AF --flight 7 --origin JFK --dep-date 2026-01-29`
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
- Preferred: env vars `AFKL_API_KEY` (and optional `AFKL_API_SECRET`)
- Or files in your state dir (`CLAWDBOT_STATE_DIR` or `./state`):
- `afkl_api_key.txt` (chmod 600)
- `afkl_api_secret.txt` (chmod 600, optional)
2) Query flight status:
- Run: `node skills/airfrance-afkl/scripts/afkl_flightstatus_query.mjs --carrier AF --flight 7 --origin JFK --dep-date 2026-01-29`
The skill explicitly relies on sensitive capabilities: reading credentials from environment variables or local files and making outbound network requests to a third-party API, but it does not declare any tool scope or permission boundaries. In an agent environment, this weakens least-privilege controls and can allow the skill to be run with broader access than necessary, increasing the blast radius if the implementation or surrounding tooling is compromised.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
2) Provide API credentials (do not print them):
- Preferred: env vars `AFKL_API_KEY` (and optional `AFKL_API_SECRET`)
- Or files in your state dir (`CLAWDBOT_STATE_DIR` or `./state`):
- `afkl_api_key.txt` (chmod 600)
- `afkl_api_secret.txt` (chmod 600, optional)
2) Query flight status:
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
2) Provide API credentials (do not print them):
- Preferred: env vars `AFKL_API_KEY` (and optional `AFKL_API_SECRET`)
- Or files in your state dir (`CLAWDBOT_STATE_DIR` or `./state`):
- `afkl_api_key.txt` (chmod 600)
- `afkl_api_secret.txt` (chmod 600, optional)
2) Query flight status:
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
## Identifiers
- `id` (e.g. `20260130+AF+0007`) — can be fetched directly:
- `GET https://api.airfranceklm.com/opendata/flightstatus/{id}`
## Status
- `flightStatusPublic` / `flightStatusPublicLangTransl`
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
## Identifiers
- `id` (e.g. `20260130+AF+0007`) — can be fetched directly:
- `GET https://api.airfranceklm.com/opendata/flightstatus/{id}`
## Status
- `flightStatusPublic` / `flightStatusPublicLangTransl`
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
## Identifiers
- `id` (e.g. `20260130+AF+0007`) — can be fetched directly:
- `GET https://api.airfranceklm.com/opendata/flightstatus/{id}`
## Status
- `flightStatusPublic` / `flightStatusPublicLangTransl`
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
## Identifiers
- `id` (e.g. `20260130+AF+0007`) — can be fetched directly:
- `GET https://api.airfranceklm.com/opendata/flightstatus/{id}`
## Status
- `flightStatusPublic` / `flightStatusPublicLangTransl`
The helper sends the API key and optional API secret as HTTP headers in a network request, which transmits sensitive authentication data off the local system. The file contains no confirmation, visible log, or warning to inform users that credentials will be sent to the provided URL.
The helper defaults to locale 'fr-FR', which drives user-visible weekday/date formatting in all generated notifications. This enforces a specific language/locale choice without offering configuration or documenting that the skill is region/language-specific.
The watcher expands beyond the stated Air France–KLM flight-status purpose by performing third-party aircraft intelligence lookups and persisting the results locally. This creates unnecessary data egress and enlarges the trust boundary to an unrelated external service, which increases privacy, integrity, and supply-chain risk without being essential to core flight tracking.
This code sends aircraft registration data to Planespotters, an additional external service not clearly justified by the skill's declared AF/KLM tracking role. Even if the transmitted data is limited, the undeclared integration can expose user-derived tracking context and makes results dependent on an unvetted third party.
The code transmits aircraft registration identifiers to api.planespotters.net via a direct fetch call. This is dangerous because it exfiltrates operational data to a third party outside the skill's primary API scope, creating privacy and dependency risks if the service is compromised, misleading, or unauthorized for this workflow.
try {
for (const t of tried) {
try {
const url = `https://api.planespotters.net/pub/photos/reg/${encodeURIComponent(t)}`;
const resp = await fetch(url, { headers: { 'accept': 'application/json' } });
const text = await resp.text();
const j = JSON.parse(text);
The manifest says this skill tracks Air France flights using the Air France–KLM Open Data APIs for flight status, alerts, and related analysis. This script instead performs lookups against the public Planespotters API and builds aircraft metadata from that source, which is a materially different external dependency and behavior than the manifest describes.
The script transmits user-provided aircraft registration data to a third-party public API outside the primary Air France–KLM data source. Even though the data is not highly sensitive, this expands the trust boundary, creates dependency on an unvetted external service, and may leak user query patterns or operational interests without explicit consent.
let intel = { reg: primary };
try {
// Planespotters API: https://api.planespotters.net/pub/photos/reg/{reg}
for (const r of regs) {
try {
const url = `https://api.planespotters.net/pub/photos/reg/${encodeURIComponent(r)}`;
The code constructs and performs a live HTTPS request to api.planespotters.net using the supplied registration, sending data outside the skill's stated AF/KLM API boundary. In this flight-monitoring context, that is more concerning because the skill is expected to operate on airline APIs, so undisclosed third-party lookups can expose usage patterns and weaken data-governance guarantees.
// Planespotters API: https://api.planespotters.net/pub/photos/reg/{reg}
for (const r of regs) {
try {
const url = `https://api.planespotters.net/pub/photos/reg/${encodeURIComponent(r)}`;
const j = await fetchJson(url);
const photo = (j.photos && j.photos[0]) || null;
const ac = photo && photo.aircraft || null;
The manifest describes using AFKL Open Data APIs for flight tracking, alerts, analysis, and polling, but does not mention secret discovery from process environment variables or filesystem state files. While this may be operationally useful, credential-loading from env and host-local files is an additional capability not justified by the end-user-facing purpose as written.
This code loads AFKL API credentials from files in a state directory and from environment variables, which is access to sensitive credentials. There is no confirmation prompt, user-facing log, or warning in this file that credentials will be read from local storage.
The code reads and writes a JSON cache file under a state directory or /tmp, introducing local persistence that is not mentioned in the manifest. While caching can be an implementation detail, persistent aircraft-intel storage is not obviously required from the narrow description centered on Air France flight-status API usage.
The script sends the provided registration to a third-party public API and persists returned data to a local cache file. While comments describe the implementation, there is no user-visible notice, prompt, or runtime disclosure that input will be transmitted externally and stored on disk.
Detected: suspicious.env_credential_access, suspicious.potential_exfiltration