Skip to content

Commit 5039331

Browse files
End to end bitbucket unit tests with bug fix in main function
1 parent bed13f1 commit 5039331

File tree

4 files changed

+136
-23
lines changed

4 files changed

+136
-23
lines changed

crates/forge_analyzer/src/checkers.rs

+18-19
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,3 @@
1-
use core::fmt;
2-
use forge_permission_resolver::permissions_resolver::{
3-
check_url_for_permissions, PermissionHashMap, RequestType,
4-
};
5-
use forge_utils::FxHashMap;
6-
use itertools::Itertools;
7-
use regex::Regex;
8-
use smallvec::SmallVec;
9-
use std::{
10-
cmp::max,
11-
collections::HashMap,
12-
collections::HashSet,
13-
iter::{self, zip},
14-
mem,
15-
ops::ControlFlow,
16-
path::PathBuf,
17-
};
18-
use tracing::{debug, info, warn};
19-
201
use crate::interp::ProjectionVec;
212
use crate::utils::projvec_from_str;
223
use crate::{
@@ -36,6 +17,24 @@ use crate::{
3617
},
3718
worklist::WorkList,
3819
};
20+
use core::fmt;
21+
use forge_permission_resolver::permissions_resolver::{
22+
check_url_for_permissions, PermissionHashMap, RequestType,
23+
};
24+
use forge_utils::FxHashMap;
25+
use itertools::Itertools;
26+
use regex::Regex;
27+
use smallvec::SmallVec;
28+
use std::{
29+
cmp::max,
30+
collections::HashMap,
31+
collections::HashSet,
32+
iter::{self, zip},
33+
mem,
34+
ops::ControlFlow,
35+
path::PathBuf,
36+
};
37+
use tracing::{debug, info, warn};
3938

4039
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Default)]
4140
pub enum Taint {

crates/forge_analyzer/src/reporter.rs

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ impl Vulnerability {
2626
pub fn check_name(&self) -> &str {
2727
&self.check_name
2828
}
29+
30+
pub fn description(&self) -> &str {
31+
&self.description
32+
}
2933
}
3034

3135
pub trait IntoVuln {

crates/fsrt/src/main.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,7 @@ pub(crate) fn scan_directory<'a>(
671671
.collect::<Vec<String>>()
672672
.join("\n");
673673

674+
let mut final_perms: HashSet<&String> = perm_interp.permissions.iter().collect();
674675
let ast = parse_schema::<&str>(&joined_schema);
675676

676677
if let std::result::Result::Ok(doc) = ast {
@@ -710,15 +711,15 @@ pub(crate) fn scan_directory<'a>(
710711
})
711712
.collect();
712713

713-
let final_perms: HashSet<&String> = perm_interp
714+
final_perms = perm_interp
714715
.permissions
715716
.iter()
716717
.filter(|f| !oauth_scopes.contains(f.as_str()))
717718
.collect();
719+
}
718720

719-
if run_permission_checker && !final_perms.is_empty() {
720-
reporter.add_vulnerabilities([PermissionVuln::new(final_perms)]);
721-
}
721+
if run_permission_checker && !final_perms.is_empty() {
722+
reporter.add_vulnerabilities([PermissionVuln::new(final_perms)]);
722723
}
723724

724725
Ok(reporter.into_report())

crates/fsrt/src/test.rs

