T09 · Insecure Skill Coding Practices
Warning
- Location
- config.json:8
- Finding
- Plaintext Portfolio Data and Cost Bases Embedded in Configuration and Source Code<![CDATA[ ## Vulnerability Details **File Location**: `config.json:8-315`; duplicated fallback data in `stock_monitor.py:38-151`; portfolio values included in generated messages at `stock_monitor.py:894-899` **Vulnerability Type**: Plaintext sensitive financial data exposure **Risk Level**: Medium ### Vulnerable Code ```json "watchlist": [ { "code": "002050", "name": "三花智控", "market": "sz", "type": "individual", "cost": 48.59, "alerts": { "cost_pct_above": 15.0, "cost_pct_below": -12.0, "change_pct_above": 4.0, "change_pct_below": -4.0, "volume_surge": 2.0, "ma_monitor": true, "rsi_monitor": true, "macd_monitor": true, "bollinger_monitor": true, "obv_monitor": true, "atr_monitor": true, "gap_monitor": true, "trailing_stop": true } } ] ``` The same type of data is embedded in the configuration-loading fallback: ```python def load_watchlist(): """Load the watchlist from the configuration file.""" try: with open('config.json', 'r', encoding='utf-8') as f: config = json.load(f) return config.get('config', {}).get('watchlist', []) except Exception as e: logging.error(f"Failed to read configuration file: {e}") return [ { "code": "002050", "name": "三花智控", "market": "sz", "type": "individual", "cost": 48.59, "alerts": { "cost_pct_above": 15.0, "cost_pct_below": -12.0, "change_pct_above": 4.0, "change_pct_below": -4.0, "volume_surge": 2.0, "ma_monitor": True, "rsi_monitor": True, "gap_monitor": True, "trailing_stop": True } }, # Additional positions and cost bases are embedded ...[truncated 2805 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove all real or private-looking portfolio entries and acquisition costs from distributable files. 2. Ship a separate example configuration containing clearly fictitious values, such as `config.example.json`. 3. Store the operational watchlist in a user-owned configuration file outside the package or repository. 4. Resolve the configuration path explicitly rather than relying on the current working directory: ```python from pathlib import Path config_path = Path(__file__).resolve().parent / "config.json" ``` 5. Fail closed when the operational configuration cannot be loaded. Do not silently substitute a private-looking fallback portfolio: ```python def load_watchlist(): config_path = Path(__file__).resolve().parent / "config.json" try: with config_path.open("r", encoding="utf-8") as f: config = json.load(f) except (OSError, json.JSONDecodeError) as exc: raise RuntimeError("Unable to load the stock-monitor configuration") from exc return config.get("config", {}).get("watchlist", []) ``` 6. Restrict configuration-file permissions to the account running the monitor. 7. Make inclusion of cost bases in generated messages opt-in and redact them by default. 8. Ensure notification channels and retained logs have appropriate access controls and retention policies. 9. Add schema validation so malformed or unintended configuration values cannot silently enter the reporting pipeline. ]]>
