Skip to content

Commit

Permalink
EAS-2582 : Map Scopes to OAuth
Browse files Browse the repository at this point in the history
  • Loading branch information
gersbach committed Jan 7, 2025
1 parent 7fa85d2 commit f6201c2
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 16 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Arguments:
-f, --function <FUNCTION> A specific function to scan. Must be an entrypoint specified in `manifest.yml`
-h, --help Print help information
-V, --version Print version information
--check-permissions Runs the permission checker
--graphql-schema-path <LOCATION> Uses the graphql schema in location; othwerwise selects ~/.config dir
```

## Installation
Expand Down Expand Up @@ -64,6 +66,12 @@ until then you can test `fsrt` by manually invoking:
fsrt ./test-apps/jira-damn-vulnerable-forge-app
```

Testing with a GraphQl Schema:

```sh
cargo test --features graphql_schema
```

## Contributions

Contributions to FSRT are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.
Expand Down
3 changes: 3 additions & 0 deletions crates/fsrt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ license.workspace = true
[lints]
workspace = true

[features]
graphql_schema = []

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
Expand Down
43 changes: 41 additions & 2 deletions crates/fsrt/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use std::{

use graphql_parser::{
query::{Mutation, Query, SelectionSet},
schema::ObjectType,
schema::{EnumType, EnumValue, ObjectType},
};

use graphql_parser::{
Expand Down Expand Up @@ -618,6 +618,40 @@ pub(crate) fn scan_directory<'a>(
&mut path.to_owned()
};

let mut scope_path = path.clone();

scope_path.push("schema/shared/agg-shared-scopes.nadel");

let scope_map = fs::read_to_string(&scope_path).unwrap_or_default();

let ast = parse_schema::<&str>(&scope_map).unwrap_or_default();

let mut scope_name_to_oauth = HashMap::new();

ast.definitions.iter().for_each(|val| {
if let graphql_parser::schema::Definition::TypeDefinition(TypeDefinition::Enum(
EnumType { values, .. },
)) = val
{
values.iter().for_each(
|EnumValue {
directives, name, ..
}| {
if let Some(directive) = directives.first() {
if let graphql_parser::schema::Value::String(oauth_scope) = &directive
.arguments
.first()
.expect("Should only be one directive")
.1
{
scope_name_to_oauth.insert(name, oauth_scope);
}
}
},
)
}
});

path.push("schema/*/*.nadel");

let joined_schema = glob(path.to_str().unwrap_or_default())
Expand Down Expand Up @@ -653,10 +687,15 @@ pub(crate) fn scan_directory<'a>(
used_graphql_perms.extend_from_slice(&graphql_perms_defid);
used_graphql_perms.extend_from_slice(&graphql_perms_varid);

let oauth_scopes: Vec<&&String> = used_graphql_perms
.iter()
.map(|val| scope_name_to_oauth.get(&val.as_str()).unwrap())
.collect();

let final_perms: Vec<&String> = perm_interp
.permissions
.iter()
.filter(|f| !used_graphql_perms.contains(&**f))
.filter(|f| !oauth_scopes.contains(&f))
.collect();

if run_permission_checker && !final_perms.is_empty() {
Expand Down
23 changes: 9 additions & 14 deletions crates/fsrt/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,6 @@ fn secret_vuln_object() {
);

let scan_result = scan_directory_test(test_forge_project);
println!("scan_result {scan_result:?}");
assert!(scan_result.contains_secret_vuln(1));
assert!(scan_result.contains_vulns(1))
}
Expand Down Expand Up @@ -635,13 +634,12 @@ fn basic_authz_vuln() {
);

let scan_result = scan_directory_test(test_forge_project);
println!("vuln, {:#?}", scan_result);
assert!(scan_result.contains_authz_vuln(1));
assert!(scan_result.contains_vulns(1));
}

#[cfg(feature = "graphql_schema")]
#[test]
#[ignore]
fn excess_scope() {
let mut test_forge_project = MockForgeProject::files_from_string(
"// src/index.tsx
Expand All @@ -663,13 +661,13 @@ fn excess_scope() {
.push("read:component:compass".into());

let scan_result = scan_directory_test(test_forge_project);
println!("scan_result {:#?}", scan_result);
assert!(scan_result.contains_perm_vuln(1));
assert!(scan_result.contains_vulns(1))
}

#[cfg(feature = "graphql_schema")]
#[test]
fn correct_scopes() {
fn graphql_correct_scopes() {
let mut test_forge_project = MockForgeProject::files_from_string(
"// src/index.tsx
import ForgeUI, { render, Macro } from '@forge/ui';
Expand Down Expand Up @@ -706,16 +704,15 @@ fn correct_scopes() {
.test_manifest
.permissions
.scopes
.push("read:component:compass".into());
.push("compass:atlassian-external".into());

let scan_result = scan_directory_test(test_forge_project);
println!("scan_result {:#?}", scan_result);
assert!(scan_result.contains_vulns(0))
}

#[cfg(feature = "graphql_schema")]
#[test]
#[ignore]
fn excess_scope_with_fragments() {
fn graphql_excess_scope_with_fragments() {
let mut test_forge_project = MockForgeProject::files_from_string(
"// src/index.tsx
import ForgeUI, { render, Macro } from '@forge/ui';
Expand All @@ -740,13 +737,13 @@ fn excess_scope_with_fragments() {
.push("read:component:compass".into());

let scan_result = scan_directory_test(test_forge_project);
println!("scan_result {:#?}", scan_result);
assert!(scan_result.contains_perm_vuln(1));
assert!(scan_result.contains_vulns(1))
}

#[cfg(feature = "graphql_schema")]
#[test]
fn correct_scopes_with_fragment() {
fn graphql_correct_scopes_with_fragment() {
let mut test_forge_project = MockForgeProject::files_from_string(
"// src/index.tsx
import ForgeUI, { render, Macro } from '@forge/ui';
Expand All @@ -769,10 +766,9 @@ fn correct_scopes_with_fragment() {
.test_manifest
.permissions
.scopes
.push("read:component:compass".into());
.push("compass:atlassian-external".into());

let scan_result = scan_directory_test(test_forge_project);
println!("scan_result {:#?}", scan_result);
assert!(scan_result.contains_vulns(0))
}

Expand Down Expand Up @@ -887,6 +883,5 @@ fn authz_function_called_in_object_bitbucket() {
);

let scan_result = scan_directory_test(test_forge_project);
println!("scan_result {:#?}", scan_result);
assert!(scan_result.contains_vulns(1))
}

0 comments on commit f6201c2

Please sign in to comment.