Skip to main content

Quality Gate

The Primus Quality Gate lets you define fine-grained pass/fail thresholds for security scans — similar to SonarQube Quality Gates but local, config-file driven, and zero-egress.

How it works

After a ScanAsync() call, the scanner evaluates your configured thresholds against the findings. The result is attached to ScanResult.QualityGate and a QualityGateStatus string ("PASSED" or "FAILED") is available at the top level.

Configuration

In appsettings.json:

{
"PrimusSecurity": {
"QualityGate": {
"MaxCritical": 0,
"MaxHigh": 0,
"MaxMedium": 5,
"MaxLow": -1,
"MaxSecrets": 0,
"MaxCriticalDependencies": 0,
"MaxUnreviewedHotspots": -1,
"BlockOnNewSecrets": true
}
}
}

A value of -1 disables that threshold (no limit). The defaults block on any Critical or High finding and on any hardcoded secret.

Preset gates

// Block on any Critical or High + zero secrets
services.AddPrimusSecurity(opts =>
{
opts.QualityGate = QualityGate.Strict;
});

// Never fail — for informational / exploratory scans
services.AddPrimusSecurity(opts =>
{
opts.QualityGate = QualityGate.InfoOnly;
});

Reading the result

var scanResult = await scanner.ScanAsync("/path/to/project");

Console.WriteLine($"Quality Gate: {scanResult.QualityGateStatus}");
// → "PASSED" or "FAILED"

var gate = scanResult.QualityGate;
if (!gate.Passed)
{
foreach (var violation in gate.Violations)
Console.Error.WriteLine(violation);
// → "Quality gate failed: 2 Critical vulnerabilities found (threshold: 0)."
// → "Quality gate failed: 1 hardcoded secret(s) detected — zero tolerance enforced."
}

// Counts always available
Console.WriteLine($"Critical: {gate.CriticalCount}, High: {gate.HighCount}");

CI integration

// Program.cs — fail the process on gate failure
var result = await scanner.ScanAsync(args[0]);

Console.WriteLine(result.QualityGateStatus);

if (result.QualityGate?.Passed == false)
{
foreach (var v in result.QualityGate.Violations)
Console.Error.WriteLine(v);
return 1; // non-zero exit → CI pipeline fails
}

return 0;

See the CI/CD Integration guide for GitHub Actions and Azure DevOps templates.

Threshold reference

FieldTypeDefaultDescription
MaxCriticalint0Max Critical severity findings. 0 = any fails. -1 = no limit.
MaxHighint0Max High severity findings.
MaxMediumint-1Max Medium findings. -1 by default.
MaxLowint-1Max Low findings.
MaxSecretsint0Max secret findings. Always 0 — secrets must be zero.
MaxCriticalDependenciesint0Max Critical CVE dependency findings.
MaxUnreviewedHotspotsint-1Max unreviewed Security Hotspots. Hotspots require human triage.
MaxDuplicateBlocksint-1Max duplicated code blocks (Phase 7). Requires EnableDuplicationDetection = true. -1 = disabled.
BlockOnNewSecretsbooltrueAlways fail if any secret is found, regardless of MaxSecrets.
DisabledboolfalseSkip gate evaluation entirely — scan always passes.

Suppression store

The .primus-suppressions.json file is a persistent triage store — suppressions recorded here survive between scans and carry a full audit trail.

{
"suppressions": [
{
"ruleId": "PS-SEC-001",
"fingerprint": "PS-SEC-001|Controllers/UserController.cs|42",
"status": "accepted",
"reason": "tableName is an internal enum, not user input",
"author": "akki@primussoftware.com",
"date": "2026-03-01"
}
]
}

Enable it in options or via CLI:

// appsettings.json
{ "PrimusSecurity": { "SuppressionsPath": ".primus-suppressions.json" } }
primus-scan ./MyApp --suppressions .primus-suppressions.json

Accepted findings are excluded from quality gate counts but remain visible in reports for audit trail purposes — matching SonarQube Developer Edition behaviour.

A–E security ratings

Every ScanResult now includes Ratings — a SonarQube-style letter grade:

var result = await scanner.ScanAsync("./MyApp");

Console.WriteLine(result.Ratings?.Security); // A, B, C, D, or E
Console.WriteLine(result.Ratings?.Reliability);
Console.WriteLine(result.Ratings?.Maintainability);
RatingCondition
A0 active vulnerabilities
B≥1 Low finding
C≥1 Medium finding
D≥1 High finding
E≥1 Critical finding

Ratings are also included in SARIF output as securityRating, reliabilityRating, and maintainabilityRating properties — readable by CI scripts without parsing the full results list.