Skip to content

Commit 3ea1936

Browse files
authored
Merge branch 'main' into julien.delange/version-0.7.8
2 parents 4965f03 + ebdc088 commit 3ea1936

31 files changed

Lines changed: 1092 additions & 693 deletions

.github/workflows/integration-tests.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ jobs:
3333
- { file: './misc/integration-test-encoding.sh', gha_alias: 'File Encoding' }
3434
- { file: './misc/integration-test-default-config.sh', gha_alias: 'Default Config' }
3535
- { file: './misc/integration-test-secrets.sh', gha_alias: 'Secrets' }
36+
- { file: './misc/integration-test-config-file.sh', gha_alias: 'Config File' }
3637
name: Run integration test - ${{ matrix.scripts.gha_alias }}
3738
steps:
3839
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

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

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -231,16 +231,13 @@ fn main() -> Result<()> {
231231
};
232232
let directory_to_analyze_option = matches.opt_str("r");
233233

234-
if directory_to_analyze_option.is_none() {
234+
let Some(directory_to_analyze) = directory_to_analyze_option.map(PathBuf::from) else {
235235
eprintln!("no directory passed, specify a directory with option -i");
236236
print_usage(&program, opts);
237237
exit(EXIT_CODE_NO_DIRECTORY)
238-
}
239-
240-
let directory_to_analyze = directory_to_analyze_option.unwrap();
241-
let directory_path = std::path::Path::new(&directory_to_analyze);
238+
};
242239

