84 lines
3.3 KiB
Python
84 lines
3.3 KiB
Python
from .models import Finding, FindingGuidance, Severity
|
|
|
|
_RULES: dict[str, tuple[str, str, str, str]] = {
|
|
"security.secret-assignment": (
|
|
"security remediation",
|
|
"A credential-shaped assignment was found in tracked content.",
|
|
"Move the value to a secret store or runtime environment and rotate it if it was genuine.",
|
|
"high",
|
|
),
|
|
"security.env-committed": (
|
|
"security remediation",
|
|
"A live environment file is tracked, so its contents require deliberate review.",
|
|
"Remove the live file from version control and retain a sanitized .env.example template.",
|
|
"high",
|
|
),
|
|
"testing.none-detected": (
|
|
"test coverage",
|
|
"No conventionally named test source was detected for a repository with source files.",
|
|
"Add a focused deterministic smoke test, or record a repository policy that explains why tests do not apply.",
|
|
"medium",
|
|
),
|
|
"testing.no-ci": (
|
|
"delivery evidence",
|
|
"No repository workflow was found that contains a validation command.",
|
|
"Add a CI validation workflow that runs the repository's deterministic checks.",
|
|
"medium",
|
|
),
|
|
"dependency.no-lockfile": (
|
|
"dependency reproducibility",
|
|
"A JavaScript manifest was found without a lockfile in its package or workspace root.",
|
|
"Commit the package-manager lockfile used by the build.",
|
|
"high",
|
|
),
|
|
"hygiene.missing-gitignore": (
|
|
"repository hygiene",
|
|
"The repository has no root ignore policy.",
|
|
"Add a minimal .gitignore for generated files, local settings and build outputs.",
|
|
"high",
|
|
),
|
|
}
|
|
|
|
|
|
def _default_rule(finding: Finding) -> tuple[str, str, str, str]:
|
|
if finding.code.startswith("dependency."):
|
|
return (
|
|
"dependency hygiene",
|
|
"The dependency declaration has a deterministic hygiene signal.",
|
|
"Review the declared version and lockfile semantics, then make the smallest reproducible correction.",
|
|
"medium",
|
|
)
|
|
if finding.code.startswith("architecture."):
|
|
return (
|
|
"architecture review",
|
|
"The bounded static graph has a structural signal.",
|
|
"Inspect the affected module boundary before planning a contained refactor.",
|
|
"medium",
|
|
)
|
|
return (
|
|
"engineering hygiene",
|
|
"A deterministic repository rule produced this signal.",
|
|
"Review the retained evidence and make the smallest safe correction.",
|
|
"medium",
|
|
)
|
|
|
|
|
|
def enrich_finding(finding: Finding) -> Finding:
|
|
remediation_class, why, action, confidence = _RULES.get(finding.code, _default_rule(finding))
|
|
severity_weight = {Severity.BLOCKER: 700, Severity.WARNING: 400, Severity.INFO: 100}[finding.severity]
|
|
priority = min(1000, severity_weight + finding.deduction * 5)
|
|
rationale = f"{finding.severity.value.title()} signal; {finding.deduction} health-point impact."
|
|
guidance = FindingGuidance(
|
|
remediation_class=remediation_class,
|
|
confidence=confidence,
|
|
why=why,
|
|
recommended_action=action,
|
|
priority=priority,
|
|
priority_rationale=rationale,
|
|
)
|
|
return finding.model_copy(update={"guidance": guidance})
|
|
|
|
|
|
def enrich_findings(findings: list[Finding]) -> list[Finding]:
|
|
return [enrich_finding(item) for item in findings]
|