Skip to content

Add SARIF output support for 13 linters + 4 Salesforce Code Analyzer engines - #8715

Merged
nvuillam merged 12 commits into
mainfrom
add-sarif-output-support-batch1
Aug 11, 2026
Merged

Add SARIF output support for 13 linters + 4 Salesforce Code Analyzer engines#8715
nvuillam merged 12 commits into
mainfrom
add-sarif-output-support-batch1

Conversation

@nvuillam

@nvuillam nvuillam commented Aug 10, 2026

Copy link
Copy Markdown
Member

Summary

Wires native SARIF output support into linters that already have it upstream but weren't hooked into MegaLinter's SARIF_REPORTER yet:

  • zizmor (--format=sarif, stdout) — also recovers a findings-reflecting return code, since --format=sarif always exits 0
  • bicep_linter (--stdout --diagnostics-format Sarif)
  • cppcheck (--output-format=sarif, both C and CPP descriptors via the shared linter file)
  • clj-kondo (--config '{:output {:format :sarif}}')
  • htmlhint (--format sarif, picked up from htmlhint.sarif which it writes to the CWD)
  • protolint (--add-reporter sarif:<path>)
  • sqlfluff (--format sarif)
  • swiftlint (--reporter sarif --output <path>)
  • osv-scanner — re-enabled (can_output_sarif was force-disabled with a "disabled until it works!" comment; no blocking bug found in git history)
  • jscpd — reporters string handled in JsCpdLinter.build_lint_command() since jscpd's reporter list is one comma-joined CLI value; picked up from copy-paste/jscpd-report.sarif (the actual filename jscpd 5.0.14 writes)
  • lintrRLinter.py builds its own R -e command rather than going through the standard command pipeline, so lintr::sarif_output() is invoked directly when SARIF is requested (requires the jsonlite R package, now installed, and a manually-set path attribute on the lint results — see below)

Also adds SARIF output to the 4 Salesforce Code Analyzer engines (SALESFORCE_CODE_ANALYZER_APEX/AURA/LWC/FLOW) via a new shared SalesforceCodeAnalyzerLinter class: the sf code-analyzer CLI infers its report format from the --output-file extension, which is already baked into a fixed argument list, so a second --output-file from the standard SARIF injection would collide. The class swaps the extension from .csv to .sarif only when SARIF output is requested, so normal runs keep producing CSV as before.

Two linters were dropped from this batch after CI proved the SARIF support isn't actually available in the pinned/released tool version, despite what the initial research found:

  • roslynator: the pinned roslynator.dotnet.cli 0.13.0 rejects --output-format sarif at runtime ("Unknown output format 'sarif'"). The 0.13.0 version bump is kept (works fine otherwise).
  • trufflehog: --sarif was merged upstream on 2026-08-05, after the latest release v3.96.0 (2026-07-24) that MegaLinter pins — no released version has it yet.

Known caveats worth a second look

  • clj-kondo's upstream SARIF output currently nests region one level too deep under artifactLocation (Invalid SARIF output clj-kondo/clj-kondo#2345), and enabling SARIF output together with EXCLUDED_DIRECTORIES forwarding in project mode causes the exclude-forwarding to be silently skipped (both share the --config flag guard in CljKondoLinter.manage_excluded_directories_config()).
  • bicep_linter's --stdout mixes the compiled ARM template JSON and the SARIF diagnostics in the same stream; Linter.manage_sarif_output()'s JSON-block extraction correctly picks out the SARIF-shaped block in CI, but it's a less common pattern worth extra scrutiny if it's ever touched again.
  • osv-scanner, jscpd, and the Salesforce aura engine report all (or nearly all) of their SARIF findings at "warning" level rather than "error" — added to the existing betterleaks-style exception list in the shared SARIF test rather than the plain errors > 1 assertion.
  • lintr::sarif_output() only works on results from lint_dir()/lint_package() in principle (it reads an internal path attribute those set on the lint results, and aborts otherwise); RLinter.py sets that attribute manually since MegaLinter calls plain lint() per file. The attribute only feeds the SARIF ROOTPATH URI, not file I/O, so this is safe but is relying on an internal implementation detail rather than a documented API.

