Skip to content

Commit 3f0fe30

Browse files
fix(security): URL-encode CLI query parameters to prevent CWE-74 injection
cli_audit.py and cli_leaderboard.py built query strings by string-interpolating user-supplied CLI args directly into URLs (e.g. f'agent={args.agent}'). A value like 'foo&admin=true' would inject extra query parameters into the HTTP request. Replace manual string concatenation with urllib.parse.urlencode() which properly percent-encodes all parameter values.
1 parent cbe0f38 commit 3f0fe30

2 files changed

Lines changed: 22 additions & 16 deletions

File tree

sdk/agentlens/cli_audit.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ def _summary_stats(entries: list[dict]) -> str:
166166
def cmd_audit(args: Any) -> None:
167167
"""Fetch and display the agent action audit trail."""
168168
import os
169+
import urllib.parse
169170
import urllib.request
170171

171172
endpoint = (
@@ -178,23 +179,25 @@ def cmd_audit(args: Any) -> None:
178179
)
179180
headers = {"x-api-key": api_key}
180181

181-
params = []
182+
# Use urllib.parse.urlencode for proper URL-encoding of parameter
183+
# values (CWE-74 — prevents query-string injection via CLI args).
184+
params: dict[str, str] = {}
182185
if getattr(args, "agent", None):
183-
params.append(f"agent={args.agent}")
186+
params["agent"] = args.agent
184187
if getattr(args, "action_filter", None):
185-
params.append(f"action={args.action_filter}")
188+
params["action"] = args.action_filter
186189
if getattr(args, "severity", None):
187-
params.append(f"severity={args.severity}")
190+
params["severity"] = args.severity
188191
if getattr(args, "model", None):
189-
params.append(f"model={args.model}")
192+
params["model"] = args.model
190193
if getattr(args, "session", None):
191-
params.append(f"session_id={args.session}")
194+
params["session_id"] = args.session
192195
if getattr(args, "since", None):
193-
params.append(f"since_hours={args.since}")
196+
params["since_hours"] = str(args.since)
194197
if getattr(args, "limit", None):
195-
params.append(f"limit={args.limit}")
198+
params["limit"] = str(args.limit)
196199

197-
qs = ("?" + "&".join(params)) if params else ""
200+
qs = ("?" + urllib.parse.urlencode(params)) if params else ""
198201
url = f"{endpoint}/audit{qs}"
199202

200203
req = urllib.request.Request(url, headers=headers)

sdk/agentlens/cli_leaderboard.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,21 +39,24 @@ def cmd_leaderboard(args: Any) -> None:
3939

4040
base_url, headers = _get_client(args)
4141

42+
import urllib.parse
4243
import urllib.request
4344

44-
params = []
45+
# Use urllib.parse.urlencode for proper URL-encoding of parameter
46+
# values (CWE-74 — prevents query-string injection via CLI args).
47+
params: dict[str, str] = {}
4548
if args.sort:
46-
params.append(f"sort={args.sort}")
49+
params["sort"] = args.sort
4750
if args.days:
48-
params.append(f"days={args.days}")
51+
params["days"] = str(args.days)
4952
if args.limit:
50-
params.append(f"limit={args.limit}")
53+
params["limit"] = str(args.limit)
5154
if args.min_sessions:
52-
params.append(f"min_sessions={args.min_sessions}")
55+
params["min_sessions"] = str(args.min_sessions)
5356
if args.order:
54-
params.append(f"order={args.order}")
57+
params["order"] = args.order
5558

56-
qs = ("?" + "&".join(params)) if params else ""
59+
qs = ("?" + urllib.parse.urlencode(params)) if params else ""
5760
url = f"{base_url}/leaderboard{qs}"
5861

5962
req = urllib.request.Request(url, headers=headers)

0 commit comments

Comments
 (0)