Configuration reference
The scanner needs only QUALOR_URL and QUALOR_TOKEN. A repository with no qualor.yml still gets
a useful scan. Add qualor.yml at the repository root when your team makes a choice: coverage report
paths, excludes, rulesets or SARIF files.
An editor can autocomplete qualor.yml from the JSON Schema in
packages/shared/schema/qualor.schema.json.
Precedence
From highest to lowest:
- command-line flags (
--project-key,--sarif, …) - environment variables (
QUALOR_*) qualor.yml(or the file named by--config/QUALOR_CONFIG)- values detected from GitLab CI or GitHub Actions
- built-in defaults
Secrets never come from qualor.yml. The token is read only from QUALOR_TOKEN or
--token-file, and a file with a token key is refused. The token is also sent only to a server URL
from QUALOR_URL or --server-url, never to server.url from the file. A merge request can edit the
file, so it must not be able to redirect the token.
qualor.yml
Only version is required. Unknown keys are an error, so typos are caught.
version: 1 # required; only 1 is valid
project:
key: acme/payments-api # default: CI_PROJECT_PATH / GITHUB_REPOSITORY
name: Payments API # default: last segment of the key
version: ${CI_COMMIT_TAG} # optional label; used by the "previous_version" new-code definition
server:
timeoutSeconds: 30 # per HTTP request
sources:
include: ['**/*']
exclude: ['generated/**', 'coverage/**'] # added to the built-in excludes
useGitignore: true
tests:
include: ['**/*.test.*', '**/*.spec.*', '**/__tests__/**', '**/src/test/**', '**/*Tests/**']
exclude: []
languages: auto # or [typescript, javascript, java, csharp]
analyzers:
eslint: { enabled: auto, configFile: null, args: [], timeoutSeconds: 900 }
pmd: { enabled: auto, rulesets: [qualor-default], timeoutSeconds: 900 }
spotbugs: { enabled: auto, classDirs: [target/classes, build/classes/java/main], auxClasspathFile: null, timeoutSeconds: 1200 }
semgrep: { enabled: auto, binary: auto, configs: [qualor-default], timeoutSeconds: 900 }
gitleaks: { enabled: true, configFile: null, timeoutSeconds: 300 }
trivy: { enabled: auto, timeoutSeconds: 600 }
roslyn: { enabled: auto, bundledAnalyzers: true }
sarif:
- path: reports/osv.sarif
engine: osv-scanner
coverage:
reports:
- path: coverage/lcov.info # globs allowed
format: auto # auto | lcov | cobertura | jacoco
pathPrefixes: []
duplication:
enabled: true
minTokens: 100
minLines: 10
exclude: []
newCode:
referenceBranch: null # branches other than main compare with this; default: the main branch
scm:
autoFetch: true # fetch missing baseline commits when the clone is shallow
mainBranch: null # default: CI_DEFAULT_BRANCH / the server's setting
gate:
wait: true # wait for the verdict and use it for the exit code
timeoutSeconds: 300
failOnError: true # a gate in "error" fails the job
Variables in values
${VAR} and ${VAR:-default} are replaced from the environment. Variables whose names look like
secrets (*TOKEN, *SECRET, *PASSWORD, *API_KEY, *PRIVATE_KEY, *_PASS, …) always resolve to
an empty string. That keeps a secret from leaking into the report through the config.
Built-in excludes
These always apply, and you can only add to them: node_modules, .git, dist, build, target
(sources only; SpotBugs still reads the classes), vendor, *.min.js, .qualor/, .NET obj/,
bin/Debug, bin/Release, generated C# (*.g.cs, *.g.i.cs, *.Designer.cs), binary files, and
nested git repositories. Files over 1 MiB are skipped for metrics and duplication, but analyzers still
see them.
Environment variables (scanner)
| Variable | Meaning |
|---|---|
QUALOR_URL |
server base URL (https://…) |
QUALOR_TOKEN |
project analysis token or personal token |
QUALOR_PROJECT_KEY |
overrides project.key |
QUALOR_CONFIG |
path to the config file |
QUALOR_CA_FILE |
a PEM CA bundle for the server’s certificate. Keep it outside the checkout |
QUALOR_LOG_LEVEL |
error, warn, info (default) or debug |
QUALOR_CACHE_DIR |
cache directory (default ~/.cache/qualor) |
QUALOR_TRIVY_CACHE_DIR |
another Trivy database directory (absolute, outside the checkout) |
QUALOR_DOTNET_ANALYZERS |
directory of the bundled Roslyn analyzer DLLs (set in qualor/scanner-dotnet) |
HTTPS_PROXY, HTTP_PROXY, NO_PROXY |
a proxy between the runner and the server (http:// proxies) |
Values read from CI, when present: the revision, the branch, the MR/PR id and its target, the default
branch and the project path, from CI_* on GitLab and GITHUB_* plus the event payload on GitHub.
Outside CI, the scanner uses git rev-parse HEAD and the current branch, and never assumes a merge
request.
Examples
Minimal, with coverage:
version: 1
coverage:
reports: [{ path: coverage/lcov.info }]
Java with a custom PMD ruleset, osv-scanner SARIF and JaCoCo:
version: 1
analyzers:
pmd: { rulesets: [config/pmd.xml] }
spotbugs: { classDirs: [target/classes] }
sarif:
- { path: osv-scanner.sarif, engine: osv-scanner }
coverage:
reports: [{ path: '**/jacoco.xml', format: jacoco }]
Monorepo with one Qualor project per service. Each service has its own config file, and each CI job
passes --config:
# services/billing/qualor.yml
version: 1
project: { key: acme/monorepo-billing, name: Billing }
sources:
include: ['services/billing/**']
coverage:
reports: [{ path: services/billing/coverage/lcov.info }]
qualor scan --config services/billing/qualor.yml
The full specification is in docs/spec/config.md.