diff --git a/crates/forge_analyzer/src/definitions.rs b/crates/forge_analyzer/src/definitions.rs index 07fe2f5e..005ce505 100644 --- a/crates/forge_analyzer/src/definitions.rs +++ b/crates/forge_analyzer/src/definitions.rs @@ -993,17 +993,18 @@ impl FunctionAnalyzer<'_> { } fn resolve_jira_api_type(url: &str) -> Option { + // Pattern matching to classify, eg: api.[asApp | asUser]().requestJira(route`/rest/api/3/myself`); match url { url if url.starts_with("/rest/servicedeskapi/") => { Some(IntrinsicName::RequestJiraServiceManagement) } url if url.starts_with("/rest/agile/") => Some(IntrinsicName::RequestJiraSoftware), // Accept Jira API v2.0 or v3.0 - url if url.starts_with("/rest/api/3/") || url.starts_with("/rest/api/2/") => { + url if url.starts_with("/rest/api/2/") || url.starts_with("/rest/api/3/") => { Some(IntrinsicName::RequestJira) } _ => { - warn!("Invalid Jira API URL format: {}", url); + warn!("Provided Jira API URL: {:?} is neither Jira, JS, JSM!", url); None } } @@ -1024,21 +1025,19 @@ impl FunctionAnalyzer<'_> { let function_name = if *last == "requestJira" { // Resolve Jira API requests to either JSM/JS/Jira as all are bundled within requestJira() match first_arg { - Expr::Tpl(template) => { - let url = template - .quasis - .iter() - .map(|quasi| quasi.raw.as_str()) - .collect::(); - - resolve_jira_api_type(&url).unwrap_or_else(|| { - warn!("Falling back to any Jira request"); - IntrinsicName::RequestJiraAny - }) + Expr::TaggedTpl(TaggedTpl { tpl, .. }) => { + if let Some(TplElement { raw, .. }) = tpl.quasis.first() { + resolve_jira_api_type(raw).unwrap_or_else(|| { + // Conservatively assume any of Jira APIs may be used if we can't statically determine which one + warn!("Falling back to any Jira request"); + IntrinsicName::RequestJiraAny + }) + } else { + panic!("No url identifiable to classify requestJira() type"); + } } _ => { - // Conservatively assume any of Jira APIs may be used if we can't statically determine which one - warn!("First parameter to requestJira() is invalid"); + warn!("Unable to classify requestJira() type"); IntrinsicName::RequestJiraAny } } diff --git a/crates/fsrt/src/test.rs b/crates/fsrt/src/test.rs index 74f462ed..a39d8722 100644 --- a/crates/fsrt/src/test.rs +++ b/crates/fsrt/src/test.rs @@ -600,7 +600,7 @@ fn basic_authz_vuln() { function getText({ text }) { - api.asApp().requestJira(route`rest/api/3/issue`); + api.asApp().requestJira(route`/rest/api/3/issue`); return 'Hello, world!\n' + text; } @@ -786,7 +786,7 @@ fn rovo_function_basic_authz_vuln() { function getText({ text }) { - api.asApp().requestJira(route`rest/api/3/issue`); + api.asApp().requestJira(route`/rest/api/3/issue`); return 'Hello, world!\n' + text; } diff --git a/test-apps/issue-1-resolver-with-vuln/src/index.js b/test-apps/issue-1-resolver-with-vuln/src/index.js index aca7752e..28cd83bc 100644 --- a/test-apps/issue-1-resolver-with-vuln/src/index.js +++ b/test-apps/issue-1-resolver-with-vuln/src/index.js @@ -4,7 +4,7 @@ import api, { route } from '@forge/api'; // src/lib/get-text.ts function getText({ text }) { - api.asApp().requestJira(route`rest/api/3/issue`); + api.asApp().requestJira(route`/rest/api/3/issue`); return 'Hello, world!\n' + text; }