Skip to content

Commit babbacc

Browse files
committed
Add support for Code Security v1.0 schema
1 parent 4534d24 commit babbacc

11 files changed

Lines changed: 523 additions & 261 deletions

File tree

crates/bins/src/bin/datadog-static-analyzer-git-hook.rs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -272,38 +272,38 @@ fn main() -> Result<()> {
272272
exit(EXIT_CODE_INVALID_CONFIGURATION)
273273
}
274274
};
275+
let sast_config = configuration_file.as_ref().and_then(|cfg| cfg.sast());
276+
275277
let mut rules: Vec<Rule> = Vec::new();
276278
let rule_config_provider = configuration_file
277279
.as_ref()
278280
.map(RuleConfigProvider::from_config)
279281
.unwrap_or_default();
280282

281-
// A list of rulesets that were fetched due to being specifically listed in a ConfigFile::use_rulesets list.
282-
let mut fetched_rulesets = Vec::<&str>::new();
283+
// Rulesets to exclude when fetching default rulesets
284+
let mut excluded_rulesets = Vec::<&str>::new();
283285
// if there is a configuration file, we load the rules from it. But it means
284286
// we cannot have the rule parameter given.
285-
if let Some(conf) = &configuration_file {
287+
if let Some(conf) = sast_config {
286288
ignore_gitignore = conf
287289
.global_config
288290
.as_ref()
289291
.and_then(|g| g.use_gitignore.map(|b| !b))
290292
.unwrap_or(false);
291293

292294
if static_analysis_enabled {
293-
if let Some(rulesets) = &conf.use_rulesets {
294-
let rules_from_api = get_rules_from_rulesets(rulesets, use_staging, use_debug)
295-
.inspect_err(|e| {
296-
if let DatadogApiError::RulesetNotFound(rs) = e {
297-
eprintln!("Error: ruleset {rs} not found");
298-
exit(EXIT_CODE_RULESET_NOT_FOUND);
299-
}
300-
})
301-
.context("error when reading rules from API")?;
302-
rules.extend(rules_from_api);
303-
for r in rulesets {
304-
fetched_rulesets.push(r.as_str());
305-
}
306-
}
295+
let explicit_rs = conf.explicit_rulesets().collect::<Vec<_>>();
296+
let rules_from_api = get_rules_from_rulesets(&explicit_rs, use_staging, use_debug)
297+
.inspect_err(|e| {
298+
if let DatadogApiError::RulesetNotFound(rs) = e {
299+
eprintln!("Error: ruleset {rs} not found");
300+
exit(EXIT_CODE_RULESET_NOT_FOUND);
301+
}
302+
})
303+
.context("error when reading rules from API")?;
304+
rules.extend(rules_from_api);
305+
excluded_rulesets.extend(explicit_rs);
306+
excluded_rulesets.extend(conf.ignore_rulesets.iter().map(String::as_str));
307307
}
308308

309309
// copy the only and ignore paths from the configuration file
@@ -328,7 +328,7 @@ fn main() -> Result<()> {
328328
if static_analysis_enabled {
329329
// if there is no config file, we take the default rules from our APIs.
330330

331-
if configuration_file.is_none() && use_debug {
331+
if sast_config.is_none() && use_debug {
332332
println!("WARNING: no configuration file detected, getting the default rules from the Datadog API");
333333
println!("Check the following resources to configure your rules:");
334334
println!(
@@ -337,10 +337,10 @@ fn main() -> Result<()> {
337337
println!(" - Static analyzer repository on GitHub: https://github.com/DataDog/datadog-static-analyzer");
338338
}
339339

340-
let should_fetch = !matches!(&configuration_file, Some(config) if config.use_default_rulesets == Some(false));
340+
let should_fetch = sast_config.is_none_or(|c| c.use_default_rulesets != Some(false));
341341
if should_fetch {
342342
let rulesets_from_api =
343-
get_all_default_rulesets(use_staging, use_debug, &fetched_rulesets)
343+
get_all_default_rulesets(use_staging, use_debug, &excluded_rulesets)
344344
.context("cannot get default rules")?;
345345
rules.extend(rulesets_from_api.into_iter().flat_map(|rs| rs.into_rules()));
346346
}

crates/bins/src/bin/datadog-static-analyzer.rs

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -279,8 +279,9 @@ fn main() -> Result<()> {
279279
exit(EXIT_CODE_INVALID_CONFIGURATION)
280280
}
281281
};
282+
let sast_config = configuration_file.as_ref().and_then(|cfg| cfg.sast());
282283

283-
if configuration_file.is_none() && use_debug {
284+
if sast_config.is_none() && use_debug {
284285
eprintln!("INFO: no configuration detected locally or remotely")
285286
}
286287

@@ -290,11 +291,11 @@ fn main() -> Result<()> {
290291
.unwrap_or_default();
291292
let mut rules: Vec<Rule> = Vec::new();
292293

293-
// A list of rulesets that were fetched due to being specifically listed in a ConfigFile::use_rulesets list.
294-
let mut fetched_rulesets = Vec::<&str>::new();
294+
// Rulesets to exclude when fetching default rulesets
295+
let mut excluded_rulesets = Vec::<&str>::new();
295296
// if there is a configuration file, we load the rules from it. But it means
296297
// we cannot have the rule parameter given.
297-
if let Some(conf) = &configuration_file {
298+
if let Some(conf) = sast_config {
298299
ignore_gitignore = conf
299300
.global_config
300301
.as_ref()
@@ -306,20 +307,18 @@ fn main() -> Result<()> {
306307
}
307308

308309
if static_analysis_enabled {
309-
if let Some(rulesets) = &conf.use_rulesets {
310-
let rules_from_api = get_rules_from_rulesets(rulesets, use_staging, use_debug)
311-
.inspect_err(|e| {
312-
if let DatadogApiError::RulesetNotFound(rs) = e {
313-
eprintln!("Error: ruleset {rs} not found");
314-
exit(EXIT_CODE_RULESET_NOT_FOUND);
315-
}
316-
})
317-
.context("error when reading rules from API")?;
318-
rules.extend(rules_from_api);
319-
for r in rulesets {
320-
fetched_rulesets.push(r.as_str());
321-
}
322-
}
310+
let explicit_rs = conf.explicit_rulesets().collect::<Vec<_>>();
311+
let rules_from_api = get_rules_from_rulesets(&explicit_rs, use_staging, use_debug)
312+
.inspect_err(|e| {
313+
if let DatadogApiError::RulesetNotFound(rs) = e {
314+
eprintln!("Error: ruleset {rs} not found");
315+
exit(EXIT_CODE_RULESET_NOT_FOUND);
316+
}
317+
})
318+
.context("error when reading rules from API")?;
319+
rules.extend(rules_from_api);
320+
excluded_rulesets.extend(explicit_rs);
321+
excluded_rulesets.extend(conf.ignore_rulesets.iter().map(String::as_str));
323322
}
324323
// copy the only and ignore paths from the configuration file
325324
if let Some(pc) = conf.global_config.as_ref().and_then(|g| g.paths.as_ref()) {
@@ -343,18 +342,19 @@ fn main() -> Result<()> {
343342
if static_analysis_enabled {
344343
// if there is no config file, we take the default rules from our APIs.
345344
if rules_file.is_none() {
346-
if configuration_file.is_none() {
347-
println!("WARNING: no configuration file detected, getting the default rules from the Datadog API");
345+
if sast_config.is_none() {
346+
println!("WARNING: no SAST configuration detected, getting the default rules from the Datadog API");
348347
println!("Check the following resources to configure your rules:");
349348
println!(
350349
" - Datadog documentation: https://docs.datadoghq.com/code_analysis/static_analysis"
351350
);
352351
println!(" - Static analyzer repository on GitHub: https://github.com/DataDog/datadog-static-analyzer");
353352
}
354-
let should_fetch = !matches!(&configuration_file, Some(config) if config.use_default_rulesets == Some(false));
353+
354+
let should_fetch = sast_config.is_none_or(|c| c.use_default_rulesets != Some(false));
355355
if should_fetch {
356356
let rulesets_from_api =
357-
get_all_default_rulesets(use_staging, use_debug, &fetched_rulesets)
357+
get_all_default_rulesets(use_staging, use_debug, &excluded_rulesets)
358358
.context("cannot get default rules")?;
359359
rules.extend(rulesets_from_api.into_iter().flat_map(|rs| rs.into_rules()));
360360
}

crates/cli/src/config_file.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ pub fn get_config(
5555
.transpose()?;
5656
let local_config: Option<file_v1::ConfigFile> = local_yaml.map(|v| match v {
5757
WithVersion::Legacy(legacy) => file_v1::YamlConfigFile::from(legacy).into(),
58-
WithVersion::CodeSecurity(v2) => v2.into(),
58+
WithVersion::CodeSecurity(v1) => v1.into(),
5959
});
6060

6161
if !should_use_datadog_backend() {
@@ -95,7 +95,7 @@ pub fn get_config(
9595
let text = decode_base64_string(remote_config_base64)
9696
.context("error when decoding base64 remote config")?;
9797

98-
let res = parse_any_schema_yaml(&text).inspect_err(|err| {
98+
let res = file_v1::parse_yaml(&text).inspect_err(|err| {
9999
if debug {
100100
eprintln!("Error when parsing remote config: {err:?}");
101101
eprintln!("Proceeding with local config");
@@ -104,10 +104,7 @@ pub fn get_config(
104104
let Ok(remote_yaml) = res else {
105105
return Ok(local_config.map(|c| (c, ConfigMethod::File)));
106106
};
107-
let remote_config: file_v1::ConfigFile = match remote_yaml {
108-
WithVersion::Legacy(legacy) => file_v1::YamlConfigFile::from(legacy).into(),
109-
WithVersion::CodeSecurity(v2) => v2.into(),
110-
};
107+
let remote_config: file_v1::ConfigFile = remote_yaml.into();
111108

112109
let config_method = if local_config.is_some() {
113110
ConfigMethod::RemoteConfigurationWithFile

crates/cli/src/datadog_utils.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,12 @@ pub fn get_secrets_rules(use_staging: bool) -> Result<Vec<SecretRule>> {
7676

7777
// Get all the rules from different rulesets from Datadog
7878
pub fn get_rules_from_rulesets(
79-
rulesets_name: &[String],
79+
rulesets_name: &[&str],
8080
use_staging: bool,
8181
debug: bool,
8282
) -> Result<Vec<Rule>> {
8383
let mut rules: Vec<Rule> = Vec::new();
84-
for ruleset_name in rulesets_name {
84+
for &ruleset_name in rulesets_name {
8585
rules.extend(get_ruleset(ruleset_name, use_staging, debug)?.into_rules());
8686
}
8787
Ok(rules)

crates/static-analysis-kernel/src/arguments.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,20 @@ impl ArgumentProvider {
5252

5353
pub fn from(config: &file_v1::ConfigFile) -> Self {
5454
let mut provider = ArgumentProvider::new();
55-
if let Some(rulesets) = &config.ruleset_configs {
56-
for (ruleset_name, ruleset_cfg) in rulesets {
57-
for (rule_shortname, rule_cfg) in &ruleset_cfg.rules {
58-
let rule_name = format!("{}/{}", ruleset_name, rule_shortname);
59-
for (arg_name, arg_values) in &rule_cfg.arguments {
60-
for (prefix, value) in arg_values.iter() {
61-
provider.add_argument(
62-
&rule_name,
63-
&prefix.into_iter().cloned().collect(),
64-
arg_name,
65-
value,
66-
);
55+
if let Some(sast_config) = config.sast() {
56+
if let Some(rulesets) = &sast_config.ruleset_configs {
57+
for (ruleset_name, ruleset_cfg) in rulesets {
58+
for (rule_shortname, rule_cfg) in &ruleset_cfg.rules {
59+
let rule_name = format!("{}/{}", ruleset_name, rule_shortname);
60+
for (arg_name, arg_values) in &rule_cfg.arguments {
61+
for (prefix, value) in arg_values.iter() {
62+
provider.add_argument(
63+
&rule_name,
64+
&prefix.into_iter().cloned().collect(),
65+
arg_name,
66+
value,
67+
);
68+
}
6769
}
6870
}
6971
}

0 commit comments

Comments
 (0)