T09 · Insecure Skill Coding Practices
- Location
config.json:3- Finding
Provider API Key Stored in Plaintext Project Configuration
- Content
View full analysis
Vulnerability Details
File Location:
config.json:3-10,SKILL.md:116,scripts/render_image.py:147-149
Vulnerability Type: Plaintext credential storage
Risk Level: MediumVulnerable Code
config.json:3-10:json "channels": { "fal": { "provider": "fal.ai", "base_url": "https://fal.run", "model_id": "openai/gpt-image-2", "edit_model_id": "openai/gpt-image-2/edit", "api_key": "" } }SKILL.md:116:text The fallback script reads `config.json`. The user must fill the channel `api_key` before using that channel.scripts/render_image.py:147-149:python api_key = (channel.get("api_key") or "").strip() if not api_key: raise SystemExit("Missing API key. Fill config.json channels.fal.api_key.")Technical Analysis
The documented configuration process requires users to place the fal.ai API key directly in
config.json, a regular file inside the project. The implementation then reads the secret from that file without checking or enforcing restrictive file permissions.Although the distributed configuration contains an empty key and therefore does not expose a credential by itself, normal use causes a live credential to be persisted in plaintext. Project files are commonly copied into archives, committed to source control, synchronized to shared storage, included in support bundles, or exposed to other local users. This creates a credential-disclosure risk that is avoidable through environment-based or secret-store-based configuration.
The key is also copied into the process environment as
FAL_KEYwhenfal_clientis available:python os.environ["FAL_KEY"] = api_keyEnvironment-based use is appropriate when the value originates from a securely provisioned environment variable, but it does not mitigate the original plaintext storage in
config.json.Attack Path
- A user follows the documented inst ...[truncated 946 chars]
- Remediation
View remediation
Remediation Suggestions
-
Remove
api_keyfrom the operational project configuration and readFAL_KEYdirectly from the environment:python api_key = os.environ.get("FAL_KEY", "").strip() if not api_key: raise SystemExit("Missing FAL_KEY environment variable.") -
Keep only non-sensitive provider settings in
config.json. -
If file-based secrets must be supported, use a separate ignored secrets file and require restrictive permissions such as
0600. -
Add secret-bearing configuration files to
.gitignoreand provide a sanitized example file. -
Add automated secret scanning to the development and release process.
-
Update
SKILL.mdto instruct users to provision credentials through environment variables or an operating-system secret manager. -
Rotate any API key that has previously been stored in a committed or shared copy of
config.json. -
Use provider-side spending limits, narrowly scoped credentials, and regular key rotation to reduce the impact of disclosure.
-
