Documentation
¶
Overview ¶
Package output ports src/sec_af/output: the four artifact generators the orchestrator writes at the end of an audit — SARIF, the full/summary JSON, the Markdown report and the compliance report.
Every generator's output is compared byte-for-byte against the Python one by golden_test.go, which feeds both implementations the same testdata/audit_result.json fixture. That is why this package carries its own JSON writer (pyjson_local.go) instead of using encoding/json, and why every number and string below is formatted through pyfmt.
Index ¶
- Variables
- func GenerateComplianceReport(result schemas.SecurityAuditResult) string
- func GenerateComplianceReportAs(result schemas.SecurityAuditResult, now time.Time, brand ComplianceBrand) string
- func GenerateComplianceReportAt(result schemas.SecurityAuditResult, now time.Time) string
- func GenerateJSON(result schemas.SecurityAuditResult, pretty bool) string
- func GenerateReport(result schemas.SecurityAuditResult) string
- func GenerateSarif(result schemas.SecurityAuditResult) string
- func GenerateSarifAs(result schemas.SecurityAuditResult, tool SarifTool) string
- func GenerateSummaryJSON(result schemas.SecurityAuditResult) string
- func RenderJSON(auditResult schemas.SecurityAuditResult) (map[string]any, error)
- func RenderReport(auditResult schemas.SecurityAuditResult) string
- func RenderSarif(auditResult schemas.SecurityAuditResult) string
- func RuleIDsAs(findings []schemas.VerifiedFinding, tool SarifTool) []schemas.VerifiedFinding
- type ComplianceBrand
- type SarifTool
Constants ¶
This section is empty.
Variables ¶
var SecAfCompliance = ComplianceBrand{
Heading: "# SEC-AF Compliance Report",
Signature: "*Report generated by SEC-AF -- Composite Intelligence Security Auditor*",
}
SecAfCompliance is sec-af's own heading and signature.
var SecAfTool = SarifTool{Name: "SEC-AF", SemanticVersion: pythonPackageVersion,
InformationURI: "https://github.com/Agent-Field/sec-af", PropertyPrefix: "sec-af"}
SecAfTool is sec-af's own identity, as its Python original wrote it.
Functions ¶
func GenerateComplianceReport ¶
func GenerateComplianceReport(result schemas.SecurityAuditResult) string
GenerateComplianceReport ports generate_compliance_report: a compliance-focused Markdown report, structured for direct PDF conversion via pandoc/weasyprint.
The header stamps the CURRENT time, exactly as the Python function does (`datetime.now(UTC)`), so two calls a minute apart differ. Tests and any caller that needs a reproducible document use GenerateComplianceReportAt.
func GenerateComplianceReportAs ¶
func GenerateComplianceReportAs(result schemas.SecurityAuditResult, now time.Time, brand ComplianceBrand) string
GenerateComplianceReportAs is GenerateComplianceReportAt signed by brand.
func GenerateComplianceReportAt ¶
func GenerateComplianceReportAt(result schemas.SecurityAuditResult, now time.Time) string
GenerateComplianceReportAt is GenerateComplianceReport with the "Generated:" timestamp supplied by the caller.
This seam is Go-only: Python reads the clock inline, so pinning it there takes monkeypatching (which is what scripts/gen_golden.py does to produce the committed golden). Nothing about the rest of the document depends on it.
func GenerateJSON ¶
func GenerateJSON(result schemas.SecurityAuditResult, pretty bool) string
GenerateJSON ports generate_json:
full_json = result.model_dump_json() if not pretty: return full_json return json.dumps(json.loads(full_json), indent=2)
so pretty=false yields pydantic's whitespace-free dump and pretty=true the two-space-indented re-serialisation of the same document. Both are produced here by walking the Go struct directly.
THE TWO BRANCHES DO NOT SPELL FLOATS THE SAME WAY, and that is not a formatting detail — it is why each branch configures its own encoder. `model_dump_json()` is pydantic-core's Rust serializer; `json.dumps` is CPython's, which renders through repr(). They agree on ordinary magnitudes and disagree below 1e-4 (VERIFIED on the pinned interpreter):
value pretty=False (pydantic) pretty=True (repr) 1e-7 1e-7 1e-07 8e-05 0.00008 8e-05 1.23e-05 0.0000123 1.23e-05
So Python's `json.loads` round trip is NOT value-preserving in its printed form, and a Go port that used one spelling for both branches is wrong for one of them. See the pydanticFloat flag in pyjson_local.go, the shared rule in pyfmt.PydanticFloat, and the audit_result_floats golden.
See pyjson_local.go for the rest of why encoding/json is not used: the ensure_ascii escaping, key order, and the "...Z" datetime form that only `model_dump_json()` produces.
func GenerateReport ¶
func GenerateReport(result schemas.SecurityAuditResult) string
GenerateReport ports generate_report: the full Markdown audit report, joined with "\n" and carrying NO trailing newline (Python's `"\n".join`).
Python parity divergence: the cost-breakdown section iterates a dict, whose order in CPython is insertion order; a Go map carries none, so the phases are listed in SORTED key order. Same deviation as the JSON writer's — see pyjson_local.go encodeMap.
func GenerateSarif ¶
func GenerateSarif(result schemas.SecurityAuditResult) string
GenerateSarif ports output/sarif.py generate_sarif: the SARIF 2.1.0 document for one audit result, serialised with `json.dumps(sarif, indent=2)`.
Findings whose verdict is "not_exploitable" are dropped entirely — they appear in neither the results nor the rules — which is what makes the SARIF artifact the "signal only" view of an audit.
The key order below is the Python dict literal's order and is part of the artifact (see pyjson_local.go).
func GenerateSarifAs ¶
func GenerateSarifAs(result schemas.SecurityAuditResult, tool SarifTool) string
GenerateSarifAs is GenerateSarif for another tool's identity.
func GenerateSummaryJSON ¶
func GenerateSummaryJSON(result schemas.SecurityAuditResult) string
GenerateSummaryJSON ports generate_summary_json: the compact, dashboard-shaped view of a result, serialised with `json.dumps(summary, indent=2)`.
The timestamp here is `result.timestamp.isoformat()` — the "+00:00" spelling, NOT the "...Z" one GenerateJSON produces.
func RenderJSON ¶
func RenderJSON(auditResult schemas.SecurityAuditResult) (map[string]any, error)
RenderJSON ports render_json: the pretty document parsed back into a map.
Python returns `json.loads(generate_json(result, pretty=True))`, a plain dict; the Go equivalent is map[string]any with the same JSON-decoded value kinds (every number becomes a float64, as it does in Go generally). An error is impossible for a document this package just generated, so it is reported as a nil map plus the error rather than being swallowed.
func RenderReport ¶
func RenderReport(auditResult schemas.SecurityAuditResult) string
RenderReport ports render_report, the alias generate_report is exported under.
func RenderSarif ¶
func RenderSarif(auditResult schemas.SecurityAuditResult) string
RenderSarif ports render_sarif, the alias generate_sarif is exported under.
func RuleIDsAs ¶
func RuleIDsAs(findings []schemas.VerifiedFinding, tool SarifTool) []schemas.VerifiedFinding
RuleIDsAs is findings with their SARIF rule ids under tool's prefix in place of sec-af's (`sec-af/sast/cwe-78` is `sec/sast/cwe-78`), so a log names one tool throughout. sec-af's own identity leaves them as they are; the slice is a copy, and the findings handed in are not changed.
Types ¶
type ComplianceBrand ¶
type ComplianceBrand struct {
Heading, Signature string
}
ComplianceBrand is who a compliance report says wrote it: its first line and its last. sec-af's is SecAfCompliance, which the golden holds; codeaf's program signs its own (internal/secaf's sec).
type SarifTool ¶
type SarifTool struct {
Name string
// SemanticVersion is a semver and Version anything else; an empty one
// is left out of the log.
SemanticVersion, Version string
InformationURI string
// PropertyPrefix leads the driver's own result properties
// (`<prefix>/verdict`) and its automation id.
PropertyPrefix string
}
SarifTool is who a SARIF log says produced it: the driver's name, its version, where to read about it, and the prefix its own properties carry. sec-af's is SecAfTool, which the goldens hold byte for byte; codeaf's program names itself (internal/secaf's sec), because a code-scanning tool shows the driver's name beside every result, and the program a person ran is sec.