Skip to content

Prevent jscpd and secretlint report-folder race - #8633

Open
trevor-vaughan wants to merge 1 commit into
oxsecurity:mainfrom
trevor-vaughan:fix/3979-secretliner-and-jscpd-toctou
Open

Prevent jscpd and secretlint report-folder race#8633
trevor-vaughan wants to merge 1 commit into
oxsecurity:mainfrom
trevor-vaughan:fix/3979-secretliner-and-jscpd-toctou

Conversation

@trevor-vaughan

Copy link
Copy Markdown
Contributor

Summary

When COPYPASTE_JSCPD and REPOSITORY_SECRETLINT ran in parallel, jscpd's per-linter cleanup deleted megalinter-reports/copy-paste while secretlint was still scanning the workspace, producing sporadic ENOENT crashes whose stack traces were then parsed as SARIF findings. This fixes the race by keeping jscpd's report out of the shared workspace until clones are found and by giving secretlint a generated ignore file that excludes only MegaLinter's own report folder. It also hardens SARIF result counting and the secretlint ignore-file handling so a crashed or misconfigured secrets scanner can no longer be reported as clean.

Assisted-By: Claude Opus 5 (1M context) noreply@anthropic.com
Closes: #3979

Changes

  • jscpd writes its report to a temp folder outside the workspace and copies it into copy-paste/ only when clones are found, so no file another linter is scanning is ever deleted mid-run; a stale report from a previous run is cleared at construction time.
  • The published copy-paste/ folder is chmod'd to 0755 so a later non-root artifact-upload step can read it, instead of inheriting mkdtemp's 0700.
  • secretlint now merges the user's ignore patterns into a single generated .secretlintignore-megalinter file (in the report folder, or a temp folder when reports are disabled), honoring a file named in LINTER_RULES_PATH and parsing the --secretlintignore=<value> form.
  • A missing --secretlintignore file no longer silently falls back to .secretlintignore or .gitignore, which would exclude the exact files a secrets scanner must inspect; it degrades with a warning.
  • A linter that exits without parsable SARIF is reported as a warning (results uncountable) rather than a clean success, and one that crashes before emitting SARIF surfaces its raw output instead of a phantom finding. Applies to can_output_sarif linters including REPOSITORY_SECRETLINT, REPOSITORY_SEMGREP, and REPOSITORY_TRIVY.
  • REPORT_OUTPUT_FOLDER is always excluded from linted directories, even when EXCLUDED_DIRECTORIES is overridden.
  • Added unit tests for jscpd report materialization and secretlint ignore handling, with ambient-env isolation in the shared linter tests; documented the secretlint ignore model and the report-folder exclusion in the descriptor and README.

TextReporter runs per linter the moment it finishes, so JsCpdLinter's
rmtree of megalinter-reports/copy-paste fired while secretlint was still
scanning the workspace in project mode. secretlint had already globbed
the jscpd report into its target set, crashed with ENOENT reading it, and
its stack trace was then parsed as SARIF and counted as a finding.

jscpd now writes its report to a temp folder outside the workspace and
copies it in only when clones are found, so MegaLinter never deletes files
other linters are scanning. secretlint gets a generated ignore file in the
report folder that merges the user's patterns with an exclusion for that
folder alone; narrowing a secrets scanner further would hide credentials
baked into build artifacts.

- fix: report a linter with unparsable SARIF as a warning, not a success
  - A linter exiting 0 while emitting a stack trace instead of SARIF was
    counted as zero findings and shown green; affects linters declaring
    can_output_sarif (REPOSITORY_SECRETLINT, REPOSITORY_SEMGREP,
    REPOSITORY_TRIVY). A linter that dies before producing SARIF now fails
    with its raw output instead of a phantom finding.
- fix(secretlint): honor ignore patterns when REPORT_OUTPUT_FOLDER is off
  - The generated ignore file was only written when reports could be
    written; otherwise it fell back to passing a base name, so a
    .secretlintignore under LINTER_RULES_PATH silently applied nothing. It
    now goes to a temp folder outside the workspace, and the
    --secretlintignore=<value> argument form is parsed too.
- fix(secretlint): stop falling back to .gitignore for a missing ignore file
  - A typo in --secretlintignore used to promote .gitignore patterns to
    authoritative status, excluding the very files a secrets scanner exists
    to inspect. It now degrades with a warning and applies no fallback.
- fix(config): always exclude REPORT_OUTPUT_FOLDER from linted directories
  - An EXCLUDED_DIRECTORIES override could drop it, making MegaLinter lint
    its own output while reporters were still writing to it.
- test: cover jscpd report wiring and isolate ambient env in unit tests
- docs: document the secretlint ignore model and report-folder exclusion

Fixes: oxsecurity#3979

Assisted-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Trevor Vaughan <peiriannydd@gmail.com>
@nvuillam

nvuillam commented Aug 5, 2026

Copy link
Copy Markdown
Member

@trevor-vaughan I am currently building a generic way of dynamically inject cli args / ignore files to all linters called in project cli_lint_mode

#8646

I think it will solve your use case (and many others), please tell me if you have any remarks :)

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.

secretlinter and jscpd run ordrer is not respected

2 participants