+109
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ trait ReportExt {
2020
#[cfg(feature = "graphql_schema")]
2121
fn contains_perm_vuln(&self, expected_len: usize) -> bool;
2222

23+
fn vuln_description_contains(&self, check_name: &str, description_snippet: &str) -> bool;
24+
2325
fn contains_vulns(&self, expected_len: i32) -> bool;
2426

2527
fn contains_authz_vuln(&self, expected_len: usize) -> bool;
@@ -59,6 +61,17 @@ impl ReportExt for Report {
5961
== expected_len
6062
}
6163

64+
#[inline]
65+
fn vuln_description_contains(&self, check_name: &str, description_snippet: &str) -> bool {
66+
self.into_vulns()
67+
.iter()
68+
.filter(|vuln| {
69+
vuln.check_name() == check_name && vuln.description().contains(&description_snippet)
70+
})
71+
.count()
72+
== 1
73+
}
74+
6275
#[inline]
6376
fn contains_vulns(&self, expected_len: i32) -> bool {
6477
self.into_vulns().len() == expected_len as usize
@@ -887,3 +900,99 @@ fn authz_function_called_in_object_bitbucket() {
887900
let scan_result = scan_directory_test(test_forge_project);
888901
assert!(scan_result.contains_vulns(1))
889902
}
903+
904+
#[test] // Tests manifest has an extra scope defined but not being used, we expect a permission vuln.
905+
fn extra_scope_bitbucket() {
906+
let test_forge_project = MockForgeProject::files_from_string(
907+
"// src/index.tsx
908+
import ForgeUI, { render, Fragment, Macro, Text } from '@forge/ui';
909+
import api, { route, fetch } from '@forge/api';
910+
const App = () => {
911+
let testObject = {
912+
someFunction() {
913+
const res = api.asUser().requestBitbucket(route`/repositories/mockworkspace/mockreposlug/default-reviewers/jcg`, {
914+
method: 'PUT',
915+
body: {}
916+
});
917+
return res;
918+
}
919+
}
920+
testObject.someFunction()
921+
return (
922+
<Fragment>
923+
<Text>Hello world!</Text>
924+
</Fragment>
925+
);
926+
};
927+
export const run = render(<Macro app={<App />} />);
928+
929+
// manifest.yaml
930+
modules:
931+
macro:
932+
- key: basic-hello-world
933+
function: main
934+
title: basic
935+
handler: nothing
936+
description: Inserts Hello world!
937+
function:
938+
- key: main
939+
handler: index.run
940+
app:
941+
id: ari:cloud:ecosystem::app/07b89c0f-949a-4905-9de9-6c9521035986
942+
permissions:
943+
scopes:
944+
- 'admin:repository:bitbucket'
945+
- 'unused:permission:defined'"
946+
);
947+
948+
let scan_result = scan_directory_test(test_forge_project);
949+
assert!(scan_result.contains_vulns(1));
950+
assert!(scan_result.vuln_description_contains("Least-Privilege", "unused:permission:defined"));
951+
}
952+
953+
#[test] // Tests manifest with no extra scopes, we expect no vulns.
954+
fn no_extra_scope_bitbucket() {
955+
let test_forge_project = MockForgeProject::files_from_string(
956+
"// src/index.tsx
957+
import ForgeUI, { render, Fragment, Macro, Text } from '@forge/ui';
958+
import api, { route, fetch } from '@forge/api';
959+
const App = () => {
960+
let testObject = {
961+
someFunction() {
962+
const res = api.asUser().requestBitbucket(route`/repositories/mockworkspace/mockreposlug/default-reviewers/jcg`, {
963+
method: 'PUT',
964+
body: {}
965+
});
966+
return res;
967+
}
968+
}
969+
testObject.someFunction()
970+
return (
971+
<Fragment>
972+
<Text>Hello world!</Text>
973+
</Fragment>
974+
);
975+
};
976+
export const run = render(<Macro app={<App />} />);
977+
978+
// manifest.yaml
979+
modules:
980+
macro:
981+
- key: basic-hello-world
982+
function: main
983+
title: basic
984+
handler: nothing
985+
description: Inserts Hello world!
986+
function:
987+
- key: main
988+
handler: index.run
989+
app:
990+
id: ari:cloud:ecosystem::app/07b89c0f-949a-4905-9de9-6c9521035986
991+
permissions:
992+
scopes:
993+
- 'admin:repository:bitbucket'"
994+
);
995+
996+
let scan_result = scan_directory_test(test_forge_project);
997+
assert!(scan_result.contains_vulns(0));
998+
}

0 commit comments

Comments
 (0)