Test plan

  • test_report_sarif passes for all linters/engines above — verified in CI across 8 rounds of real bug fixes surfaced by the new tests (a crash in RoslynatorLinter's restore step, wrong filenames/flag placement for jscpd/trufflehog/htmlhint, a missing R package, a forgotten make megalinter-build, zizmor's always-0 exit code, a test-fixture activation gap for sqlfluff, several rounds on lintr's SARIF path handling, and two linters — roslynator, trufflehog — whose upstream SARIF support turned out not to be released yet)
  • Existing success/failure lint-mode tests still pass

…engines

zizmor, bicep_linter, cppcheck, clj-kondo, roslynator, htmlhint, protolint,
sqlfluff, swiftlint, osv-scanner, trufflehog, jscpd and lintr now support
can_output_sarif. The 4 SALESFORCE_CODE_ANALYZER_* engines switch their
report format from CSV to SARIF automatically when SARIF reporting is
requested, via a new shared SalesforceCodeAnalyzerLinter class. Roslynator
is bumped to 0.13.0, the first release including SARIF output.
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

⚠️MegaLinter analysis: Success with warnings

⚠️ PYTHON / bandit - 177 errors
---------------------
>> Issue: [B311:blacklist] Standard pseudo-random generators are not suitable for security/cryptographic purposes.
   Severity: Low   Confidence: High
   CWE: CWE-330 (https://cwe.mitre.org/data/definitions/330.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_calls.html#b311-random
   Location: ./megalinter/utils_sarif.py:156:61
155	                        rule["id"] = (
156	                            rule["id"] + "_DUPLICATE_" + str(random.randint(1, 99999))
157	                        )

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: ./megalinter/utilstest.py:122:4
121	    )
122	    assert os.path.isdir(config.get(request_id, "DEFAULT_WORKSPACE")), (
123	        "DEFAULT_WORKSPACE "
124	        + config.get(request_id, "DEFAULT_WORKSPACE")
125	        + " is not a valid folder"
126	    )
127	

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: ./megalinter/utilstest.py:167:4
166	    tmp_report_folder = tempfile.gettempdir() + os.path.sep + str(uuid.uuid4())
167	    assert os.path.isdir(workspace), f"Test folder {workspace} is not existing"
168	    linter_name = linter.linter_name

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: ./megalinter/utilstest.py:241:4
240	    tmp_report_folder = tempfile.gettempdir() + os.path.sep + str(uuid.uuid4())
241	    assert os.path.isdir(workspace), f"Test folder {workspace} is not existing"
242	    if os.path.isfile(workspace + os.path.sep + "no_test_failure"):

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: ./megalinter/utilstest.py:490:4
489	    )
490	    assert os.path.isdir(workspace), f"Test folder {workspace} is not existing"
491	    expected_file_name = ""

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: ./megalinter/utilstest.py:590:4
589	        workspace += os.path.sep + "bad"
590	    assert os.path.isdir(workspace), f"Test folder {workspace} is not existing"
591	    # Call linter

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: ./megalinter/utilstest.py:690:4
689	        workspace = workspace + os.path.sep + "fix"
690	    assert os.path.isdir(workspace), f"Test folder {workspace} is not existing"
691	

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: ./megalinter/utilstest.py:796:12
795	            ]
796	            assert (len(list(diffs))) > 0, f"No changes in the {file} file"
797	

