Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
[![crates.io](https://img.shields.io/crates/v/pysentry)](https://crates.io/crates/pysentry)
[![Downloads](https://static.pepy.tech/badge/pysentry-rs/week)](https://pepy.tech/projects/pysentry-rs)

[**Documentation**](https://nyudenkov.github.io/pysentry/) · [**Benchmarks**](benchmarks/results/) · [Help test & improve](https://github.com/nyudenkov/pysentry/issues/12) · [Usage survey](https://tally.so/r/mYNPNv)
[**Documentation**](https://docs.pysentry.com) · [**Benchmarks**](benchmarks/results/) · [Help test & improve](https://github.com/nyudenkov/pysentry/issues/12) · [Usage survey](https://tally.so/r/mYNPNv)

</div>

Expand Down Expand Up @@ -43,7 +43,7 @@ pip install pysentry-rs # PyPI
cargo install pysentry # crates.io
```

Pre-built binaries are attached to [GitHub Releases](https://github.com/nyudenkov/pysentry/releases). See the [installation guide](https://nyudenkov.github.io/pysentry/getting-started/installation) for all options.
Pre-built binaries are attached to [GitHub Releases](https://github.com/nyudenkov/pysentry/releases). See the [installation guide](https://docs.pysentry.com/getting-started/installation) for all options.

> **Naming:** the Python package installs the binary as `pysentry-rs`; the Rust crate and release binaries are plain `pysentry`. Examples below use `pysentry-rs` — substitute accordingly.

Expand All @@ -69,7 +69,7 @@ pysentry-rs --format sarif --output results.sarif
pysentry-rs --forbid-quarantined
```

More examples in the [quickstart guide](https://nyudenkov.github.io/pysentry/getting-started/quickstart).
More examples in the [quickstart guide](https://docs.pysentry.com/getting-started/quickstart).

## Pre-commit

Expand Down Expand Up @@ -97,7 +97,7 @@ steps:
fail-on: high
```

On any other CI system, `pysentry-rs --fail-on high` exits non-zero when findings reach the threshold. Details in the [CI guide](https://nyudenkov.github.io/pysentry/ci).
On any other CI system, `pysentry-rs --fail-on high` exits non-zero when findings reach the threshold. Details in the [CI guide](https://docs.pysentry.com/ci).

## Configuration

Expand All @@ -117,17 +117,17 @@ enabled = ["pypa", "osv"]
ids = ["CVE-2023-12345"]
```

All options are covered in the [configuration guide](https://nyudenkov.github.io/pysentry/configuration/config-files).
All options are covered in the [configuration guide](https://docs.pysentry.com/configuration/config-files).

## Documentation

Full documentation lives at [https://nyudenkov.github.io/pysentry/](https://nyudenkov.github.io/pysentry):
[Installation](https://nyudenkov.github.io/pysentry/getting-started/installation) ·
[Quickstart](https://nyudenkov.github.io/pysentry/getting-started/quickstart) ·
[CLI options](https://nyudenkov.github.io/pysentry/configuration/cli-options) ·
[Configuration files](https://nyudenkov.github.io/pysentry/configuration/config-files) ·
[Environment variables](https://nyudenkov.github.io/pysentry/configuration/environment-variables) ·
[Troubleshooting](https://nyudenkov.github.io/pysentry/troubleshooting)
Full documentation lives at [https://docs.pysentry.com](https://docs.pysentry.com):
[Installation](https://docs.pysentry.com/getting-started/installation) ·
[Quickstart](https://docs.pysentry.com/getting-started/quickstart) ·
[CLI options](https://docs.pysentry.com/configuration/cli-options) ·
[Configuration files](https://docs.pysentry.com/configuration/config-files) ·
[Environment variables](https://docs.pysentry.com/configuration/environment-variables) ·
[Troubleshooting](https://docs.pysentry.com/troubleshooting)

## Requirements

Expand Down
4 changes: 2 additions & 2 deletions docs/docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ const config: Config = {
v4: true,
},

url: 'https://nyudenkov.github.io',
baseUrl: '/pysentry/',
url: 'https://docs.pysentry.com',
baseUrl: '/',

organizationName: 'nyudenkov',
projectName: 'pysentry',
Expand Down
81 changes: 81 additions & 0 deletions src/audit/merge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,33 @@ impl AuditArgs {
ignore_while_no_fix.extend(config.ignore.while_no_fix.clone());
merged.ignore_while_no_fix = ignore_while_no_fix;

let mut ignore_packages = self.ignore_packages.clone();
ignore_packages.extend(config.ignore.packages.clone());
merged.ignore_packages = ignore_packages;

// fail_on_partial defaults to true (fail-closed); the CLI flag and config
// can only relax it, matching the "flags turn ON" idiom (cf. no_fail_on_unknown).
if !self.no_fail_on_partial && !config.sources.fail_on_partial {
merged.no_fail_on_partial = true;
}

// Per-group fail thresholds (config-only). Normalize keys to PEP 735 form so
// they compare against graph attribution's normalized group names. Levels were
// validated at config load; the fallback keeps this infallible.
merged.group_fail_on = config
.groups
.iter()
.map(|(name, policy)| {
// invariant: levels were validated in Config::validate at load, so parse
// cannot fail here; the fallback keeps this map infallible without a panic.
let level = policy.fail_on.parse().unwrap_or(SeverityLevel::Medium);
(
crate::parsers::manifest_reader::normalize_group_name(name),
level,
)
})
.collect();

// CLI -v flag overrides config quiet. Only apply config quiet when not explicitly verbose.
if config.output.quiet && !crate::logging::is_verbose(&self.verbosity) {
merged.config_quiet = true;
Expand Down Expand Up @@ -396,6 +423,60 @@ mod tests {
assert!(merged.direct_only);
}

#[test]
fn test_fail_on_partial_config_relaxes_default() {
let args = parse_audit_args(&["."]);
let mut config = crate::config::Config::default();
config.sources.fail_on_partial = false;
let merged = args.merge_with_config(&config);
assert!(merged.no_fail_on_partial);
}

#[test]
fn test_fail_on_partial_strict_by_default() {
let args = parse_audit_args(&["."]);
let config = crate::config::Config::default(); // fail_on_partial = true
let merged = args.merge_with_config(&config);
assert!(!merged.no_fail_on_partial);
}

#[test]
fn test_cli_no_fail_on_partial_overrides_config_strict() {
let args = parse_audit_args(&["--no-fail-on-partial", "."]);
let config = crate::config::Config::default(); // fail_on_partial = true
let merged = args.merge_with_config(&config);
assert!(merged.no_fail_on_partial);
}

#[test]
fn test_ignore_packages_merged_from_config() {
let args = parse_audit_args(&["."]);
let mut config = crate::config::Config::default();
config.ignore.packages = vec!["internal-pkg".to_string()];
let merged = args.merge_with_config(&config);
assert_eq!(merged.ignore_packages, vec!["internal-pkg"]);
}

// compact-XOR-detailed must hold on the merged/effective config (#174/Q16).
// No guard exists in perform_audit: Config::validate rejects both-true at load,
// and merge's precedence clears the loser. These cases pin that invariant.
#[test]
fn test_compact_detailed_mutually_exclusive_post_merge() {
// CLI --compact + config detailed.
let args = parse_audit_args(&["--compact", "."]);
let mut config = crate::config::Config::default();
config.defaults.detailed = true;
let merged = args.merge_with_config(&config);
assert!(!(merged.compact && merged.detailed));

// CLI --detailed + config compact.
let args = parse_audit_args(&["--detailed", "."]);
let mut config = crate::config::Config::default();
config.defaults.compact = true;
let merged = args.merge_with_config(&config);
assert!(!(merged.compact && merged.detailed));
}

#[test]
fn test_empty_groups_does_not_force_direct_only() {
let args = parse_audit_args(&["."]);
Expand Down
Loading
Loading