Skip to content

Commit

Permalink
In the analyze request, receive and use the configuration file.
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobotb committed Mar 5, 2024
1 parent 1ac5296 commit 3f8cc79
Show file tree
Hide file tree
Showing 2 changed files with 179 additions and 1 deletion.
4 changes: 4 additions & 0 deletions crates/static-analysis-server/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
pub const ERROR_DECODING_BASE64: &str = "error-decoding-base64";
// when the code is not base64
pub const ERROR_CODE_NOT_BASE64: &str = "code-not-base64";
// when the configuration file is not valid base64
pub const ERROR_CONFIGURATION_NOT_BASE64: &str = "configuration-not-base64";
// when it was not possible to parse the configuration file
pub const ERROR_COULD_NOT_PARSE_CONFIGURATION: &str = "could-not-parse-configuration";
// rules and core language are different
pub const ERROR_CODE_LANGUAGE_MISMATCH: &str = "language-mismatch";
// no root node when trying to get the AST
Expand Down
176 changes: 175 additions & 1 deletion crates/static-analysis-server/src/request.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,43 @@
use crate::constants::{
ERROR_CHECKSUM_MISMATCH, ERROR_CODE_LANGUAGE_MISMATCH, ERROR_CODE_NOT_BASE64,
ERROR_DECODING_BASE64,
ERROR_CONFIGURATION_NOT_BASE64, ERROR_COULD_NOT_PARSE_CONFIGURATION, ERROR_DECODING_BASE64,
};
use crate::model::analysis_request::{AnalysisRequest, ServerRule};
use crate::model::analysis_response::{AnalysisResponse, RuleResponse};
use crate::model::violation::violation_to_server;
use kernel::analysis::analyze::analyze;
use kernel::config_file::parse_config_file;
use kernel::model::analysis::AnalysisOptions;
use kernel::model::rule::{Rule, RuleCategory, RuleInternal, RuleSeverity};
use kernel::path_restrictions::PathRestrictions;
use kernel::utils::decode_base64_string;