--------------------------------------------------
>> Issue: [B108:hardcoded_tmp_directory] Probable insecure usage of temp file/directory.
   Severity: Medium   Confidence: Medium
   CWE: CWE-377 (https://cwe.mitre.org/data/definitions/377.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b108_hardcoded_tmp_directory.html
   Location: ./server/server.py:81:42
80	    if item.fileUploadId:
81	        uploaded_file_path = os.path.join("/tmp/server-files", item.fileUploadId)
82	        if not os.path.isdir(uploaded_file_path):

--------------------------------------------------
>> Issue: [B108:hardcoded_tmp_directory] Probable insecure usage of temp file/directory.
   Severity: Medium   Confidence: Medium
   CWE: CWE-377 (https://cwe.mitre.org/data/definitions/377.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b108_hardcoded_tmp_directory.html
   Location: ./server/server.py:103:38
102	    file_upload_id = "FILE_" + str(uuid1())
103	    uploaded_file_path = os.path.join("/tmp/server-files", file_upload_id)
104	    os.makedirs(uploaded_file_path)

--------------------------------------------------
>> Issue: [B108:hardcoded_tmp_directory] Probable insecure usage of temp file/directory.
   Severity: Medium   Confidence: Medium
   CWE: CWE-377 (https://cwe.mitre.org/data/definitions/377.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b108_hardcoded_tmp_directory.html
   Location: ./server/server_worker.py:98:34
97	        temp_dir = self.create_temp_dir()
98	        upload_dir = os.path.join("/tmp/server-files", file_upload_id)
99	        if os.path.exists(upload_dir):

--------------------------------------------------

Code scanned:
	Total lines of code: 28309
	Total lines skipped (#nosec): 0
	Total potential issues skipped due to specifically being disabled (e.g., #nosec BXXX): 0

Run metrics:
	Total issues (by severity):
		Undefined: 0
		Low: 115
		Medium: 54
		High: 8
	Total issues (by confidence):
		Undefined: 0
		Low: 44
		Medium: 24
		High: 109
Files skipped (0):

(Truncated to last 6666 characters out of 125127)
⚠️ BASH / bash-exec - 1 error
Results of bash-exec linter (version 5.3.9)
See documentation on https://megalinter.io/beta/descriptors/bash_bash_exec/
-----------------------------------------------

✅ [SUCCESS] .automation/build_schemas_doc.sh
✅ [SUCCESS] .automation/format-tables.sh
✅ [SUCCESS] .vscode/testlinter.sh
✅ [SUCCESS] build.sh
✅ [SUCCESS] entrypoint.sh
❌ [ERROR] sh/megalinter_exec.sh
    Error: File:[sh/megalinter_exec.sh] is not executable

✅ [SUCCESS] sh/setup-runtime-user.sh
⚠️ SPELL / lychee - 54 errors
t 72:22) | Rejected status code: 404 Not Found

Errors in megalinter/descriptors/markdown.megalinter-descriptor.yml
[404] https://github.com/rvben/rumdl/blob/main/docs/RULES.md (at 166:23) | Rejected status code: 404 Not Found
[403] https://www.npmjs.com/package/markdown-table-formatter (at 103:17) | Rejected status code: 403 Forbidden

Errors in megalinter/descriptors/r.megalinter-descriptor.yml
[TIMEOUT] https://rstudio.com/ (at 67:16) | Request timed out | Followed 1 redirect. Redirects: https://rstudio.com/ --[301]--> https://www.rstudio.com/

Errors in megalinter/descriptors/repository.megalinter-descriptor.yml
[404] https://raw.githubusercontent.com/oxsecurity/megalinter/main/docs/assets/icons/linters/betterleaks.png (at 297:26) | Rejected status code: 404 Not Found

Errors in megalinter/descriptors/rst.megalinter-descriptor.yml
[403] https://docutils.sourceforge.io/docs/ref/rst/directives.html#raw-data-pass-through (at 34:38) | Rejected status code: 403 Forbidden

Errors in megalinter/descriptors/salesforce.megalinter-descriptor.yml
[403] https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/guide/config.html (at 374:37) | Rejected status code: 403 Forbidden
[403] https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/guide/engine-flow.html (at 371:17) | Rejected status code: 403 Forbidden
[403] https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/guide/get-started.html (at 176:17) | Error (cached)
[403] https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/guide/get-started.html (at 276:17) | Error (cached)
[403] https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/guide/get-started.html (at 74:17) | Rejected status code: 403 Forbidden
[403] https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/guide/rules-flow.html (at 373:23) | Rejected status code: 403 Forbidden

Errors in megalinter/descriptors/shared/biome.megalinter-linter.yml
[404] https://biomejs.dev/linter/rules/ (at 21:19) | Rejected status code: 404 Not Found

Errors in megalinter/descriptors/shared/cppcheck.megalinter-linter.yml
[403] https://cppcheck.sourceforge.io/ (at 3:13) | Rejected status code: 403 Forbidden
[403] https://cppcheck.sourceforge.io/ (at 4:14) | Rejected status code: 403 Forbidden
[403] https://cppcheck.sourceforge.io/manual.html#configuration (at 8:33) | Rejected status code: 403 Forbidden

Errors in megalinter/descriptors/spell.megalinter-descriptor.yml
[404] https://vale.sh/docs/topics/vocab/ (at 190:38) | Rejected status code: 404 Not Found | Followed 2 redirects. Redirects: https://vale.sh/docs/topics/vocab/ --[301]--> https://docs.vale.sh/topics/vocab/ --[302]--> https://docs.vale.sh/topics/vocab
[404] https://vale.sh/docs/vale-cli/structure/ (at 183:95) | Rejected status code: 404 Not Found | Followed 2 redirects. Redirects: https://vale.sh/docs/vale-cli/structure/ --[301]--> https://docs.vale.sh/vale-cli/structure/ --[302]--> https://docs.vale.sh/vale-cli/structure

Errors in megalinter/descriptors/tsx.megalinter-descriptor.yml
[404] https://eslint-react.xyz/docs/getting-started/installation (at 81:37) | Rejected status code: 404 Not Found

Errors in megalinter/descriptors/xml.megalinter-descriptor.yml
[406] https://gitlab.gnome.org/GNOME/libxml2/-/wikis/home (at 38:17) | Rejected status code: 406 Not Acceptable

Errors in README.md
[ERROR] https://ampcode.com/ (at 247:1) | HTTP/2 protocol error. Server may not support HTTP/2 properly
[301] https://future-architect.github.io/authors/%E5%AE%AE%E6%B0%B8%E5%B4%87%E5%8F%B2 (at 1973:104) | Rejected status code: 301 Moved Permanently
[TIMEOUT] https://generated.at/ (at 1314:301) | Request timed out
[404] https://github.com/oxsecurity/megalinter/stargazers (at 2119:3) | Rejected status code: 404 Not Found
[404] https://github.com/oxsecurity/megalinter/stargazers/ (at 23:1) | Rejected status code: 404 Not Found
[403] https://javascript.plainenglish.io/node-js-coding-standard-tools-with-megalinter-on-gitlab-ci-a43b55915811 (at 1956:3) | Rejected status code: 403 Forbidden
[403] https://medium.com/@caodanju/30-seconds-to-setup-megalinter-your-go-to-tool-for-automated-code-quality-and-iac-security-969d90a5a99c (at 1941:3) | Rejected status code: 403 Forbidden
[403] https://medium.com/@RunningMattress (at 1950:255) | Rejected status code: 403 Forbidden
[403] https://medium.com/@RunningMattress/level-up-your-unity-packages-with-ci-cd-9498d2791211 (at 1950:3) | Rejected status code: 403 Forbidden
[403] https://medium.com/@SeasonedDeveloper (at 1937:255) | Rejected status code: 403 Forbidden
[403] https://medium.com/@SeasonedDeveloper/looking-for-the-best-ci-cd-pipeline-linting-tool-try-megalinter-d89c9eba850d (at 1937:3) | Rejected status code: 403 Forbidden
[403] https://medium.com/datamindedbe/integrating-megalinter-to-automate-linting-across-multiple-codebases-a-technical-description-a200bb235b71 (at 1938:3) | Rejected status code: 403 Forbidden
[403] https://nicolas.vuillamy.fr/improve-uniformize-and-secure-your-code-base-with-megalinter-62ebab422c1 (at 1959:3) | Rejected status code: 403 Forbidden
[403] https://nicolas.vuillamy.fr/megalinter-sells-his-soul-and-joins-ox-security-2a91a0027628 (at 1958:3) | Rejected status code: 403 Forbidden
[403] https://nklya.medium.com/ (at 1955:255) | Rejected status code: 403 Forbidden
[403] https://nklya.medium.com/hot-to-linter-basic-things-like-trailing-whitespaces-and-newlines-7b40da8f688d (at 1955:3) | Rejected status code: 403 Forbidden
[403] https://npmjs.org/package/mega-linter-runner (at 1229:1) | Error (cached)
[403] https://npmjs.org/package/mega-linter-runner (at 1230:1) | Error (cached)
[403] https://npmjs.org/package/mega-linter-runner (at 1231:1) | Error (cached)
[403] https://npmjs.org/package/mega-linter-runner (at 21:1) | Rejected status code: 403 Forbidden | Followed 1 redirect. Redirects: https://npmjs.org/package/mega-linter-runner --[301]--> https://www.npmjs.com/package/mega-linter-runner
[403] https://openai.com/codex/ (at 239:1) | Rejected status code: 403 Forbidden
[403] https://pmd.sourceforge.io/pmd-6.55.0/pmd_userdocs_tools_ci.html (at 2040:3) | Rejected status code: 403 Forbidden
[403] https://www.npmjs.com/package/@downatthebottomofthemolehole/megalinter-mcp-server (at 1915:354) | Rejected status code: 403 Forbidden

Hint: Followed 762 redirects. You might want to consider replacing redirecting URLs with the resolved URLs. Use verbose mode (`-v`/`-vv`) to see redirection details.
Hint: Rejected redirectional status codes. This means some redirects were not followed. You might want to increase the limit for `-m`/`--max-redirects`.

(Truncated to last 6666 characters out of 31634)
⚠️ MARKDOWN / markdownlint - 336 errors
-Linter"]
docs/plugins.md:9 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "Plugins"]
docs/quick-start.md:9 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "Quick Start"]
docs/removed-linters.md:9 error MD024/no-duplicate-heading Multiple headings with the same content [Context: "Removed linters"]
docs/reporters.md:9 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "Reporters"]
docs/reporters/AzureCommentReporter.md:6 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "Azure Comment Reporter"]
docs/reporters/BitbucketCommentReporter.md:6 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "Bitbucket Comment Reporter"]
docs/reporters/ConfigReporter.md:5 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "IDE Configuration Reporter"]
docs/reporters/ConsoleReporter.md:5 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "Console Reporter"]
docs/reporters/EmailReporter.md:5 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "E-mail Reporter"]
docs/reporters/FileIoReporter.md:5 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "File.io Reporter"]
docs/reporters/GitHubCommentReporter.md:6 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "GitHub Comment Reporter"]
docs/reporters/GitHubCommentReporter.md:27:196 error MD056/table-column-count Table column count [Expected: 4; Actual: 3; Too few cells, row will be missing data]
docs/reporters/GitHubCommentReporter.md:27:46 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/reporters/GitHubCommentReporter.md:27:174 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/reporters/GitHubCommentReporter.md:27:196 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/reporters/GitHubCommentReporter.md:28:179 error MD056/table-column-count Table column count [Expected: 4; Actual: 3; Too few cells, row will be missing data]
docs/reporters/GitHubCommentReporter.md:28:46 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/reporters/GitHubCommentReporter.md:28:160 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/reporters/GitHubCommentReporter.md:28:179 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/reporters/GitHubCommentReporter.md:29:159 error MD056/table-column-count Table column count [Expected: 4; Actual: 3; Too few cells, row will be missing data]
docs/reporters/GitHubCommentReporter.md:29:48 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/reporters/GitHubCommentReporter.md:29:143 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/reporters/GitHubCommentReporter.md:29:159 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/reporters/GitHubCommentReporter.md:30:171 error MD056/table-column-count Table column count [Expected: 4; Actual: 3; Too few cells, row will be missing data]
docs/reporters/GitHubCommentReporter.md:30:46 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/reporters/GitHubCommentReporter.md:30:152 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/reporters/GitHubCommentReporter.md:30:171 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/reporters/GitHubStatusReporter.md:6 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "GitHub Status Reporter"]
docs/reporters/GitlabCommentReporter.md:6 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "Gitlab Comment Reporter"]
docs/reporters/JsonReporter.md:5 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "JSON Reporter"]
docs/reporters/MarkdownSummaryReporter.md:6 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "Markdown Summary Reporter"]
docs/reporters/SarifReporter.md:6 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "SARIF Reporter (beta)"]
docs/reporters/TapReporter.md:5 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "TAP Reporter"]
docs/reporters/TextReporter.md:5 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "Text Reporter"]
docs/reporters/UpdatedSourcesReporter.md:5 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "Updated Sources Reporter"]
docs/special-thanks.md:9 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "Special thanks"]
docs/special-thanks.md:23:3 error MD045/no-alt-text Images should have alternate text (alt text)
docs/sponsor.md:5 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "Sponsoring"]
docs/supported-linters.md:9 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "Supported Linters"]
mega-linter-runner/README.md:27:274 error MD051/link-fragments Link fragments should be valid [Context: "[**apply formatting and auto-fixes**](#apply-fixes)"]
mega-linter-runner/README.md:27:217 error MD051/link-fragments Link fragments should be valid [Context: "[**reports in several formats**](#reports)"]
README.md:219:127 error MD051/link-fragments Link fragments should be valid [Context: "[many additional features](#mega-linter-vs-super-linter)"]
README.md:2146:3 error MD045/no-alt-text Images should have alternate text (alt text)
skills/megalinter-check/performance.md:27:601 error MD013/line-length Line length [Expected: 600; Actual: 713]
skills/megalinter-setup/agents/megalinter-runner.md:33:601 error MD013/line-length Line length [Expected: 600; Actual: 620]

