Skip to content

Commit 9855b7c

Browse files
author
JKAW
committed
fix: address PR review comments
- Fix JQL injection bypass: escape backslashes before double-quotes - Replace str.startswith() with Path.is_relative_to() for path traversal check - Add path traversal guard to download_issue_attachments() - Redact client_secret in VS Code config JSON blob
1 parent 08f54e9 commit 9855b7c

3 files changed

Lines changed: 17 additions & 5 deletions

File tree

src/mcp_atlassian/jira/attachments.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,13 @@ def download_attachment(self, url: str, target_path: str) -> bool:
3838
target_path = os.path.abspath(target_path)
3939

4040
# Guard against path traversal
41-
base_dir = os.path.abspath(os.getcwd())
42-
if not target_path.startswith(base_dir):
41+
base_dir = Path(os.getcwd()).resolve()
42+
resolved = Path(target_path).resolve()
43+
if not resolved.is_relative_to(base_dir):
4344
raise ValueError(
44-
f"Path traversal detected: {target_path} is outside {base_dir}"
45+
f"Path traversal detected: {resolved} is outside {base_dir}"
4546
)
47+
target_path = str(resolved)
4648

4749
logger.info(f"Downloading attachment from {url} to {target_path}")
4850

@@ -214,6 +216,15 @@ def download_issue_attachments(
214216
if not os.path.isabs(target_dir):
215217
target_dir = os.path.abspath(target_dir)
216218

219+
# Guard against path traversal
220+
base_dir = Path(os.getcwd()).resolve()
221+
resolved_dir = Path(target_dir).resolve()
222+
if not resolved_dir.is_relative_to(base_dir):
223+
raise ValueError(
224+
f"Path traversal detected: {resolved_dir} is outside {base_dir}"
225+
)
226+
target_dir = str(resolved_dir)
227+
217228
logger.info(
218229
f"Downloading attachments for {issue_key} to directory: {target_dir}"
219230
)

src/mcp_atlassian/jira/search.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ def search_issues(
5959

6060
# Build the project filter query part
6161
# Sanitize project names to prevent JQL injection
62-
projects = [p.replace('"', '\\"') for p in projects]
62+
# Escape backslashes before double-quotes to prevent bypass
63+
projects = [p.replace('\\', '\\\\').replace('"', '\\"') for p in projects]
6364

6465
if len(projects) == 1:
6566
project_query = f'project = "{projects[0]}"'

src/mcp_atlassian/utils/oauth_setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ def run_oauth_flow(args: OAuthSetupArgs) -> bool:
338338
"CONFLUENCE_URL": "https://your-company.atlassian.net/wiki",
339339
"JIRA_URL": "https://your-company.atlassian.net",
340340
"ATLASSIAN_OAUTH_CLIENT_ID": oauth_config.client_id,
341-
"ATLASSIAN_OAUTH_CLIENT_SECRET": oauth_config.client_secret,
341+
"ATLASSIAN_OAUTH_CLIENT_SECRET": "<redacted - set via environment variable>",
342342
"ATLASSIAN_OAUTH_REDIRECT_URI": oauth_config.redirect_uri,
343343
"ATLASSIAN_OAUTH_SCOPE": oauth_config.scope,
344344
"ATLASSIAN_OAUTH_CLOUD_ID": oauth_config.cloud_id,

0 commit comments

Comments
 (0)