T09 · Insecure Skill Coding Practices
- Location
scripts/neis_cli.py:197- Finding
Optional API Key Exposed Through Curl Process Arguments
- Content
View full analysis
Vulnerability Details
File Location:
scripts/neis_cli.py, lines 197-212 and 226-240
Vulnerability Type: Sensitive information exposure through process command-line arguments
Risk Level: LowVulnerable Code
python def optional_api_key() -> str | None: api_key = os.environ.get("NEIS_API_KEY", "").strip() return api_key or None class NeisClient: def fetch(self, endpoint: str, params: dict[str, Any]) -> dict[str, Any]: query = {"Type": "json", "pIndex": 1, "pSize": 100} query.update({key: value for key, value in params.items() if value is not None and value != ""}) if self.api_key: query["KEY"] = self.api_key url = f"{API_BASE_URL}/{endpoint}?{urllib.parse.urlencode(query)}"python @staticmethod def _default_open(url: str) -> str: try: with urllib.request.urlopen(url, timeout=10) as response: return response.read().decode("utf-8") except urllib.error.URLError as exc: # Some local Python runtimes in macOS shells fail DNS resolution even though curl works. if not isinstance(exc.reason, OSError): raise try: result = subprocess.run( ["curl", "-fsSL", url], check=True, capture_output=True, text=True, )Technical Analysis
The optional
NEIS_API_KEYis inserted into the query string of the HTTPS request URL. Ifurllib.request.urlopen()raises a qualifyingURLError, the implementation invokes curl and supplies the complete authenticated URL as a command-line argument.Command-line arguments can be observable through local process-inspection facilities, monitoring agents, audit logs, crash diagnostics, or process telemetry. Consequently, a local party with access to such facilities may capture the API key while the fallback curl process is running.
The subprocess invocation uses an argument list rather than a shell co ...[truncated 1523 chars]
- Remediation
View remediation
Remediation Suggestions
- Remove the curl fallback and return a controlled connection error when the standard Python HTTPS client fails.
- If curl must remain, prevent the authenticated URL from appearing in its argument vector. Supply sensitive configuration through standard input or another mechanism that does not expose the key through process arguments, after verifying the selected mechanism is not logged.
- Prefer authentication in an HTTP header if the NEIS API supports it. If query-string authentication is mandatory, ensure complete URLs are never logged, included in exceptions, or passed through observable command-line arguments.
- Keep the API base URL fixed and continue using list-form subprocess invocation without
shell=True. - Add a regression test that configures
NEIS_API_KEY, triggers the fallback, interceptssubprocess.run, and verifies that no subprocess argument contains the key. - Document that school lookup parameters and the optional credential are transmitted to the official NEIS service over HTTPS.