(Truncated to last 6666 characters out of 44876)
⚠️ YAML / prettier - 14 errors
unchanged)
mega-linter-runner/generators/mega-linter-custom-flavor/templates/action.yml 5ms (unchanged)
mega-linter-runner/generators/mega-linter-custom-flavor/templates/check-new-megalinter-version.yml 16ms (unchanged)
mega-linter-runner/generators/mega-linter-custom-flavor/templates/megalinter-custom-flavor-builder.yml 27ms (unchanged)
[error] mega-linter-runner/generators/mega-linter-custom-flavor/templates/megalinter-custom-flavor.yml: SyntaxError: Implicit map keys need to be followed by map values (6:1)
[error]   4 | label: <%= CUSTOM_FLAVOR_LABEL %>
[error]   5 | linters:
[error] > 6 | <%= CUSTOM_FLAVOR_LINTERS %>
[error]     | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error]   7 |
mega-linter-runner/generators/mega-linter-custom-flavor/templates/zizmor.yml 4ms (unchanged)
mega-linter-runner/generators/mega-linter/templates/.drone.yml 3ms (unchanged)
mega-linter-runner/generators/mega-linter/templates/.gitlab-ci.yml 7ms (unchanged)
mega-linter-runner/generators/mega-linter/templates/azure-pipelines.yml 6ms (unchanged)
mega-linter-runner/generators/mega-linter/templates/bitbucket-pipelines.yml 7ms (unchanged)
mega-linter-runner/generators/mega-linter/templates/concourse-task.yml 6ms (unchanged)
[error] mega-linter-runner/generators/mega-linter/templates/mega-linter.yml: SyntaxError: Implicit map keys need to be followed by map values (67:11)
[error]   65 |           # Only define `secrets.PAT` if you fully understand the trade-off.
[error]   66 |           token: ${{ secrets.PAT || secrets.GITHUB_TOKEN }}
[error] > 67 |           <%- PERSIST_CREDENTIALS %>
[error]      |           ^^^^^^^^^^^^^^^^^^^^^^^^^^
[error]   68 |
[error]   69 |           # If you use VALIDATE_ALL_CODEBASE = true, you can remove this line to
[error]   70 |           # improve performance
megalinter/descriptors/action.megalinter-descriptor.yml 17ms (unchanged)
megalinter/descriptors/ansible.megalinter-descriptor.yml 7ms (unchanged)
megalinter/descriptors/api.megalinter-descriptor.yml 10ms (unchanged)
megalinter/descriptors/arm.megalinter-descriptor.yml 11ms (unchanged)
megalinter/descriptors/bash.megalinter-descriptor.yml 19ms (unchanged)
megalinter/descriptors/bicep.megalinter-descriptor.yml 8ms (unchanged)
megalinter/descriptors/c.megalinter-descriptor.yml 4ms (unchanged)
megalinter/descriptors/clojure.megalinter-descriptor.yml 11ms (unchanged)
megalinter/descriptors/cloudformation.megalinter-descriptor.yml 6ms (unchanged)
megalinter/descriptors/coffee.megalinter-descriptor.yml 4ms (unchanged)
megalinter/descriptors/copypaste.megalinter-descriptor.yml 7ms (unchanged)
megalinter/descriptors/cpp.megalinter-descriptor.yml 6ms (unchanged)
megalinter/descriptors/csharp.megalinter-descriptor.yml 11ms (unchanged)
megalinter/descriptors/css.megalinter-descriptor.yml 9ms (unchanged)
megalinter/descriptors/dart.megalinter-descriptor.yml 5ms (unchanged)
megalinter/descriptors/dockerfile.megalinter-descriptor.yml 6ms (unchanged)
megalinter/descriptors/editorconfig.megalinter-descriptor.yml 9ms (unchanged)
megalinter/descriptors/env.megalinter-descriptor.yml 4ms (unchanged)
megalinter/descriptors/gherkin.megalinter-descriptor.yml 4ms (unchanged)
megalinter/descriptors/go.megalinter-descriptor.yml 9ms (unchanged)
megalinter/descriptors/graphql.megalinter-descriptor.yml 4ms (unchanged)
megalinter/descriptors/groovy.megalinter-descriptor.yml 4ms (unchanged)
megalinter/descriptors/html.megalinter-descriptor.yml 8ms (unchanged)
megalinter/descriptors/java.megalinter-descriptor.yml 9ms (unchanged)
megalinter/descriptors/javascript.megalinter-descriptor.yml 11ms (unchanged)
megalinter/descriptors/json.megalinter-descriptor.yml 13ms (unchanged)
megalinter/descriptors/jsx.megalinter-descriptor.yml 4ms (unchanged)
megalinter/descriptors/kotlin.megalinter-descriptor.yml 5ms (unchanged)
megalinter/descriptors/kubernetes.megalinter-descriptor.yml 10ms (unchanged)
megalinter/descriptors/latex.megalinter-descriptor.yml 3ms (unchanged)
megalinter/descriptors/lua.megalinter-descriptor.yml 7ms (unchanged)
megalinter/descriptors/markdown.megalinter-descriptor.yml 10ms (unchanged)
megalinter/descriptors/perl.megalinter-descriptor.yml 8ms (unchanged)
megalinter/descriptors/php.megalinter-descriptor.yml 25ms (unchanged)
megalinter/descriptors/powershell.megalinter-descriptor.yml 7ms (unchanged)
megalinter/descriptors/protobuf.megalinter-descriptor.yml 8ms (unchanged)
megalinter/descriptors/python.megalinter-descriptor.yml 73ms (unchanged)
megalinter/descriptors/r.megalinter-descriptor.yml 5ms (unchanged)
megalinter/descriptors/raku.megalinter-descriptor.yml 3ms (unchanged)
megalinter/descriptors/repository.megalinter-descriptor.yml 90ms (unchanged)
megalinter/descriptors/robotframework.megalinter-descriptor.yml 10ms (unchanged)
megalinter/descriptors/rst.megalinter-descriptor.yml 14ms (unchanged)
megalinter/descriptors/ruby.megalinter-descriptor.yml 8ms (unchanged)
megalinter/descriptors/rust.megalinter-descriptor.yml 5ms (unchanged)
megalinter/descriptors/salesforce.megalinter-descriptor.yml 20ms (unchanged)
megalinter/descriptors/scala.megalinter-descriptor.yml 4ms (unchanged)
megalinter/descriptors/shared/biome.megalinter-linter.yml 5ms (unchanged)
megalinter/descriptors/shared/clang-format.megalinter-linter.yml 3ms (unchanged)
megalinter/descriptors/shared/cppcheck.megalinter-linter.yml 3ms (unchanged)
megalinter/descriptors/shared/cpplint.megalinter-linter.yml 2ms (unchanged)
megalinter/descriptors/shared/dotnet-format.megalinter-linter.yml 4ms (unchanged)
megalinter/descriptors/shared/eslint.megalinter-linter.yml 5ms (unchanged)
megalinter/descriptors/shared/prettier.megalinter-linter.yml 4ms (unchanged)
megalinter/descriptors/shared/v8r.megalinter-linter.yml 3ms (unchanged)
megalinter/descriptors/snakemake.megalinter-descriptor.yml 6ms (unchanged)
megalinter/descriptors/spell.megalinter-descriptor.yml 24ms (unchanged)
megalinter/descriptors/sql.megalinter-descriptor.yml 9ms (unchanged)
megalinter/descriptors/swift.megalinter-descriptor.yml 7ms (unchanged)
megalinter/descriptors/tekton.megalinter-descriptor.yml 5ms (unchanged)
megalinter/descriptors/terraform.megalinter-descriptor.yml 13ms (unchanged)
megalinter/descriptors/tsx.megalinter-descriptor.yml 5ms (unchanged)
megalinter/descriptors/typescript.megalinter-descriptor.yml 11ms (unchanged)
megalinter/descriptors/vbdotnet.megalinter-descriptor.yml 3ms (unchanged)
megalinter/descriptors/xml.megalinter-descriptor.yml 6ms (unchanged)
megalinter/descriptors/yaml.megalinter-descriptor.yml 9ms (unchanged)
server/docker-compose-dev.yml 7ms (unchanged)
server/docker-compose.yml 4ms (unchanged)
trivy-secret.yaml 1ms (unchanged)
zizmor.yml 3ms (unchanged)