#[tracing::instrument(skip_all)]
pub fn process_analysis_request(request: AnalysisRequest) -> AnalysisResponse {
tracing::debug!("Processing analysis request");

// Decode the configuration and extract the path restrictions, if present.
let path_restrictions = match request.configuration_base64.map(decode_base64_string) {
Some(Err(_)) => {
tracing::info!("Validation error: configuration is not a base64 string");
return AnalysisResponse {
rule_responses: vec![],
errors: vec![ERROR_CONFIGURATION_NOT_BASE64.to_string()],
};
}
Some(Ok(cfg)) => match parse_config_file(&cfg) {
Err(_) => {
tracing::info!("Validation error: could not parse configuration");
return AnalysisResponse {
rule_responses: vec![],
errors: vec![ERROR_COULD_NOT_PARSE_CONFIGURATION.to_string()],
};
}
Ok(cfg_file) => Some(PathRestrictions::from_ruleset_configs(&cfg_file.rulesets)),
},
None => None,
};

let rules_with_invalid_language: Vec<ServerRule> = request
.rules
.iter()
Expand All @@ -38,6 +62,12 @@ pub fn process_analysis_request(request: AnalysisRequest) -> AnalysisResponse {
let server_rules_to_rules: Vec<Rule> = request
.rules
.iter()
.filter(|r| {
path_restrictions
.as_ref()
.map(|pr| pr.rule_applies(&r.name, &request.filename))
.unwrap_or(true)
})
.map(|r| Rule {
name: r.name.clone(),
short_description_base64: r.short_description_base64.clone(),
Expand All @@ -57,6 +87,14 @@ pub fn process_analysis_request(request: AnalysisRequest) -> AnalysisResponse {
})
.collect();

if server_rules_to_rules.is_empty() {
tracing::info!("Successfully completed analysis for 0 rules");
return AnalysisResponse {
rule_responses: vec![],
errors: vec![],
};
}

// Convert the rules from the server into internal rules
let rules: Result<Vec<RuleInternal>, anyhow::Error> = server_rules_to_rules
.iter()
Expand Down Expand Up @@ -331,4 +369,140 @@ mod tests {
response.errors.get(0).unwrap()
);
}

#[test]
fn test_request_configuration_includes_excludes() {
let base_rule = ServerRule {
name: "myrule".to_string(),
short_description_base64: None,
description_base64: None,
category: Some(RuleCategory::BestPractices),
severity: Some(RuleSeverity::Warning),
language: Language::Python,
rule_type: RuleType::TreeSitterQuery,
entity_checked: None,
code_base64: "ZnVuY3Rpb24gdmlzaXQobm9kZSwgZmlsZW5hbWUsIGNvZGUpIHsKICAgIGNvbnN0IGZ1bmN0aW9uTmFtZSA9IG5vZGUuY2FwdHVyZXNbIm5hbWUiXTsKICAgIGlmKGZ1bmN0aW9uTmFtZSkgewogICAgICAgIGNvbnN0IGVycm9yID0gYnVpbGRFcnJvcihmdW5jdGlvbk5hbWUuc3RhcnQubGluZSwgZnVuY3Rpb25OYW1lLnN0YXJ0LmNvbCwgZnVuY3Rpb25OYW1lLmVuZC5saW5lLCBmdW5jdGlvbk5hbWUuZW5kLmNvbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImludmFsaWQgbmFtZSIsICJDUklUSUNBTCIsICJzZWN1cml0eSIpOwoKICAgICAgICBjb25zdCBlZGl0ID0gYnVpbGRFZGl0KGZ1bmN0aW9uTmFtZS5zdGFydC5saW5lLCBmdW5jdGlvbk5hbWUuc3RhcnQuY29sLCBmdW5jdGlvbk5hbWUuZW5kLmxpbmUsIGZ1bmN0aW9uTmFtZS5lbmQuY29sLCAidXBkYXRlIiwgImJhciIpOwogICAgICAgIGNvbnN0IGZpeCA9IGJ1aWxkRml4KCJ1c2UgYmFyIiwgW2VkaXRdKTsKICAgICAgICBhZGRFcnJvcihlcnJvci5hZGRGaXgoZml4KSk7CiAgICB9Cn0=".to_string(),
checksum: Some("f546e49732dc071fd5da82e1a2d9bcf5cf9a824c3679d8b59237c4ba23340057".to_string()),
pattern: None,
tree_sitter_query_base64: Some("KGZ1bmN0aW9uX2RlZmluaXRpb24KICAgIG5hbWU6IChpZGVudGlmaWVyKSBAbmFtZQogIHBhcmFtZXRlcnM6IChwYXJhbWV0ZXJzKSBAcGFyYW1zCik=".to_string()),
variables: None,
};
let mut request = AnalysisRequest {
filename: "path/to/myfile.py".to_string(),
language: Language::Python,
file_encoding: "utf-8".to_string(),
code_base64: "ZGVmIGZvbyhhcmcxKToKICAgIHBhc3M=".to_string(),
configuration_base64: None,
options: None,
rules: vec![
ServerRule {
name: "rs_one/rule_a".to_string(),
..base_rule.clone()
},
ServerRule {
name: "rs_one/rule_b".to_string(),
..base_rule.clone()
},
ServerRule {
name: "rs_one/rule_c".to_string(),
..base_rule.clone()
},
ServerRule {
name: "rs_two/rule_a".to_string(),
..base_rule.clone()
},
],
};

// No includes/excludes
let response = process_analysis_request(request.clone());
assert_eq!(4, response.rule_responses.len());

// rs_one excludes 'path/to'
request.configuration_base64 =
Some("cnVsZXNldHM6CiAgcnNfb25lOgogICAgaWdub3JlOiBbcGF0aC90b10K".to_string());
let response = process_analysis_request(request.clone());
assert_eq!(1, response.rule_responses.len());

let response = process_analysis_request(AnalysisRequest {
filename: "other/path/myfile.py".to_string(),
..request.clone()
});
assert_eq!(4, response.rule_responses.len());

// rs_one only allows 'path/to'
request.configuration_base64 =
Some("cnVsZXNldHM6CiAgcnNfb25lOgogICAgb25seTogW3BhdGgvdG9dCg==".to_string());
let response = process_analysis_request(request.clone());
assert_eq!(4, response.rule_responses.len());

let response = process_analysis_request(AnalysisRequest {
filename: "other/path/myfile.py".to_string(),
..request.clone()
});
assert_eq!(1, response.rule_responses.len());

// rs_one/rule_a excludes 'path/to'
request.configuration_base64 = Some("cnVsZXNldHM6CiAgcnNfb25lOgogICAgcnVsZXM6CiAgICAgIHJ1bGVfYToKICAgICAgICBpZ25vcmU6IFtwYXRoL3RvXQo=".to_string());
let response = process_analysis_request(request.clone());
assert_eq!(3, response.rule_responses.len());

let response = process_analysis_request(AnalysisRequest {
filename: "other/path/myfile.py".to_string(),
..request.clone()
});
assert_eq!(4, response.rule_responses.len());

// rs_one/rule_a only allows 'path/to'
request.configuration_base64 = Some("cnVsZXNldHM6CiAgcnNfb25lOgogICAgcnVsZXM6CiAgICAgIHJ1bGVfYToKICAgICAgICBvbmx5OiBbcGF0aC90b10K".to_string());
let response = process_analysis_request(request.clone());
assert_eq!(4, response.rule_responses.len());

let response = process_analysis_request(AnalysisRequest {
filename: "other/path/myfile.py".to_string(),
..request.clone()
});
assert_eq!(3, response.rule_responses.len());
}

#[test]
fn test_request_configuration_invalid() {
// invalid base64
let mut request = AnalysisRequest {
filename: "path/to/myfile.py".to_string(),
language: Language::Python,
file_encoding: "utf-8".to_string(),
code_base64: "ZGVmIGZvbyhhcmcxKToKICAgIHBhc3M=".to_string(),
configuration_base64: Some(":::::::".to_string()),
options: None,
rules: vec![ServerRule {
name: "myrule".to_string(),
short_description_base64: None,
description_base64: None,
category: Some(RuleCategory::BestPractices),
severity: Some(RuleSeverity::Warning),
language: Language::Python,
rule_type: RuleType::TreeSitterQuery,
entity_checked: None,
code_base64: "ZnVuY3Rpb24gdmlzaXQobm9kZSwgZmlsZW5hbWUsIGNvZGUpIHsKICAgIGNvbnN0IGZ1bmN0aW9uTmFtZSA9IG5vZGUuY2FwdHVyZXNbIm5hbWUiXTsKICAgIGlmKGZ1bmN0aW9uTmFtZSkgewogICAgICAgIGNvbnN0IGVycm9yID0gYnVpbGRFcnJvcihmdW5jdGlvbk5hbWUuc3RhcnQubGluZSwgZnVuY3Rpb25OYW1lLnN0YXJ0LmNvbCwgZnVuY3Rpb25OYW1lLmVuZC5saW5lLCBmdW5jdGlvbk5hbWUuZW5kLmNvbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImludmFsaWQgbmFtZSIsICJDUklUSUNBTCIsICJzZWN1cml0eSIpOwoKICAgICAgICBjb25zdCBlZGl0ID0gYnVpbGRFZGl0KGZ1bmN0aW9uTmFtZS5zdGFydC5saW5lLCBmdW5jdGlvbk5hbWUuc3RhcnQuY29sLCBmdW5jdGlvbk5hbWUuZW5kLmxpbmUsIGZ1bmN0aW9uTmFtZS5lbmQuY29sLCAidXBkYXRlIiwgImJhciIpOwogICAgICAgIGNvbnN0IGZpeCA9IGJ1aWxkRml4KCJ1c2UgYmFyIiwgW2VkaXRdKTsKICAgICAgICBhZGRFcnJvcihlcnJvci5hZGRGaXgoZml4KSk7CiAgICB9Cn0=".to_string(),
checksum: Some("f546e49732dc071fd5da82e1a2d9bcf5cf9a824c3679d8b59237c4ba23340057".to_string()),
pattern: None,
tree_sitter_query_base64: Some("KGZ1bmN0aW9uX2RlZmluaXRpb24KICAgIG5hbWU6IChpZGVudGlmaWVyKSBAbmFtZQogIHBhcmFtZXRlcnM6IChwYXJhbWV0ZXJzKSBAcGFyYW1zCik=".to_string()),
variables: None,
}],
};
let response = process_analysis_request(request.clone());
assert_eq!(
&ERROR_CONFIGURATION_NOT_BASE64.to_string(),
response.errors.get(0).unwrap()
);

// invalid configuration
request.configuration_base64 = Some("ZVVZ".to_string());
let response = process_analysis_request(request);
assert_eq!(
&ERROR_COULD_NOT_PARSE_CONFIGURATION.to_string(),
response.errors.get(0).unwrap()
);
}
}

0 comments on commit 3f8cc79

Please sign in to comment.