T09 · Insecure Skill Coding Practices
- Location
notion_api.py:303- Finding
Notion API token can be transmitted to an arbitrary network destination
- Content
View full analysis
str: """Return the Notion API base URL.""" return os.environ.get("NOTION_BASE_URL", DEFAULT_BASE_URL).rstrip("/") def build_headers() -> dict: """Build standard HTTP headers for Notion API calls.""" return { "Authorization": f"Bearer {get_api_key()}", "Notion-Version": get_version(), "Content-Type": "application/json", } def _request( method: str, path: str, body: dict | None = None, params: dict | None = None, ) -> dict: """ Make an HTTP request to the Notion API and return the parsed JSON response. Raises SystemExit on non-2xx responses with the API error detail. """ base = get_base_url() url = f"{base}{path}" if params: qs = "&".join(f"{k}={urllib.parse.quote(str(v))}" for k, v in params.items()) url = f"{url}?{qs}" data = None if body is not None: data = json.dumps(body).encode("utf-8") req = urllib.request.Request(url, data=data, method=method) for k, v in build_headers().items(): req.add_header(k, v) try: with urllib.request.urlopen(req) as resp: raw = resp.read().decode("utf-8") return json.loads(raw) ``` ### Technical Analysis The API base URL is read directly from the `NOTION_BASE_URL` environment variable without validating its scheme or hostname. The request builder then unconditionally adds the Notion integration token through the `Authorization: Bearer` header. Consequently, the token is not restricted to `https://api.notion.com`. If an attacker, compromised launcher, CI configuration, shell profile, or agent environment can influence `NOTION_BASE_URL`, any invocation that reaches `_request()` sends the token to the selected endpoint. The implem ...[truncated 1590 chars]- Remediation
View remediation