(Truncated to last 6666 characters out of 12562)
⚠️ YAML / yamllint - 37 errors
.grype.yaml
  6:1       warning  missing document start "---"  (document-start)

mega-linter-runner/.eslintrc.yml
  11:9      warning  too few spaces inside empty braces  (braces)

mega-linter-runner/generators/mega-linter-custom-flavor/templates/megalinter-custom-flavor-builder.yml
  48:15     warning  too few spaces inside empty braces  (braces)

mega-linter-runner/generators/mega-linter-custom-flavor/templates/megalinter-custom-flavor.yml
  7:1       error    syntax error: could not find expected ':' (syntax)

mega-linter-runner/generators/mega-linter/templates/mega-linter.yml
  38:15     warning  too few spaces inside empty braces  (braces)
  69:11     error    syntax error: could not find expected ':' (syntax)

megalinter/descriptors/copypaste.megalinter-descriptor.yml
  19:301    warning  line too long (313 > 300 characters)  (line-length)
  25:301    warning  line too long (384 > 300 characters)  (line-length)

megalinter/descriptors/javascript.megalinter-descriptor.yml
  52:301    warning  line too long (475 > 300 characters)  (line-length)
  328:301   warning  line too long (307 > 300 characters)  (line-length)

megalinter/descriptors/jsx.megalinter-descriptor.yml
  29:301    warning  line too long (475 > 300 characters)  (line-length)

