docs: add SECURITY.md and GPG release signing for OpenSSF passing badge
Satisfies vulnerability_report_process/know_secure_design/know_common_errors via SECURITY.md; delivery_unsigned via GPG-signed releases (public key in docs/signing-key.asc); N/A justifications for the 4 N/A-able MUST criteria (crypto_password_storage, vulnerability_report_private, vulnerability_report_response, release_notes_vulns). Backs the OpenSSF Best Practices passing badge and closes the CIIBestPracticesID alert.
This commit is contained in:
+33
-11
@@ -2,12 +2,15 @@
|
||||
"""List incomplete OpenSSF Best Practices criteria and generate a pre-filled edit URL.
|
||||
|
||||
Usage:
|
||||
cii_criteria.py <project_id> [section] [--exclude n1,n2,n3]
|
||||
cii_criteria.py <project_id> [section] [--exclude n1,n2,n3] [--na n1,n2,n3]
|
||||
|
||||
section: passing | silver | gold | baseline-1 | baseline-2 | baseline-3
|
||||
(default: passing)
|
||||
--exclude: comma-separated criterion names to SKIP from the pre-filled URL
|
||||
(they stay at their current Unmet/?/N/A in the form).
|
||||
--na: comma-separated criterion names to mark as N/A in the pre-filled URL
|
||||
(emits <name>_status=N/A with the justification from the JSON).
|
||||
|
||||
section: passing | silver | gold | baseline-1 | baseline-2 | baseline-3
|
||||
(default: passing)
|
||||
--exclude: comma-separated criterion names to SKIP from the pre-filled URL
|
||||
(they stay at their current Unmet/?/N/A in the form).
|
||||
|
||||
The script fetches the upstream criteria.yml to learn which criteria belong to
|
||||
which tier/section, then restricts BOTH the printed incomplete list AND the
|
||||
@@ -121,11 +124,15 @@ def main():
|
||||
choices=list(SECTION_TO_TIER.keys()))
|
||||
parser.add_argument("--exclude", default="",
|
||||
help="Comma-separated criterion names to skip in the URL.")
|
||||
parser.add_argument("--na", default="",
|
||||
help="Comma-separated criterion names to mark as N/A "
|
||||
"with the justification from cii_justifications.json.")
|
||||
args = parser.parse_args()
|
||||
|
||||
project_id = args.project_id
|
||||
section = args.section
|
||||
exclude = {x.strip() for x in args.exclude.split(",") if x.strip()}
|
||||
na_set = {x.strip() for x in args.na.split(",") if x.strip()}
|
||||
|
||||
justifications = load_justifications()
|
||||
|
||||
@@ -174,23 +181,38 @@ def main():
|
||||
for n, c in sorted(na.items()):
|
||||
print(f" - {n}: N/A | note: {c['justification'][:70]}")
|
||||
|
||||
# Build URL params, restricted to the section AND honoring --exclude.
|
||||
# N/A criteria are never pre-filled as Met (they are excluded from `doable`).
|
||||
# Build URL params, restricted to the section AND honoring --exclude /
|
||||
# --na. N/A criteria (already excluded from `doable`) are never pre-filled
|
||||
# as Met; --na criteria that ARE in `doable` are emitted as N/A instead.
|
||||
params = []
|
||||
for n in sorted(doable):
|
||||
if exclude and n in exclude:
|
||||
if n in exclude:
|
||||
continue # leave at current Unmet/?/N/A in the form
|
||||
if n in na_set:
|
||||
continue # emitted as N/A below instead of Met
|
||||
params.append((f"{n}_status", "Met"))
|
||||
params.append((f"{n}_justification",
|
||||
justifications.get(n, "TODO: justify")))
|
||||
# N/A criteria: set status=N/A with a justification from the JSON, but only
|
||||
# when they belong to the targeted section (so the params stay in scope).
|
||||
for n in sorted(na_set):
|
||||
if n in exclude:
|
||||
continue
|
||||
if n not in scoped:
|
||||
continue
|
||||
params.append((f"{n}_status", "N/A"))
|
||||
params.append((f"{n}_justification",
|
||||
justifications.get(n, "TODO: justify N/A")))
|
||||
params.append(("overrides", "*,osps_*"))
|
||||
edit_url = f"{BASE}/projects/{project_id}/{section}/edit?{urllib.parse.urlencode(params)}"
|
||||
print(f"\n=== Edit URL ({section}"
|
||||
+ (f", excluding {sorted(exclude)}" if exclude else "")
|
||||
+ f") ===\n{edit_url}")
|
||||
+ (f", excluding {sorted(exclude)}" if exclude else "")
|
||||
+ (f", N/A {sorted(na_set)}" if na_set else "")
|
||||
+ f") ===\n{edit_url}")
|
||||
print(f"\nURL param count for _status/_justification pairs: "
|
||||
f"{sum(1 for k,_ in params if k.endswith('_status'))}")
|
||||
print("WARNING: pre-fills Unmet/? criteria as 'Met' with a placeholder justification.")
|
||||
print("WARNING: pre-fills Unmet/? criteria as 'Met' (and --na as 'N/A') using "
|
||||
"justifications from cii_justifications.json.")
|
||||
print("Review EVERY field before Save; only mark Met what you genuinely satisfy.")
|
||||
|
||||
|
||||
|
||||
@@ -32,5 +32,15 @@
|
||||
"warnings_fixed": "Because Clippy uses `-D warnings`, any warning fails CI and must be fixed before a change can merge.",
|
||||
"dynamic_analysis": "Dynamic analysis is performed via cargo-fuzz (libFuzzer) fuzz targets in fuzz/ that exercise the report/metrics parsers with arbitrary input.",
|
||||
"dynamic_analysis_enable_assertions": "The cargo-fuzz build enables debug/overflow assertions: fuzz/Cargo.toml sets `[profile.release] debug-assertions = true` (and `[profile.dev] debug-assertions = true`), so debug_assert! checks and integer-overflow traps remain active while libFuzzer feeds arbitrary input; crashes that violate internal invariants are caught as bugs.",
|
||||
"dynamic_analysis_fixed": "Fuzzing findings are triaged and fixed; crashes found by the harness are treated as bugs."
|
||||
"dynamic_analysis_fixed": "Fuzzing findings are triaged and fixed; crashes found by the harness are treated as bugs.",
|
||||
|
||||
"delivery_unsigned": "Release artifacts are GPG-signed in CI (.github/workflows/release.yml) with a dedicated RSA-4096 project key; each GitHub Release attaches the macOS and Linux binaries, their detached ASCII-armored signatures (*.tar.gz.asc), and the public key at docs/signing-key.asc. Verification steps are documented in SECURITY.md.",
|
||||
"vulnerability_report_process": "SECURITY.md defines the vulnerability reporting process: reports accepted via GitHub Security Advisories (preferred) or public GitHub Issues, what to include, expected acknowledgement/triage timelines, and a coordinated-disclosure policy.",
|
||||
"know_secure_design": "SECURITY.md documents secure-development practices: input validation at all 89 tool boundaries, no hardcoded secrets, safe bounded parsing of untrusted MT5 HTML/XML reports, single-MT5-instance least-privilege, cargo-deny/cargo-audit dependency auditing in CI, parser fuzzing, and Wine/MT5 isolation.",
|
||||
"know_common_errors": "SECURITY.md lists common implementation errors avoided: integer-overflow (checked arithmetic + fuzz overflow traps), path traversal in .set file handling, UTF-16LE encoding pitfalls, avoiding unsafe, and proper error propagation via Result/?.",
|
||||
|
||||
"crypto_password_storage": "No passwords are stored by the application; the only secrets are a user-provided MT5/Wine path config (gitignored) and an optional release PAT — neither is a password persisted by the project.",
|
||||
"vulnerability_report_private": "No private vulnerability reporting channel is offered; reports are accepted via public GitHub Security Advisories/issues.",
|
||||
"vulnerability_report_response": "No formal fixed-response SLA; handled case-by-case via the public reporting channel.",
|
||||
"release_notes_vulns": "Release notes do not currently track security vulnerabilities separately."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user