T09 · Insecure Skill Coding Practices
Warning
- Location
- src/platforms/index.js:330
- Finding
- Platform API Credentials Stored in Plaintext with Unrestricted Default Permissions<![CDATA[ ## Vulnerability Details **File Locations**: - `src/platforms/index.js:330-354` - `bin/cli.js:132-157` - `commands/platform.js:60-72` **Vulnerability Type**: Plaintext credential storage and command-line secret exposure **Risk Level**: Medium ### Vulnerable Code `src/platforms/index.js:330-354`: ```js _getConfigPath() { const path = require('path'); return path.join(process.env.HOME || process.env.USERPROFILE, '.crossborder-ecom', 'config.json'); } _loadConfig() { const fs = require('fs'); try { if (fs.existsSync(this.configPath)) { return JSON.parse(fs.readFileSync(this.configPath, 'utf-8')); } } catch (e) { console.error('Failed to load config:', e.message); } return { platforms: {} }; } _saveConfig() { const fs = require('fs'); const path = require('path'); const dir = path.dirname(this.configPath); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } fs.writeFileSync(this.configPath, JSON.stringify(this.config, null, 2)); } ``` `bin/cli.js:132-157`: ```js const configDir = path.join(process.env.HOME || process.env.USERPROFILE, '.crossborder-ecom'); if (!fs.existsSync(configDir)) { fs.mkdirSync(configDir, { recursive: true }); } const configPath = path.join(configDir, 'config.json'); const defaultConfig = { platforms: {}, feishu: { enabled: false, appId: '', appSecret: '', bitableToken: '' }, pricing: { defaultMargin: 30, strategy: 'competitive' }, inventory: { lowStockThreshold: 10, syncInterval: 300 } }; fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2)); ``` `commands/platform.js:60-72`: ```js const config = { name: platform, apiKey: options.apiKey || '', connected: false, createdAt: new Date().toISOString() }; await platformManager.addPlatform(config); ``` ### Technical Analysis The application instructs users to store commerce-platform and Feishu credentials in `~/.crossborder-ecom/config.jso ...[truncated 2232 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Create the configuration directory with owner-only permissions: ```js fs.mkdirSync(dir, { recursive: true, mode: 0o700 }); ``` 2. Create and update the configuration file with mode `0600`: ```js fs.writeFileSync( this.configPath, JSON.stringify(this.config, null, 2), { encoding: 'utf8', mode: 0o600 } ); fs.chmodSync(this.configPath, 0o600); ``` 3. Check and correct permissions on existing configuration files before loading them. Refuse to load files owned by an unexpected user where the platform supports ownership checks. 4. Remove the `--api-key` command-line option for secret entry. Use one of the following instead: - Hidden interactive input that does not echo the value. - Environment variables supplied through a protected runtime environment. - An operating-system credential manager or secrets service. 5. Prefer storing only non-sensitive configuration in JSON. Store secret values separately in a credential manager and reference them by identifier. 6. Warn users if plaintext credentials from an older configuration format are detected, then provide a migration and credential-rotation procedure. 7. Recommend least-privilege API credentials restricted to required accounts, operations, source addresses, and expiration periods. ]]>