megalinter/descriptors/perl.megalinter-descriptor.yml
  25:301    warning  line too long (310 > 300 characters)  (line-length)

megalinter/descriptors/php.megalinter-descriptor.yml
  200:301   warning  line too long (389 > 300 characters)  (line-length)
  214:301   warning  line too long (302 > 300 characters)  (line-length)

megalinter/descriptors/repository.megalinter-descriptor.yml
  27:301    warning  line too long (666 > 300 characters)  (line-length)
  193:301   warning  line too long (408 > 300 characters)  (line-length)
  299:301   warning  line too long (345 > 300 characters)  (line-length)
  478:301   warning  line too long (306 > 300 characters)  (line-length)
  557:301   warning  line too long (374 > 300 characters)  (line-length)
  642:301   warning  line too long (316 > 300 characters)  (line-length)
  980:301   warning  line too long (1263 > 300 characters)  (line-length)
  1077:301  warning  line too long (879 > 300 characters)  (line-length)
  1091:301  warning  line too long (358 > 300 characters)  (line-length)
  1154:301  warning  line too long (346 > 300 characters)  (line-length)
  1161:301  warning  line too long (307 > 300 characters)  (line-length)

megalinter/descriptors/salesforce.megalinter-descriptor.yml
  54:301    warning  line too long (359 > 300 characters)  (line-length)

