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.
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:
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:
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.json sets "strict": true (the full strict bundle: strictNullChecks,
noImplicitAny, strictFunctionTypes, strictBindCallApply,
strictPropertyInitialization, noImplicitThis, alwaysStrict), applied uniformly across
client, server, and shared code.eslint.config.js layers @eslint/js recommended + typescript-eslint recommended rules,
plus project additions (no-var: error, react-hooks/rules-of-hooks: error,
no-unused-vars, no-explicit-any, exhaustive-deps as warnings).ci.yml) runs npm run lint and npm 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.strict bundle — noUnusedLocals,
noUnusedParameters, noImplicitReturns, noFallthroughCasesInSwitch,
noUncheckedIndexedAccess, exactOptionalPropertyTypes.npm run lint has no --max-warnings 0, so ESLint warnings don’t fail CI — only hard
errors do.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:
npm start), and waits on
/api/health before scanning — the same production code path that ships in the Docker
image, not a mock target.zaproxy/action-baseline spiders 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.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. See
dynamic_analysis_fixed below for current findings and their
disposition.This is independent of 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.
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:
expect() assertions across 153 test files (3,040 in server/__tests__/, 1,048 in
client/__tests__/) as of 2026-07-14, run on every push via the build job in
ci.yml (npm test -- --coverage).NDEBUG concern the criterion warns about:
vitest and supertest are devDependencies only (never dependencies) in
package.json, and the production Docker image runs npm prune --omit=dev
(Dockerfile) before copying in the built dist/ output — so the assertion
layer is structurally excluded from what ships, not just conventionally disabled.assert()-equivalent left enabled in shipped code either: neither
server/ nor shared/ import Node’s assert module 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.shared/schema.ts) — that’s boundary validation of
untrusted input, not the test-only correctness assertions this criterion is about.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.yml runs with fail_action: true: any unignored WARN or FAIL alert fails the build,
stricter than this criterion’s “medium or higher” bar.CSP: Wildcard Directive) is fixed, not merely
accepted: font-src/style-src are scoped to 'self' rather than Helmet’s default
https: wildcard (server/routes.ts:360-361)..zap/rules.tsv, and, for the one with a real security tradeoff (COEP), in
the docs/SECURITY_ASSESSMENT.md risk register per the
accept-risk path §3.2 defines.CSP: style-src unsafe-inline) shares a ZAP plugin ID with the fixed
wildcard-directive check, so its .zap/rules.tsv entry 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.Update policy: revisit each entry when the underlying tooling changes, or roughly every 6 months to keep the 2-12 month evidence windows current.