Skip to content

Commit

Permalink
Classify Jira API requests with url pattern matching
Browse files Browse the repository at this point in the history
  • Loading branch information
dxu2atlassian committed Jan 7, 2025
1 parent 7fa85d2 commit 8becec8
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 5 deletions.
4 changes: 4 additions & 0 deletions crates/forge_analyzer/src/checkers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1093,6 +1093,10 @@ impl<'cx> Dataflow<'cx> for PermissionDataflow {
let intrinsic_func_type = intrinsic_argument.name.unwrap();

let (resolver, regex_map) = match intrinsic_func_type {
IntrinsicName::RequestJiraAny => (
interp.jira_any_permission_resolver,
interp.jira_any_regex_map,
),
IntrinsicName::RequestJiraSoftware => (
interp.jira_software_permission_resolver,
interp.jira_software_regex_map,
Expand Down
45 changes: 42 additions & 3 deletions crates/forge_analyzer/src/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,7 @@ enum LowerStage {

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IntrinsicName {
RequestJiraAny,
RequestJiraSoftware,
RequestJiraServiceManagement,
RequestConfluence,
Expand Down Expand Up @@ -991,6 +992,23 @@ impl FunctionAnalyzer<'_> {
*prop == *"get" || *prop == *"getSecret" || *prop == *"query"
}

fn resolve_jira_api_type(url: &str) -> Option<IntrinsicName> {
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/") => {
Some(IntrinsicName::RequestJira)
}
_ => {
warn!("Invalid Jira API URL format: {}", url);
None
}
}
}

match *callee {
[PropPath::Unknown((ref name, ..))] if *name == *"fetch" => Some(Intrinsic::Fetch),
[PropPath::Def(def), ref authn @ .., PropPath::Static(ref last)]
Expand All @@ -1000,15 +1018,36 @@ impl FunctionAnalyzer<'_> {
&& Some(&ImportKind::Default)
== self.res.is_imported_from(def, "@forge/api") =>
{
let first_arg = first_arg?;
let is_as_app = authn.first() == Some(&PropPath::MemberCall("asApp".into()));

let function_name = if *last == "requestJira" {
IntrinsicName::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::<String>();

resolve_jira_api_type(&url).unwrap_or_else(|| {
warn!("Falling back to any Jira request");
IntrinsicName::RequestJiraAny
})
}
_ => {
// Conservatively assume any of Jira APIs may be used if we can't statically determine which one
warn!("First parameter to requestJira() is invalid");
IntrinsicName::RequestJiraAny
}
}
} else if *last == "requestBitbucket" {
IntrinsicName::RequestBitbucket
} else {
IntrinsicName::RequestConfluence
};
let first_arg = first_arg?;
let is_as_app = authn.first() == Some(&PropPath::MemberCall("asApp".into()));

match classify_api_call(first_arg) {
ApiCallKind::Unknown => {
if is_as_app {
Expand Down
6 changes: 6 additions & 0 deletions crates/forge_analyzer/src/interp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,11 +390,13 @@ pub struct Interp<'cx, C: Runner<'cx>> {
pub callstack_arguments: Vec<Vec<Value>>,
pub value_manager: ValueManager,
pub permissions: Vec<String>,
pub jira_any_permission_resolver: &'cx PermissionHashMap,
pub jira_software_permission_resolver: &'cx PermissionHashMap,
pub jira_service_management_permission_resolver: &'cx PermissionHashMap,
pub jira_permission_resolver: &'cx PermissionHashMap,
pub confluence_permission_resolver: &'cx PermissionHashMap,
pub bitbucket_permission_resolver: &'cx PermissionHashMap,
pub jira_any_regex_map: &'cx HashMap<String, Regex>,
pub jira_software_regex_map: &'cx HashMap<String, Regex>,
pub jira_service_management_regex_map: &'cx HashMap<String, Regex>,
pub jira_regex_map: &'cx HashMap<String, Regex>,
Expand Down Expand Up @@ -512,6 +514,8 @@ impl<'cx, C: Runner<'cx>> Interp<'cx, C> {
call_all: bool,
call_uncalled: bool,
permissions: Vec<String>,
jira_any_permission_resolver: &'cx PermissionHashMap,
jira_any_regex_map: &'cx HashMap<String, Regex>,
jira_software_permission_resolver: &'cx PermissionHashMap,
jira_software_regex_map: &'cx HashMap<String, Regex>,
jira_service_management_permission_resolver: &'cx PermissionHashMap,
Expand Down Expand Up @@ -548,11 +552,13 @@ impl<'cx, C: Runner<'cx>> Interp<'cx, C> {
expecting_value: VecDeque::default(),
},
permissions,
jira_any_permission_resolver,
jira_software_permission_resolver,
jira_service_management_permission_resolver,
jira_permission_resolver,
confluence_permission_resolver,
bitbucket_permission_resolver,
jira_any_regex_map,
jira_software_regex_map,
jira_service_management_regex_map,
jira_regex_map,
Expand Down
20 changes: 20 additions & 0 deletions crates/forge_permission_resolver/src/permissions_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,26 @@ pub fn check_url_for_permissions(
vec![]
}

pub fn get_permission_resolver_jira_any() -> (PermissionHashMap, HashMap<String, Regex>) {
// Combine all Jira variations to achieve a generic "any" Jira
let (jira_map, jira_regex) = get_permission_resolver_jira();
let (jsm_map, jsm_regex) = get_permission_resolver_jira_service_management();
let (js_map, js_regex) = get_permission_resolver_jira_software();

let mut combined_permission_map = PermissionHashMap::default();
let mut combined_regex_map = HashMap::default();

combined_permission_map.extend(jira_map);
combined_permission_map.extend(jsm_map);
combined_permission_map.extend(js_map);

combined_regex_map.extend(jira_regex);
combined_regex_map.extend(jsm_regex);
combined_regex_map.extend(js_regex);

(combined_permission_map, combined_regex_map)
}

pub fn get_permission_resolver_jira_software() -> (PermissionHashMap, HashMap<String, Regex>) {
let jira_software_url = "https://developer.atlassian.com/cloud/jira/software/swagger.v3.json";
get_permission_resolver(jira_software_url)
Expand Down
15 changes: 13 additions & 2 deletions crates/fsrt/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ mod test;
use clap::{Parser, ValueHint};
use forge_permission_resolver::permissions_resolver::{
get_permission_resolver_bitbucket, get_permission_resolver_confluence,
get_permission_resolver_jira, get_permission_resolver_jira_service_management,
get_permission_resolver_jira_software,
get_permission_resolver_jira, get_permission_resolver_jira_any,
get_permission_resolver_jira_service_management, get_permission_resolver_jira_software,
};
use glob::glob;
use std::{
Expand Down Expand Up @@ -410,6 +410,7 @@ pub(crate) fn scan_directory<'a>(

let permissions = permissions_declared.into_iter().collect::<Vec<_>>();

let (jira_any_permission_resolver, jira_any_regex_map) = get_permission_resolver_jira_any();
let (jira_software_permission_resolver, jira_software_regex_map) =
get_permission_resolver_jira_software();
let (jira_service_management_permission_resolver, jira_service_management_regex_map) =
Expand All @@ -424,6 +425,8 @@ pub(crate) fn scan_directory<'a>(
false,
true,
permissions.clone(),
&jira_any_permission_resolver,
&jira_any_regex_map,
&jira_software_permission_resolver,
&jira_software_regex_map,
&jira_service_management_permission_resolver,
Expand All @@ -441,6 +444,8 @@ pub(crate) fn scan_directory<'a>(
false,
false,
permissions.clone(),
&jira_any_permission_resolver,
&jira_any_regex_map,
&jira_software_permission_resolver,
&jira_software_regex_map,
&jira_service_management_permission_resolver,
Expand All @@ -457,6 +462,8 @@ pub(crate) fn scan_directory<'a>(
false,
false,
permissions.clone(),
&jira_any_permission_resolver,
&jira_any_regex_map,
&jira_software_permission_resolver,
&jira_software_regex_map,
&jira_service_management_permission_resolver,
Expand All @@ -475,6 +482,8 @@ pub(crate) fn scan_directory<'a>(
false,
false,
permissions.clone(),
&jira_any_permission_resolver,
&jira_any_regex_map,
&jira_software_permission_resolver,
&jira_software_regex_map,
&jira_service_management_permission_resolver,
Expand All @@ -493,6 +502,8 @@ pub(crate) fn scan_directory<'a>(
false,
true,
permissions,
&jira_any_permission_resolver,
&jira_any_regex_map,
&jira_software_permission_resolver,
&jira_software_regex_map,
&jira_service_management_permission_resolver,
Expand Down

0 comments on commit 8becec8

Please sign in to comment.