megalinter/descriptors/spell.megalinter-descriptor.yml
  181:301   warning  line too long (315 > 300 characters)  (line-length)

megalinter/descriptors/terraform.megalinter-descriptor.yml
  28:301    warning  line too long (330 > 300 characters)  (line-length)
  88:301    warning  line too long (346 > 300 characters)  (line-length)
  155:301   warning  line too long (328 > 300 characters)  (line-length)

megalinter/descriptors/tsx.megalinter-descriptor.yml
  29:301    warning  line too long (475 > 300 characters)  (line-length)

megalinter/descriptors/typescript.megalinter-descriptor.yml
  39:301    warning  line too long (475 > 300 characters)  (line-length)
  318:301   warning  line too long (314 > 300 characters)  (line-length)

mkdocs.yml
  8:301     warning  line too long (590 > 300 characters)  (line-length)
  72:5      warning  wrong indentation: expected 6 but found 4  (indentation)
  85:5      warning  wrong indentation: expected 6 but found 4  (indentation)

zizmor.yml
  1:1       warning  missing document start "---"  (document-start)

✅ Linters with no issues

actionlint, betterleaks, black, checkov, cspell, flake8, git_diff, grype, hadolint, isort, jscpd, jsonlint, markdown-table-formatter, mypy, npm-groovy-lint, osv-scanner, pylint, ruff, secretlint, shellcheck, shfmt, syft, trivy, trivy-sbom, trufflehog, v8r, v8r, xmllint, zizmor

See detailed reports in MegaLinter artifacts

MegaLinter is provided by OX Security
Show us your support by starring ⭐ the repository

- roslynator: the dotnet-restore pre-step ran through execute_lint_command(),
  which now also triggers manage_sarif_output() before sarif_output_file is
  ever set by the real build_lint_command() call — crashed with a TypeError.
  Route the restore step through the lower-level subprocess primitive instead.
- jscpd (pinned v5.0.14): its SARIF reporter actually writes
  copy-paste/jscpd-report.sarif, not jscpd-sarif.json as newer versions do.
- trufflehog: --sarif is a global flag parsed before the subcommand; inserting
  it after "filesystem" like a normal cli_sarif_args append made the CLI
  reject it as unknown. Insert it before "filesystem" in the linter class.
- lintr: sarif_output() requires the jsonlite R package, which wasn't
  installed.
- htmlhint: its formatter writes htmlhint.sarif to the CWD in addition to
  stdout; wire sarif_default_output_file instead of relying on stdout capture.
