T09 · Insecure Skill Coding Practices
- Location
references/plugin-patterns.md:100- Finding
Unrestricted Configuration Object Exposed in Client Bundle
- Content
View full analysis
): Plugin { return { name: 'vite-plugin-config', config() { return { define: { __APP_CONFIG__: JSON.stringify(userConfig), }, } }, } } ``` ### Technical Analysis The documented plugin pattern accepts an unrestricted configuration object and serializes the entire object into Vite's `define` configuration. Vite statically substitutes this value into generated client-side code, making all included properties available in the browser bundle. The example does not enforce a schema, allowlist public properties, reject secret-like fields, or warn users that values supplied through this mechanism are public. If a developer passes a mixed configuration object containing API tokens, credentials, private service endpoints, or other server-only settings, those values will be embedded in downloadable JavaScript. Serialization with `JSON.stringify` prevents direct JavaScript syntax injection in this example, but it does not provide confidentiality or remove sensitive properties. ### Attack Path 1. A developer implements the documented `configPlugin` pattern. 2. The developer passes a configuration object containing both public settings and sensitive server-side values. 3. The plugin serializes the complete object through `JSON.stringify(userConfig)`. 4. Vite substitutes the serialized value into application code under `__APP_CONFIG__`. 5. The generated bundle is served to application users. 6. An unauthenticated attacker downloads or inspects the JavaScript bundle and extracts the embedded values. 7. If an exposed value is an active credential, the attacker may use it against the corresponding service with the per ...[truncated 664 chars]- Remediation
View remediation
` with a narrowly defined type containing only explicitly public fields. - Construct a new client configuration object from an allowlist rather than serializing the caller-provided object directly. - Reject keys associated with secrets, such as `token`, `secret`, `password`, `privateKey`, and server-only credentials. - Clearly document that every value passed to Vite's `define` option is embedded in client code and must be treated as public. - Keep credentials and private environment variables exclusively in server-side code. - Add tests that build the plugin and scan generated artifacts for known secret fixtures. - If this pattern has already been used with sensitive values, remove affected bundles, revoke and rotate exposed credentials, and review access logs for unauthorized use. A safer pattern is: ```ts interface PublicAppConfig { apiBaseUrl: string featureFlags: Record } export default function configPlugin(config: PublicAppConfig): Plugin { const publicConfig: PublicAppConfig = { apiBaseUrl: config.apiBaseUrl, featureFlags: config.featureFlags, } return { name: 'vite-plugin-config', config() { return { define: { __APP_CONFIG__: JSON.stringify(publicConfig), }, } }, } } ``` ]]>
