From 0ce2619512084db8e241abaf6fcfaed3e46304e3 Mon Sep 17 00:00:00 2001 From: dxu2atlassian <136645827+dxu2atlassian@users.noreply.github.com> Date: Sun, 15 Dec 2024 16:22:06 -0800 Subject: [PATCH] Change serde parsing logic to accomodate JS swagger, added permission resolver with unit tests and refactor products to use match statement --- crates/forge_analyzer/src/checkers.rs | 126 ++++++------------ crates/forge_analyzer/src/definitions.rs | 1 + crates/forge_analyzer/src/interp.rs | 10 +- .../src/permissions_resolver.rs | 66 ++++++++- crates/fsrt/src/main.rs | 13 ++ 5 files changed, 125 insertions(+), 91 deletions(-) diff --git a/crates/forge_analyzer/src/checkers.rs b/crates/forge_analyzer/src/checkers.rs index e374ca7..e664c06 100644 --- a/crates/forge_analyzer/src/checkers.rs +++ b/crates/forge_analyzer/src/checkers.rs @@ -1092,6 +1092,29 @@ impl<'cx> Dataflow<'cx> for PermissionDataflow { let mut permissions_within_call: Vec = vec![]; let intrinsic_func_type = intrinsic_argument.name.unwrap(); + let (resolver, regex_map) = match intrinsic_func_type { + IntrinsicName::RequestJiraSoftware => ( + interp.jira_software_permission_resolver, + interp.jira_software_regex_map, + ), + IntrinsicName::RequestJiraServiceManagement => ( + interp.jira_service_management_permission_resolver, + interp.jira_service_management_regex_map, + ), + IntrinsicName::RequestConfluence => ( + interp.confluence_permission_resolver, + interp.confluence_regex_map, + ), + IntrinsicName::RequestJira => { + (interp.jira_permission_resolver, interp.jira_regex_map) + } + IntrinsicName::RequestBitbucket => ( + interp.bitbucket_permission_resolver, + interp.bitbucket_regex_map, + ), + _ => unreachable!("Invalid intrinsic function type"), + }; + if intrinsic_argument.first_arg.is_none() { interp.permissions.drain(..); } else { @@ -1099,92 +1122,27 @@ impl<'cx> Dataflow<'cx> for PermissionDataflow { .first_arg .iter() .for_each(|first_arg_vec| { - if let Some(second_arg_vec) = intrinsic_argument.second_arg.clone() { - first_arg_vec.iter().for_each(|first_arg| { - let first_arg = first_arg.replace(&['\"'][..], ""); - second_arg_vec.iter().for_each(|second_arg| { - if intrinsic_func_type - == IntrinsicName::RequestJiraServiceManagement - { - let permissions = check_url_for_permissions( - interp.jira_service_management_permission_resolver, - interp.jira_service_management_regex_map, - translate_request_type(Some(second_arg)), - &first_arg, - ); - permissions_within_call.extend_from_slice(&permissions) - } else if intrinsic_func_type - == IntrinsicName::RequestConfluence - { - let permissions = check_url_for_permissions( - interp.confluence_permission_resolver, - interp.confluence_regex_map, - translate_request_type(Some(second_arg)), - &first_arg, - ); - permissions_within_call.extend_from_slice(&permissions) - } else if intrinsic_func_type == IntrinsicName::RequestJira { - let permissions = check_url_for_permissions( - interp.jira_permission_resolver, - interp.jira_regex_map, - translate_request_type(Some(second_arg)), - &first_arg, - ); - permissions_within_call.extend_from_slice(&permissions) - } else if intrinsic_func_type == IntrinsicName::RequestBitbucket - { - let permissions = check_url_for_permissions( - interp.bitbucket_permission_resolver, - interp.bitbucket_regex_map, - translate_request_type(Some(second_arg)), - &first_arg, - ); - permissions_within_call.extend_from_slice(&permissions) - } + first_arg_vec.iter().for_each(|first_arg| { + let first_arg = first_arg.replace(&['\"'][..], ""); + let request_types = intrinsic_argument + .second_arg + .as_ref() + .map(|args| { + args.iter() + .map(|arg| translate_request_type(Some(arg))) + .collect::>() + .into_iter() }) - }) - } else { - first_arg_vec.iter().for_each(|first_arg| { - let first_arg = first_arg.replace(&['\"'][..], ""); - if intrinsic_func_type - == IntrinsicName::RequestJiraServiceManagement - { - let permissions = check_url_for_permissions( - interp.jira_service_management_permission_resolver, - interp.jira_service_management_regex_map, - RequestType::Get, - &first_arg, - ); - permissions_within_call.extend_from_slice(&permissions) - } else if intrinsic_func_type == IntrinsicName::RequestConfluence { - let permissions = check_url_for_permissions( - interp.confluence_permission_resolver, - interp.confluence_regex_map, - RequestType::Get, - &first_arg, - ); - permissions_within_call.extend_from_slice(&permissions) - } else if intrinsic_func_type == IntrinsicName::RequestJira { - let permissions = check_url_for_permissions( - interp.jira_permission_resolver, - interp.jira_regex_map, - RequestType::Get, - &first_arg, - ); - permissions_within_call.extend_from_slice(&permissions) - } else if intrinsic_func_type == IntrinsicName::RequestBitbucket { - let permissions = check_url_for_permissions( - interp.bitbucket_permission_resolver, - interp.bitbucket_regex_map, - RequestType::Get, - &first_arg, - ); - permissions_within_call.extend_from_slice(&permissions) - } - }) - } - }); + .unwrap_or_else(|| vec![RequestType::Get].into_iter()); + for req_type in request_types { + let permissions = check_url_for_permissions( + resolver, regex_map, req_type, &first_arg, + ); + permissions_within_call.extend_from_slice(&permissions); + } + }); + }); interp .permissions .retain(|permissions| !permissions_within_call.contains(permissions)); diff --git a/crates/forge_analyzer/src/definitions.rs b/crates/forge_analyzer/src/definitions.rs index d8535e0..e9b7bd2 100644 --- a/crates/forge_analyzer/src/definitions.rs +++ b/crates/forge_analyzer/src/definitions.rs @@ -617,6 +617,7 @@ enum LowerStage { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum IntrinsicName { + RequestJiraSoftware, RequestJiraServiceManagement, RequestConfluence, RequestJira, diff --git a/crates/forge_analyzer/src/interp.rs b/crates/forge_analyzer/src/interp.rs index 2b10c3c..f4649a7 100644 --- a/crates/forge_analyzer/src/interp.rs +++ b/crates/forge_analyzer/src/interp.rs @@ -390,11 +390,13 @@ pub struct Interp<'cx, C: Runner<'cx>> { pub callstack_arguments: Vec>, pub value_manager: ValueManager, pub permissions: Vec, + 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 jira_service_management_regex_map: &'cx HashMap, pub bitbucket_permission_resolver: &'cx PermissionHashMap, + pub jira_software_regex_map: &'cx HashMap, + pub jira_service_management_regex_map: &'cx HashMap, pub jira_regex_map: &'cx HashMap, pub confluence_regex_map: &'cx HashMap, pub bitbucket_regex_map: &'cx HashMap, @@ -510,6 +512,8 @@ impl<'cx, C: Runner<'cx>> Interp<'cx, C> { call_all: bool, call_uncalled: bool, permissions: Vec, + jira_software_permission_resolver: &'cx PermissionHashMap, + jira_software_regex_map: &'cx HashMap, jira_service_management_permission_resolver: &'cx PermissionHashMap, jira_service_management_regex_map: &'cx HashMap, jira_permission_resolver: &'cx PermissionHashMap, @@ -544,11 +548,13 @@ impl<'cx, C: Runner<'cx>> Interp<'cx, C> { expecting_value: VecDeque::default(), }, permissions, + jira_software_permission_resolver, jira_service_management_permission_resolver, jira_permission_resolver, confluence_permission_resolver, - jira_service_management_regex_map, bitbucket_permission_resolver, + jira_software_regex_map, + jira_service_management_regex_map, jira_regex_map, confluence_regex_map, bitbucket_regex_map, diff --git a/crates/forge_permission_resolver/src/permissions_resolver.rs b/crates/forge_permission_resolver/src/permissions_resolver.rs index 8404bb1..62466ba 100644 --- a/crates/forge_permission_resolver/src/permissions_resolver.rs +++ b/crates/forge_permission_resolver/src/permissions_resolver.rs @@ -35,6 +35,10 @@ struct RequestDetails { default )] permission: Vec, + + // For parsing Jira Software as that swagger doesn't follow "x-atlassian-oauth2-scopes" scope style + #[serde(default)] + security: Vec, } #[derive(Default, Debug, Clone, PartialEq, Eq, Deserialize)] @@ -44,6 +48,12 @@ struct PermissionData { scopes: Vec, } +#[derive(Default, Debug, Clone, PartialEq, Eq, Deserialize)] +struct SecurityData { + #[serde(default, rename = "OAuth2")] + oauth2: Vec, +} + #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] pub enum RequestType { Get, @@ -85,6 +95,11 @@ pub fn check_url_for_permissions( vec![] } +pub fn get_permission_resolver_jira_software() -> (PermissionHashMap, HashMap) { + let jira_software_url = "https://developer.atlassian.com/cloud/jira/software/swagger.v3.json"; + get_permission_resolver(jira_software_url) +} + pub fn get_permission_resolver_jira_service_management( ) -> (PermissionHashMap, HashMap) { let jira_service_management_url = @@ -199,12 +214,25 @@ fn get_request_type( } fn get_scopes(endpoint_data: &RequestDetails) -> Vec { - endpoint_data + let mut scopes = endpoint_data .permission .iter() .flat_map(|data| &*data.scopes) .cloned() - .collect() + .collect::>(); + + if scopes.is_empty() { + // For Jira Software if the initial scopes are empty, try the scopes from the security field + scopes.extend( + endpoint_data + .security + .iter() + .flat_map(|sec| &sec.oauth2) + .cloned(), + ); + } + + scopes } #[cfg(test)] @@ -285,9 +313,6 @@ mod test { let request_type = RequestType::Get; let result = check_url_for_permissions(&permission_map, ®ex_map, request_type, url); - println!("Permission Map: {:?}", permission_map); - println!("Regex Map: {:?}", regex_map); - assert!(!result.is_empty(), "Should have parsed permissions"); assert!( result.contains(&String::from("manage:servicedesk-customer")), @@ -362,4 +387,35 @@ mod test { "Should require admin:repository:bitbucket permission" ); } + + #[test] + fn test_get_issues_for_epic() { + let (permission_map, regex_map) = get_permission_resolver_jira_software(); + let url = "/rest/agile/1.0/sprint/23"; + let request_type = RequestType::Get; + let result = check_url_for_permissions(&permission_map, ®ex_map, request_type, url); + + assert!(!result.is_empty(), "Should have parsed permissions"); + assert!( + result.contains(&String::from("read:sprint:jira-software")), + "Should require read:sprint:jira-software permission" + ); + } + + #[test] + fn test_get_all_boards() { + let (permission_map, regex_map) = get_permission_resolver_jira_software(); + let url = "/rest/agile/1.0/board"; + let request_type = RequestType::Get; + let result = check_url_for_permissions(&permission_map, ®ex_map, request_type, url); + + assert!(!result.is_empty(), "Should have parsed permissions"); + + let expected_permission: Vec = vec![ + String::from("read:board-scope:jira-software"), + String::from("read:project:jira"), + ]; + + assert_eq!(result, expected_permission); + } } diff --git a/crates/fsrt/src/main.rs b/crates/fsrt/src/main.rs index 9968a8c..ffbb8d1 100644 --- a/crates/fsrt/src/main.rs +++ b/crates/fsrt/src/main.rs @@ -8,6 +8,7 @@ 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, }; use std::{ @@ -275,6 +276,8 @@ pub(crate) fn scan_directory<'a>( let permissions = permissions_declared.into_iter().collect::>(); + 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) = get_permission_resolver_jira_service_management(); let (jira_permission_resolver, jira_regex_map) = get_permission_resolver_jira(); @@ -287,6 +290,8 @@ pub(crate) fn scan_directory<'a>( false, true, permissions.clone(), + &jira_software_permission_resolver, + &jira_software_regex_map, &jira_service_management_permission_resolver, &jira_service_management_regex_map, &jira_permission_resolver, @@ -302,6 +307,8 @@ pub(crate) fn scan_directory<'a>( false, false, permissions.clone(), + &jira_software_permission_resolver, + &jira_software_regex_map, &jira_service_management_permission_resolver, &jira_service_management_regex_map, &jira_permission_resolver, @@ -316,6 +323,8 @@ pub(crate) fn scan_directory<'a>( false, false, permissions.clone(), + &jira_software_permission_resolver, + &jira_software_regex_map, &jira_service_management_permission_resolver, &jira_service_management_regex_map, &jira_permission_resolver, @@ -332,6 +341,8 @@ pub(crate) fn scan_directory<'a>( false, false, permissions.clone(), + &jira_software_permission_resolver, + &jira_software_regex_map, &jira_service_management_permission_resolver, &jira_service_management_regex_map, &jira_permission_resolver, @@ -348,6 +359,8 @@ pub(crate) fn scan_directory<'a>( false, true, permissions, + &jira_software_permission_resolver, + &jira_software_regex_map, &jira_service_management_permission_resolver, &jira_service_management_regex_map, &jira_permission_resolver,