OpenSSF Best Practices Badge Evidence
This document records the evidence behind Questarr’s self-assessment against the OpenSSF Best Practices Badge criteria. Each section is named after the criterion ID used in the badge questionnaire, so it can be linked directly as the justification for that question. This is evidence, not policy — for the underlying process/config being evidenced, see the linked files in each section.[report_responses]
The project MUST acknowledge a majority of bug reports submitted in the last 2-12 months (inclusive); the response need not include a fix.Status: Met. Assessed 2026-07-06 against issues labeled
bug created 2025-07-06 to 2026-05-06 on
Doezer/Questarr:
- 51 bug-labeled issues in the window; 42 filed by outside users (9 were the maintainer’s own).
- 41 of 42 (98%) external reports show a maintainer/contributor response: a direct comment from the maintainer (38), a substantive reply from an active contributor (2), or clear maintainer action — assignment, milestone, and closure (1).
- Only one report (#544) has no visible acknowledgement on GitHub; it was closed within minutes by an automation bot, which may reflect off-platform triage that doesn’t surface as a comment.
[enhancement_responses]
The project SHOULD respond to a majority (>50%) of enhancement requests in the last 2-12 months (inclusive).Status: Met. Assessed 2026-07-06 against issues labeled
enhancement created 2025-07-06 to 2026-05-06:
- 79 enhancement issues in the window; 25 filed by outside users (54 were the maintainer’s own roadmap items).
- 24 of 25 (96%) external requests show some form of response: a maintainer comment (19), implementation without a comment — assigned, milestoned, and closed as completed (3), or self-resolution by the reporter/community before a maintainer response was needed (2).
- One request (#271) was closed by its own reporter two minutes after filing with no visible engagement from anyone.
- The 4 enhancement issues still open in this window each have active comment threads (6-25 comments), indicating ongoing discussion rather than neglect.
[warnings_strict]
It is SUGGESTED that projects be maximally strict with warnings in the software produced by the project, where practical.Status: Met, with room to tighten further. Assessed 2026-07-06 against
tsconfig.json, eslint.config.js, and .github/workflows/ci.yml:
tsconfig.jsonsets"strict": true(the full strict bundle:strictNullChecks,noImplicitAny,strictFunctionTypes,strictBindCallApply,strictPropertyInitialization,noImplicitThis,alwaysStrict), applied uniformly across client, server, and shared code.eslint.config.jslayers@eslint/jsrecommended +typescript-eslintrecommended rules, plus project additions (no-var: error,react-hooks/rules-of-hooks: error,no-unused-vars,no-explicit-any,exhaustive-depsas warnings).- CI (
ci.yml) runsnpm run lintandnpm run check(tsc) as unconditional, blocking steps on every push, plus project-specific checks with no off-the-shelf equivalent (check:overrides,check:deprecated) and separate SAST/vulnerability-scan workflows. - Not yet enabled: the opt-in strictness flags beyond the
strictbundle —noUnusedLocals,noUnusedParameters,noImplicitReturns,noFallthroughCasesInSwitch,noUncheckedIndexedAccess,exactOptionalPropertyTypes. npm run linthas no--max-warnings 0, so ESLint warnings don’t fail CI — only hard errors do.
[dynamic_analysis]
It is SUGGESTED that at least one dynamic analysis tool be applied to any proposed major production release of the software before its release.Status: Met.
.github/workflows/dast.yml runs an
OWASP ZAP baseline scan against a live instance of the app:
- Builds the production bundle, boots it on the runner (
npm start), and waits on/api/healthbefore scanning — the same production code path that ships in the Docker image, not a mock target. zaproxy/action-baselinespiders the running app and passively checks every response for common runtime issues (missing security headers, verbose error output, cookie flags, outdated libraries, etc.) — varying inputs by construction, satisfying the criterion independent of the project’s static coverage numbers.- Runs on every push to
main/release/*(so it’s applied ahead of any tag cut from those branches) plus a weekly schedule and manual dispatch, mirroring the cadence already used by vulnerability-scan.yml. fail_action: true,rules_file_name: .zap/rules.tsv: blocking, matching the gate sast.yml already has for Semgrep. Seedynamic_analysis_fixedbelow for current findings and their disposition.
warnings_strict’s test-coverage numbers above (branch coverage
threshold is currently 76%, short of the criterion’s 80% automated-test-suite alternative) —
the ZAP scan satisfies dynamic_analysis on its own via the “tool that varies inputs” path,
regardless of coverage.
[dynamic_analysis_enable_assertions]
It is SUGGESTED that the project use a configuration for at least some dynamic analysis (such as testing or fuzzing) which enables many assertions. In many cases these assertions should not be enabled in production builds.Status: Met. Questarr’s dynamic analysis is its Vitest suite (
server/__tests__/, client/__tests__/),
which is nothing but assertions — expect() calls that fail the run the moment observed
behavior diverges from expected behavior:
- 4,088+
expect()assertions across 153 test files (3,040 inserver/__tests__/, 1,048 inclient/__tests__/) as of 2026-07-14, run on every push via thebuildjob inci.yml(npm test -- --coverage). - This is the JS/TS analogue of the C/C++
NDEBUGconcern the criterion warns about:vitestandsupertestaredevDependenciesonly (neverdependencies) inpackage.json, and the production Docker image runsnpm prune --omit=dev(Dockerfile) before copying in the builtdist/output — so the assertion layer is structurally excluded from what ships, not just conventionally disabled. - There is no runtime
assert()-equivalent left enabled in shipped code either: neitherserver/norshared/import Node’sassertmodule outside of test files, so there’s nothing production-side that could throw on an assertion failure or leak internal state the way the criterion warns about. - This is distinct from the request-input validation Questarr does run in production
(express-validator, Zod schemas in
shared/schema.ts) — that’s boundary validation of untrusted input, not the test-only correctness assertions this criterion is about.
[dynamic_analysis_fixed]
All medium and higher severity exploitable vulnerabilities discovered with dynamic code analysis MUST be fixed in a timely way after they are confirmed.Status: Met. Full policy and results table:
docs/VULNERABILITY_MANAGEMENT.md §3.3.
dast.ymlruns withfail_action: true: any unignored WARN or FAIL alert fails the build, stricter than this criterion’s “medium or higher” bar.- The one Medium-severity finding to date (
CSP: Wildcard Directive) is fixed, not merely accepted:font-src/style-srcare scoped to'self'rather than Helmet’s defaulthttps:wildcard (server/routes.ts:360-361). - Three Low/Informational findings are accepted with reasoning recorded in
.zap/rules.tsv, and, for the one with a real security tradeoff (COEP), in thedocs/SECURITY_ASSESSMENT.mdrisk register per the accept-risk path §3.2 defines. - One finding (
CSP: style-src unsafe-inline) shares a ZAP plugin ID with the fixed wildcard-directive check, so its.zap/rules.tsventry also suppresses that check’s DAST coverage; a dedicated Vitest assertion (server/__tests__/security.test.ts) covers that regression instead. See §3.3 for the full results table and reasoning.