243-
if !directory_path.is_dir() {
240+
if !directory_to_analyze.is_dir() {
244241
eprintln!("directory to analyze is not correct");
245242
exit(EXIT_CODE_INVALID_DIRECTORY)
246243
}
@@ -254,7 +251,7 @@ fn main() -> Result<()> {
254251
exit(EXIT_CODE_NO_SECRET_OR_STATIC_ANALYSIS)
255252
}
256253

257-
let configuration_file_and_method = get_config(directory_to_analyze.as_str(), use_debug);
254+
let configuration_file_and_method = get_config(&directory_to_analyze, use_debug);
258255

259256
let (configuration_file, configuration_method): (
260257
Option<file_v1::ConfigFile>,
@@ -267,43 +264,44 @@ fn main() -> Result<()> {
267264
Err(err) => {
268265
eprintln!(
269266
"Error reading configuration file from {}:\n {}",
270-
directory_to_analyze, err
267+
directory_to_analyze.display(),
268+
err
271269
);
272270
exit(EXIT_CODE_INVALID_CONFIGURATION)
273271
}
274272
};
273+
let sast_config = configuration_file.as_ref().and_then(|cfg| cfg.sast());
274+
275275
let mut rules: Vec<Rule> = Vec::new();
276276
let rule_config_provider = configuration_file
277277
.as_ref()
278278
.map(RuleConfigProvider::from_config)
279279
.unwrap_or_default();
280280

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();
281+
// Rulesets to exclude when fetching default rulesets
282+
let mut excluded_rulesets = Vec::<&str>::new();
283283
// if there is a configuration file, we load the rules from it. But it means
284284
// we cannot have the rule parameter given.
285-
if let Some(conf) = &configuration_file {
285+
if let Some(conf) = sast_config {
286286
ignore_gitignore = conf
287287
.global_config
288288
.as_ref()
289289
.and_then(|g| g.use_gitignore.map(|b| !b))
290290
.unwrap_or(false);
291291

292292
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-
}
293+
let explicit_rs = conf.explicit_rulesets().collect::<Vec<_>>();
294+
let rules_from_api = get_rules_from_rulesets(&explicit_rs, 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+
excluded_rulesets.extend(explicit_rs);
304+
excluded_rulesets.extend(conf.ignore_rulesets.iter().map(String::as_str));
307305
}
308306

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

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

340-
let should_fetch = !matches!(&configuration_file, Some(config) if config.use_default_rulesets == Some(false));
338+
let should_fetch = sast_config.is_none_or(|c| c.use_default_rulesets != Some(false));
341339
if should_fetch {
342340
let rulesets_from_api =
343-
get_all_default_rulesets(use_staging, use_debug, &fetched_rulesets)
341+
get_all_default_rulesets(use_staging, use_debug, &excluded_rulesets)
344342
.context("cannot get default rules")?;
345343
rules.extend(rulesets_from_api.into_iter().flat_map(|rs| rs.into_rules()));
346344
}
@@ -354,7 +352,7 @@ fn main() -> Result<()> {
354352

355353
// ignore all directories that are in gitignore
356354
if !ignore_gitignore {
357-
match read_files_from_gitignore(directory_to_analyze.as_str()) {
355+
match read_files_from_gitignore(&directory_to_analyze) {
358356
Ok(paths_from_gitignore) => {
359357
path_config
360358
.ignore
@@ -367,7 +365,7 @@ fn main() -> Result<()> {
367365
}
368366
}
369367

370-
let files_in_repository = get_files(directory_to_analyze.as_str(), vec![], &path_config)
368+
let files_in_repository = get_files(&directory_to_analyze, vec![], &path_config)
371369
.expect("unable to get the list of files to analyze");
372370

373371
let num_cores_requested = matches
@@ -385,7 +383,7 @@ fn main() -> Result<()> {
385383
use_debug,
386384
configuration_method,
387385
ignore_gitignore,
388-
source_directory: directory_to_analyze.clone(),
386+
source_directory: directory_to_analyze.to_string_lossy().to_string(),
389387
source_subdirectories: vec![],
390388
path_config,
391389
rules_file: None,
@@ -487,7 +485,7 @@ fn main() -> Result<()> {
487485

488486
let changed_files: Vec<PathBuf> = modifications
489487
.keys()
490-
.map(|f| directory_path.join(f))
488+
.map(|f| directory_to_analyze.join(f))
491489
.collect();
492490

493491
if configuration.use_debug {

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

Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -242,26 +242,23 @@ fn main() -> Result<()> {
242242

243243
let rules_file = matches.opt_str("r");
244244

245-
if directory_to_analyze_option.is_none() {
245+
let Some(directory_to_analyze) = directory_to_analyze_option.map(PathBuf::from) else {
246246
eprintln!("no directory passed, specify a directory with option -i");
247247
print_usage(&program, opts);
248248
exit(EXIT_CODE_NO_DIRECTORY)
249-
}
250-
251-
let directory_to_analyze = directory_to_analyze_option.unwrap();
252-
let directory_path = std::path::Path::new(&directory_to_analyze);
249+
};
253250

254-
if !directory_path.is_dir() {
251+
if !directory_to_analyze.is_dir() {
255252
eprintln!("directory to analyze is not correct");
256253
exit(EXIT_CODE_INVALID_DIRECTORY)
257254
}
258255

259-
if !are_subdirectories_safe(directory_path, &subdirectories_to_analyze) {
256+
if !are_subdirectories_safe(&directory_to_analyze, &subdirectories_to_analyze) {
260257
eprintln!("sub-directories are not safe and point outside of the repository");
261258
exit(EXIT_CODE_UNSAFE_SUBDIRECTORIES)
262259
}
263260

264-
let configuration_file_and_method = get_config(directory_to_analyze.as_str(), use_debug);
261+
let configuration_file_and_method = get_config(&directory_to_analyze, use_debug);
265262

266263
let (configuration_file, configuration_method): (
267264
Option<file_v1::ConfigFile>,
@@ -274,13 +271,15 @@ fn main() -> Result<()> {
274271
Err(err) => {
275272
eprintln!(
276273
"Error reading configuration file from {}:\n {}",
277-
directory_to_analyze, err
274+
directory_to_analyze.display(),
275+
err
278276
);
279277
exit(EXIT_CODE_INVALID_CONFIGURATION)
280278
}
281279
};
280+
let sast_config = configuration_file.as_ref().and_then(|cfg| cfg.sast());
282281

283-
if configuration_file.is_none() && use_debug {
282+
if sast_config.is_none() && use_debug {
284283
eprintln!("INFO: no configuration detected locally or remotely")
285284
}
286285

@@ -290,11 +289,11 @@ fn main() -> Result<()> {
290289
.unwrap_or_default();
291290
let mut rules: Vec<Rule> = Vec::new();
292291

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();
292+
// Rulesets to exclude when fetching default rulesets
293+
let mut excluded_rulesets = Vec::<&str>::new();
295294
// if there is a configuration file, we load the rules from it. But it means
296295
// we cannot have the rule parameter given.
297-
if let Some(conf) = &configuration_file {
296+
if let Some(conf) = sast_config {
298297
ignore_gitignore = conf
299298
.global_config
300299
.as_ref()
@@ -306,20 +305,18 @@ fn main() -> Result<()> {
306305
}
307306

308307
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-
}
308+
let explicit_rs = conf.explicit_rulesets().collect::<Vec<_>>();
309+
let rules_from_api = get_rules_from_rulesets(&explicit_rs, use_staging, use_debug)
310+
.inspect_err(|e| {
311+
if let DatadogApiError::RulesetNotFound(rs) = e {
312+
eprintln!("Error: ruleset {rs} not found");
313+
exit(EXIT_CODE_RULESET_NOT_FOUND);
314+
}
315+
})
316+
.context("error when reading rules from API")?;
317+
rules.extend(rules_from_api);
318+
excluded_rulesets.extend(explicit_rs);
319+
excluded_rulesets.extend(conf.ignore_rulesets.iter().map(String::as_str));
323320
}
324321
// copy the only and ignore paths from the configuration file
325322
if let Some(pc) = conf.global_config.as_ref().and_then(|g| g.paths.as_ref()) {
@@ -343,18 +340,19 @@ fn main() -> Result<()> {
343340
if static_analysis_enabled {
344341
// if there is no config file, we take the default rules from our APIs.
345342
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");
343+
if sast_config.is_none() {
344+
println!("WARNING: no SAST configuration detected, getting the default rules from the Datadog API");
348345
println!("Check the following resources to configure your rules:");
349346
println!(
350347
" - Datadog documentation: https://docs.datadoghq.com/code_analysis/static_analysis"
351348
);
352349
println!(" - Static analyzer repository on GitHub: https://github.com/DataDog/datadog-static-analyzer");
353350
}
354-
let should_fetch = !matches!(&configuration_file, Some(config) if config.use_default_rulesets == Some(false));
351+
352+
let should_fetch = sast_config.is_none_or(|c| c.use_default_rulesets != Some(false));
355353
if should_fetch {
356354
let rulesets_from_api =
357-
get_all_default_rulesets(use_staging, use_debug, &fetched_rulesets)
355+
get_all_default_rulesets(use_staging, use_debug, &excluded_rulesets)
358356
.context("cannot get default rules")?;
359357
rules.extend(rulesets_from_api.into_iter().flat_map(|rs| rs.into_rules()));
360358
}
@@ -382,7 +380,7 @@ fn main() -> Result<()> {
382380

383381
// ignore all directories that are in gitignore
384382
if !ignore_gitignore {
385-
match read_files_from_gitignore(directory_to_analyze.as_str()) {
383+
match read_files_from_gitignore(&directory_to_analyze) {
386384
Ok(paths_from_gitignore) => {
387385
path_config
388386
.ignore
@@ -403,7 +401,7 @@ fn main() -> Result<()> {
403401
let languages = get_languages_for_rules(&rules);
404402

405403
let files_in_repository = get_files(
406-
directory_to_analyze.as_str(),
404+
&directory_to_analyze,
407405
subdirectories_to_analyze.clone(),
408406
&path_config,
409407
)
@@ -424,7 +422,7 @@ fn main() -> Result<()> {
424422
use_debug,
425423
configuration_method,
426424
ignore_gitignore,
427-
source_directory: directory_to_analyze.clone(),
425+
source_directory: directory_to_analyze.to_string_lossy().to_string(),
428426
source_subdirectories: subdirectories_to_analyze.clone(),
429427
path_config,
430428
rules_file,
@@ -542,7 +540,7 @@ fn main() -> Result<()> {
542540

543541
// if diff-aware is enabled, we filter the files and keep only the files we want to analyze from diff-aware
544542
let files_to_analyze = if let Some(dap) = &diff_aware_parameters {
545-
filter_files_by_diff_aware_info(&files_filtered_by_size, directory_path, dap)
543+
filter_files_by_diff_aware_info(&files_filtered_by_size, &directory_to_analyze, dap)
546544
} else {
547545
files_filtered_by_size
548546
};

0 commit comments

Comments
 (0)