- zizmor: --format=sarif always exits 0 regardless of findings, so
  MegaLinter's return-code-gated error counting never ran. Recover a
  findings-reflecting return code from the SARIF results after the fact.
- osv-scanner and the Salesforce Code Analyzer aura (eslint) engine report
  all their SARIF findings at "warning" level, same as the existing
  betterleaks exception in the shared SARIF test — add them to that list.
- sqlfluff: active_only_if_file_found: ['.sqlfluff'] deactivated it because
  the SARIF test scans the test-folder root, which had no root-level config
  (only bad/good/fix subfolders did) — add one at the root.
…F check

- roslynator: the pinned roslynator.dotnet.cli 0.13.0 actually rejects
  --output-format sarif ("Unknown output format 'sarif'") — the NuGet
  release/GitHub changelog version correlation used to pin this doesn't
  hold. Revert the SARIF wiring, keep the 0.13.0 version bump (works fine
  otherwise).
- jscpd: its SARIF output reports individual clones as "warning" and only
  the duplication-threshold breach as "error" (one per run), same pattern
  as the existing betterleaks/osv-scanner/salesforce-aura exceptions.
- cspell: add "jsonlite" (R package name) to ignoreWords.
- trufflehog: --sarif was merged upstream on 2026-08-05, after the latest
  release v3.96.0 (2026-07-24) that MegaLinter pins — no released version
  has the flag yet. Revert the SARIF wiring entirely.
- r_lintr: the jsonlite install-block change from the previous fix never
  reached the generated Dockerfile because make megalinter-build wasn't
  re-run after editing the descriptor.
lintr::sarif_output() rejects an absolute filename ("Package path needs to
be a relative path"). Compute the path relative to the R process's cwd
(set to the file's own directory) instead of passing the absolute
sarif_output_file.
Path(file).parent is frequently just "." for files directly under the
workspace, and os.path.relpath() resolves a relative start against this
Python process's own cwd, not the R subprocess's actual working directory
(self.workspace + the file's subdirectory, set via setwd()). Ground the
relpath computation in that real absolute directory instead.
lintr::sarif_output() rejects not just absolute paths but any relative
path containing ".." — it can never point directly at self.sarif_output_file,
which lives under the report folder outside the linted file's directory
tree entirely. Write a bare filename in the R process's own cwd instead,
then move it into place ourselves after the command runs.
Read lintr's actual source: sarif_output() aborts with "Package path needs
to be a relative path" whenever attr(lints, "path") is NULL, which it
always is for plain lint() results — that attribute is only ever set by
lint_dir()/lint_package(). The filename argument was never the problem
across the last several attempts; it only controls where the file gets
written. Set the path attribute manually (it only feeds the SARIF
ROOTPATH URI, not file I/O) and keep the existing move-into-place logic.
@bdovaz

bdovaz commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

@nvuillam @echoix

I see that in utilstest.py you're setting it up so that several linters report everything as a warning instead of warnings and errors, and you're preventing an assert from being triggered.

Back in the day, in several pull requests I made to improve Sarif-related issues, what I did was identify all of that and create issues in their respective official repositories, and even create pull requests where I saw that it was possible.

Because many have warning or error rules, but when you use Sarif, everything is treated as a warning—in other words, support for Sarif is partial.

@nvuillam

Copy link
Copy Markdown
Member Author

@bdovaz you're right, i just launched a task to precise in doc when sarif is partially supported and submit PRs to related repos !

All three currently report every SARIF finding at "warning" level
regardless of actual severity, which was hidden behind an exclusion in
utilstest.py's SARIF test rather than documented anywhere a user would
see it. Note it in each linter's docs, with a link to the upstream fix
(osv-scanner, PR opened) or discussion (jscpd, issue opened; betterleaks,
already tracked in gitleaks/gitleaks#1858).
@nvuillam

Copy link
Copy Markdown
Member Author

Good point, thanks. I looked into each of the three linters this touched (osv-scanner, jscpd, the Salesforce Code Analyzer aura engine) plus the pre-existing betterleaks case:

  • osv-scanner: genuine gap, the CVSS score was already being computed for the security-severity property a few lines above where level gets hardcoded — opened google/osv-scanner#2982 mapping Critical/High to error.
  • jscpd: also a real gap (every clone is warning, size/token count is available but unused), but there's no existing size cutoff in the codebase to base a fix on, so I didn't want to just invent one — opened kucherenko/jscpd#908 proposing it and asking what threshold they'd want.
  • Salesforce Code Analyzer (aura engine): turned out not to be a mapping bug — the severity→SARIF-level function is correct (Critical/High → error), the Aura ruleset does have High-severity rules available, our specific bad-fixture just didn't happen to trip one of them. So this one stays excluded from the strict test for now, but I didn't file anything upstream since there's no actual bug to report.

Documented the partial support (with the links above) directly in each linter's linter_text so it shows up on the docs pages, in c226608.

@nvuillam
nvuillam merged commit b448cd7 into main Aug 11, 2026
278 of 279 checks passed
@nvuillam
nvuillam deleted the add-sarif-output-support-batch1 branch August 11, 2026 21:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants