Repository: semgrep/semgrep (language: python)

Issue:
Test tree-sitter c++ parser
This will help https://github.com/returntocorp/semgrep/issues/1952

test plan:
pad@yrax:~/work/lang-cpp/Cataclysm-DDA$ yy -lang cpp -test_parse_tree_sitter .
+ /home/pad/semgrep/_build/default/cli/Main.exe -lang cpp -test_parse_tree_sitter .
11 / 852/OVERLOAD/work/lang-cpp/Cataclysm-DDA/src/activity_actor.cpp: exn = Tree_sitter_run.Tree_sitter_error.Error(_)
...

NB total files = 852; NB total lines = 474254; perfect = 662; pbs = 190; timeout = 0; =========> 77%
nb good = 174878,  nb passed = 0 =========> 0.000000%
nb good = 174878,  nb bad = 299376 =========> 36.874333%

For comparison, the C++/C/cpp parser in pfff is doing:
NB total files = 852; NB total lines = 473336; perfect = 110; pbs = 742; timeout = 0; =========> 12%
nb good = 160267,  nb passed = 187 =========> 0.116680%
nb good = 160267,  nb bad = 313069 =========> 33.859035%
pad@yrax:~/work/lang-cpp/Cataclysm-DDA$


Discussion / hints:
@mjambon I tried to use lang-stat in ocaml-tree-sitter but I didn't manage to compile in the ocaml-tree-sitter/lang/ directory.
Got some errors about git repositories and kotlin.
ah sorry, it works now! I forgot 'make update' to get the kotlin submodule.

Now I get an error about npx though.
Generate parser in .
/bin/sh: 4: npx: not found

I tried make setup, but got some permissions errors. I had to chown pad /usr/local/lib and /usr/local/bin. and rm /usr/loca/bin/npx with a sudo.
ok, great!

Fix patch:
diff --git a/.dockerignore b/.dockerignore
index 4a961f826775..361f541075c0 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -188,9 +188,17 @@ cli/src/semgrep/bin/*
 
 # Ignore bench files
 **/perf/bench
-!**/perf/bench/dogfood
-!**/perf/bench/gitlab
-!**/perf/bench/dummy
+
+# The following instructions have no effect because 'perf/bench' is a parent
+# directory that is gitignored by the pattern above, causing git's scanner
+# to stop without inspecting the children paths.
+#
+# TODO: If we want to not gitignore these paths, they should
+# go in a separate folder i.e. not side-by-side with temporary folders.
+#
+# !perf/bench/dogfood
+# !perf/bench/gitlab
+# !perf/bench/dummy
 
 # Local actions testing
 **/act/
@@ -212,3 +220,461 @@ cli/src/semgrep/bin/*
 **/.direnv
 # Personal symlink to semgrep-proprietary used by 'make pro'
 semgrep-proprietary
+
+# From cli/src/semgrep/semgrep_interfaces/.gitignore
+cli/src/semgrep/semgrep_interfaces/__pycache__/
+
+cli/src/semgrep/semgrep_interfaces/**/*.swp
+
+# OCaml compilation products
+cli/src/semgrep/semgrep_interfaces/**/*.cmi
+cli/src/semgrep/semgrep_interfaces/**/*.cmo
+cli/src/semgrep/semgrep_interfaces/**/Language
+
+# From cli/src/semgrep/semgrep_interfaces/tests/.gitignore
+cli/src/semgrep/semgrep_interfaces/tests/**/__pycache__
+cli/src/semgrep/semgrep_interfaces/tests/**/*.ast.json
+
+# From cli/tests/default/e2e/targets/ignores_default/.gitignore
+cli/tests/default/e2e/targets/ignores_default/**/ignore.js
+# From cli/tests/default/e2e/targets/ignores/.gitignore
+cli/tests/default/e2e/targets/ignores/**/bad/
+# From languages/bash/tree-sitter/semgrep-bash/.gitignore
+# Dune build data
+languages/bash/tree-sitter/semgrep-bash/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/bash/tree-sitter/semgrep-bash/**/.merlin
+
+# Dune-generated files
+languages/bash/tree-sitter/semgrep-bash/**/*.install
+
+# From languages/cairo/tree-sitter/semgrep-cairo/.gitignore
+# Dune build data
+languages/cairo/tree-sitter/semgrep-cairo/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/cairo/tree-sitter/semgrep-cairo/**/.merlin
+
+# Dune-generated files
+languages/cairo/tree-sitter/semgrep-cairo/**/*.install
+
+# From languages/circom/tree-sitter/semgrep-circom/.gitignore
+# Dune build data
+languages/circom/tree-sitter/semgrep-circom/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/circom/tree-sitter/semgrep-circom/**/.merlin
+
+# Dune-generated files
+languages/circom/tree-sitter/semgrep-circom/**/*.install
+
+# From languages/cpp/tree-sitter/semgrep-cpp/.gitignore
+# Dune build data
+languages/cpp/tree-sitter/semgrep-cpp/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/cpp/tree-sitter/semgrep-cpp/**/.merlin
+
+# Dune-generated files
+languages/cpp/tree-sitter/semgrep-cpp/**/*.install
+
+# From languages/csharp/tree-sitter/semgrep-c-sharp/.gitignore
+# Dune build data
+languages/csharp/tree-sitter/semgrep-c-sharp/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/csharp/tree-sitter/semgrep-c-sharp/**/.merlin
+
+# Dune-generated files
+languages/csharp/tree-sitter/semgrep-c-sharp/**/*.install
+
+# From languages/dart/tree-sitter/semgrep-dart/.gitignore
+# Dune build data
+languages/dart/tree-sitter/semgrep-dart/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/dart/tree-sitter/semgrep-dart/**/.merlin
+
+# Dune-generated files
+languages/dart/tree-sitter/semgrep-dart/**/*.install
+
+# From languages/dockerfile/tree-sitter/semgrep-dockerfile/.gitignore
+# Dune build data
+languages/dockerfile/tree-sitter/semgrep-dockerfile/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/dockerfile/tree-sitter/semgrep-dockerfile/**/.merlin
+
+# Dune-generated files
+languages/dockerfile/tree-sitter/semgrep-dockerfile/**/*.install
+
+# From languages/go/tree-sitter/semgrep-go/.gitignore
+# Dune build data
+languages/go/tree-sitter/semgrep-go/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/go/tree-sitter/semgrep-go/**/.merlin
+
+# Dune-generated files
+languages/go/tree-sitter/semgrep-go/**/*.install
+
+# From languages/hack/tree-sitter/semgrep-hack/.gitignore
+# Dune build data
+languages/hack/tree-sitter/semgrep-hack/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/hack/tree-sitter/semgrep-hack/**/.merlin
+
+# Dune-generated files
+languages/hack/tree-sitter/semgrep-hack/**/*.install
+
+# From languages/html/tree-sitter/semgrep-html/.gitignore
+# Dune build data
+languages/html/tree-sitter/semgrep-html/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/html/tree-sitter/semgrep-html/**/.merlin
+
+# Dune-generated files
+languages/html/tree-sitter/semgrep-html/**/*.install
+
+# From languages/java/tree-sitter/semgrep-java/.gitignore
+# Dune build data
+languages/java/tree-sitter/semgrep-java/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/java/tree-sitter/semgrep-java/**/.merlin
+
+# Dune-generated files
+languages/java/tree-sitter/semgrep-java/**/*.install
+
+# From languages/jsonnet/tree-sitter/semgrep-jsonnet/.gitignore
+# Dune build data
+languages/jsonnet/tree-sitter/semgrep-jsonnet/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/jsonnet/tree-sitter/semgrep-jsonnet/**/.merlin
+
+# Dune-generated files
+languages/jsonnet/tree-sitter/semgrep-jsonnet/**/*.install
+
+# From languages/julia/tree-sitter/semgrep-julia/.gitignore
+# Dune build data
+languages/julia/tree-sitter/semgrep-julia/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/julia/tree-sitter/semgrep-julia/**/.merlin
+
+# Dune-generated files
+languages/julia/tree-sitter/semgrep-julia/**/*.install
+
+# From languages/kotlin/tree-sitter/semgrep-kotlin/.gitignore
+# Dune build data
+languages/kotlin/tree-sitter/semgrep-kotlin/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/kotlin/tree-sitter/semgrep-kotlin/**/.merlin
+
+# Dune-generated files
+languages/kotlin/tree-sitter/semgrep-kotlin/**/*.install
+
+# From languages/lisp/tree-sitter/semgrep-clojure/.gitignore
+# Dune build data
+languages/lisp/tree-sitter/semgrep-clojure/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/lisp/tree-sitter/semgrep-clojure/**/.merlin
+
+# Dune-generated files
+languages/lisp/tree-sitter/semgrep-clojure/**/*.install
+
+# From languages/lua/tree-sitter/semgrep-lua/.gitignore
+# Dune build data
+languages/lua/tree-sitter/semgrep-lua/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/lua/tree-sitter/semgrep-lua/**/.merlin
+
+# Dune-generated files
+languages/lua/tree-sitter/semgrep-lua/**/*.install
+
+# From languages/move_on_aptos/tree-sitter/semgrep-move-on-aptos/.gitignore
+# Dune build data
+languages/move_on_aptos/tree-sitter/semgrep-move-on-aptos/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/move_on_aptos/tree-sitter/semgrep-move-on-aptos/**/.merlin
+
+# Dune-generated files
+languages/move_on_aptos/tree-sitter/semgrep-move-on-aptos/**/*.install
+
+# From languages/move_on_sui/tree-sitter/semgrep-move-on-sui/.gitignore
+# Dune build data
+languages/move_on_sui/tree-sitter/semgrep-move-on-sui/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/move_on_sui/tree-sitter/semgrep-move-on-sui/**/.merlin
+
+# Dune-generated files
+languages/move_on_sui/tree-sitter/semgrep-move-on-sui/**/*.install
+
+# From languages/ocaml/tree-sitter/semgrep-ocaml/.gitignore
+# Dune build data
+languages/ocaml/tree-sitter/semgrep-ocaml/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/ocaml/tree-sitter/semgrep-ocaml/**/.merlin
+
+# Dune-generated files
+languages/ocaml/tree-sitter/semgrep-ocaml/**/*.install
+
+# From languages/php/tree-sitter/semgrep-php/.gitignore
+# Dune build data
+languages/php/tree-sitter/semgrep-php/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/php/tree-sitter/semgrep-php/**/.merlin
+
+# Dune-generated files
+languages/php/tree-sitter/semgrep-php/**/*.install
+
+# From languages/promql/tree-sitter/semgrep-promql/.gitignore
+# Dune build data
+languages/promql/tree-sitter/semgrep-promql/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/promql/tree-sitter/semgrep-promql/**/.merlin
+
+# Dune-generated files
+languages/promql/tree-sitter/semgrep-promql/**/*.install
+
+# From languages/protobuf/tree-sitter/semgrep-proto/.gitignore
+# Dune build data
+languages/protobuf/tree-sitter/semgrep-proto/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/protobuf/tree-sitter/semgrep-proto/**/.merlin
+
+# Dune-generated files
+languages/protobuf/tree-sitter/semgrep-proto/**/*.install
+
+# From languages/python/tree-sitter/semgrep-python/.gitignore
+# Dune build data
+languages/python/tree-sitter/semgrep-python/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/python/tree-sitter/semgrep-python/**/.merlin
+
+# Dune-generated files
+languages/python/tree-sitter/semgrep-python/**/*.install
+
+# From languages/ql/tree-sitter/semgrep-ql/.gitignore
+# Dune build data
+languages/ql/tree-sitter/semgrep-ql/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/ql/tree-sitter/semgrep-ql/**/.merlin
+
+# Dune-generated files
+languages/ql/tree-sitter/semgrep-ql/**/*.install
+
+# From languages/r/tree-sitter/semgrep-r/.gitignore
+# Dune build data
+languages/r/tree-sitter/semgrep-r/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/r/tree-sitter/semgrep-r/**/.merlin
+
+# Dune-generated files
+languages/r/tree-sitter/semgrep-r/**/*.install
+
+# From languages/regexp/.gitignore
+languages/regexp/Parser.ml
+languages/regexp/Parser.mli
+
+# From languages/ruby/tree-sitter/semgrep-ruby/.gitignore
+# Dune build data
+languages/ruby/tree-sitter/semgrep-ruby/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/ruby/tree-sitter/semgrep-ruby/**/.merlin
+
+# Dune-generated files
+languages/ruby/tree-sitter/semgrep-ruby/**/*.install
+
+# From languages/rust/tree-sitter/semgrep-rust/.gitignore
+# Dune build data
+languages/rust/tree-sitter/semgrep-rust/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/rust/tree-sitter/semgrep-rust/**/.merlin
+
+# Dune-generated files
+languages/rust/tree-sitter/semgrep-rust/**/*.install
+
+# From languages/solidity/tree-sitter/semgrep-solidity/.gitignore
+# Dune build data
+languages/solidity/tree-sitter/semgrep-solidity/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/solidity/tree-sitter/semgrep-solidity/**/.merlin
+
+# Dune-generated files
+languages/solidity/tree-sitter/semgrep-solidity/**/*.install
+
+# From languages/swift/tree-sitter/semgrep-swift/.gitignore
+# Dune build data
+languages/swift/tree-sitter/semgrep-swift/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/swift/tree-sitter/semgrep-swift/**/.merlin
+
+# Dune-generated files
+languages/swift/tree-sitter/semgrep-swift/**/*.install
+
+# From languages/terraform/tree-sitter/semgrep-hcl/.gitignore
+# Dune build data
+languages/terraform/tree-sitter/semgrep-hcl/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/terraform/tree-sitter/semgrep-hcl/**/.merlin
+
+# Dune-generated files
+languages/terraform/tree-sitter/semgrep-hcl/**/*.install
+
+# From languages/typescript/tree-sitter/semgrep-tsx/.gitignore
+# Dune build data
+languages/typescript/tree-sitter/semgrep-tsx/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/typescript/tree-sitter/semgrep-tsx/**/.merlin
+
+# Dune-generated files
+languages/typescript/tree-sitter/semgrep-tsx/**/*.install
+
+# From languages/typescript/tree-sitter/semgrep-typescript/.gitignore
+# Dune build data
+languages/typescript/tree-sitter/semgrep-typescript/**/_build
+
+# Merlin files for Vim and Emacs generated by dune < 2.8
+languages/typescript/tree-sitter/semgrep-typescript/**/.merlin
+
+# Dune-generated files
+languages/typescript/tree-sitter/semgrep-typescript/**/*.install
+
+# From libs/ocaml-tree-sitter-core/.gitignore
+libs/ocaml-tree-sitter-core/bin
+libs/ocaml-tree-sitter-core/**/tmp
+libs/ocaml-tree-sitter-core/downloads
+
+# Dune build data
+libs/ocaml-tree-sitter-core/**/_build
+
+# Merlin configuring file for Vim and Emacs
+libs/ocaml-tree-sitter-core/**/.merlin
+
+# Dune generated files
+libs/ocaml-tree-sitter-core/**/*.install
+
+# Local OPAM switch
+libs/ocaml-tree-sitter-core/_opam/
+
+# for codemap stuff of pad
+libs/ocaml-tree-sitter-core/skip_list.txt
+
+# Generated by ./configure
+libs/ocaml-tree-sitter-core/tree-sitter-config.mk
+libs/ocaml-tree-sitter-core/tree-sitter-config.sh
+
+# The default installation root of tree-sitter
+libs/ocaml-tree-sitter-core/tree-sitter
+libs/ocaml-tree-sitter-core/tree-sitter-*.*.*
+
+# The file containing the tree-sitter version being used
+libs/ocaml-tree-sitter-core/tree-sitter-version
+
+# From libs/ocaml-tree-sitter-core/test/.gitignore
+# Files generated by ocaml-tree-sitter
+libs/ocaml-tree-sitter-core/test/**/CST.ml
+libs/ocaml-tree-sitter-core/test/**/Parse.ml
+libs/ocaml-tree-sitter-core/test/**/CST_*.ml
+libs/ocaml-tree-sitter-core/test/**/Parse_*.ml
+
+# Various output files
+libs/ocaml-tree-sitter-core/test/*/test.out
+libs/ocaml-tree-sitter-core/test/*/stat.txt
+
+libs/ocaml-tree-sitter-core/test/**/node_modules
+libs/ocaml-tree-sitter-core/test/**/package.json
+libs/ocaml-tree-sitter-core/test/**/package-lock.json
+libs/ocaml-tree-sitter-core/test/**/tree-sitter-parser.js
+
+# Files generated by tree-sitter
+libs/ocaml-tree-sitter-core/test/*/src
+libs/ocaml-tree-sitter-core/test/*/src.orig
+libs/ocaml-tree-sitter-core/test/*/build
+libs/ocaml-tree-sitter-core/test/*/binding.gyp
+libs/ocaml-tree-sitter-core/test/*/index.js
+libs/ocaml-tree-sitter-core/test/*/tree-sitter-parser.js
+libs/ocaml-tree-sitter-core/test/*/package.json
+libs/ocaml-tree-sitter-core/test/*/Cargo.toml
+libs/ocaml-tree-sitter-core/test/*/bindings
+
+# Files generated by ocaml-tree-sitter
+libs/ocaml-tree-sitter-core/test/*/ocaml-src
+libs/ocaml-tree-sitter-core/test/*/parse-*
+libs/ocaml-tree-sitter-core/test/*/parse
+libs/ocaml-tree-sitter-core/test/*/grammar-rev.js.orig
+libs/ocaml-tree-sitter-core/test/*/grammar-rev.js
+
+# From libs/pcre2/.gitignore
+libs/pcre2/**/.*.swp
+libs/pcre2/**/.merlin
+libs/pcre2/**/*.install
+libs/pcre2/**/_build
+
+# From libs/spacegrep/.gitignore
+libs/spacegrep/**/_build
+libs/spacegrep/**/.merlin
+libs/spacegrep/tmp
+
+# From libs/testo/.gitignore
+libs/testo/_build
+
+# From libs/testo/docsrc/tutorial/.gitignore
+libs/testo/docsrc/tutorial/my_project
+
+# From libs/testo/tests/.gitignore
+# These files are created by the meta-test
+libs/testo/tests/custom-snapshots
+
+# From perf/perf-matching/input/.gitignore
+perf/perf-matching/input/**/l5000.cs
+
+# rules generated from .sgrep files
+perf/perf-matching/input/**/*.tmp.yaml
+
+# uncompressed from .gz
+perf/perf-matching/input/**/semgrep_targets.txt
+
+# From stats/autofix-printing-stats/.gitignore
+stats/autofix-printing-stats/**/tmp
+
+# From stats/parsing-stats/.gitignore
+stats/parsing-stats/**/tmp
+stats/parsing-stats/**/lang/**/stats.json
+stats/parsing-stats/**/test-parsing/**/stats.json
+
+# From stats/parsing-stats/test-parsing/.gitignore
+stats/parsing-stats/test-parsing/run-all
+stats/parsing-stats/test-parsing/run-lang
+
+# From tests/semgrep-rules/.gitignore
+tests/semgrep-rules/**/__pycache__/
+tests/semgrep-rules/**/.benchmarks/
+tests/semgrep-rules/**/.pytest_cache/
+tests/semgrep-rules/**/.DS_Store
+tests/semgrep-rules/**/.vscode/
+tests/semgrep-rules/**/.venv
+
diff --git a/.github/workflows/libs/semgrep.libsonnet b/.github/workflows/libs/semgrep.libsonnet
index ea16ffb2608f..a6dca64bb74f 100644
--- a/.github/workflows/libs/semgrep.libsonnet
+++ b/.github/workflows/libs/semgrep.libsonnet
@@ -265,10 +265,10 @@ local opam_switch = '4.14.0';
 // locally in the project folder (/home/runner/work/semgrep/semgrep/_opam)
 // coupling: default is above opam_switch
 local opam_setup = function(opam_switch="4.14.0") {
-      uses: 'ocaml/setup-ocaml@v2',
+      uses: 'ocaml/setup-ocaml@v3',
       with: {
         'ocaml-compiler': opam_switch,
-        'opam-depext': false,
+	'opam-pin': false,
       },
     };
 
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index 6845e29c77b9..8405c13ba2bc 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -2,7 +2,7 @@
 jobs:
   brew-build:
     name: Build Semgrep via Brew from HEAD
-    runs-on: macos-12
+    runs-on: macos-latest
     steps:
       - env:
           HOMEBREW_NO_INSTALL_FROM_API: 1
diff --git a/.github/workflows/release-homebrew.jsonnet b/.github/workflows/release-homebrew.jsonnet
index 9d61a980406a..b4ffda07e7c0 100644
--- a/.github/workflows/release-homebrew.jsonnet
+++ b/.github/workflows/release-homebrew.jsonnet
@@ -61,7 +61,7 @@ local unless_dry_run = {
 // brew bump-formula-pr below can update Pypi dependency hashes in semgrep.rb
 // This job assumes the presence of a workflow with a 'inputs.dry-mode'
 local homebrew_core_pr_job(version) = {
-  'runs-on': 'macos-12',
+  'runs-on': 'macos-latest',
   steps: [
     {
       run: 'brew update',
@@ -176,7 +176,7 @@ local env = {
 //    Homebrew formula if needed.
 local brew_build_job = {
   name: 'Build Semgrep via Brew from HEAD',
-  'runs-on': 'macos-12',
+  'runs-on': 'macos-latest',
   steps: [
     {
       run: 'brew update --debug --verbose',
diff --git a/.github/workflows/release-homebrew.yml b/.github/workflows/release-homebrew.yml
index 46a61d50ee4e..0436facc8271 100644
--- a/.github/workflows/release-homebrew.yml
+++ b/.github/workflows/release-homebrew.yml
@@ -2,7 +2,7 @@
 jobs:
   brew-build:
     name: Build Semgrep via Brew from HEAD
-    runs-on: macos-12
+    runs-on: macos-latest
     steps:
       - env:
           HOMEBREW_NO_INSTALL_FROM_API: 1
@@ -16,7 +16,7 @@ jobs:
         name: Check installed correctly
         run: brew test semgrep --HEAD
   homebrew-core-pr:
-    runs-on: macos-12
+    runs-on: macos-latest
     steps:
       - run: brew update
       - name: 'ugly: fix the python path for brew bump-formula-pr'
diff --git a/.gitignore b/.gitignore
index a2d8f5a709d1..28ad0eb8893e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -187,9 +187,17 @@ log_config.json
 
 # Ignore bench files
 perf/bench
-!perf/bench/dogfood
-!perf/bench/gitlab
-!perf/bench/dummy
+
+# The following instructions have no effect because 'perf/bench' is a parent
+# directory that is gitignored by the pattern above, causing git's scanner
+# to stop without inspecting the children paths.
+#
+# TODO: If we want to not gitignore these paths, they should
+# go in a separate folder i.e. not side-by-side with temporary folders.
+#
+# !perf/bench/dogfood
+# !perf/bench/gitlab
+# !perf/bench/dummy
 
 # Local actions testing
 act/
diff --git a/.ocamlformat b/.ocamlformat
index ae00b20a9302..88625b814c2e 100644
--- a/.ocamlformat
+++ b/.ocamlformat
@@ -1,5 +1,12 @@
 profile = conventional
 
+# Preserve begin/end or parentheses to group expressions. Discussed November
+# 2024 with mixed opinions. Some prefer the consistency of always using
+# parentheses. Others find it easier to visually parse large blocks when
+# delimited by begin/end. For now we'll relax the constraints and let people
+# experiment. We can easily move back to enforcing parens only if we want to.
+exp-grouping = preserve
+
 # 'all' is now deprecated in ocamlformat >= 0.20.0. I don't understand why.
 # See https://github.com/ocaml-ppx/ocamlformat/issues/1882
 break-cases = all
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 98143bdad495..a97b440b5df8 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -43,7 +43,9 @@ repos:
       - id: destroyed-symlinks
       - id: detect-private-key
       - id: end-of-file-fixer
-        exclude: "^\\.github/workflows/.+|semgrep\\.opam|.gitmodules$"
+        # Exclude generated files.
+        # coupling: apply the same exclusions in semgrep-proprietary
+        exclude: "^\\.github/workflows/.+|semgrep\\.opam|.gitmodules|.dockerignore$"
       - id: fix-byte-order-marker
       - id: fix-encoding-pragma
         args: [--remove]
@@ -146,14 +148,21 @@ repos:
   # ----------------------------------------------------------
   # Extra hooks using additional_dependencies
   # ----------------------------------------------------------
-  - repo: local
-    hooks:
-      - id: gitignore-to-dockerignore
-        name: Convert .gitignore to .dockerignore
-        language: node
-        entry: gitignore-to-dockerignore
-        files: ^.(git|docker)ignore$
-        additional_dependencies: ["gitignore-to-dockerignore@2.0.0"]
+  # TODO: CI seems to be using the wrong version of
+  # gitignore-to-dockerignore since the result lacks the
+  # patterns derived from git submodules. This causes errors in
+  # the Pro-to-OSS sync job because Pro also generates this file
+  # but does so correctly. Maybe it's a caching bug. Compare
+  # with the pre-commit config in semgrep-proprietary.
+  #
+  #- repo: local
+  #  hooks:
+  #    - id: gitignore-to-dockerignore
+  #      name: Convert .gitignore to .dockerignore
+  #      language: node
+  #      entry: gitignore-to-dockerignore
+  #      files: ^.(git|docker)ignore$
+  #      additional_dependencies: ["gitignore-to-dockerignore@3.0.0"]
 
   # ----------------------------------------------------------
   # Simple docker-based hooks
diff --git a/Makefile b/Makefile
index 99a3a74f14db..593b646b0d27 100644
--- a/Makefile
+++ b/Makefile
@@ -269,8 +269,17 @@ install-deps-for-semgrep-core:
 # of installing all the packages in one shot and detecting possible
 # version conflicts.
 # OPAMSOLVERTIMEOUT default is 60 but seems not enough
+#
+# TODO: We use `--assume-depexts` because as of 2024-11-13 brew
+# has moved off `pkg-config`. When you install `pkg-config` you
+# get `pkgconf` instead, which OCaml doesn't recognize as satisfying
+# the dependency though it contains the same elements. This has been
+# reported to brew via https://github.com/ocaml/opam-repository/issues/26876.
+# We can remove it if that issue is resolved.
+# Per the note above install-deps-ALPINE-for-semgrep-core, we may want
+# to keep it and add `--no-cache`
 install-opam-deps:
-	OPAMSOLVERTIMEOUT=1200 opam install -y --deps-only $(REQUIRED_DEPS)
+	OPAMSOLVERTIMEOUT=1200 opam install -y --assume-depexts --deps-only $(REQUIRED_DEPS)
 
 # This will fail if semgrep.opam isn't up-to-date (in git),
 # and dune isn't installed yet. You can always install dune with
diff --git a/changelog.d/code-7677.added b/changelog.d/code-7677.added
new file mode 100644
index 000000000000..6eb6676a4a7c
--- /dev/null
+++ b/changelog.d/code-7677.added
@@ -0,0 +1,2 @@
+Improved logic for interfile analysis in TypeScript projects using
+[project references](https://www.typescriptlang.org/docs/handbook/project-references.html).
diff --git a/changelog.d/code-7715.fixed b/changelog.d/code-7715.fixed
new file mode 100644
index 000000000000..cfa30a5308ba
--- /dev/null
+++ b/changelog.d/code-7715.fixed
@@ -0,0 +1,3 @@
+Improved error handling for some networking errors (e.g., premature server
+disconnection). In some cases this would previously cause a fatal error, but we
+should instead be able to recover in most instances (and now can).
diff --git a/changelog.d/gh-2554.added b/changelog.d/gh-2554.added
new file mode 100644
index 000000000000..81d7e49bd477
--- /dev/null
+++ b/changelog.d/gh-2554.added
@@ -0,0 +1,1 @@
+Added the ability to validate temporary AWS tokens in the secrets product.
diff --git a/changelog.d/saf-1721.fixed b/changelog.d/saf-1721.fixed
new file mode 100644
index 000000000000..1e42ee13cb5e
--- /dev/null
+++ b/changelog.d/saf-1721.fixed
@@ -0,0 +1,1 @@
+Swift: Ellipses and metavariable ellipses can now be used as function parameters in patterns.
diff --git a/changelog.d/saf-1722.fixed b/changelog.d/saf-1722.fixed
new file mode 100644
index 000000000000..1cbce896df32
--- /dev/null
+++ b/changelog.d/saf-1722.fixed
@@ -0,0 +1,1 @@
+Semgrep will no longer freeze when tracing is enabled and it has a low memory limit
diff --git a/changelog.d/saf-1724.fixed b/changelog.d/saf-1724.fixed
new file mode 100644
index 000000000000..904f26670a3a
--- /dev/null
+++ b/changelog.d/saf-1724.fixed
@@ -0,0 +1,1 @@
+osemgrep-pro: Autofix and nosemgrep now work properly
diff --git a/changelog.d/ssc-1942.added b/changelog.d/ssc-1942.added
new file mode 100644
index 000000000000..cf10f0d91829
--- /dev/null
+++ b/changelog.d/ssc-1942.added
@@ -0,0 +1,1 @@
+Poetry.lock & Pyproject.toml parsers can now handle multiline strings.
diff --git a/cli/Makefile b/cli/Makefile
index 95aa536aa9ad..d4e4ec35be60 100644
--- a/cli/Makefile
+++ b/cli/Makefile
@@ -187,7 +187,11 @@ osemgrep-qa:
 .PHONY:
 regenerate-tests:
 	pipenv run pytest -n auto \
-	  --run-only-snapshots --snapshot-update --allow-snapshot-deletion
+	  --run-only-snapshots --snapshot-update --allow-snapshot-deletion \
+	  -m 'not pysemfail'
+	PYTEST_USE_OSEMGREP=true pipenv run pytest -n auto \
+	  --run-only-snapshots --snapshot-update --allow-snapshot-deletion \
+	  -m 'pysemfail'
 
 .PHONY: setup
 setup:
diff --git a/cli/setup.py b/cli/setup.py
index 590323e0a4c0..fe9e36489ef5 100644
--- a/cli/setup.py
+++ b/cli/setup.py
@@ -44,16 +44,10 @@ def get_tag(self):
             # tags. Instead, package maintainers must explicitly identify if their package
             # supports glibc and/or libmusl. Semgrep-core is statically compiled,
             # so this isn't a concern for us.
-            #
-            # For linux_aarch64, we indicate that we support both platforms
-            #   (musllinux_1_0 == libmusl, manylinux2014 == glibc)
-            #
-            # For linux_x86_64, we use the catch-all "any" tag
-            #
             if plat == "linux_aarch64":
                 plat = "musllinux_1_0_aarch64.manylinux2014_aarch64"
             elif plat == "linux_x86_64":
-                plat = "any"
+                plat = "musllinux_1_0_x86_64.manylinux2014_x86_64"
             return python, abi, plat
 
     cmdclass = {WHEEL_CMD: BdistWheel}
diff --git a/cli/src/semdep/matchers/base.py b/cli/src/semdep/matchers/base.py
index 41601712dad2..719000925b5e 100644
--- a/cli/src/semdep/matchers/base.py
+++ b/cli/src/semdep/matchers/base.py
@@ -11,9 +11,9 @@
 from typing import Union
 
 import semgrep.semgrep_interfaces.semgrep_output_v1 as out
-from semgrep.subproject import LockfileDependencySource
+from semgrep.subproject import LockfileOnlyDependencySource
+from semgrep.subproject import ManifestLockfileDependencySource
 from semgrep.subproject import ManifestOnlyDependencySource
-from semgrep.subproject import PackageManagerType
 from semgrep.subproject import Subproject
 
 
@@ -58,7 +58,7 @@ class LockfileManifestMatcher(SubprojectMatcher):
     """
 
     manifest_kind: out.ManifestKind
-    package_manager_type: PackageManagerType
+    lockfile_kind: out.LockfileKind
 
     @abstractmethod
     def _is_manifest_match(self, path: Path) -> bool:
@@ -144,15 +144,22 @@ def make_subprojects(
                 root_dir = self._get_subproject_root(
                     (matching_manifest_path, lockfile_path)
                 )
-            dep_source = LockfileDependencySource(
-                manifest=out.Manifest(
-                    kind=self.manifest_kind, path=out.Fpath(str(matching_manifest_path))
+
+            lockfile = out.Lockfile(self.lockfile_kind, out.Fpath(str(lockfile_path)))
+            dep_source: Union[
+                ManifestLockfileDependencySource, LockfileOnlyDependencySource
+            ]
+            if matching_manifest_path:
+                dep_source = ManifestLockfileDependencySource(
+                    manifest=out.Manifest(
+                        kind=self.manifest_kind,
+                        path=out.Fpath(str(matching_manifest_path)),
+                    ),
+                    lockfile=lockfile,
                 )
-                if matching_manifest_path
-                else None,
-                package_manager_type=self.package_manager_type,
-                lockfile_path=lockfile_path,
-            )
+            else:
+                dep_source = LockfileOnlyDependencySource(lockfile)
+
             subprojects.append(
                 Subproject(root_dir=root_dir, dependency_source=dep_source)
             )
@@ -287,8 +294,9 @@ def make_subprojects(
         for manifest_path in manifests:
             root_dir = self._get_subproject_root(manifest_path)
             manifest_dep_source = ManifestOnlyDependencySource(
-                manifest_kind=self.manifest_kind,
-                manifest_path=manifest_path,
+                manifest=out.Manifest(
+                    kind=self.manifest_kind, path=out.Fpath(str(manifest_path))
+                ),
             )
             subprojects.append(
                 Subproject(root_dir=root_dir, dependency_source=manifest_dep_source)
diff --git a/cli/src/semdep/matchers/gradle.py b/cli/src/semdep/matchers/gradle.py
index 65d0f7d90834..72d8bde27ba8 100644
--- a/cli/src/semdep/matchers/gradle.py
+++ b/cli/src/semdep/matchers/gradle.py
@@ -8,9 +8,9 @@
 
 import semgrep.semgrep_interfaces.semgrep_output_v1 as out
 from semdep.matchers.base import SubprojectMatcher
-from semgrep.subproject import LockfileDependencySource
+from semgrep.subproject import LockfileOnlyDependencySource
+from semgrep.subproject import ManifestLockfileDependencySource
 from semgrep.subproject import ManifestOnlyDependencySource
-from semgrep.subproject import PackageManagerType
 from semgrep.subproject import Subproject
 
 
@@ -105,6 +105,11 @@ def make_subprojects(
                 lockfile_path, dep_source_files
             )
 
+            lockfile = out.Lockfile(
+                kind=out.LockfileKind(out.GradleLockfile_()),
+                path=out.Fpath(str(lockfile_path)),
+            )
+
             # track that these build and settings files are already accounted for
             if build_path is not None:
                 used_build_paths.add(build_path)
@@ -126,16 +131,22 @@ def make_subprojects(
                     path=out.Fpath(str(settings_path)),
                 )
 
-            subprojects.append(
-                Subproject(
-                    root_dir=project_root,
-                    dependency_source=LockfileDependencySource(
-                        package_manager_type=PackageManagerType.GRADLE,
-                        manifest=manifest,
-                        lockfile_path=lockfile_path,
-                    ),
+            if manifest is not None:
+                subprojects.append(
+                    Subproject(
+                        root_dir=project_root,
+                        dependency_source=ManifestLockfileDependencySource(
+                            manifest=manifest, lockfile=lockfile
+                        ),
+                    )
+                )
+            else:
+                subprojects.append(
+                    Subproject(
+                        root_dir=project_root,
+                        dependency_source=LockfileOnlyDependencySource(lockfile),
+                    )
                 )
-            )
 
         # next, handle settings.gradle files. Settings.gradle defines a multi-project gradle build,
         # so any time we see one, we know that it is at the root of a gradle project.
@@ -147,12 +158,19 @@ def make_subprojects(
 
             project_root = settings_path.parent
             possible_build_path = settings_path.parent / self.BUILD_FILENAME
-            manifest_path: Optional[Path] = None
             if possible_build_path in dep_source_files:
+                # if a build file is present, favor using that as the manifest file, but build
+                # files might be missing for multi-project builds
                 used_build_paths.add(possible_build_path)
-                manifest_path = possible_build_path
+                manifest = out.Manifest(
+                    kind=out.ManifestKind(out.BuildGradle()),
+                    path=out.Fpath(str(possible_build_path)),
+                )
             else:
-                manifest_path = settings_path
+                manifest = out.Manifest(
+                    kind=out.ManifestKind(out.SettingsGradle()),
+                    path=out.Fpath(str(settings_path)),
+                )
 
             root_dirs.add(project_root)
             used_settings_paths.add(settings_path)
@@ -160,10 +178,7 @@ def make_subprojects(
             subprojects.append(
                 Subproject(
                     root_dir=project_root,
-                    dependency_source=ManifestOnlyDependencySource(
-                        manifest_kind=out.ManifestKind(out.BuildGradle()),
-                        manifest_path=manifest_path,
-                    ),
+                    dependency_source=ManifestOnlyDependencySource(manifest),
                 )
             )
 
@@ -186,8 +201,10 @@ def make_subprojects(
                 Subproject(
                     root_dir=build_path.parent,
                     dependency_source=ManifestOnlyDependencySource(
-                        manifest_kind=out.ManifestKind(out.BuildGradle()),
-                        manifest_path=build_path,
+                        manifest=out.Manifest(
+                            kind=out.ManifestKind(out.BuildGradle()),
+                            path=out.Fpath(str(build_path)),
+                        )
                     ),
                 )
             )
diff --git a/cli/src/semdep/matchers/pip_requirements.py b/cli/src/semdep/matchers/pip_requirements.py
index d175ba0427bd..873a50957487 100644
--- a/cli/src/semdep/matchers/pip_requirements.py
+++ b/cli/src/semdep/matchers/pip_requirements.py
@@ -8,13 +8,14 @@
 from typing import Optional
 from typing import Set
 from typing import Tuple
+from typing import Union
 
 import semgrep.semgrep_interfaces.semgrep_output_v1 as out
 from semdep.matchers.base import SubprojectMatcher
 from semgrep.subproject import DependencySource
-from semgrep.subproject import LockfileDependencySource
+from semgrep.subproject import LockfileOnlyDependencySource
+from semgrep.subproject import ManifestLockfileDependencySource
 from semgrep.subproject import MultiLockfileDependencySource
-from semgrep.subproject import PackageManagerType
 from semgrep.subproject import Subproject
 
 
@@ -39,6 +40,9 @@ class PipRequirementsMatcher(SubprojectMatcher):
     manifest_kind: out.ManifestKind = field(
         default_factory=lambda: out.ManifestKind(value=out.RequirementsIn())
     )
+    lockfile_kind: out.LockfileKind = field(
+        default_factory=lambda: out.LockfileKind(value=out.PipRequirementsTxt())
+    )
 
     def _is_requirements_match(self, path: Path) -> bool:
         for ext in self.requirements_file_extensions:
@@ -150,7 +154,9 @@ def make_subprojects(
             root_dir,
             local_requirements_paths,
         ) in requirements_files_by_root_dir.items():
-            lockfile_sources: List[LockfileDependencySource] = []
+            lockfile_sources: List[
+                Union[LockfileOnlyDependencySource, ManifestLockfileDependencySource]
+            ] = []
             for req_path in sorted(
                 local_requirements_paths
             ):  # sorting so that there is a deterministic order in tests
@@ -165,14 +171,18 @@ def make_subprojects(
                     )
                 else:
                     manifest = None
-                lockfile_sources.append(
-                    LockfileDependencySource(
-                        manifest=manifest,
-                        package_manager_type=PackageManagerType.PIP,
-                        lockfile_path=req_path,
-                    )
+
+                lockfile = out.Lockfile(
+                    kind=self.lockfile_kind, path=out.Fpath(str(req_path))
                 )
 
+                if manifest is not None:
+                    lockfile_sources.append(
+                        ManifestLockfileDependencySource(manifest, lockfile)
+                    )
+                else:
+                    lockfile_sources.append(LockfileOnlyDependencySource(lockfile))
+
             # use the correct dependency source type depending on the number
             # of lockfiles
             dep_source: DependencySource
diff --git a/cli/src/semdep/parsers/poetry.py b/cli/src/semdep/parsers/poetry.py
index dee0eb97eb19..f2a6075eff85 100644
--- a/cli/src/semdep/parsers/poetry.py
+++ b/cli/src/semdep/parsers/poetry.py
@@ -77,8 +77,17 @@ class ValueLineWrapper:
 # foo
 plain_value = upto("\n")
 
+# Multi-line string handling for both single and double triple quotes
+multi_line_quoted_value = (
+    (string("'''") | string('"""'))  # Match both triple single and triple double quotes
+    >> any_char.until(
+        (string("'''") | string('"""')) << string("\n").optional()
+    ).result("")
+    << (string("'''\n") | string('"""\n'))
+)
+
 # A value in a key-value pair.
-value = list_value | object_value | quoted_value | plain_value
+value = multi_line_quoted_value | list_value | object_value | quoted_value | plain_value
 
 key = regex(r'("[^"]*"|[^\s=]+)\s*=\s*', flags=0, group=1).map(lambda x: x.strip('"'))
 
diff --git a/cli/src/semdep/subproject_matchers.py b/cli/src/semdep/subproject_matchers.py
index 012dab210442..5551e84ce682 100644
--- a/cli/src/semdep/subproject_matchers.py
+++ b/cli/src/semdep/subproject_matchers.py
@@ -8,7 +8,6 @@
 from semdep.matchers.base import SubprojectMatcher
 from semdep.matchers.gradle import GradleMatcher
 from semdep.matchers.pip_requirements import PipRequirementsMatcher
-from semgrep.subproject import PackageManagerType
 
 # NOTE: the order that these matchers are defined in matters. In find_subprojects, we
 # use each dependency source file for at most one matcher, running the matchers in the
@@ -25,47 +24,47 @@
     ExactLockfileManifestMatcher(
         lockfile_name="package-lock.json",
         manifest_name="package.json",
-        package_manager_type=PackageManagerType.NPM,
+        lockfile_kind=out.LockfileKind(out.NpmPackageLockJson()),
         manifest_kind=out.ManifestKind(out.PackageJson()),
     ),
     ExactLockfileManifestMatcher(
         lockfile_name="yarn.lock",
         manifest_name="package.json",
-        package_manager_type=PackageManagerType.YARN,
+        lockfile_kind=out.LockfileKind(out.YarnLock()),
         manifest_kind=out.ManifestKind(out.PackageJson()),
     ),
     ExactLockfileManifestMatcher(
         lockfile_name="pnpm-lock.yaml",
         manifest_name="package.json",
-        package_manager_type=PackageManagerType.PNPM,
+        lockfile_kind=out.LockfileKind(out.PnpmLock_()),
         manifest_kind=out.ManifestKind(out.PackageJson()),
     ),
     # Gem
     ExactLockfileManifestMatcher(
         lockfile_name="Gemfile.lock",
         manifest_name="Gemfile",
-        package_manager_type=PackageManagerType.RUBY_GEM,
+        lockfile_kind=out.LockfileKind(out.GemfileLock_()),
         manifest_kind=out.ManifestKind(out.Gemfile()),
     ),
     # Go modules
     ExactLockfileManifestMatcher(
         lockfile_name="go.mod",
         manifest_name="go.mod",
-        package_manager_type=PackageManagerType.GO_MOD,
+        lockfile_kind=out.LockfileKind(out.GoMod2()),
         manifest_kind=out.ManifestKind(out.GoMod_()),
     ),
     # Cargo
     ExactLockfileManifestMatcher(
         lockfile_name="Cargo.lock",
         manifest_name="Cargo.toml",
-        package_manager_type=PackageManagerType.CARGO,
+        lockfile_kind=out.LockfileKind(out.CargoLock()),
         manifest_kind=out.ManifestKind(out.CargoToml()),
     ),
     # Maven
     ExactLockfileManifestMatcher(
         lockfile_name="maven_dep_tree.txt",
         manifest_name="pom.xml",
-        package_manager_type=PackageManagerType.MAVEN,
+        lockfile_kind=out.LockfileKind(out.MavenDepTree()),
         manifest_kind=out.ManifestKind(out.PomXml()),
     ),
     ExactManifestOnlyMatcher(
@@ -77,49 +76,49 @@
     ExactLockfileManifestMatcher(
         lockfile_name="composer.lock",
         manifest_name="composer.json",
-        package_manager_type=PackageManagerType.COMPOSER,
+        lockfile_kind=out.LockfileKind(out.ComposerLock_()),
         manifest_kind=out.ManifestKind(out.ComposerJson()),
     ),
     # Nuget
     ExactLockfileManifestMatcher(
         lockfile_name="packages.lock.json",
         manifest_name="nuget.manifest.json",
-        package_manager_type=PackageManagerType.NUGET,
+        lockfile_kind=out.LockfileKind(out.NugetPackagesLockJson()),
         manifest_kind=out.ManifestKind(out.NugetManifestJson()),
     ),
     # Pub
     ExactLockfileManifestMatcher(
         lockfile_name="pubspec.lock",
         manifest_name="pubspec.yaml",
-        package_manager_type=PackageManagerType.DART_PUB,
+        lockfile_kind=out.LockfileKind(out.PubspecLock_()),
         manifest_kind=out.ManifestKind(out.PubspecYaml()),
     ),
     # Swift PM
     ExactLockfileManifestMatcher(
         lockfile_name="Package.resolved",
         manifest_name="Package.swift",
-        package_manager_type=PackageManagerType.SWIFT_PM,
+        lockfile_kind=out.LockfileKind(out.SwiftPackageResolved()),
         manifest_kind=out.ManifestKind(out.PackageSwift_()),
     ),
     # Hex
     ExactLockfileManifestMatcher(
         lockfile_name="mix.lock",
         manifest_name="mix.exs",
-        package_manager_type=PackageManagerType.ELIXIR_HEX,
+        lockfile_kind=out.LockfileKind(out.MixLock_()),
         manifest_kind=out.ManifestKind(out.MixExs()),
     ),
     # Pipenv
     ExactLockfileManifestMatcher(
         lockfile_name="Pipfile.lock",
         manifest_name="Pipfile",
-        package_manager_type=PackageManagerType.PIPENV,
+        lockfile_kind=out.LockfileKind(out.PipfileLock()),
         manifest_kind=out.ManifestKind(out.Pipfile_()),
     ),
     # Poetry
     ExactLockfileManifestMatcher(
         lockfile_name="poetry.lock",
         manifest_name="pyproject.toml",
-        package_manager_type=PackageManagerType.POETRY,
+        lockfile_kind=out.LockfileKind(out.PoetryLock_()),
         manifest_kind=out.ManifestKind(out.PyprojectToml_()),
     ),
 ]
diff --git a/cli/src/semgrep/app/scans.py b/cli/src/semgrep/app/scans.py
index 7dbbb3072a3f..1d735cba9d09 100644
--- a/cli/src/semgrep/app/scans.py
+++ b/cli/src/semgrep/app/scans.py
@@ -63,12 +63,12 @@ def __init__(
         """
         state = get_state()
         self.local_id = str(state.local_scan_id)
-        self.sms_scan_id = str(state.env.sms_scan_id)
         self.scan_metadata = out.ScanMetadata(
             cli_version=out.Version(__VERSION__),
             unique_id=out.Uuid(self.local_id),
             requested_products=[],
             dry_run=dry_run,
+            sms_scan_id=state.env.sms_scan_id,
         )
         self.scan_response: Optional[out.ScanResponse] = None
         self.dry_run = dry_run
@@ -243,8 +243,6 @@ def start_scan(
         returns ignored list
         """
         state = get_state()
-        if self.sms_scan_id:
-            logger.debug(f"SMS scan id: {self.sms_scan_id}")
         request = out.ScanRequest(
             meta=out.RawJson(
                 {
diff --git a/cli/src/semgrep/commands/ci.py b/cli/src/semgrep/commands/ci.py
index 4e1e4116d61f..a643f49430ec 100644
--- a/cli/src/semgrep/commands/ci.py
+++ b/cli/src/semgrep/commands/ci.py
@@ -319,11 +319,9 @@ def ci(
             )
             sys.exit(FATAL_EXIT_CODE)
 
-        if (partial_config and not partial_output) or (
-            not partial_config and partial_output
-        ):
+        if partial_config and not partial_output:
             logger.info(
-                "Both or none of --x-partial-config and --x-partial-output must be specified."
+                "When --x-partial-config is specified, --x-partial-output must also be specified."
             )
             sys.exit(FATAL_EXIT_CODE)
 
diff --git a/cli/src/semgrep/commands/scan.py b/cli/src/semgrep/commands/scan.py
index c23d70eda0d7..05fffe1255e9 100644
--- a/cli/src/semgrep/commands/scan.py
+++ b/cli/src/semgrep/commands/scan.py
@@ -377,8 +377,9 @@ def convert(
         "allow_untrusted_validators",
         is_flag=True,
     ),
+    # the slash below defines one flag to enable and another to disable; see https://click.palletsprojects.com/en/stable/options/#boolean-flags
     optgroup.option(
-        "--allow-dynamic-dependency-resolution",
+        "--allow-dynamic-dependency-resolution/--no-dynamic-dependency-resolution",
         "allow_dynamic_dependency_resolution",
         is_flag=True,
         default=False,
diff --git a/cli/src/semgrep/env.py b/cli/src/semgrep/env.py
index 3170e8238925..4636453bd509 100644
--- a/cli/src/semgrep/env.py
+++ b/cli/src/semgrep/env.py
@@ -68,7 +68,7 @@ class Env:
     )
     app_token: Optional[str] = field(default=EnvFactory("SEMGREP_APP_TOKEN"))
 
-    # Unique identifier for the scan initiated by SMS
+    # Unique identifier for the managed_scan in semgrep-app
     sms_scan_id: Optional[str] = field(default=EnvFactory("SEMGREP_MANAGED_SCAN_ID"))
 
     version_check_url: str = field(
diff --git a/cli/src/semgrep/resolve_subprojects.py b/cli/src/semgrep/resolve_subprojects.py
index a4b035c89c9c..40dbfa7018c9 100644
--- a/cli/src/semgrep/resolve_subprojects.py
+++ b/cli/src/semgrep/resolve_subprojects.py
@@ -40,10 +40,10 @@
 from semgrep.semgrep_interfaces.semgrep_output_v1 import FoundDependency
 from semgrep.semgrep_interfaces.semgrep_output_v1 import ScaParserName
 from semgrep.subproject import DependencySource
-from semgrep.subproject import LockfileDependencySource
+from semgrep.subproject import LockfileOnlyDependencySource
+from semgrep.subproject import ManifestLockfileDependencySource
 from semgrep.subproject import ManifestOnlyDependencySource
 from semgrep.subproject import MultiLockfileDependencySource
-from semgrep.subproject import PackageManagerType
 from semgrep.subproject import ResolutionMethod
 from semgrep.subproject import ResolvedSubproject
 from semgrep.subproject import Subproject
@@ -54,42 +54,44 @@
 logger = getLogger(__name__)
 
 # argument order is lockfile path, manifest path
-PARSERS_BY_PACKAGE_MANAGER_TYPE: Dict[PackageManagerType, SemgrepParser] = {
-    PackageManagerType.PIPENV: parse_pipfile,
-    PackageManagerType.PIP: parse_requirements,
-    PackageManagerType.POETRY: parse_poetry,
-    PackageManagerType.NPM: parse_package_lock,
-    PackageManagerType.YARN: parse_yarn,
-    PackageManagerType.PNPM: parse_pnpm,
-    PackageManagerType.RUBY_GEM: parse_gemfile,
-    PackageManagerType.COMPOSER: parse_composer_lock,
-    PackageManagerType.GO_MOD: parse_go_mod,
-    PackageManagerType.CARGO: to_parser(parse_cargo, ScaParserName(CargoParser())),
-    PackageManagerType.MAVEN: parse_pom_tree,
-    PackageManagerType.GRADLE: parse_gradle,
-    PackageManagerType.NUGET: parse_packages_lock_c_sharp,
-    PackageManagerType.DART_PUB: parse_pubspec_lock,
-    PackageManagerType.SWIFT_PM: parse_package_resolved,
-    PackageManagerType.ELIXIR_HEX: parse_mix,
+PARSERS_BY_LOCKFILE_KIND: Dict[out.LockfileKind, SemgrepParser] = {
+    out.LockfileKind(out.PipfileLock()): parse_pipfile,
+    out.LockfileKind(out.PipRequirementsTxt()): parse_requirements,
+    out.LockfileKind(out.PoetryLock_()): parse_poetry,
+    out.LockfileKind(out.NpmPackageLockJson()): parse_package_lock,
+    out.LockfileKind(out.YarnLock()): parse_yarn,
+    out.LockfileKind(out.PnpmLock_()): parse_pnpm,
+    out.LockfileKind(out.GemfileLock_()): parse_gemfile,
+    out.LockfileKind(out.ComposerLock_()): parse_composer_lock,
+    out.LockfileKind(out.GoMod2()): parse_go_mod,
+    out.LockfileKind(out.CargoLock()): to_parser(
+        parse_cargo, ScaParserName(CargoParser())
+    ),
+    out.LockfileKind(out.MavenDepTree()): parse_pom_tree,
+    out.LockfileKind(out.GradleLockfile_()): parse_gradle,
+    out.LockfileKind(out.NugetPackagesLockJson()): parse_packages_lock_c_sharp,
+    out.LockfileKind(out.PubspecLock_()): parse_pubspec_lock,
+    out.LockfileKind(out.SwiftPackageResolved()): parse_package_resolved,
+    out.LockfileKind(out.MixLock_()): parse_mix,
 }
 
-ECOSYSTEM_BY_PACKAGE_MANAGER_TYPE: Dict[PackageManagerType, Ecosystem] = {
-    PackageManagerType.PIPENV: Ecosystem(out.Pypi()),
-    PackageManagerType.PIP: Ecosystem(out.Pypi()),
-    PackageManagerType.POETRY: Ecosystem(out.Pypi()),
-    PackageManagerType.NPM: Ecosystem(out.Npm()),
-    PackageManagerType.YARN: Ecosystem(out.Npm()),
-    PackageManagerType.PNPM: Ecosystem(out.Npm()),
-    PackageManagerType.RUBY_GEM: Ecosystem(out.Gem()),
-    PackageManagerType.COMPOSER: Ecosystem(out.Composer()),
-    PackageManagerType.GO_MOD: Ecosystem(out.Gomod()),
-    PackageManagerType.CARGO: Ecosystem(out.Cargo()),
-    PackageManagerType.MAVEN: Ecosystem(out.Maven()),
-    PackageManagerType.GRADLE: Ecosystem(out.Maven()),
-    PackageManagerType.NUGET: Ecosystem(out.Nuget()),
-    PackageManagerType.DART_PUB: Ecosystem(out.Pub()),
-    PackageManagerType.SWIFT_PM: Ecosystem(out.SwiftPM()),
-    PackageManagerType.ELIXIR_HEX: Ecosystem(out.Mix()),
+ECOSYSTEM_BY_LOCKFILE_KIND: Dict[out.LockfileKind, Ecosystem] = {
+    out.LockfileKind(out.PipfileLock()): Ecosystem(out.Pypi()),
+    out.LockfileKind(out.PipRequirementsTxt()): Ecosystem(out.Pypi()),
+    out.LockfileKind(out.PoetryLock_()): Ecosystem(out.Pypi()),
+    out.LockfileKind(out.NpmPackageLockJson()): Ecosystem(out.Npm()),
+    out.LockfileKind(out.YarnLock()): Ecosystem(out.Npm()),
+    out.LockfileKind(out.PnpmLock_()): Ecosystem(out.Npm()),
+    out.LockfileKind(out.GemfileLock_()): Ecosystem(out.Gem()),
+    out.LockfileKind(out.ComposerLock_()): Ecosystem(out.Composer()),
+    out.LockfileKind(out.GoMod2()): Ecosystem(out.Gomod()),
+    out.LockfileKind(out.CargoLock()): Ecosystem(out.Cargo()),
+    out.LockfileKind(out.MavenDepTree()): Ecosystem(out.Maven()),
+    out.LockfileKind(out.GradleLockfile_()): Ecosystem(out.Maven()),
+    out.LockfileKind(out.NugetPackagesLockJson()): Ecosystem(out.Nuget()),
+    out.LockfileKind(out.PubspecLock_()): Ecosystem(out.Pub()),
+    out.LockfileKind(out.SwiftPackageResolved()): Ecosystem(out.SwiftPM()),
+    out.LockfileKind(out.MixLock_()): Ecosystem(out.Mix()),
 }
 
 DEPENDENCY_GRAPH_SUPPORTED_MANIFEST_KINDS = [
@@ -99,40 +101,54 @@
 
 
 def _resolve_dependencies_dynamically(
-    manifest_path: Path, manifest_kind: out.ManifestKind
+    dependency_source: Union[
+        ManifestOnlyDependencySource, ManifestLockfileDependencySource
+    ]
 ) -> Tuple[
-    Optional[Ecosystem],
-    List[FoundDependency],
+    Optional[Tuple[Ecosystem, List[FoundDependency]]],
     Sequence[Union[DependencyParserError, DependencyResolutionError]],
     List[Path],
 ]:
     """
     Handle the RPC call to resolve dependencies dynamically.
     """
-    manifest_arg = out.Manifest(kind=manifest_kind, path=out.Fpath(str(manifest_path)))
-    response = resolve_dependencies([manifest_arg])
+    response = resolve_dependencies([dependency_source.to_semgrep_output()])
     if response is None:
         # we failed to resolve somehow
         # TODO: handle this and generate an error
-        return (None, [], [], [])
+        return None, [], []
     if len(response) > 1:
         logger.warning(
             f"Too many responses from dynamic dependency resolution RPC. Expected 1, got {len(response)}"
         )
     result = response[0][1]
     if isinstance(result.value, out.ResolutionOk):
-        resolved_deps = result.value.value
+        resolved_deps, errors = result.value.value
         # right now we only support lockfileless for the maven ecosystem, so hardcode that here
         # TODO: move this ecosystem identification into the ocaml code when we redo the interface there
         ecosystem = Ecosystem(out.Maven())
-        return ecosystem, resolved_deps, [], [manifest_path]
+        wrapped_errors = [
+            DependencyResolutionError(
+                type_=e_type,
+                dependency_source_file=Path(dependency_source.manifest.path.value),
+            )
+            for e_type in errors
+        ]
+        return (
+            (ecosystem, resolved_deps),
+            wrapped_errors,
+            [Path(dependency_source.manifest.path.value)],
+        )
     else:
         # some error occured in resolution, track it
-        error = DependencyResolutionError(
-            type_=result.value.value,
-            dependency_source_file=manifest_path,
-        )
-        return (None, [], [error], [])
+        wrapped_errors = [
+            DependencyResolutionError(
+                type_=e_type,
+                dependency_source_file=Path(dependency_source.manifest.path.value),
+            )
+            for e_type in result.value.value
+        ]
+        return (None, wrapped_errors, [])
 
 
 def _resolve_dependency_source(
@@ -154,44 +170,40 @@ def _resolve_dependency_source(
     """
     ecosystem: Optional[Ecosystem] = None
 
-    if isinstance(dep_source, LockfileDependencySource):
-        parser = PARSERS_BY_PACKAGE_MANAGER_TYPE[dep_source.package_manager_type]
-        ecosystem = ECOSYSTEM_BY_PACKAGE_MANAGER_TYPE[dep_source.package_manager_type]
-        manifest_path = (
-            Path(dep_source.manifest.path.value)
-            if dep_source.manifest is not None
-            else None
-        )
-        manifest_kind = (
-            dep_source.manifest.kind if dep_source.manifest is not None else None
-        )
+    if isinstance(dep_source, LockfileOnlyDependencySource) or isinstance(
+        dep_source, ManifestLockfileDependencySource
+    ):
+        lockfile_path = Path(dep_source.lockfile.path.value)
+        parser = PARSERS_BY_LOCKFILE_KIND[dep_source.lockfile.kind]
+        ecosystem = ECOSYSTEM_BY_LOCKFILE_KIND[dep_source.lockfile.kind]
         if (
             enable_dynamic_resolution
             and prioritize_dependency_graph_generation
-            and manifest_path is not None
-            and manifest_kind is not None
-            and manifest_kind in DEPENDENCY_GRAPH_SUPPORTED_MANIFEST_KINDS
+            and isinstance(dep_source, ManifestLockfileDependencySource)
+            and dep_source.manifest.kind in DEPENDENCY_GRAPH_SUPPORTED_MANIFEST_KINDS
         ):
             (
-                ecosystem,
-                new_deps,
+                resolved_info,
                 new_errors,
                 new_targets,
-            ) = _resolve_dependencies_dynamically(manifest_path, manifest_kind)
-            if ecosystem is not None:
+            ) = _resolve_dependencies_dynamically(dep_source)
+            manifest_path = Path(dep_source.manifest.path.value)
+            if resolved_info is not None:
                 # TODO: Reimplement this once more robust error handling for lockfileless resolution is implemented
-                return ecosystem, new_deps, new_errors, new_targets
+                new_ecosystem, new_deps = resolved_info
+                return new_ecosystem, new_deps, new_errors, new_targets
             else:
                 # dynamic resolution failed, fall back to lockfile parsing
-                resolved_deps, parse_errors = parser(
-                    dep_source.lockfile_path, manifest_path
-                )
-            return ecosystem, resolved_deps, parse_errors, [dep_source.lockfile_path]
+                resolved_deps, parse_errors = parser(lockfile_path, manifest_path)
+            return ecosystem, resolved_deps, parse_errors, [lockfile_path]
         else:
             resolved_deps, parse_errors = parser(
-                dep_source.lockfile_path, manifest_path
+                lockfile_path,
+                Path(dep_source.manifest.path.value)
+                if isinstance(dep_source, ManifestLockfileDependencySource)
+                else None,
             )
-            return ecosystem, resolved_deps, parse_errors, [dep_source.lockfile_path]
+            return ecosystem, resolved_deps, parse_errors, [lockfile_path]
     elif isinstance(dep_source, MultiLockfileDependencySource):
         all_resolved_deps: List[FoundDependency] = []
         all_parse_errors: List[
@@ -212,9 +224,13 @@ def _resolve_dependency_source(
         isinstance(dep_source, ManifestOnlyDependencySource)
         and enable_dynamic_resolution
     ):
-        return _resolve_dependencies_dynamically(
-            dep_source.manifest_path, dep_source.manifest_kind
+        resolved_info, new_errors, new_targets = _resolve_dependencies_dynamically(
+            dep_source
         )
+        if resolved_info is None:
+            return None, [], new_errors, new_targets
+        new_ecosystem, new_deps = resolved_info
+        return new_ecosystem, new_deps, new_errors, new_targets
     else:
         # dependency source type is not supported, do nothing
         return (None, [], [], [])
diff --git a/cli/src/semgrep/rpc_call.py b/cli/src/semgrep/rpc_call.py
index 14d1cfc38220..1382779b9b36 100644
--- a/cli/src/semgrep/rpc_call.py
+++ b/cli/src/semgrep/rpc_call.py
@@ -62,8 +62,8 @@ def validate(fp: out.Fpath) -> bool:
 
 
 def resolve_dependencies(
-    args: List[out.Manifest],
-) -> Optional[List[Tuple[out.Manifest, out.ResolutionResult]]]:
+    args: List[out.DependencySource],
+) -> Optional[List[Tuple[out.DependencySource, out.ResolutionResult]]]:
     call = out.FunctionCall(out.CallResolveDependencies(args))
     ret: Optional[out.RetResolveDependencies] = rpc_call(
         call, out.RetResolveDependencies
diff --git a/cli/src/semgrep/rule.py b/cli/src/semgrep/rule.py
index 5533cac598d9..2ae59d2d1337 100644
--- a/cli/src/semgrep/rule.py
+++ b/cli/src/semgrep/rule.py
@@ -314,9 +314,12 @@ def get_subrules(raw: Union[AnyStr, Dict, List]) -> str:
             for k in sorted(RuleValidation.PATTERN_KEYS):
                 next_raw = self.raw.get(k)
                 if next_raw is not None:
+                    # print(k)
+                    # print(next_raw)
                     patterns_to_add.append(get_subrules(next_raw))
                     if k == "join" and "on" in next_raw:
                         patterns_to_add += get_subrules(next_raw["on"])
+            # print(patterns_to_add)
             res = " ".join(sorted(patterns_to_add))
             if not res:
                 raise ValueError(f"This rule contains no hashable patterns: {self.id}")
diff --git a/cli/src/semgrep/rule_match.py b/cli/src/semgrep/rule_match.py
index 8973606036ac..f2285d651af6 100644
--- a/cli/src/semgrep/rule_match.py
+++ b/cli/src/semgrep/rule_match.py
@@ -30,7 +30,9 @@
 from semgrep.semgrep_interfaces.semgrep_output_v1 import Transitivity
 from semgrep.util import get_lines_from_file
 from semgrep.util import get_lines_from_git_blob
+from semgrep.verbose_logging import getLogger
 
+logger = getLogger(__name__)
 
 CliUniqueKey = Tuple[str, str, int, int, str, Optional[str]]
 
@@ -340,7 +342,9 @@ def get_match_based_key(self) -> Tuple[str, Path, str]:
     def get_match_based_id(self) -> str:
         match_id = self.get_match_based_key()
         match_id_str = str(match_id)
-        return f"{hashlib.blake2b(str.encode(match_id_str)).hexdigest()}_{str(self.match_based_index)}"
+        code = f"{hashlib.blake2b(str.encode(match_id_str)).hexdigest()}_{str(self.match_based_index)}"
+        logger.debug(f"match_key = {match_id_str} match_id = {code}")
+        return code
 
     @code_hash.default
     def get_code_hash(self) -> str:
diff --git a/cli/src/semgrep/semgrep_interfaces b/cli/src/semgrep/semgrep_interfaces
index 64dd682a52ed..1c6af386e5e3 160000
--- a/cli/src/semgrep/semgrep_interfaces
+++ b/cli/src/semgrep/semgrep_interfaces
@@ -1,1 +1,1 @@
-Subproject commit 64dd682a52edb70b47ad52687bc1c3e0bc4e8c3b
+Subproject commit 1c6af386e5e3497c8a384ed9a9fcf2814c76c892
diff --git a/cli/src/semgrep/subproject.py b/cli/src/semgrep/subproject.py
index 2f2be72e9146..f0d62f9bc121 100644
--- a/cli/src/semgrep/subproject.py
+++ b/cli/src/semgrep/subproject.py
@@ -21,25 +21,6 @@
 from semgrep.semgrep_interfaces.semgrep_output_v1 import FoundDependency
 
 
-class PackageManagerType(Enum):
-    PIP = auto()
-    POETRY = auto()
-    PIPENV = auto()
-    NPM = auto()
-    YARN = auto()
-    PNPM = auto()
-    RUBY_GEM = auto()
-    GO_MOD = auto()
-    CARGO = auto()
-    MAVEN = auto()
-    GRADLE = auto()
-    COMPOSER = auto()
-    NUGET = auto()
-    DART_PUB = auto()
-    SWIFT_PM = auto()
-    ELIXIR_HEX = auto()
-
-
 class ResolutionMethod(Enum):
     LOCKFILE_PARSING = auto()
 
@@ -52,37 +33,50 @@ def get_display_paths(self) -> List[Path]:
 
 @dataclass(frozen=True)
 class ManifestOnlyDependencySource(DependencySource):
-    manifest_path: Path
-    # we don't include package manager type for the manifest-only source,
-    # because it's possible that we don't know (e.g. package.json, pyproject.toml
-    # are both used for multiple package managers.)
-    manifest_kind: out.ManifestKind
+    manifest: out.Manifest
 
     def get_display_paths(self) -> List[Path]:
-        return [self.manifest_path]
+        return [Path(self.manifest.path.value)]
+
+    def to_semgrep_output(self) -> out.DependencySource:
+        return out.DependencySource(out.ManifestOnlyDependencySource(self.manifest))
 
 
 @dataclass(frozen=True)
-class PackageManagerDependencySource(DependencySource):
-    package_manager_type: PackageManagerType
+class LockfileOnlyDependencySource(DependencySource):
+    lockfile: out.Lockfile
+
+    def get_display_paths(self) -> List[Path]:
+        return [Path(self.lockfile.path.value)]
+
+    def to_semgrep_output(self) -> out.DependencySource:
+        return out.DependencySource(out.LockfileOnlyDependencySource(self.lockfile))
 
 
 @dataclass(frozen=True)
-class LockfileDependencySource(PackageManagerDependencySource):
-    manifest: Optional[out.Manifest]
-    lockfile_path: Path
+class ManifestLockfileDependencySource(DependencySource):
+    manifest: out.Manifest
+    lockfile: out.Lockfile
 
     def get_display_paths(self) -> List[Path]:
-        return [self.lockfile_path]
+        return [Path(self.lockfile.path.value)]
+
+    def to_semgrep_output(self) -> out.DependencySource:
+        return out.DependencySource(
+            out.ManifestLockfileDependencySource((self.manifest, self.lockfile))
+        )
 
 
 @dataclass(frozen=True)
 class MultiLockfileDependencySource(DependencySource):
     # note: we use a tuple instead of a list here to allow hashing the whole type
-    sources: Tuple[LockfileDependencySource, ...]
+    sources: Tuple[
+        Union[LockfileOnlyDependencySource, ManifestLockfileDependencySource], ...
+    ]
 
     def get_display_paths(self) -> List[Path]:
-        return [source.lockfile_path for source in self.sources]
+        # aggregate all display paths for each of the child sources
+        return [path for source in self.sources for path in source.get_display_paths()]
 
 
 @dataclass(frozen=True)
@@ -98,7 +92,7 @@ class ResolvedDependencies:
     def from_resolved_interfaces(
         cls, resolved: out.ResolutionOk
     ) -> "ResolvedDependencies":
-        return cls.from_found_dependencies(resolved.value)
+        return cls.from_found_dependencies(resolved.value[0])
 
     @classmethod
     def from_found_dependencies(
diff --git a/dune-project b/dune-project
index 1230956e5b03..9a66c59788f3 100644
--- a/dune-project
+++ b/dune-project
@@ -520,9 +520,7 @@ For more information see https://semgrep.dev
     ; web stuff
     uri
     (uuidm (>= 0.9.9))
-    ; cohttp >= 6.0.0 requires opam 2.1.0 which used to not available in Windows
-    ; TODO: now that opam 2.2.0 is out and support windows, upgrade to 6.0.0
-    (cohttp (= 5.3.0))
+    (cohttp (= 6.0.0~beta2))
     cohttp-lwt-unix
     cohttp-lwt-jsoo
     tls-lwt ; needed for TLS support in the cohttp HTTP client (only TLS 1.3 seems to work)
diff --git a/flake.lock b/flake.lock
index c83d08ba1422..df29edb52c80 100644
--- a/flake.lock
+++ b/flake.lock
@@ -52,11 +52,11 @@
     },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1727634051,
-        "narHash": "sha256-S5kVU7U82LfpEukbn/ihcyNt2+EvG7Z5unsKW9H/yFA=",
+        "lastModified": 1730785428,
+        "narHash": "sha256-Zwl8YgTVJTEum+L+0zVAWvXAGbWAuXHax3KzuejaDyo=",
         "owner": "nixos",
         "repo": "nixpkgs",
-        "rev": "06cf0e1da4208d3766d898b7fdab6513366d45b9",
+        "rev": "4aa36568d413aca0ea84a1684d2d46f55dbabad7",
         "type": "github"
       },
       "original": {
@@ -129,11 +129,11 @@
     "opam-repository_2": {
       "flake": false,
       "locked": {
-        "lastModified": 1727704097,
-        "narHash": "sha256-fBOT/Dl+w6K+7UMkwLeYIcsNuzCIo+q6/X/S1TaHzR8=",
+        "lastModified": 1731009864,
+        "narHash": "sha256-fLJf04bNaP6WNzaWjHNKRIdCXsde8QqryuLzs0YhdlA=",
         "owner": "ocaml",
         "repo": "opam-repository",
-        "rev": "1b4da5019e5ea60af76c94aacc672a7e9659a832",
+        "rev": "d8b8ceaa570cc7f665bee86876819d0144909af7",
         "type": "github"
       },
       "original": {
diff --git a/flake.nix b/flake.nix
index c244fb44dfde..2f8b2615be81 100644
--- a/flake.nix
+++ b/flake.nix
@@ -270,8 +270,6 @@
           ## You can force versions of certain packages here
           # force the ocaml compiler to be 4.14.2 and from opam
           ocaml-base-compiler = "4.14.2";
-          # don't use bleeding edge cohttp
-          cohttp-lwt = "5.3.0";
           #TODO: needed for semgrep pro, should be in ../flake.nix instead
           #coupling: if you add one thing here, need to update also the
           # buildInputs overlay below
diff --git a/interfaces/Input_to_core.atd b/interfaces/Input_to_core.atd
index 18c47a23fecf..4a08b2ff316b 100644
--- a/interfaces/Input_to_core.atd
+++ b/interfaces/Input_to_core.atd
@@ -40,13 +40,10 @@ type manifest_target <ocaml attr="deriving show"> = {
   manifest_kind : manifest_kind;
 }
 
-
 (* A lockfile to be matched against dependency patterns *)
-(* Optionally associated to a [manifest_target], which specifies which dependencies are direct *)
 type lockfile_target <ocaml attr="deriving show"> = {
   path : string;
   lockfile_kind : lockfile_kind;
-  manifest_target : manifest_target option;
 }
 
 
diff --git a/languages/java/generic/java_to_generic.ml b/languages/java/generic/java_to_generic.ml
index 5fa717c62cb9..37fc6e300aa7 100644
--- a/languages/java/generic/java_to_generic.ml
+++ b/languages/java/generic/java_to_generic.ml
@@ -158,7 +158,7 @@ let rec modifier (x, tok) =
   | Volatile -> G.attr G.Volatile tok
   | Synchronized -> G.unhandled_keywordattr (s, tok)
   | Native -> G.unhandled_keywordattr (s, tok)
-  | DefaultModifier -> G.unhandled_keywordattr (s, tok)
+  | DefaultModifier -> G.attr G.DefaultImpl tok
   | Sealed -> G.attr G.SealedClass tok
   | NonSealed -> G.unhandled_keywordattr (s, tok)
   | Annotation v1 -> annotation v1
diff --git a/languages/swift/generic/Parse_swift_tree_sitter.ml b/languages/swift/generic/Parse_swift_tree_sitter.ml
index 0f638a7fb540..c091def53723 100644
--- a/languages/swift/generic/Parse_swift_tree_sitter.ml
+++ b/languages/swift/generic/Parse_swift_tree_sitter.ml
@@ -2329,26 +2329,32 @@ and map_universally_allowed_pattern (env : env)
       in
       pat_init |> add_pat_args (G.Id (id, id_info)) |> add_pat_type
 
-and map_parameter (env : env) ((v1, v2, v3, v4, v5, v6) : CST.parameter)
-    ?(attrs = []) default =
-  let _v1TODO =
-    (* If present, this is the externally-visible label for this parameter. In
-     * this context, the local name will be more relevant, so since we can
-     * currently only represent one in the AST, let's omit this one. *)
-    (* TODO include the externally-visible param label in the AST? *)
-    Option.map (map_simple_identifier env) v1
-  in
-  let v2 = map_simple_identifier env v2 in
-  let _tcolon = (* ":" *) token env v3 in
-  let attrs = map_parameter_modifiers_opt env v4 @ attrs in
-  let v5 = map_possibly_implicitly_unwrapped_type env v5 in
-  let ptype = { v5 with G.t_attrs = attrs @ v5.G.t_attrs } in
-  let param = G.param_of_id ?pdefault:default ~ptype v2 in
-  match v6 with
-  | Some tok ->
-      let dots = (* three_dot_operator_custom *) token env tok in
-      G.ParamRest (dots, param)
-  | None -> G.Param param
+and map_parameter (env : env) (x : CST.parameter) ?(attrs = []) default =
+  match x with
+  | `Opt_simple_id_simple_id_COLON_opt_param_modifs_poss_impl_unwr_type_opt_three_dot_op
+      (v1, v2, v3, v4, v5, v6) -> (
+      let _v1TODO =
+        (* If present, this is the externally-visible label for this parameter. In
+         * this context, the local name will be more relevant, so since we can
+         * currently only represent one in the AST, let's omit this one. *)
+        (* TODO include the externally-visible param label in the AST? *)
+        Option.map (map_simple_identifier env) v1
+      in
+      let v2 = map_simple_identifier env v2 in
+      let _tcolon = (* ":" *) token env v3 in
+      let attrs = map_parameter_modifiers_opt env v4 @ attrs in
+      let v5 = map_possibly_implicitly_unwrapped_type env v5 in
+      let ptype = { v5 with G.t_attrs = attrs @ v5.G.t_attrs } in
+      let param = G.param_of_id ?pdefault:default ~ptype v2 in
+      match v6 with
+      | Some tok ->
+          let dots = (* three_dot_operator_custom *) token env tok in
+          G.ParamRest (dots, param)
+      | None -> G.Param param)
+  | `Semg_ellips tok -> G.ParamEllipsis (token env tok)
+  | `Semg_ellips_meta tok ->
+      let id = str env tok in
+      G.Param (G.param_of_id id)
 
 and map_possibly_implicitly_unwrapped_type (env : env)
     ((v1, _v2TODO) : CST.possibly_implicitly_unwrapped_type) =
diff --git a/languages/swift/tree-sitter/semgrep-swift b/languages/swift/tree-sitter/semgrep-swift
index dbeb36594a26..ad96c4eae153 160000
--- a/languages/swift/tree-sitter/semgrep-swift
+++ b/languages/swift/tree-sitter/semgrep-swift
@@ -1,1 +1,1 @@
-Subproject commit dbeb36594a268cfcd14cd9867568c39bd2412570
+Subproject commit ad96c4eae153b70ec5bcab5ccd0cba0c91a33bff
diff --git a/languages/typescript/tree-sitter/Parse_typescript_tree_sitter.ml b/languages/typescript/tree-sitter/Parse_typescript_tree_sitter.ml
index 8f5b30a606a6..f6bb23da517a 100644
--- a/languages/typescript/tree-sitter/Parse_typescript_tree_sitter.ml
+++ b/languages/typescript/tree-sitter/Parse_typescript_tree_sitter.ml
@@ -1057,8 +1057,8 @@ and class_body (env : env) ((v1, v2, v3) : CST.class_body) :
               match v1 with
               | `Abst_meth_sign x ->
                   (* TODO: types *)
-                  let _v = abstract_method_signature env x in
-                  None
+                  let v = abstract_method_signature env x in
+                  Some (Field v)
               | `Index_sign x ->
                   let _t = index_signature env x in
                   None
diff --git a/libs/commons/ATD_string_wrap.ml b/libs/commons/ATD_string_wrap.ml
index cfe85a9f8f35..ffa4218d0aa7 100644
--- a/libs/commons/ATD_string_wrap.ml
+++ b/libs/commons/ATD_string_wrap.ml
@@ -6,10 +6,12 @@
 open Common
 
 module Fpath = struct
-  type t = Fpath.t [@@deriving show]
+  type t = Fpath.t [@@deriving show, eq]
 
   let unwrap = Fpath.to_string
   let wrap = Fpath.v
+  let to_yojson = Fpath_.to_yojson
+  let of_yojson = Fpath_.of_yojson
 end
 
 module Uri = struct
diff --git a/libs/commons/Common.ml b/libs/commons/Common.ml
index 790e442b637b..e08773993684 100644
--- a/libs/commons/Common.ml
+++ b/libs/commons/Common.ml
@@ -164,6 +164,7 @@ exception Todo
 exception Impossible
 exception Multi_found (* to be consistent with Not_found *)
 exception UnixExit of int
+exception ErrorOnFile of string * Fpath.t
 
 let exn_to_s exn = Printexc.to_string exn
 
diff --git a/libs/commons/Common.mli b/libs/commons/Common.mli
index 286ce8039377..61bb49c8c115 100644
--- a/libs/commons/Common.mli
+++ b/libs/commons/Common.mli
@@ -63,7 +63,7 @@ val on : ('b -> 'b -> 'c) -> ('a -> 'b) -> 'a -> 'a -> 'c
 (* Exceptions *)
 (*****************************************************************************)
 (* see also Exception.ml functions as well as Time_limit.Timeout
- * in the process_limits library.
+ * and Memory_limit.ExceededMemoryLimit in the process_limits library.
  *)
 
 exception Todo
@@ -74,6 +74,12 @@ exception Impossible
 (* similar to Not_found but to use when something returns too many findings *)
 exception Multi_found
 
+(* it's usually far easier to diagnose an error when you know on which
+ * file it occured. An [Invalid_argument("index out of bounds")] is not
+ * as good as [ErrorOnFile("out of bounds in lines_of_file()", 'foo.c')]
+ *)
+exception ErrorOnFile of string (* error message *) * Fpath.t
+
 (* If the user use some [exit 0] in his code, then no one can intercept this
  * exit and do something before exiting. There is exn handler for exit 0
  * so better never use exit 0 but instead use an exception and just at
diff --git a/libs/commons/Logs_.mli b/libs/commons/Logs_.mli
index 4ad47a2296b3..ef515dc4d8aa 100644
--- a/libs/commons/Logs_.mli
+++ b/libs/commons/Logs_.mli
@@ -123,7 +123,9 @@ val setup :
      ...
      Logs.info (fun m -> m ~tags "Hello.");
 *)
+val create_tag : string -> string Logs.Tag.def
 val create_tags : string list -> Logs.Tag.set
+val create_tag_set : string Logs.Tag.def list -> Logs.Tag.set
 
 (*
    Log a string directly.
diff --git a/libs/commons/UFile.ml b/libs/commons/UFile.ml
index 263f98a63e62..c523b8acd782 100644
--- a/libs/commons/UFile.ml
+++ b/libs/commons/UFile.ml
@@ -1,6 +1,6 @@
 (* Martin Jambon
  *
- * Copyright (C) 2023 Semgrep Inc.
+ * Copyright (C) 2023-2024 Semgrep Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License
@@ -273,10 +273,19 @@ let find_first_match_with_whole_line path ?split term =
  * each time the same file for each match.
  * Note that the returned lines do not contain \n.
  *)
-let lines_of_file (start_line, end_line) file : string list =
+let lines_of_file_exn (start_line, end_line) file : string list =
   let arr = cat_array file in
   let lines = List_.enum start_line end_line in
   match arr with
   (* This is the case of the empty file. *)
   | [| "" |] -> []
-  | _ -> lines |> List_.map (fun i -> arr.(i))
+  | _ ->
+      lines
+      |> List_.map (fun i ->
+             try arr.(i) with
+             | Invalid_argument s ->
+                 let exn =
+                   Common.ErrorOnFile
+                     (spf "lines_of_file(): %s on index %d" s i, file)
+                 in
+                 Exception.catch_and_reraise exn)
diff --git a/libs/commons/UFile.mli b/libs/commons/UFile.mli
index e48f89ea875b..002b095b6e25 100644
--- a/libs/commons/UFile.mli
+++ b/libs/commons/UFile.mli
@@ -35,14 +35,15 @@ val cat : Fpath.t -> string list
 val cat_array : Fpath.t -> string array
 val write_file : file:Fpath.t -> string -> unit
 
-(* [lines_of_file (start_line, end_line) file] returns
+(* [lines_of_file_exn (start_line, end_line) file] returns
  * the list of lines from start_line to end_line included.
  *
  * Note that the returned lines do not contain \n.
  *
  * This function is slow, you should not use it!
+ * This may raise Common.ErrorOnFile for out of bounds access.
  *)
-val lines_of_file : int * int -> Fpath.t -> string list
+val lines_of_file_exn : int * int -> Fpath.t -> string list
 
 (* Read the contents of file.
 
diff --git a/libs/networking/http_helpers/http_helpers.ml b/libs/networking/http_helpers/http_helpers.ml
index 51da42d1ad18..e0b4aede2473 100644
--- a/libs/networking/http_helpers/http_helpers.ml
+++ b/libs/networking/http_helpers/http_helpers.ml
@@ -105,11 +105,10 @@ let get_proxy uri =
       Some (Uri.of_string proxy_uri)
   | None -> None
 
-(* Why this wrapper function? Client.call takes a uri, and some other things *)
-(* and then makes a Request.t with said uri and sends that request to the same uri *)
-(* By using Client.callv, we can make a request that has some uri, but then really *)
-(* send it to a different uri. This is used for proxying requests *)
-
+(* Why this wrapper function? Client.call takes a uri, and some other things
+   and then makes a Request.t with said uri and sends that request to the same
+   uri By using Client.callv, we can make a request that has some uri, but then
+   really send it to a different uri. This is used for proxying requests *)
 let default_resp_handler (response, body) =
   let%lwt body_str = Cohttp_lwt.Body.to_string body in
   Lwt.return (response, body_str)
@@ -173,7 +172,6 @@ let call_client ?(body = Cohttp_lwt.Body.empty) ?(headers = [])
        node. Currently, AWS does not support specifying a minimum TLS version
        of v1.3, and we will need to figure out a better solution for ensuring
        reliable metrics delivery. *)
-    (* try%lwt here since callv will raise a Lwt error not a normal one *)
     try%lwt
       let%lwt responses_stream = Client.callv url stream_req in
       Lwt.return_ok responses_stream
@@ -187,14 +185,15 @@ let call_client ?(body = Cohttp_lwt.Body.empty) ?(headers = [])
   in
   let handle_responses responses_stream =
     (* Assume that we only get one response back *)
-    (* MUST USE GET HERE *)
-    let%lwt repsonses =
+    match%lwt
       responses_stream |> Lwt_stream.map_s resp_handler |> Lwt_stream.to_list
-    in
-    match repsonses with
+    with
     | [ (response, response_body) ] -> Lwt.return_ok (response, response_body)
     | [] -> Lwt.return_error "No responses from a single request"
-    | _ -> Lwt.return_error "Multiple responses from a single request"
+    | _ :: _ -> Lwt.return_error "Multiple responses from a single request"
+    | exception Cohttp_lwt.Connection.Retry ->
+        Lwt.return_error
+          "Error in request: maybe the server hung up prematurely?"
   in
   Lwt_result.bind responses_stream_opt handle_responses
 
diff --git a/libs/networking/http_mock_client/Http_mock_client.ml b/libs/networking/http_mock_client/Http_mock_client.ml
index 377f7c0d0b55..6cfcd15f8c5e 100644
--- a/libs/networking/http_mock_client/Http_mock_client.ml
+++ b/libs/networking/http_mock_client/Http_mock_client.ml
@@ -36,6 +36,11 @@ module Make (M : S) : Cohttp_lwt.S.Client = struct
   open M
 
   type ctx = unit
+  type 'a with_context = ?ctx:ctx -> 'a
+  type 'a io = 'a Lwt.t
+  type body = Cohttp_lwt.Body.t
+
+  let map_context (x : 'a with_context) (f : 'a -> 'b) ?ctx = x ?ctx |> f
 
   let mock_response_of_request (req : Cohttp.Request.t) (body : Body.t) =
     Logs.debug (fun m ->
@@ -46,32 +51,6 @@ module Make (M : S) : Cohttp_lwt.S.Client = struct
     let%lwt response = make_response req body in
     Lwt.return (response.response, response.body)
 
-  let callv ?ctx uri (reqs : (Cohttp.Request.t * Body.t) Lwt_stream.t) =
-    ignore ctx;
-    Logs.debug (fun m ->
-        m "[Testing client] Request URI: %s" (Uri.to_string uri));
-    let response_stream =
-      Lwt_stream.map_s
-        (fun (req, body) -> mock_response_of_request req body)
-        reqs
-    in
-    Lwt.return response_stream
-
-  let head ?ctx ?headers _ =
-    ignore ctx;
-    ignore headers;
-    failwith
-      "head is not implemented in the HTTP mock client. If you need this \
-       functionality, please implement it."
-
-  let post_form ?ctx ?headers ~params _ =
-    ignore ctx;
-    ignore headers;
-    ignore params;
-    failwith
-      "post_form is not implemented in the HTTP mock client. If you need this \
-       functionality, please implement it."
-
   let call ?ctx ?headers ?(body = `Empty) ?chunked meth uri =
     ignore ctx;
     Logs.debug (fun m ->
@@ -89,6 +68,13 @@ module Make (M : S) : Cohttp_lwt.S.Client = struct
     let req = Request.make_for_client ~headers ~chunked meth uri in
     mock_response_of_request req body
 
+  let head ?ctx ?headers _ =
+    ignore ctx;
+    ignore headers;
+    failwith
+      "head is not implemented in the HTTP mock client. If you need this \
+       functionality, please implement it."
+
   let get ?ctx ?headers uri = call ?ctx ?headers `GET uri
 
   let delete ?ctx ?body ?chunked ?headers uri =
@@ -102,6 +88,30 @@ module Make (M : S) : Cohttp_lwt.S.Client = struct
 
   let patch ?ctx ?body ?chunked ?headers uri =
     call ?ctx ?headers ?body ?chunked `PATCH uri
+
+  let set_cache _ =
+    failwith
+      "http set_cache is not implemented in the HTTP mock client. If you need \
+       this functionality, please implement it."
+
+  let post_form ?ctx ?headers ~params _ =
+    ignore ctx;
+    ignore headers;
+    ignore params;
+    failwith
+      "post_form is not implemented in the HTTP mock client. If you need this \
+       functionality, please implement it."
+
+  let callv ?ctx uri (reqs : (Cohttp.Request.t * Body.t) Lwt_stream.t) =
+    ignore ctx;
+    Logs.debug (fun m ->
+        m "[Testing client] Request URI: %s" (Uri.to_string uri));
+    let response_stream =
+      Lwt_stream.map_s
+        (fun (req, body) -> mock_response_of_request req body)
+        reqs
+    in
+    Lwt.return response_stream
 end
 
 (*****************************************************************************)
diff --git a/libs/process_limits/Memory_limit.ml b/libs/process_limits/Memory_limit.ml
index 03084585d3c0..fd85244874a4 100644
--- a/libs/process_limits/Memory_limit.ml
+++ b/libs/process_limits/Memory_limit.ml
@@ -21,6 +21,11 @@ open Common
    Avoid segfaults when the process runs out of memory.
 
    See also https://gitlab.com/gadmm/memprof-limits/.
+
+  NOTE: Be careful when adding logging, tracing, or other kinds of
+  opentelmetry!!! This module uses a GC alarm to do its work, and our telemetry
+  library can and will deadlock if it is called from the gc alarm. See the
+  comment above the GC alarm function `limit_memory` for more info.
 *)
 
 (*****************************************************************************)
@@ -83,6 +88,14 @@ let run_with_memory_limit _caps ?get_context
     else mem_limit_warning
   in
   let heap_warning = ref heap_warning_start in
+  (* NOTE: DO NOT TRACE LIMIT MEMORY!!!
+     NOTE: DO NOT LOG ANYTHING HERE WITHOUT APPLYING THE NO TELEMETRY TAG
+     SET!!!
+
+     Doing ANY sort of telemetry within a gc alarm can cause deadlocks!!! the no
+     telemetry tag set will disable telemetry for logs and so they will be
+     reported to the user, just not to any sort of telemetry backend.
+  *)
   let limit_memory () =
     let stat = Gc.quick_stat () in
     let heap_bytes = stat.heap_words * (Sys.word_size / 8) in
@@ -90,12 +103,13 @@ let run_with_memory_limit _caps ?get_context
     let mem_bytes = heap_bytes + stack_bytes in
     if mem_limit > 0 && mem_bytes > mem_limit then (
       Logs.err (fun m ->
-          m "%sexceeded heap+stack memory limit: %d bytes (stack=%d, heap=%d)"
+          m ~tags:Tracing.no_telemetry_tag_set
+            "%sexceeded heap+stack memory limit: %d bytes (stack=%d, heap=%d)"
             (context ()) mem_bytes stack_bytes heap_bytes);
       raise (ExceededMemoryLimit "Exceeded memory limit"))
     else if !heap_warning > 0 && heap_bytes > !heap_warning then (
       Logs.warn (fun m ->
-          m
+          m ~tags:Tracing.no_telemetry_tag_set
             "%slarge heap size: %d MiB (memory limit is %d MiB). If a crash \
              follows, you could suspect OOM."
             (context ()) (heap_bytes / mb) mem_limit_mb);
@@ -106,7 +120,7 @@ let run_with_memory_limit _caps ?get_context
       && not !stack_already_warned
     then (
       Logs.warn (fun m ->
-          m
+          m ~tags:Tracing.no_telemetry_tag_set
             "%slarge stack size: %d bytes. If a crash follows, you should \
              suspect a stack overflow. Make sure the maximum stack size is set \
              to 'unlimited' or to a value greater than %d bytes so as to \
diff --git a/libs/process_limits/dune b/libs/process_limits/dune
index 264e2c2e2952..a55e9d64b89b 100644
--- a/libs/process_limits/dune
+++ b/libs/process_limits/dune
@@ -9,5 +9,6 @@
  (libraries
    unix
    commons
+   tracing
  )
 )
diff --git a/libs/tracing/Tracing.mli b/libs/tracing/Tracing.mli
index a62869583a2b..c39950ece1c0 100644
--- a/libs/tracing/Tracing.mli
+++ b/libs/tracing/Tracing.mli
@@ -45,10 +45,43 @@ type level =
 
 val show_level : level -> string
 
+(*****************************************************************************)
+(* Logging *)
+(*****************************************************************************)
+
+val no_telemetry_tag : string Logs.Tag.def
+(** [no_telemetry_tag] is a logging tag that when applied to a log, said log
+    will not be emitted by the tracing/telemetry backend.
+
+    Example:
+    {[
+      let tags = Logs.Tag.(
+          add no_telemetry_tag (name no_telemetry_tag) tags)
+      in
+      Logs.info (fun m ->
+          m ~tags
+            "This log will not be sent to the telemetry backend");
+    ]}
+*)
+
+val no_telemetry_tag_set : Logs.Tag.set
+(** [no_telemetry_tag_set] is a logging tag set containing {!no_telemetry_tag}.
+    See {!no_telemetry_tag} for more information, and an example *)
+
+val otel_reporter : Logs.reporter
+(** [otel_reporter] is a reporter that can be used with {!Logs.set_reporter} to
+    send logs to the Otel backend.To disable logging for just this reporter, tag
+    the log with {!no_telemetry_tag}
+
+    NOTE: This reporter WILL cause deadlocks if it is used in a GC alarm. To add
+    Logs to a GC alarm and not trigger this, tag them with
+    {!no_telemetry_tag} *)
+
 (*****************************************************************************)
 (* Functions to instrument the code *)
 (*****************************************************************************)
 
+(* for adding data *)
 val add_data_to_span : span -> (string * Trace_core.user_data) list -> unit
 (** Expose the Trace function to add data to a span *)
 
@@ -58,6 +91,7 @@ val add_data : (string * Trace_core.user_data) list -> config option -> unit
 val add_global_attribute : string -> Trace_core.user_data -> unit
 (** Expose the Trace function to add global attributes to the top level span *)
 
+(* manual span entering and exiting *)
 val enter_span :
   ?level:level ->
   ?__FUNCTION__:string ->
@@ -74,10 +108,6 @@ val exit_span : span -> unit
 (** [exit_span span] will exit a span. Must be called after `enter_span`. Prefer
     [with_span] instead as it has better error handling *)
 
-val otel_reporter : Logs.reporter
-(** [otel_reporter] is a reporter that can be used with {!Logs.set_reporter} to
-    send logs to the Otel backend*)
-
 (* with span funcs *)
 
 val with_span :
diff --git a/libs/tracing/js/Tracing.ml b/libs/tracing/js/Tracing.ml
index 9767113549ed..0ce8e0a63a5e 100644
--- a/libs/tracing/js/Tracing.ml
+++ b/libs/tracing/js/Tracing.ml
@@ -84,6 +84,8 @@ let trace_data_only ?(level = Info) ~__FUNCTION__ ~__FILE__ ~__LINE__ _name
   ignore level;
   ()
 
+let no_telemetry_tag = Logs_.create_tag "no_telemetry"
+let no_telemetry_tag_set = Logs_.create_tag_set [ no_telemetry_tag ]
 let otel_reporter : Logs.reporter = Logs.nop_reporter
 (*****************************************************************************)
 (* Entry points for setting up tracing *)
diff --git a/libs/tracing/js/dune b/libs/tracing/js/dune
index 2ba3cdd59f46..b9797da7f700 100644
--- a/libs/tracing/js/dune
+++ b/libs/tracing/js/dune
@@ -4,6 +4,7 @@
  (name tracing_js)
  (libraries
    trace
+   commons
    logs
  )
  (implements tracing)
diff --git a/libs/tracing/unix/Tracing.ml b/libs/tracing/unix/Tracing.ml
index 4e354719c338..12a0ab62f8a8 100644
--- a/libs/tracing/unix/Tracing.ml
+++ b/libs/tracing/unix/Tracing.ml
@@ -15,6 +15,7 @@
 
 module Otel = Opentelemetry
 module Log = Log_commons.Log
+open Common
 
 (*****************************************************************************)
 (* Prelude *)
@@ -194,6 +195,7 @@ let add_global_attribute = Otel.Globals.add_global_attribute
 (*****************************************************************************)
 (* Logging *)
 (*****************************************************************************)
+(* TODO: upstream almost all of this into the otel library*)
 
 (* Log a message to otel with some attrs *)
 let log ?(attrs = []) ~level msg =
@@ -215,6 +217,9 @@ let log ?(attrs = []) ~level msg =
   (* Noop if no backend is set *)
   Otel.Logs.emit ~attrs [ log ]
 
+let no_telemetry_tag = Logs_.create_tag "no_telemetry"
+let no_telemetry_tag_set = Logs_.create_tag_set [ no_telemetry_tag ]
+
 let otel_reporter : Logs.reporter =
   let report src level ~over k msgf =
     msgf (fun ?header ?(tags : Logs.Tag.set option) fmt ->
@@ -224,9 +229,9 @@ let otel_reporter : Logs.reporter =
         in
         Format.kasprintf
           (fun msg ->
+            let tags = tags ||| no_telemetry_tag_set in
             let attrs =
               let tags =
-                let tags = tags |> Option.value ~default:Logs.Tag.empty in
                 (* This looks weird but is the easiest way to print log tags *)
                 Logs.Tag.fold
                   (fun (tag : Logs.Tag.t) acc ->
@@ -244,6 +249,7 @@ let otel_reporter : Logs.reporter =
                 ("message", `String msg);
               ]
             in
+            let do_not_emit = Logs.Tag.mem no_telemetry_tag tags in
             (match level with
             (* Let's not send debug logs for now, as they can be expensive and
                and we're not sure of the usefulness *)
@@ -251,6 +257,10 @@ let otel_reporter : Logs.reporter =
                enable sending debug logs here we probably want to send them from
                pysemgrep too! *)
             | Logs.Debug -> ()
+            (* Let's allow users to tag their logs when they don't want them
+               emitted. This could be because they're in the GC alarm, or
+               because they log info we don't want to leave the machine *)
+            | _ when do_not_emit -> ()
             | _ -> log ~attrs ~level msg);
             k ())
           fmt)
diff --git a/semgrep.opam b/semgrep.opam
index 6c6c0ece2916..b8e435db7e73 100644
--- a/semgrep.opam
+++ b/semgrep.opam
@@ -1,6 +1,6 @@
 # This file is generated by dune, edit dune-project instead
 opam-version: "2.0"
-version: "1.92.0"
+version: "1.95.0"
 synopsis:
   "Like grep but for code: fast and syntax-aware semantic code pattern for many languages"
 description: """
@@ -66,7 +66,7 @@ depends: [
   "conf-libcurl" {= "1"}
   "uri"
   "uuidm" {>= "0.9.9"}
-  "cohttp" {= "5.3.0"}
+  "cohttp" {= "6.0.0~beta2"}
   "cohttp-lwt-unix"
   "cohttp-lwt-jsoo"
   "tls-lwt"
diff --git a/src/core/Core_error.ml b/src/core/Core_error.ml
index d1ada18373b2..0c8e3f941956 100644
--- a/src/core/Core_error.ml
+++ b/src/core/Core_error.ml
@@ -245,9 +245,16 @@ let known_exn_to_error ?(file : Fpath.t option) (e : Exception.t) : t option =
         Some (Tok.first_loc_of_file file)
       in
       Some (mk_error ~msg:"Heap space exceeded" ?loc Out.OutOfMemory)
+  | Common.ErrorOnFile (s, file) ->
+      let loc = Some (Tok.first_loc_of_file file) in
+      (* TODO: see the comment below we want OtherErrorWithAttachedFile *)
+      Some (mk_error ~msg:s ?loc Out.OtherParseError)
   (* general case, can't extract line information from it, default to line 1 *)
   | _exn -> None
 
+(* TODO: remove the file parameter and instead rewrap exns in the caller
+ * using Common.ErrorOnFile
+ *)
 let exn_to_error ?(file : Fpath.t option) (e : Exception.t) : t =
   match known_exn_to_error ?file e with
   | Some err -> err
diff --git a/src/core/Semgrep_output_utils.ml b/src/core/Semgrep_output_utils.ml
index b895966fa8bf..c7a6e006260c 100644
--- a/src/core/Semgrep_output_utils.ml
+++ b/src/core/Semgrep_output_utils.ml
@@ -40,10 +40,10 @@ let first_and_last = function
  * python: # 'lines' already contains '\n' at the end of each line
  *   lines="".join(rule_match.lines).rstrip(),
  *)
-let lines_of_file_at_range (range : position * position) (file : Fpath.t) :
+let lines_of_file_at_range_exn (range : position * position) (file : Fpath.t) :
     string list =
   let start, end_ = range in
-  UFile.lines_of_file (start.line, end_.line) file
+  UFile.lines_of_file_exn (start.line, end_.line) file
 [@@profiling]
 
 (* Returns the text between the positions; start inclusive, end exclusive.
diff --git a/src/core/Semgrep_output_utils.mli b/src/core/Semgrep_output_utils.mli
index 750eed1720eb..ec66f33c7660 100644
--- a/src/core/Semgrep_output_utils.mli
+++ b/src/core/Semgrep_output_utils.mli
@@ -4,8 +4,9 @@
 
 (* internal function used for the 'lines:' field in the JSON output
  * but also now in Output.ml for the Emacs output.
+ * This may raise Common.ErrorOnFile "out of bound access".
  *)
-val lines_of_file_at_range :
+val lines_of_file_at_range_exn :
   Semgrep_output_v1_t.position * Semgrep_output_v1_t.position ->
   Fpath.t ->
   string list
diff --git a/src/core_scan/Core_scan.ml b/src/core_scan/Core_scan.ml
index b0bcc1088b0b..19f1b3de320d 100644
--- a/src/core_scan/Core_scan.ml
+++ b/src/core_scan/Core_scan.ml
@@ -726,9 +726,10 @@ let rules_for_target ~analyzer ~products ~origin ~respect_rule_paths rules =
 (* SCA *)
 (*****************************************************************************)
 
-let lockfile_xtarget_resolve (lockfile : Target.lockfile) : Lockfile_xtarget.t =
+let lockfile_xtarget_resolve (manifest : Target.manifest option)
+    (lockfile : Target.lockfile) : Lockfile_xtarget.t =
   Lockfile_xtarget.resolve Parse_lockfile.parse_manifest
-    Parse_lockfile.parse_lockfile lockfile
+    Parse_lockfile.parse_lockfile lockfile manifest
 
 let rules_for_lockfile_kind ~lockfile_kind rules =
   rules
@@ -754,7 +755,7 @@ let supply_chain_rules ~lockfile_kind ~respect_rule_paths ~origin rules =
 let sca_rules_filtering (target : Target.regular) (rules : Rule.t list) :
     Rule.t list * Match_dependency.dependency_match_table =
   let lockfile_xtarget_opt =
-    target.lockfile |> Option.map lockfile_xtarget_resolve
+    target.lockfile |> Option.map (lockfile_xtarget_resolve None)
   in
   (* If a rule tried to a find a dependency match and failed, then it will
      never produce any matches of any kind *)
@@ -791,7 +792,10 @@ let mk_target_handler (caps : < Cap.time_limit >) (config : Core_scan_config.t)
   function
   | Lockfile
       ({ path = { internal_path_to_content; origin }; kind; _ } as lockfile) ->
-      let lockfile_xtarget = lockfile_xtarget_resolve lockfile in
+      (* TODO: (sca) we always pass None as the manifest target here, but this code path only
+       * applies to Supply Chain scans in the core which we never use. We should pass the real
+       * manifest here. *)
+      let lockfile_xtarget = lockfile_xtarget_resolve None lockfile in
       let rules =
         supply_chain_rules ~lockfile_kind:kind ~origin
           ~respect_rule_paths:config.respect_rule_paths valid_rules
diff --git a/src/engine/Formula_cache.ml b/src/engine/Formula_cache.ml
index 47224d2e8b64..f275dd1833f4 100644
--- a/src/engine/Formula_cache.ml
+++ b/src/engine/Formula_cache.ml
@@ -49,7 +49,7 @@ let mk_specialized_formula_cache (rules : R.taint_rule list) =
     rules
     |> List.concat_map (fun (rule : R.taint_mode R.rule_info) ->
            let (`Taint (spec : R.taint_spec)) = rule.R.mode in
-           R.formula_of_mode (`Taint spec))
+           R.formulas_of_mode (`Taint spec))
   in
   flat_formulas
   |> List.iter (fun formula ->
diff --git a/src/osemgrep/cli/Help.ml b/src/osemgrep/cli/Help.ml
index a837aeaa525a..c42087bf5579 100644
--- a/src/osemgrep/cli/Help.ml
+++ b/src/osemgrep/cli/Help.ml
@@ -35,6 +35,8 @@
 (* Entry point *)
 (*****************************************************************************)
 
+(* coupling: keep the summaries for the login/scan/ci subcommands similar
+   to what's printed by print_semgrep_dashdash_help. *)
 let print_help (stdout : Cap.Console.stdout) =
   (* TODO: add a Out.printf_color? *)
   CapConsole.ocolor_format_printf stdout
@@ -53,8 +55,8 @@ For more information about Semgrep, visit @{<cyan;ul>https://semgrep.dev@}
 
 @{<ul>Commands@}:
   @{<cyan>semgrep login@}                Enable Pro rules, Supply Chain, and secrets scanning
-  @{<cyan>semgrep ci@}                   Run Semgrep on the latest git diff (for use in CI)
-  @{<cyan>semgrep scan@}                 Run Semgrep rules on local directories or files
+  @{<cyan>semgrep ci@}                   Run Semgrep on a git diff (for use in CI)
+  @{<cyan>semgrep scan@}                 Run Semgrep rules on local folders or files
 
 @{<ul>Help@}:
   @{<cyan>semgrep COMMAND --help@}       For more information on each command
@@ -62,6 +64,7 @@ For more information about Semgrep, visit @{<cyan;ul>https://semgrep.dev@}
 For the CLI docs visit @{<cyan;ul>https://semgrep.dev/docs/cli-reference@}
 |}
 
+(* coupling: see above *)
 let print_semgrep_dashdash_help (stdout : Cap.Console.stdout) =
   CapConsole.ocolor_format_printf stdout
     {|@{<ul>Usage@}: @{<cyan>semgrep [OPTIONS] COMMAND [ARGS]...@}
@@ -76,14 +79,14 @@ let print_semgrep_dashdash_help (stdout : Cap.Console.stdout) =
   -h, --help  Show this message and exit.
 
 @{<ul>Commands@}:
-  @{<cyan>ci@}                   The recommended way to run semgrep in CI
+  @{<cyan>ci@}                   Run Semgrep on a git diff (for use in CI)
   @{<cyan>install-semgrep-pro@}  Install the Semgrep Pro Engine
   @{<cyan>login@}                Obtain and save credentials for @{<cyan;ul>semgrep.dev@}
   @{<cyan>logout@}               Remove locally stored credentials to @{<cyan;ul>semgrep.dev@}
   @{<cyan>lsp@}                  Start the Semgrep LSP server (useful for IDEs)
   @{<cyan>publish@}              Upload rule to @{<cyan;ul>semgrep.dev@}
-  @{<cyan>scan@}                 Run semgrep rules on files
-  @{<cyan>show@}                 Show various information about Semgrep
+  @{<cyan>scan@}                 Run Semgrep rules on local folders or files
+  @{<cyan>show@}                 Show various types of information
   @{<cyan>test@}                 Test the rules
   @{<cyan>validate@}             Validate the rules
 |}
diff --git a/src/osemgrep/cli_ci/Ci_CLI.ml b/src/osemgrep/cli_ci/Ci_CLI.ml
index 5b9b2a2a810a..7637e8d9b2d2 100644
--- a/src/osemgrep/cli_ci/Ci_CLI.ml
+++ b/src/osemgrep/cli_ci/Ci_CLI.ml
@@ -50,8 +50,7 @@ type conf = {
    * opti flags.
    *)
   scan_conf : Scan_CLI.conf;
-  merge_partial_results_dir : Fpath.t option;
-  merge_partial_results_output : Fpath.t option;
+  x_distributed_scan_conf : Distributed_scan_stub.conf;
 }
 [@@deriving show]
 
@@ -176,6 +175,20 @@ let o_x_merge_partial_results_output : string option Term.t =
   in
   Arg.value (Arg.opt (Arg.some' Arg.string) None info)
 
+(* internal *)
+let o_x_validate_partial_results_expected : string option Term.t =
+  let info =
+    Arg.info [ "x-validate-partial-results-expected" ] ~doc:{|Internal flag.|}
+  in
+  Arg.value (Arg.opt (Arg.some' Arg.string) None info)
+
+(* internal *)
+let o_x_validate_partial_results_actual : string option Term.t =
+  let info =
+    Arg.info [ "x-validate-partial-results-actual" ] ~doc:{|Internal flag.|}
+  in
+  Arg.value (Arg.opt (Arg.some' Arg.string) None info)
+
 (*************************************************************************)
 (* 'scan' subset supported by 'ci' *)
 (*************************************************************************)
@@ -195,11 +208,11 @@ let scan_subset_cmdline_term : Scan_CLI.conf Term.t =
       json json_outputs junit_xml junit_xml_outputs matching_explanations
       max_chars_per_line max_lines_per_finding max_log_list_entries
       max_memory_mb max_target_bytes metrics num_jobs no_secrets_validation
-      nosem optimizations oss output pro pro_intrafile pro_lang
-      pro_path_sensitive respect_gitignore rewrite_rule_ids sarif sarif_outputs
-      scan_unknown_extensions secrets text text_outputs timeout
-      _timeout_interfileTODO timeout_threshold trace trace_endpoint
-      version_check vim vim_outputs =
+      _no_dynamic_dependency_resolution nosem optimizations oss output pro
+      pro_intrafile pro_lang pro_path_sensitive respect_gitignore
+      rewrite_rule_ids sarif sarif_outputs scan_unknown_extensions secrets text
+      text_outputs timeout _timeout_interfileTODO timeout_threshold trace
+      trace_endpoint version_check vim vim_outputs =
     let output_format : Output_format.t =
       Scan_CLI.output_format_conf ~text ~files_with_matches ~json ~emacs ~vim
         ~sarif ~gitlab_sast ~gitlab_secrets ~junit_xml
@@ -335,13 +348,14 @@ let scan_subset_cmdline_term : Scan_CLI.conf Term.t =
     $ SC.o_matching_explanations $ SC.o_max_chars_per_line
     $ SC.o_max_lines_per_finding $ SC.o_max_log_list_entries
     $ SC.o_max_memory_mb $ SC.o_max_target_bytes $ SC.o_metrics $ SC.o_num_jobs
-    $ SC.o_no_secrets_validation $ SC.o_nosem $ SC.o_optimizations $ SC.o_oss
-    $ SC.o_output $ SC.o_pro $ SC.o_pro_intrafile $ SC.o_pro_languages
-    $ SC.o_pro_path_sensitive $ SC.o_respect_gitignore $ SC.o_rewrite_rule_ids
-    $ SC.o_sarif $ SC.o_sarif_outputs $ SC.o_scan_unknown_extensions
-    $ SC.o_secrets $ SC.o_text $ SC.o_text_outputs $ SC.o_timeout
-    $ SC.o_timeout_interfile $ SC.o_timeout_threshold $ SC.o_trace
-    $ SC.o_trace_endpoint $ SC.o_version_check $ SC.o_vim $ SC.o_vim_outputs)
+    $ SC.o_no_secrets_validation $ SC.o_no_dynamic_dependency_resolution
+    $ SC.o_nosem $ SC.o_optimizations $ SC.o_oss $ SC.o_output $ SC.o_pro
+    $ SC.o_pro_intrafile $ SC.o_pro_languages $ SC.o_pro_path_sensitive
+    $ SC.o_respect_gitignore $ SC.o_rewrite_rule_ids $ SC.o_sarif
+    $ SC.o_sarif_outputs $ SC.o_scan_unknown_extensions $ SC.o_secrets
+    $ SC.o_text $ SC.o_text_outputs $ SC.o_timeout $ SC.o_timeout_interfile
+    $ SC.o_timeout_threshold $ SC.o_trace $ SC.o_trace_endpoint
+    $ SC.o_version_check $ SC.o_vim $ SC.o_vim_outputs)
 
 (*************************************************************************)
 (* Turn argv into conf *)
@@ -356,7 +370,8 @@ let cmdline_term : conf Term.t =
   let combine scan_conf audit_on code secrets dry_run _internal_ci_scan_results
       _x_dump_n_rule_partitions _x_dump_rule_partitions_dir
       x_merge_partial_results_dir x_merge_partial_results_output
-      _x_partial_config _x_partial_output subdir supply_chain suppress_errors
+      _x_partial_config _x_partial_output x_validate_partial_results_actual
+      x_validate_partial_results_expected subdir supply_chain suppress_errors
       _git_meta _github_meta =
     let products =
       (if secrets then [ `Secrets ] else [])
@@ -370,9 +385,17 @@ let cmdline_term : conf Term.t =
       suppress_errors;
       products;
       subdir;
-      merge_partial_results_dir = Option.map Fpath.v x_merge_partial_results_dir;
-      merge_partial_results_output =
-        Option.map Fpath.v x_merge_partial_results_output;
+      x_distributed_scan_conf =
+        {
+          merge_partial_results_dir =
+            Option.map Fpath.v x_merge_partial_results_dir;
+          merge_partial_results_output =
+            Option.map Fpath.v x_merge_partial_results_output;
+          validate_partial_results_expected =
+            Option.map Fpath.v x_validate_partial_results_expected;
+          validate_partial_results_actual =
+            Option.map Fpath.v x_validate_partial_results_actual;
+        };
     }
   in
   Term.(
@@ -380,7 +403,9 @@ let cmdline_term : conf Term.t =
     $ SC.o_secrets $ o_dry_run $ o_internal_ci_scan_results
     $ o_x_dump_n_rule_partitions $ o_x_dump_rule_partitions_dir
     $ o_x_merge_partial_results_dir $ o_x_merge_partial_results_output
-    $ o_x_partial_config $ o_x_partial_output $ o_subdir $ o_supply_chain
+    $ o_x_partial_config $ o_x_partial_output
+    $ o_x_validate_partial_results_actual
+    $ o_x_validate_partial_results_expected $ o_subdir $ o_supply_chain
     $ o_suppress_errors $ Git_metadata.env $ Github_metadata.env)
 
 let doc = "the recommended way to run semgrep in CI"
diff --git a/src/osemgrep/cli_ci/Ci_CLI.mli b/src/osemgrep/cli_ci/Ci_CLI.mli
index 598b6474b792..2c29d21e0ad5 100644
--- a/src/osemgrep/cli_ci/Ci_CLI.mli
+++ b/src/osemgrep/cli_ci/Ci_CLI.mli
@@ -15,8 +15,8 @@ type conf = {
   subdir : string;
   (* BIG ONE: 'semgrep ci' shares many flags with 'semgrep scan' *)
   scan_conf : Scan_CLI.conf;
-  merge_partial_results_dir : Fpath.t option;
-  merge_partial_results_output : Fpath.t option;
+  (* internal only *)
+  x_distributed_scan_conf : Distributed_scan_stub.conf;
 }
 [@@deriving show]
 
diff --git a/src/osemgrep/cli_ci/Ci_subcommand.ml b/src/osemgrep/cli_ci/Ci_subcommand.ml
index bfdc5aa7bfac..4cb00ef31d0a 100644
--- a/src/osemgrep/cli_ci/Ci_subcommand.ml
+++ b/src/osemgrep/cli_ci/Ci_subcommand.ml
@@ -709,39 +709,6 @@ let upload_findings ~dry_run
           deployment_config.name);
   override
 
-(*****************************************************************************)
-(* Distributed scans *)
-(*****************************************************************************)
-
-let hook_pro_read_and_merge_partial_scan_results :
-    (input_dir:Fpath.t -> output_json:Fpath.t -> unit) option ref =
-  ref None
-
-let maybe_merge_partial_scan_results_then_exit (ci_conf : Ci_CLI.conf) =
-  match
-    (ci_conf.merge_partial_results_dir, ci_conf.merge_partial_results_output)
-  with
-  | Some _, None
-  | None, Some _ ->
-      Logs.err (fun m ->
-          m
-            "Both or none of --x-merge-partial-results-dir and \
-             --x-merge-partial-results-output must be present.");
-      Error.exit_code_exn (Exit_code.fatal ~__LOC__)
-  | None, None -> ()
-  | Some input_dir, Some output_file -> (
-      match !hook_pro_read_and_merge_partial_scan_results with
-      | None ->
-          Logs.err (fun m ->
-              m
-                "You have requested a setting that requires the pro engine, \
-                 but do not have the pro engine installed.");
-          Error.exit_code_exn (Exit_code.fatal ~__LOC__)
-      | Some read_and_merge_partial_scan_results ->
-          read_and_merge_partial_scan_results input_dir output_file;
-          (* Not really an error, but abusing exit_code_exn for short circuiting *)
-          Error.exit_code_exn (Exit_code.ok ~__LOC__))
-
 (*****************************************************************************)
 (* Main logic *)
 (*****************************************************************************)
@@ -798,7 +765,10 @@ let run_conf (caps : caps) (ci_conf : Ci_CLI.conf) : Exit_code.t =
 
   (* ===== Begin of steps related to distributed scans ===== *)
   (* If we are doing a distributed scan step, complete the step, then exit *)
-  maybe_merge_partial_scan_results_then_exit ci_conf;
+  Distributed_scan_stub.maybe_merge_partial_scan_results_then_exit
+    ci_conf.x_distributed_scan_conf;
+  Distributed_scan_stub.maybe_validate_partial_scan_results_then_exit
+    ci_conf.x_distributed_scan_conf;
 
   (* ===== End of steps related to distributed scans ===== *)
 
diff --git a/src/osemgrep/cli_ci/Ci_subcommand.mli b/src/osemgrep/cli_ci/Ci_subcommand.mli
index af46368b14cf..fc89b6cb9aa4 100644
--- a/src/osemgrep/cli_ci/Ci_subcommand.mli
+++ b/src/osemgrep/cli_ci/Ci_subcommand.mli
@@ -23,7 +23,3 @@ val run_conf : caps -> Ci_CLI.conf -> Exit_code.t
 (* used by Test_is_blocking_Helpers.ml used itself in Test.ml *)
 val rule_is_blocking : JSON.t -> bool
 val finding_is_blocking : Semgrep_output_v1_t.cli_match -> bool
-
-(* internal *)
-val hook_pro_read_and_merge_partial_scan_results :
-  (input_dir:Fpath.t -> output_json:Fpath.t -> unit) option ref
diff --git a/src/osemgrep/cli_ci/dune b/src/osemgrep/cli_ci/dune
index 7e26439512c6..8d90344b4293 100644
--- a/src/osemgrep/cli_ci/dune
+++ b/src/osemgrep/cli_ci/dune
@@ -12,6 +12,7 @@
     osemgrep_core
     osemgrep_networking
     osemgrep_cli_scan ; reusing the same flags and most of the code
+    osemgrep_distributed_scan_stub
   )
  (preprocess
    (pps
diff --git a/src/osemgrep/cli_login/Test_login_subcommand.ml b/src/osemgrep/cli_login/Test_login_subcommand.ml
index 4798c086bfb6..7e0a83079477 100644
--- a/src/osemgrep/cli_login/Test_login_subcommand.ml
+++ b/src/osemgrep/cli_login/Test_login_subcommand.ml
@@ -26,6 +26,28 @@ let t = Testo.create
  * and 'use_click_runner=True' in test_login.py
  *)
 
+(*****************************************************************************)
+(* Constants *)
+(*****************************************************************************)
+
+(* This token does not have to be valid because we mock the deployment
+ * request and response that is supposed to come from our endpoint and
+ * check for its validity.
+ *)
+let fake_token = "token1234"
+
+(* deployment_response in semgrep_output_v1.atd
+ * alt: we could build it using Semgrep_output_v1_t deployment_response record.
+ *)
+let fake_deployment =
+  {|
+  { "deployment":
+    { "id": 1234,
+      "name": "deployment1234"
+    }
+  }
+|}
+
 (*****************************************************************************)
 (* Helpers *)
 (*****************************************************************************)
@@ -41,6 +63,10 @@ let with_fake_deployment_response return_value f =
   in
   Http_mock_client.with_testing_client make_response_fn f ()
 
+let with_semgrep_logged_in f =
+  Semgrep_envvars.with_envvar "SEMGREP_APP_TOKEN" fake_token (fun () ->
+      with_fake_deployment_response fake_deployment f)
+
 (*****************************************************************************)
 (* Tests *)
 (*****************************************************************************)
@@ -73,24 +99,6 @@ let test_login_no_tty caps : Testo.t =
          Unix.dup2 old_stdin Unix.stdin;
          Exit_code.Check.fatal exit_code))
 
-(* This token does not have to be valid because we mock the deployment
- * request and response that is supposed to come from our endpoint and
- * check for its validity.
- *)
-let fake_token = "token1234"
-
-(* deployment_response in semgrep_output_v1.atd
- * alt: we could build it using Semgrep_output_v1_t deployment_response record.
- *)
-let fake_deployment =
-  {|
-  { "deployment":
-    { "id": 1234,
-      "name": "deployment1234"
-    }
-  }
-|}
-
 let test_login_with_env_token caps : Testo.t =
   t ~checked_output:(Testo.stderr ())
     ~normalize:
diff --git a/src/osemgrep/cli_login/Test_login_subcommand.mli b/src/osemgrep/cli_login/Test_login_subcommand.mli
index 532e36825ab0..cd09913da2a1 100644
--- a/src/osemgrep/cli_login/Test_login_subcommand.mli
+++ b/src/osemgrep/cli_login/Test_login_subcommand.mli
@@ -4,5 +4,6 @@ val tests : < Cap.network ; Cap.stdout > -> Testo.t list
 (* to be reused in other tests *)
 
 val with_fake_deployment_response : string -> (unit -> 'a) -> 'a
+val with_semgrep_logged_in : (unit -> 'a) -> 'a
 val fake_token : string
 val fake_deployment : string
diff --git a/src/osemgrep/cli_scan/Diff_scan.ml b/src/osemgrep/cli_scan/Diff_scan.ml
index c54c1b1f8411..319bada6412e 100644
--- a/src/osemgrep/cli_scan/Diff_scan.ml
+++ b/src/osemgrep/cli_scan/Diff_scan.ml
@@ -62,8 +62,9 @@ let remove_matches_in_baseline caps (commit : string) (baseline : Core_result.t)
       |> Option.value ~default:p
     in
     let start_range, end_range = m.range_loc in
+    (* TODO: what if we get an exn? *)
     let syntactic_ctx =
-      UFile.lines_of_file
+      UFile.lines_of_file_exn
         (start_range.pos.line, end_range.pos.line)
         m.path.internal_path_to_content
     in
diff --git a/src/osemgrep/cli_scan/Scan_CLI.ml b/src/osemgrep/cli_scan/Scan_CLI.ml
index f6e22fb269e2..42ee64cbec91 100644
--- a/src/osemgrep/cli_scan/Scan_CLI.ml
+++ b/src/osemgrep/cli_scan/Scan_CLI.ml
@@ -894,6 +894,16 @@ let o_allow_dynamic_dependency_resolution : bool Term.t =
   in
   Arg.value (Arg.flag info)
 
+let o_no_dynamic_dependency_resolution : bool Term.t =
+  let info =
+    Arg.info
+      [ "no-dynamic-dependency-resolution" ]
+      ~doc:
+        {|Experimental: disable resolving dependencies dynamically by communicating with package managers during the scan.
+          This is the default, so this flag currently has no effect.|}
+  in
+  Arg.value (Arg.flag info)
+
 (* ------------------------------------------------------------------ *)
 (* Test and debug options *)
 (* ------------------------------------------------------------------ *)
@@ -1294,15 +1304,15 @@ let cmdline_term caps ~allow_empty_config : conf Term.t =
       gitlab_secrets_outputs _historical_secrets include_ incremental_output
       json json_outputs junit_xml junit_xml_outputs lang matching_explanations
       max_chars_per_line max_lines_per_finding max_log_list_entries
-      max_memory_mb max_target_bytes metrics num_jobs no_secrets_validation
-      nosem optimizations oss output pattern pro project_root pro_intrafile
-      pro_lang pro_path_sensitive remote replacement respect_gitignore
-      rewrite_rule_ids sarif sarif_outputs scan_unknown_extensions secrets
-      severity show_supported_languages strict target_roots test
-      test_ignore_todo text text_outputs time_flag timeout
-      _timeout_interfileTODO timeout_threshold trace trace_endpoint
-      _use_osemgrep_sarif validate version version_check vim vim_outputs
-      x_ignore_semgrepignore_files x_ls x_ls_long =
+      max_memory_mb max_target_bytes metrics num_jobs
+      _no_dynamic_dependency_resolution no_secrets_validation nosem
+      optimizations oss output pattern pro project_root pro_intrafile pro_lang
+      pro_path_sensitive remote replacement respect_gitignore rewrite_rule_ids
+      sarif sarif_outputs scan_unknown_extensions secrets severity
+      show_supported_languages strict target_roots test test_ignore_todo text
+      text_outputs time_flag timeout _timeout_interfileTODO timeout_threshold
+      trace trace_endpoint _use_osemgrep_sarif validate version version_check
+      vim vim_outputs x_ignore_semgrepignore_files x_ls x_ls_long =
     (* Print a warning if any of the internal or experimental options.
        We don't want users to start relying on these. *)
     if x_ignore_semgrepignore_files || x_ls || x_ls_long then
@@ -1513,16 +1523,17 @@ let cmdline_term caps ~allow_empty_config : conf Term.t =
     $ o_json_outputs $ o_junit_xml $ o_junit_xml_outputs $ o_lang
     $ o_matching_explanations $ o_max_chars_per_line $ o_max_lines_per_finding
     $ o_max_log_list_entries $ o_max_memory_mb $ o_max_target_bytes $ o_metrics
-    $ o_num_jobs $ o_no_secrets_validation $ o_nosem $ o_optimizations $ o_oss
-    $ o_output $ o_pattern $ o_pro $ o_project_root $ o_pro_intrafile
-    $ o_pro_languages $ o_pro_path_sensitive $ o_remote $ o_replacement
-    $ o_respect_gitignore $ o_rewrite_rule_ids $ o_sarif $ o_sarif_outputs
-    $ o_scan_unknown_extensions $ o_secrets $ o_severity
-    $ o_show_supported_languages $ o_strict $ o_target_roots $ o_test
-    $ Test_CLI.o_test_ignore_todo $ o_text $ o_text_outputs $ o_time $ o_timeout
-    $ o_timeout_interfile $ o_timeout_threshold $ o_trace $ o_trace_endpoint
-    $ o_use_osemgrep_sarif $ o_validate $ o_version $ o_version_check $ o_vim
-    $ o_vim_outputs $ o_ignore_semgrepignore_files $ o_ls $ o_ls_long)
+    $ o_num_jobs $ o_no_dynamic_dependency_resolution $ o_no_secrets_validation
+    $ o_nosem $ o_optimizations $ o_oss $ o_output $ o_pattern $ o_pro
+    $ o_project_root $ o_pro_intrafile $ o_pro_languages $ o_pro_path_sensitive
+    $ o_remote $ o_replacement $ o_respect_gitignore $ o_rewrite_rule_ids
+    $ o_sarif $ o_sarif_outputs $ o_scan_unknown_extensions $ o_secrets
+    $ o_severity $ o_show_supported_languages $ o_strict $ o_target_roots
+    $ o_test $ Test_CLI.o_test_ignore_todo $ o_text $ o_text_outputs $ o_time
+    $ o_timeout $ o_timeout_interfile $ o_timeout_threshold $ o_trace
+    $ o_trace_endpoint $ o_use_osemgrep_sarif $ o_validate $ o_version
+    $ o_version_check $ o_vim $ o_vim_outputs $ o_ignore_semgrepignore_files
+    $ o_ls $ o_ls_long)
 
 let doc = "run semgrep rules on files"
 
diff --git a/src/osemgrep/cli_scan/Scan_CLI.mli b/src/osemgrep/cli_scan/Scan_CLI.mli
index 7d1b8e244537..f75a871ebdac 100644
--- a/src/osemgrep/cli_scan/Scan_CLI.mli
+++ b/src/osemgrep/cli_scan/Scan_CLI.mli
@@ -107,6 +107,7 @@ val o_max_memory_mb : int Cmdliner.Term.t
 val o_max_target_bytes : int Cmdliner.Term.t
 val o_metrics : Metrics_.config Cmdliner.Term.t
 val o_num_jobs : int Cmdliner.Term.t
+val o_no_dynamic_dependency_resolution : bool Cmdliner.Term.t
 val o_no_secrets_validation : bool Cmdliner.Term.t
 val o_nosem : bool Cmdliner.Term.t
 val o_optimizations : bool Cmdliner.Term.t
diff --git a/src/osemgrep/cli_scan/Test_scan_subcommand.ml b/src/osemgrep/cli_scan/Test_scan_subcommand.ml
index cca47967a720..8a6f6fb40ede 100644
--- a/src/osemgrep/cli_scan/Test_scan_subcommand.ml
+++ b/src/osemgrep/cli_scan/Test_scan_subcommand.ml
@@ -12,8 +12,8 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the file
  * LICENSE for more details.
  *)
-
-open Printf
+open Common
+open Fpath_.Operators
 
 let t = Testo.create
 
@@ -66,6 +66,13 @@ let normalize =
     Testo.mask_line ~after:"Semgrep version: " ();
   ]
 
+let with_settings settings f =
+  UTmp.with_temp_file (fun file ->
+      Semgrep_envvars.with_envvar "SEMGREP_SETTINGS_FILE" !!file (fun () ->
+          let res = Semgrep_settings.save settings in
+          assert res;
+          f ()))
+
 let without_settings f =
   Semgrep_envvars.with_envvar "SEMGREP_SETTINGS_FILE" "nosettings.yaml" f
 
@@ -79,9 +86,9 @@ let with_env_app_token ?(token = dummy_app_token) f =
 (*****************************************************************************)
 
 let test_nosettings ~env_app_token_set () =
-  printf "cwd: %s\n%!" (Unix.getcwd ());
+  Logs.debug (fun m -> m "cwd: %s\n%!" (Unix.getcwd ()));
   let settings_opt = Semgrep_settings.from_file () in
-  Alcotest.(check bool) "no settings from file" true (settings_opt = None);
+  Alcotest.(check bool) "no settings from file" true (settings_opt =*= None);
   let settings_with_include_env = Semgrep_settings.load () in
   let expected_settings =
     if env_app_token_set then
@@ -93,13 +100,13 @@ let test_nosettings ~env_app_token_set () =
   in
   Alcotest.(check bool)
     "default settings loaded" true
-    (expected_settings = settings_with_include_env);
+    (expected_settings =*= settings_with_include_env);
   let settings_with_no_include_env =
     Semgrep_settings.load ~include_env:false ()
   in
   Alcotest.(check bool)
     "default settings loaded with app token and no env" true
-    (settings_with_no_include_env = Semgrep_settings.default)
+    (settings_with_no_include_env =*= Semgrep_settings.default)
 
 let test_basic_output (caps : Scan_subcommand.caps) () =
   with_env_app_token (fun () ->
@@ -165,6 +172,17 @@ let tests (caps : < Scan_subcommand.caps >) =
       t "no semgrep settings file with env set" (fun () ->
           without_settings (fun () ->
               with_env_app_token (test_nosettings ~env_app_token_set:true)));
+      t "semgrep settings file with env set" (fun () ->
+          with_settings Semgrep_settings.default (fun () ->
+              with_env_app_token (fun () ->
+                  let settings_with_include_env = Semgrep_settings.load () in
+                  match settings_with_include_env with
+                  | { api_token = Some tok; _ }
+                    when tok =*= Auth.unsafe_token_of_string dummy_app_token ->
+                      ()
+                  | _ ->
+                      failwith
+                        "SEMGREP_APP_TOKEN should override the settings file")));
       t "basic output" ~checked_output:(Testo.stdxxx ()) ~normalize
         (test_basic_output caps);
       t "basic verbose output"
diff --git a/src/osemgrep/cli_show/Show_CLI.ml b/src/osemgrep/cli_show/Show_CLI.ml
index 6bb629a61511..1d510e1e9951 100644
--- a/src/osemgrep/cli_show/Show_CLI.ml
+++ b/src/osemgrep/cli_show/Show_CLI.ml
@@ -129,12 +129,12 @@ let cmdline_term : conf Term.t =
 
   Term.(const combine $ o_args $ CLI_common.o_common $ o_json)
 
-let doc = "Show various information"
+let doc = "Show various types of information"
 
 let man : Cmdliner.Manpage.block list =
   [
     `S Cmdliner.Manpage.s_description;
-    `P "Display various information";
+    `P "Display various types of information";
     `P "Here are the different subcommands";
     (* the sub(sub)commands *)
     `Pre "semgrep show version";
diff --git a/src/osemgrep/cli_show/Show_subcommand.ml b/src/osemgrep/cli_show/Show_subcommand.ml
index 47a200a61c52..7f748c5250e7 100644
--- a/src/osemgrep/cli_show/Show_subcommand.ml
+++ b/src/osemgrep/cli_show/Show_subcommand.ml
@@ -95,9 +95,17 @@ let run_conf (caps : caps) (conf : Show_CLI.conf) : Exit_code.t =
   | DumpCST (file, lang) ->
       Test_parsing.dump_tree_sitter_cst lang file;
       Exit_code.ok ~__LOC__
-  | DumpAST (file, lang) ->
+  | DumpAST (file, lang) -> (
       (* mostly a copy paste of Core_CLI.dump_ast *)
-      let { Parsing_result2.ast; skipped_tokens = _; _ } =
+      let Parsing_result2.
+            {
+              ast;
+              errors;
+              tolerated_errors;
+              skipped_tokens;
+              inserted_tokens;
+              stat = _;
+            } =
         (* alt: call Parse_target.just_parse_with_lang()
          * but usually we also want the naming/typing info.
          * we could add a flag --naming, but simpler to just call
@@ -112,7 +120,20 @@ let run_conf (caps : caps) (conf : Show_CLI.conf) : Exit_code.t =
       UFormat.set_margin 120;
       let s = dump_v_to_format ~json:conf.json v in
       print s;
-      Exit_code.ok ~__LOC__
+      match (errors @ tolerated_errors, skipped_tokens @ inserted_tokens) with
+      | [], [] -> Exit_code.ok ~__LOC__
+      | _, _ ->
+          Logs.err (fun m ->
+              m "errors=%s\ntolerated errors=%s\nskipped=%s\ninserted=%s"
+                (Parsing_result2.format_errors errors)
+                (Parsing_result2.format_errors tolerated_errors)
+                (skipped_tokens
+                |> List_.map Tok.show_location
+                |> String.concat ", ")
+                (inserted_tokens
+                |> List_.map Tok.show_location
+                |> String.concat ", "));
+          Exit_code.invalid_code ~__LOC__)
   | DumpConfig config_str ->
       let settings = Semgrep_settings.load () in
       let token_opt = settings.api_token in
diff --git a/src/osemgrep/cli_show/Test_show_subcommand.ml b/src/osemgrep/cli_show/Test_show_subcommand.ml
index aeb63a0a6757..2ba191a3c56e 100644
--- a/src/osemgrep/cli_show/Test_show_subcommand.ml
+++ b/src/osemgrep/cli_show/Test_show_subcommand.ml
@@ -134,7 +134,6 @@ let test_version (caps : caps) : Testo.t =
 (* similar to test_misc.py test_cli_test_show_supported_languages *)
 let test_supported_languages (caps : caps) : Testo.t =
   t ~checked_output:(Testo.stdout ()) __FUNCTION__ (fun () ->
-      CapConsole.print caps#stdout (spf "Snapshot for %s" __FUNCTION__);
       let exit_code =
         Show_subcommand.main caps [| "semgrep-show"; "supported-languages" |]
       in
@@ -150,7 +149,6 @@ let test_dump_config (caps : caps) : Testo.t =
       ]
     __FUNCTION__
     (fun () ->
-      CapConsole.print caps#stdout (spf "Snapshot for %s" __FUNCTION__);
       let files = [ F.File ("rule.yml", eqeq_basic_content) ] in
       let exit_code =
         Testutil_files.with_tempfiles ~chdir:true ~verbose:true files
@@ -162,7 +160,6 @@ let test_dump_config (caps : caps) : Testo.t =
 
 let test_dump_rule_v2 (caps : caps) : Testo.t =
   t ~checked_output:(Testo.stdout ()) __FUNCTION__ (fun () ->
-      CapConsole.print caps#stdout (spf "Snapshot for %s" __FUNCTION__);
       let files = [ F.File ("rule.yml", eqeq_basic_content_v2) ] in
       let exit_code =
         Testutil_files.with_tempfiles ~chdir:true ~verbose:true files
@@ -187,7 +184,6 @@ let test_dump_ast (caps : caps) : Testo.t =
       ]
     __FUNCTION__
     (fun () ->
-      CapConsole.print caps#stdout (spf "Snapshot for %s" __FUNCTION__);
       let files = [ F.File ("foo.py", foo_py_content) ] in
       let exit_code =
         Testutil_files.with_tempfiles ~chdir:true ~verbose:true files
@@ -197,6 +193,18 @@ let test_dump_ast (caps : caps) : Testo.t =
       in
       Exit_code.Check.ok exit_code)
 
+let test_dump_ast_when_error (caps : caps) : Testo.t =
+  t ~checked_output:(Testo.stdxxx ()) ~normalize:[ Testutil_logs.mask_time ]
+    __FUNCTION__ (fun () ->
+      let files = [ F.File ("error.js", "function (") ] in
+      let exit_code =
+        Testutil_files.with_tempfiles ~chdir:true ~verbose:true files
+          (fun _cwd ->
+            Show_subcommand.main caps
+              [| "semgrep-show"; "dump-ast"; "error.js" |])
+      in
+      Exit_code.Check.invalid_code exit_code)
+
 let test_dump_pattern (caps : caps) : Testo.t =
   t ~checked_output:(Testo.stdout ())
     ~normalize:
@@ -206,7 +214,6 @@ let test_dump_pattern (caps : caps) : Testo.t =
       ]
     __FUNCTION__
     (fun () ->
-      CapConsole.print caps#stdout (spf "Snapshot for %s" __FUNCTION__);
       let exit_code =
         Show_subcommand.main caps
           [| "semgrep-show"; "dump-pattern"; "python"; "foo(..., $X == $X)" |]
@@ -219,7 +226,6 @@ let test_identity (caps : caps) : Testo.t =
    * and just capture stdout here.
    *)
   t ~checked_output:(Testo.stdxxx ()) __FUNCTION__ (fun () ->
-      CapConsole.print caps#stdout (spf "Snapshot for %s" __FUNCTION__);
       let exit_code =
         (* we need to be logged in otherwise we will not contact the server *)
         with_fake_login fake_settings (fun () ->
@@ -230,7 +236,6 @@ let test_identity (caps : caps) : Testo.t =
 
 let test_deployment (caps : caps) : Testo.t =
   t ~checked_output:(Testo.stdxxx ()) __FUNCTION__ (fun () ->
-      CapConsole.print caps#stdout (spf "Snapshot for %s" __FUNCTION__);
       let exit_code =
         with_fake_login fake_settings (fun () ->
             with_fake_deployment_response fake_deployment (fun () ->
@@ -255,6 +260,7 @@ let tests (caps : caps) =
       test_deployment caps;
       test_dump_pattern caps;
       test_dump_ast caps;
+      test_dump_ast_when_error caps;
       test_dump_config caps;
       test_dump_rule_v2 caps;
       (* TODO? engine_path and command_for_core *)
diff --git a/src/osemgrep/configuring/Semgrep_settings.ml b/src/osemgrep/configuring/Semgrep_settings.ml
index 21d37bd2b58c..782d2db4fa76 100644
--- a/src/osemgrep/configuring/Semgrep_settings.ml
+++ b/src/osemgrep/configuring/Semgrep_settings.ml
@@ -98,10 +98,6 @@ let set_api_token_from_env settings =
       Logs.info (fun m -> m "No API token found in environment variables");
       settings
 
-(* See cli/src/semgrep/settings.py `generate_default_settings` *)
-let generate_default_settings ?(include_env = true) () =
-  if include_env then set_api_token_from_env default else default
-
 (*****************************************************************************)
 (* Entry points *)
 (*****************************************************************************)
@@ -154,22 +150,27 @@ let from_file ?(maturity = Maturity.Default) () =
           m "Failed to load settings from %s: %s" !!settings msg);
       None
 
-(* Try loading from the file, and/or environment  *)
-(* coupling: cli/src/semgrep/settings.py get_default_contents *)
-(* You may ask WTF is this behavior. In pysemgrep we: *)
-(* 1. Generate default settings, but set the api token from the envrionment if *)
-(*    it exists. *)
-(* 2. Check if the settings file exists, if not return the default settings *)
-(* 3. If the settings file exists, but the api token is not set, set it from *)
-(*    the environment. *)
-(* 4. If the settings file exists and the api token is set, return the settings *)
-(*    file *)
-(* This happens due to some weird unpacking python semantics :/ *)
-(* This is a bit convoluted, but we need to keep the same behavior as pysemgrep *)
-(* to avoid breaking changes. *)
-let load ?maturity ?include_env () =
-  (* Step 1. *)
-  let default_settings = generate_default_settings ?include_env () in
+(* Try loading settings from the file and/or environment.
+ * coupling: cli/src/semgrep/settings.py get_default_contents()
+ * You may ask WTF is this behavior. In pysemgrep we:
+ * 1. Generate default settings, but set the api token from the envrionment if
+ *    it exists.
+ * 2. Check if the settings file exists, if not return the default settings
+ * 3. If the settings file exists, but the api token is not set in the settings
+ *    file, set it from the environment.
+ * 4. If the settings file exists and the api token is set, return the settings
+ *    file
+ * This happens due to some weird unpacking python semantics :/
+ * This is a bit convoluted, but we need to keep the same behavior as pysemgrep
+ * to avoid breaking changes.
+ *)
+let load ?maturity ?(include_env = true) () =
+  (* Step 1.
+   * See cli/src/semgrep/settings.py `generate_default_settings`
+   *)
+  let default_settings =
+    if include_env then set_api_token_from_env default else default
+  in
   (* Step 2. *)
   match from_file ?maturity () with
   (* Step 2. iff settings file doesn't exist *)
@@ -177,7 +178,7 @@ let load ?maturity ?include_env () =
       Logs.info (fun m -> m "No settings file found, using default settings");
       default_settings
   (* Step 3. *)
-  | Some ({ api_token = None; _ } as settings) when include_env ||| false ->
+  | Some ({ api_token = None; _ } as settings) when include_env ->
       Logs.info (fun m ->
           m
             "Settings file found, but API token is not set in file, pulling \
diff --git a/src/osemgrep/configuring/Semgrep_settings.mli b/src/osemgrep/configuring/Semgrep_settings.mli
index 72b338a19471..e1f11737d26e 100644
--- a/src/osemgrep/configuring/Semgrep_settings.mli
+++ b/src/osemgrep/configuring/Semgrep_settings.mli
@@ -12,12 +12,22 @@ val default : t
 (** Default settings. Exposed for testing *)
 
 (* Load the settings file (default: ~/.semgrep/settings.yml).
-   This function is intended for testing. *)
+ * This function is intended for testing.
+ * Return 'None' if the settings file can't be loaded.
+ *)
 val from_file : ?maturity:Maturity.t -> unit -> t option
 
 (* Load the settings file and additionally read 'api_token'
-   from the 'SEMGREP_APP_TOKEN' environment variable if 'include_env'
-   is true (default). *)
+ * from the 'SEMGREP_APP_TOKEN' environment variable if 'include_env'
+ * is true (default). The full spec is actually a bit complicated:
+ * 1. Generate default settings, but set the api token from the envrionment if
+ *    it exists.
+ * 2. Check if the settings file exists, if not return the default settings
+ * 3. If the settings file exists, but the api token is not set in the settings
+ *    file, set it from the environment.
+ * 4. If the settings file exists and the api token is set, return the settings
+ *    file
+ *)
 val load : ?maturity:Maturity.t -> ?include_env:bool -> unit -> t
 
 (* save and returns whether the save was successful *)
diff --git a/src/osemgrep/core/Metrics_.mli b/src/osemgrep/core/Metrics_.mli
index 73f505553ea6..2a1ba3bdd326 100644
--- a/src/osemgrep/core/Metrics_.mli
+++ b/src/osemgrep/core/Metrics_.mli
@@ -1,12 +1,10 @@
-(*
-   Configures metrics upload.
-
-   On   - Metrics always sent
-   Off  - Metrics never sent
-   Auto - Metrics only sent if config is pulled from the registry
-          or if using the Semgrep App.
-*)
-type config = On | Off | Auto [@@deriving show]
+(** Metrics upload configuration *)
+type config =
+  | On  (** Always send metrics *)
+  | Off  (** Never send metrics *)
+  | Auto
+      (** Send metrics when config is pulled from registry or using the Semgrep Cloud Platform *)
+[@@deriving show]
 
 (* For Cmdliner, to process the --metrics=<xxx> flag *)
 val converter : config Cmdliner.Arg.conv
@@ -16,32 +14,30 @@ val metrics_url : Uri.t
 
 type t = {
   mutable config : config;
-  (* this works with the Auto option *)
   mutable is_using_registry : bool;
   mutable is_using_app : bool;
-  (* The user agent used when sending data to https://metrics.semgrep.dev.
-   * We override the default value (e.g. "ocaml-cohttp/5.3.0") with a
-   * custom string built from:
-   *  - the version of semgrep
-   *  - the subcommand
-   *  - any custom value specified in $SEMGREP_USER_AGENT_APPEND.
-   *    For example, we set this variable to "Docker" when running
-   *    from a Docker container (see Dockerfile) allowing us to measure
-   *    usage of semgrep running from Docker container images that
-   *    we distribute.
-   *
-   * For example, this field might contain
-   *    ["Semgrep/1.39.0"; "(Docker)"; "(command/scan)"]
-   * which when concatenated will look like
-   *   "Semgrep/1.39.0 (Docker) (command/scan)"
-   *
-   * alt: we could encode this information in the payload instead.
-   *)
   mutable user_agent : string list;
-  (* The stuff we send to https://metrics.semgrep.dev
-   * Note that the user_agent itself contain useful metrics.
-   *)
+      (** The user agent used when sending data to {v https://metrics.semgrep.dev v}.
+
+          We override the default value (e.g. ["ocaml-cohttp/MAJOR.MINOR.PATCH"])
+          with a custom string built from:
+          - the version of semgrep
+          - the subcommand
+          - any custom value specified in [SEMGREP_USER_AGENT_APPEND]. For
+            example, we set this variable to ["Docker"] when running from a Docker
+            container (see Dockerfile) allowing us to measure usage of semgrep
+            running from Docker container images that we distribute.
+
+          For example, this field might contain
+          [["Semgrep/1.39.0"; "(Docker)"; "(command/scan)"]],
+          which when concatenated will look like
+          ["Semgrep/1.39.0 (Docker) (command/scan)"].
+
+          alt: we could encode this information in the payload instead. *)
   mutable payload : Semgrep_metrics_t.payload;
+      (** The actual metrics payload to send to {v https://metrics.semgrep.dev v}.
+
+          Note that the {!user_agent} itself contains useful metrics. *)
 }
 
 (* g stands for global. This is initialized with a default payload,
diff --git a/src/osemgrep/experiments/distributed_scans/Distributed_scan_stub.ml b/src/osemgrep/experiments/distributed_scans/Distributed_scan_stub.ml
new file mode 100644
index 000000000000..3f49fbf33701
--- /dev/null
+++ b/src/osemgrep/experiments/distributed_scans/Distributed_scan_stub.ml
@@ -0,0 +1,66 @@
+(* internal only *)
+type conf = {
+  merge_partial_results_dir : Fpath.t option;
+  merge_partial_results_output : Fpath.t option;
+  validate_partial_results_expected : Fpath.t option;
+  validate_partial_results_actual : Fpath.t option;
+}
+[@@deriving show]
+
+let hook_pro_read_and_merge_partial_scan_results :
+    (input_dir:Fpath.t -> output_json:Fpath.t -> unit) option ref =
+  ref None
+
+let hook_pro_read_and_validate_partial_scan_results :
+    (expected:Fpath.t -> actual:Fpath.t -> bool) option ref =
+  ref None
+
+let maybe_merge_partial_scan_results_then_exit (conf : conf) =
+  match (conf.merge_partial_results_dir, conf.merge_partial_results_output) with
+  | Some _, None
+  | None, Some _ ->
+      Logs.err (fun m ->
+          m
+            "Both or none of --x-merge-partial-results-dir and \
+             --x-merge-partial-results-output must be present.");
+      Error.exit_code_exn (Exit_code.fatal ~__LOC__)
+  | None, None -> ()
+  | Some input_dir, Some output_file -> (
+      match !hook_pro_read_and_merge_partial_scan_results with
+      | None ->
+          Logs.err (fun m ->
+              m
+                "You have requested a setting that requires the pro engine, \
+                 but do not have the pro engine installed.");
+          Error.exit_code_exn (Exit_code.fatal ~__LOC__)
+      | Some read_and_merge_partial_scan_results ->
+          read_and_merge_partial_scan_results input_dir output_file;
+          (* Not really an error, but abusing exit_code_exn for short circuiting *)
+          Error.exit_code_exn (Exit_code.ok ~__LOC__))
+
+let maybe_validate_partial_scan_results_then_exit (conf : conf) =
+  match
+    ( conf.validate_partial_results_expected,
+      conf.validate_partial_results_actual )
+  with
+  | Some _, None
+  | None, Some _ ->
+      Logs.err (fun m ->
+          m
+            "Both or none of --x-validate-partial-results-actual and \
+             --x-validate-partial-results-expected must be present.");
+      Error.exit_code_exn (Exit_code.fatal ~__LOC__)
+  | None, None -> ()
+  | Some expected, Some actual -> (
+      match !hook_pro_read_and_validate_partial_scan_results with
+      | None ->
+          Logs.err (fun m ->
+              m
+                "You have requested a setting that requires the pro engine, \
+                 but do not have the pro engine installed.");
+          Error.exit_code_exn (Exit_code.fatal ~__LOC__)
+      | Some read_and_validate_partial_scan_results ->
+          (* Abusing exit_code_exn for short circuiting, even for the non-error case. *)
+          if read_and_validate_partial_scan_results expected actual then
+            Error.exit_code_exn (Exit_code.ok ~__LOC__)
+          else Error.exit_code_exn (Exit_code.fatal ~__LOC__))
diff --git a/src/osemgrep/experiments/distributed_scans/Distributed_scan_stub.mli b/src/osemgrep/experiments/distributed_scans/Distributed_scan_stub.mli
new file mode 100644
index 000000000000..4df4d386ae67
--- /dev/null
+++ b/src/osemgrep/experiments/distributed_scans/Distributed_scan_stub.mli
@@ -0,0 +1,16 @@
+type conf = {
+  merge_partial_results_dir : Fpath.t option;
+  merge_partial_results_output : Fpath.t option;
+  validate_partial_results_expected : Fpath.t option;
+  validate_partial_results_actual : Fpath.t option;
+}
+[@@deriving show]
+
+val hook_pro_read_and_merge_partial_scan_results :
+  (input_dir:Fpath.t -> output_json:Fpath.t -> unit) option ref
+
+val hook_pro_read_and_validate_partial_scan_results :
+  (expected:Fpath.t -> actual:Fpath.t -> bool) option ref
+
+val maybe_merge_partial_scan_results_then_exit : conf -> unit
+val maybe_validate_partial_scan_results_then_exit : conf -> unit
diff --git a/src/osemgrep/experiments/distributed_scans/dune b/src/osemgrep/experiments/distributed_scans/dune
new file mode 100644
index 000000000000..a2c73150e74b
--- /dev/null
+++ b/src/osemgrep/experiments/distributed_scans/dune
@@ -0,0 +1,13 @@
+(library
+  (public_name semgrep.osemgrep_distributed_scan_stub)
+  (name osemgrep_distributed_scan_stub)
+  (wrapped false)
+  (libraries
+    osemgrep_core
+  )
+ (preprocess
+   (pps
+     ppx_deriving.show
+   )
+ )
+)
diff --git a/src/osemgrep/reporting/Cli_json_output.ml b/src/osemgrep/reporting/Cli_json_output.ml
index f8281f486ecb..d23129deaedc 100644
--- a/src/osemgrep/reporting/Cli_json_output.ml
+++ b/src/osemgrep/reporting/Cli_json_output.ml
@@ -309,7 +309,7 @@ let cli_match_of_core_match ~fixed_lines fixed_env (hrules : Rule.hrules)
        * entirety of every line involved in the match, not just the text that
        * matched. *)
       let lines =
-        Semgrep_output_utils.lines_of_file_at_range (start, end_) path
+        Semgrep_output_utils.lines_of_file_at_range_exn (start, end_) path
       in
       let lines = lines |> String.concat "\n" in
       {
diff --git a/src/osemgrep/reporting/Gitlab_output.ml b/src/osemgrep/reporting/Gitlab_output.ml
index c9be6fee712a..43c601de14f0 100644
--- a/src/osemgrep/reporting/Gitlab_output.ml
+++ b/src/osemgrep/reporting/Gitlab_output.ml
@@ -119,8 +119,7 @@ let format_cli_match (cli_match : Out.cli_match) : (string * JSON.yojson) list =
               [] )
   in
   let id =
-    (* TODO the ?index argument needs to be provided (for ci_unique_key duplicates) *)
-    Semgrep_hashing_functions.ci_unique_key cli_match
+    Semgrep_hashing_functions.syntactic_id cli_match
     |> Uuidm.of_binary_string |> Option.get |> Uuidm.to_string
   in
   let r =
diff --git a/src/osemgrep/reporting/Output.ml b/src/osemgrep/reporting/Output.ml
index 44b9e8c58149..7aa729b042c9 100644
--- a/src/osemgrep/reporting/Output.ml
+++ b/src/osemgrep/reporting/Output.ml
@@ -143,8 +143,8 @@ let format (kind : Output_format.t) (ctx : Out.format_context)
                     * contains a string, not a string list.
                     *)
                    match
-                     Semgrep_output_utils.lines_of_file_at_range (start, end_)
-                       path
+                     Semgrep_output_utils.lines_of_file_at_range_exn
+                       (start, end_) path
                    with
                    | [] -> ""
                    | x :: _ -> x (* TOPORT rstrip? *)
diff --git a/src/osemgrep/reporting/Semgrep_hashing_functions.ml b/src/osemgrep/reporting/Semgrep_hashing_functions.ml
index a1a240f0ff29..3afd3a915e50 100644
--- a/src/osemgrep/reporting/Semgrep_hashing_functions.ml
+++ b/src/osemgrep/reporting/Semgrep_hashing_functions.ml
@@ -1,28 +1,45 @@
-module Out = Semgrep_output_v1_t
+(* Austin Theriault
+ *
+ * Copyright (C) 2023-2024 Semgrep Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation, with the
+ * special exception on linking described in file LICENSE.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the file
+ * LICENSE for more details.
+ *)
 open Common
+open Fpath_.Operators
+module Out = Semgrep_output_v1_t
 
 (*****************************************************************************)
 (* Prelude *)
 (*****************************************************************************)
-(* Hashing functions to identify semgrep findings. This is mostly useful
- * for the Semgrep backend to know whether a finding has already been reported
- * because it matches the same hash than a previous finding (from a previous
- * scan).
- * This module provides 2 hashing functions:
+(* Hashing functions to identify semgrep findings.
+ *
+ * This is mostly useful for the Semgrep backend to know whether a finding has
+ * already been reported because it matches the same hash than a previous
+ * finding (from a previous scan).
+ *
+ * This module provides two hashing functions:
  *  - "CI/CLI unique key", using murmur, a.k.a "Syntactic ID"
  *    currently used for our Gitlab output and for baseline finding filtering
  *  - "Match-based" ID
  *    currently used for the fingerprint of a finding in scan and ci and
- *    used by our backend
+ *    used by our backend; used also for our JSON and SARIF output.
  *
  * Why two hashing functions? From Austin:
- *  We had the original cli match hash (the one with murmur), but that one
- *  changes whenever file formatting changes. "Match-based" ID tried to fix
- *  this, but in the long term we noticed that it doesn't have as much
- *  granularity as cli match, so sometimes some matches will have the same
- *  match-based ID but not be the same. We've discussed getting rid of cli
- *  match (it's also insecure using the murmur hash, especially for secrets...),
- *  but it would be a big effort apparently and break some things.
+ *   We had the original cli match hash (the one with murmur), but that one
+ *   changes whenever file formatting changes. "Match-based" ID tried to fix
+ *   this, but in the long term we noticed that it doesn't have as much
+ *   granularity as cli match, so sometimes some matches will have the same
+ *   match-based ID but not be the same. We've discussed getting rid of cli
+ *   match (it's also insecure using the murmur hash, especially for secrets...),
+ *   but it would be a big effort apparently and break some things.
  *
  * For full context, see also
  * https://www.notion.so/semgrep/Identifying-unique-findings-match_based_id-and-syntactic_id-cf1a59099c06417d96f777802050ea18#0fde2306cb7c4c5991387b458dcfb064
@@ -38,6 +55,8 @@ open Common
  *
  * e.g. if we have the hash 123AVDe234 from step 1 and the finding is the first
  * instance seen in the file, the hash we store would be 123AVDe234_0.
+ *
+ * This is a port of a few functions in rule_match.py and rule.py
  *)
 
 (*****************************************************************************)
@@ -80,11 +99,39 @@ let name (c : Out.cli_match) =
     | None -> default
   else default
 
+(* TODO(pad): the algorithm below is not what pysemgrep and formula_string do.
+ * For instance, pysemgrep does care about the taint labels in a taint source
+ * so we need to redesign the whole thing to better match what pysemgrep does.
+ *
+ * See Unit_reporting.ml for some tests.
+ * coupling: rule.py formula_string
+ *)
+let string_of_formulas (xs : Rule.formula list) : string =
+  (* We need to do this as flattening and sorting does not always produce the
+   * same result: [[a c] b] become "a c b" while [a c b] becomes "a b c". *)
+  let rec go formula =
+    match formula.Rule.f with
+    | Rule.P p -> fst p.pstr
+    | Rule.Anywhere (_, formula)
+    | Rule.Inside (_, formula)
+    | Rule.Not (_, formula) ->
+        go formula
+    | Rule.Or (_, formulae)
+    | Rule.And (_, formulae) ->
+        let xs = List_.map go formulae in
+        String.concat " " (List.sort String.compare xs)
+  in
+  let xpat_strs = List_.map go xs in
+  let sorted_xpat_strs = List.sort String.compare xpat_strs in
+  let xpat_str = String.concat " " sorted_xpat_strs in
+  xpat_str
+
 (*****************************************************************************)
 (* Entry points *)
 (*****************************************************************************)
 
-let ci_unique_key (c : Out.cli_match) =
+(* coupling: rule_match.py get_ci_unique_key() *)
+let ci_unique_key (c : Out.cli_match) : string * Fpath.t * string * int =
   (* ugly: coupling: Cli_json_output.index_match_based_ids() *)
   let index =
     let fingerprint = c.extra.fingerprint in
@@ -94,119 +141,86 @@ let ci_unique_key (c : Out.cli_match) =
       Logs.warn (fun m -> m "wrong fingerprint format: %s" fingerprint);
       0)
   in
-  (* TODO the third element should be "syntactic_context", as defined in rule_match.py:
-        # The code that matched, with whitespace and nosem comments removed.
-        #
-        # This is useful to so that findings can be considered the same
-        # when `    5 == 5` is updated to `  5 == 5  # nosemgrep`,
-        # and thus CI systems don't retrigger notifications.
-        lines = [*self.lines]
-        if len(lines) > 0:
-            lines[0] = NOSEM_INLINE_COMMENT_RE.sub("", lines[0])
-            lines[0] = lines[0].rstrip() + "\n"
+  (* TODO the code for syntactic_context should be:
+   *     # The code that matched, with whitespace and nosem comments removed.
+   *     #
+   *     # This is useful to so that findings can be considered the same
+   *     # when `    5 == 5` is updated to `  5 == 5  # nosemgrep`,
+   *     # and thus CI systems don't retrigger notifications.
+   *     lines = [*self.lines]
+   *     if len(lines) > 0:
+   *         lines[0] = NOSEM_INLINE_COMMENT_RE.sub("", lines[0])
+   *         lines[0] = lines[0].rstrip() + "\n"
+   *
+   *     code = "".join(lines)  # the lines end with newlines already
+   *     code = textwrap.dedent(code)
+   *     code = code.strip()
+   *     return code
+   *)
+  let syntactic_context = String.trim c.extra.lines in
+  (name c, c.path, syntactic_context, index)
 
-        code = "".join(lines)  # the lines end with newlines already
-        code = textwrap.dedent(code)
-        code = code.strip()
-        return code
-  *)
-  (* TODO the return value in python's ci_unique_key is the hex output of the
-     murmur3 hash, here the binary value is returned directly
-  *)
+(* coupling: rule_match.py get_syntactic_id()
+ * TODO? the return value in pysemgrep is the hex output of the
+ * murmur3 hash, here the binary value is returned directly
+ *)
+let syntactic_id (c : Out.cli_match) : string =
+  let name_, path, syntactic_context, index = ci_unique_key c in
   let repr = Python_str_repr.repr in
-  spf "(%s, %s, %s, %u)"
-    (repr (name c))
-    (repr (Fpath.to_string c.path))
-    (repr (String.trim c.extra.lines))
+  spf "(%s, %s, %s, %u)" (repr name_) (repr !!path) (repr syntactic_context)
     index
   |> Murmur3.hash128
 
+(* coupling: rule_match.py main part of get_match_based_key() *)
+let match_formula_interpolated_str (rule : Rule.t) metavars : string =
+  (* We don't use pattern sanitizers (nor propagators) at all in
+   * calculating match based id, which seems weird, but this is because if code
+   * matches a pattern sanitizer, then its ALWAYS sanitized which means it
+   * would never show up as a taint mode finding. So we can safely ignore it,
+   * since it shouldn't affect the match based id.
+   *)
+  let mode =
+    match rule.mode with
+    | `Taint { Rule.sources; sanitizers = _; sinks; propagators = _ } ->
+        `Taint { Rule.sources; sanitizers = None; sinks; propagators = [] }
+    | (`Search _ | `Extract _ | `Steps _ | `SCA _) as mode -> mode
+  in
+  let xs = Rule.formulas_of_mode mode in
+  let str = string_of_formulas xs in
+  Metavar_replacement.interpolate_metavars str
+    (Metavar_replacement.of_out (metavars ||| []))
+
 (* This is a cursed function that calculates everything but the index part
- * of the match_based_id. It's cursed because we need hashes to be exactly
- * the same, but the algorithm used on the python side to generate
- * the final string thats hashed has some python specific quirks.
+ * of the match_based_id (hence the partial suffix). It is cursed because we
+ * need hashes to be exactly the same, but the algorithm used on the Python
+ * side to generate the final string thats hashed has some Python specific
+ * quirks.
  *
- * The way match based ID is calculated on the python side
- * is as follows:
+ * The way match based ID is calculated on the python side is as follows:
  * (see https://github.com/returntocorp/semgrep/blob/2d34ce584d16c4e954349690a5f12fae877a94d6/cli/src/semgrep/rule.py#L289-L334)
  * 1. Sort all top level keys (i.e pattern, patterns etc.) alphabetically
- * 2. For each key: DFS the tree and find all pattern values (i.e. the rhs of pattern: <THING>)
+ * 2. For each key: DFS the tree and find all pattern values (i.e. the rhs of
+ *    pattern: <THING>)
  * 3. Sort all pattern values alphabetically and concatenate them with a space
  * 4. Concatenate all the sorted pattern values with a space
  * 5. Hash the tuple `(sorted_pattern_values, path, rule_id)` w/ blake2b
- * 6. Append the index of the match in the list of matches for the rule (see [index_match_based_ids])
- *
- * Austin: I wrote the initial match based ID, and below is a port of it.
- * Looking back it seems like I ended up writing a roundabout version of the
- * below algorithm which is how this function works
- * 1. Same as before
- * 2. for each rule: get all xpatterns, then sort them, and concatenate them
- *    with a space
- * 3. Sort all of step 2 results alphabetically and concatenate them with a space
- * 4. Same as step 5 and 6
- *
- * Assumptions:
- * Somewhere it seems that all keys are already sorted alphabetically when the
- * rule is parsed.
- * I have not seen this code. I simply have faith that it is true and this will
- * not change.
- *
- * I'm also hoping that [interpolate_metavariables] works similar to what we do
- * on the python side
- *
- * I have not tested this code beyond checking a bunch of examples manually.
+ * 6. Append the index of the match in the list of matches for the rule
+ *    (see [index_match_based_ids])
  *
- * There's some weird thing we do w/ join mode. I am hoping that this doesn't
- * matter irl
- *
- * We also don't use pattern sanitizers at all in calculating match based id,
- * which seems weird, but this because if code matches a pattern sanitizer,
- * then its ALWAYS sanitized which means it would never show up as a taint mode
- * finding. So we can safely ignore it, since it shouldn't affect the
- * match based id.
+ * coupling: rule_match.py get_match_based_id ()
  *)
 let match_based_id_partial (rule : Rule.t) (rule_id : Rule_ID.t) metavars path :
     string =
-  (* the python implementation does not include sanitizers and propagators; so
-   * as to not break fingerprints we ignore sanitizers, too. see above
-   * assumptions on why.
+  let str_interp = match_formula_interpolated_str rule metavars in
+  (* We have been hashing w/ this PosixPath thing in Python so we must recreate
+   * it here. We also have been hashing a tuple formatted as below.
    *)
-  let mode =
-    match rule.mode with
-    | `Taint { Rule.sources; sanitizers = _; sinks; propagators = _ } ->
-        `Taint { Rule.sources; sanitizers = None; sinks; propagators = [] }
-    | (`Search _ | `Extract _ | `Steps _ | `SCA _) as mode -> mode
-  in
-  let formulae = Rule.formula_of_mode mode in
-  (* We need to do this as flattening and sorting does not always produce the
-   * same result: [[a c] b] become "a c b" while [a c b] becomes "a b c". *)
-  let rec go formula =
-    match formula.Rule.f with
-    | Rule.P p -> fst p.pstr
-    | Rule.Anywhere (_, formula)
-    | Rule.Inside (_, formula)
-    | Rule.Not (_, formula) ->
-        go formula
-    | Rule.Or (_, formulae)
-    | Rule.And (_, formulae) ->
-        let xs = List_.map go formulae in
-        String.concat " " (List.sort String.compare xs)
-  in
-  let xpat_strs = List_.map go formulae in
-  let sorted_xpat_strs = List.sort String.compare xpat_strs in
-  let xpat_str = String.concat " " sorted_xpat_strs in
-  let metavars = Option.value ~default:[] metavars in
-  let xpat_str_interp =
-    Metavar_replacement.interpolate_metavars xpat_str
-      (Metavar_replacement.of_out metavars)
-  in
-  (* We have been hashing w/ this PosixPath thing in python so we must recreate it here  *)
-  (* We also have been hashing a tuple formatted as below *)
   let string =
     spf "(%s, PosixPath(%s), %s)"
-      (Python_str_repr.repr xpat_str_interp)
+      (Python_str_repr.repr str_interp)
       (Python_str_repr.repr path)
       (Python_str_repr.repr (Rule_ID.to_string rule_id))
   in
   let hash = Digestif.BLAKE2B.digest_string string |> Digestif.BLAKE2B.to_hex in
+  Logs.debug (fun m -> m "match_key = %s, match_id = %s" string hash);
   hash
diff --git a/src/osemgrep/reporting/Semgrep_hashing_functions.mli b/src/osemgrep/reporting/Semgrep_hashing_functions.mli
index a16715c04cd3..5e08da4f46e5 100644
--- a/src/osemgrep/reporting/Semgrep_hashing_functions.mli
+++ b/src/osemgrep/reporting/Semgrep_hashing_functions.mli
@@ -1,6 +1,6 @@
 module Out = Semgrep_output_v1_t
 
-val ci_unique_key : Out.cli_match -> string
+val syntactic_id : Out.cli_match -> string
 (** A unique key designed with notification user experience in mind.
     Results in fewer unique findings than core_unique_key.
 
@@ -11,3 +11,7 @@ val match_based_id_partial :
   Rule.t -> Rule_ID.t -> Out.metavars option -> string -> string
 (** The fingerprint used to uniquely identify a match. Since this is used by the
     backend, it is crucial to have identical output as in pysemgrep. *)
+
+(* for unit testing *)
+val match_formula_interpolated_str :
+  Rule.t -> Semgrep_output_v1_t.metavars option -> string
diff --git a/src/osemgrep/reporting/Unit_reporting.ml b/src/osemgrep/reporting/Unit_reporting.ml
new file mode 100644
index 000000000000..31f39c69c0b6
--- /dev/null
+++ b/src/osemgrep/reporting/Unit_reporting.ml
@@ -0,0 +1,163 @@
+(* Yoann Padioleau
+ *
+ * Copyright (C) 2024 Semgrep, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation, with the
+ * special exception on linking described in file LICENSE.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the file
+ * LICENSE for more details.
+ *)
+open Common
+module Out = Semgrep_output_v1_t
+
+(*****************************************************************************)
+(* Prelude *)
+(*****************************************************************************)
+
+let t = Testo.create
+
+(*****************************************************************************)
+(* Helpers *)
+(*****************************************************************************)
+
+(*****************************************************************************)
+(* Tests *)
+(*****************************************************************************)
+
+(* If you add an entry, double check pysemgrep returns similar things by
+ * copy pasting the rule, write a target with a match, then
+ * use --debug on the rule with a match and inspect the logs (we now log the
+ * 'match_key=' in rule_match.py so just look at the logs).
+ * coupling: see also test_match_based_id.py
+ *)
+let string_of_formulas_expectations =
+  [
+    (* e2e/rules/eqeq.yaml 1st rule *)
+    ( "basic rule",
+      {|
+rules:
+  - id: assert-eqeq-is-ok
+    pattern: $X == $X
+    message: "possibly useless comparison but in eq function"
+    languages: [python]
+    severity: ERROR
+|},
+      [ ("$X", "1") ],
+      "1 == 1" );
+    (* e2e/rules/eqeq.yaml 2nd rule *)
+    ( "many patterns",
+      {|
+rules:
+  - id: eqeq-is-bad
+    patterns:
+      - pattern-not-inside: |
+          def __eq__(...):
+              ...
+      - pattern-not-inside: assert(...)
+      - pattern-not-inside: assertTrue(...)
+      - pattern-not-inside: assertFalse(...)
+      - pattern-either:
+          - pattern: $X == $X
+          - pattern: $X != $X
+          - patterns:
+              - pattern-inside: |
+                  def __init__(...):
+                      ...
+              - pattern: self.$X == self.$X
+      - pattern-not: 1 == 1
+    message: "useless comparison operation `$X == $X` or `$X != $X`"
+    languages: [python]
+    severity: ERROR
+    metadata:
+      shortlink: https://sg.run/xyz1
+      source: https://semgrep.dev/r/eqeq-bad
+|},
+      [ ("$X", "a+b") ],
+      "a+b != a+b a+b == a+b def __init__(...):\n\
+      \    ...\n\
+      \ self.a+b == self.a+b 1 == 1 assert(...) assertFalse(...) \
+       assertTrue(...) def __eq__(...):\n\
+      \    ...\n" );
+    (* e2e/rules/taint_trace.yaml TODO: wrong generation! *)
+    ( "taint with labels (WRONG TOFIX)",
+      {|
+rules:
+  - id: taint-trace
+    message: found an error
+    languages:
+      - cpp
+      - c
+    severity: WARNING
+    mode: taint
+    metadata:
+      interfile: true
+    pattern-sources:
+      - label: USER_CONTROLLED
+        patterns:
+          - pattern: SOURCE()
+      - label: SCALAR
+        requires: USER_CONTROLLED
+        patterns:
+          - pattern-either:
+              - pattern: $LHS + $RHS
+          - focus-metavariable:
+              - $RHS
+              - $LHS
+    pattern-sinks:
+      - requires: USER_CONTROLLED and SCALAR
+        patterns:
+          - pattern-either:
+              - pattern: SINK(<... $SRC ...>)
+          - focus-metavariable: $SRC
+|},
+      [ ("$RHS", "res1"); ("$SRC", "res2") ],
+      (* TODO: this is wrong; osemgrep generate what is below but it should generate
+       * instead:
+       * "$LHS res1 $LHS + res1 SCALAR USER_CONTROLLED SOURCE() USER_CONTROLLED res2 SINK(<... res2 ...>) USER_CONTROLLED and SCALAR"
+       * to take into account the label/requore/focus-metavariable in the rule
+       *)
+      "$LHS + res1 SINK(<... res2 ...>) SOURCE()" );
+  ]
+
+let test_string_of_formulas _caps =
+  Testo.categorize "string_of_formulas"
+    (string_of_formulas_expectations
+    |> List_.map (fun (title, rule, mvars, expected) ->
+           t title (fun () ->
+               UTmp.with_temp_file ~contents:rule (fun file ->
+                   match Parse_rule.parse file with
+                   | Ok [ rule ] ->
+                       let mvars =
+                         mvars
+                         |> List_.map (fun (mvar, mvalue_str) ->
+                                ( mvar,
+                                  Out.
+                                    {
+                                      abstract_content = mvalue_str;
+                                      propagated_value = None;
+                                      (* not used by Metavar_replacement *)
+                                      start = { line = 0; col = 0; offset = 0 };
+                                      end_ = { line = 0; col = 0; offset = 0 };
+                                    } ))
+                       in
+                       let res =
+                         Semgrep_hashing_functions
+                         .match_formula_interpolated_str rule (Some mvars)
+                       in
+                       Alcotest.(check string) __LOC__ expected res
+                   | _ ->
+                       failwith
+                         (spf "could not parse or more than one rule for %s"
+                            title)))))
+
+(*****************************************************************************)
+(* Entry point *)
+(*****************************************************************************)
+
+let tests caps =
+  Testo.categorize_suites "Osemgrep reporting" [ test_string_of_formulas caps ]
diff --git a/src/osemgrep/reporting/Unit_reporting.mli b/src/osemgrep/reporting/Unit_reporting.mli
new file mode 100644
index 000000000000..8938804eb1fc
--- /dev/null
+++ b/src/osemgrep/reporting/Unit_reporting.mli
@@ -0,0 +1,1 @@
+val tests : < > -> Testo.t list
diff --git a/src/parsing/Parse_lockfile.ml b/src/parsing/Parse_lockfile.ml
index eb98062eabf0..541dbdef2b30 100644
--- a/src/parsing/Parse_lockfile.ml
+++ b/src/parsing/Parse_lockfile.ml
@@ -6,7 +6,23 @@ let parse_lockfile :
     Fpath.t ->
     Dependency.t list = function
   (* TODO: add parsers, guard behind semgrep-pro  *)
-  | PackageLockJsonV3 -> fun _ _ -> []
+  | PipRequirementsTxt
+  | PoetryLock
+  | PipfileLock
+  | NpmPackageLockJson
+  | YarnLock
+  | PnpmLock
+  | GemfileLock
+  | GoMod
+  | CargoLock
+  | MavenDepTree
+  | GradleLockfile
+  | ComposerLock
+  | NugetPackagesLockJson
+  | PubspecLock
+  | SwiftPackageResolved
+  | MixLock ->
+      fun _ _ -> []
 
 let parse_manifest :
     In.manifest_kind -> Fpath.t -> Dependency.manifest_dependency list =
@@ -19,6 +35,7 @@ let parse_manifest :
   | `CargoToml
   | `PomXml
   | `BuildGradle
+  | `SettingsGradle
   | `ComposerJson
   | `NugetManifestJson
   | `PubspecYaml
diff --git a/src/parsing/Parse_rule.ml b/src/parsing/Parse_rule.ml
index 32ecd335479c..5a078e4541f6 100644
--- a/src/parsing/Parse_rule.ml
+++ b/src/parsing/Parse_rule.ml
@@ -786,7 +786,8 @@ let parse_aws_request env key value : (Rule.aws_request, Rule_error.t) result =
   in
   let/ access_key_id = take_key request_dict env parse_string "access_key_id" in
   let/ region = take_key request_dict env parse_string "region" in
-  Ok Rule.{ secret_access_key; access_key_id; region }
+  let/ session_token = take_opt request_dict env parse_string "session_token" in
+  Ok Rule.{ secret_access_key; access_key_id; region; session_token }
 
 let parse_aws_validator env key value : (Rule.validator, Rule_error.t) result =
   let/ validator_dict = parse_dict env key value in
diff --git a/src/reporting/Core_text_output.ml b/src/reporting/Core_text_output.ml
index 109ec552990b..b342dba72e04 100644
--- a/src/reporting/Core_text_output.ml
+++ b/src/reporting/Core_text_output.ml
@@ -56,7 +56,7 @@ let print_match_lines ?(str = "") ?(spaces = 0) (caps : < Cap.stdout >)
     (file : Fpath.t) (start_line : int) (end_line : int) : unit =
   let print = CapConsole.print caps#stdout in
   let prefix = spf "%s:%d" !!file start_line in
-  let lines_str = UFile.lines_of_file (start_line, end_line) file in
+  let lines_str = UFile.lines_of_file_exn (start_line, end_line) file in
   let prefix = if str = "" then prefix else prefix ^ " " ^ str in
   let spaces_string = String.init spaces (fun _ -> ' ') in
   print (spaces_string ^ prefix);
diff --git a/src/reporting/Nosemgrep.ml b/src/reporting/Nosemgrep.ml
index 1d1e1dc4878e..5f33606f5d7c 100644
--- a/src/reporting/Nosemgrep.ml
+++ b/src/reporting/Nosemgrep.ml
@@ -12,6 +12,7 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the file
  * LICENSE for more details.
  *)
+open Common
 open Fpath_.Operators
 module OutJ = Semgrep_output_v1_j
 
@@ -115,7 +116,7 @@ let rule_match_nosem (pm : Pattern_match.t) : bool * Core_error.t list =
     (* Minus one, because we need the preceding line. *)
     let start, end_ = pm.range_loc in
     let start_line = max 0 (start.pos.line - 1) in
-    UFile.lines_of_file (start_line, end_.pos.line) path
+    UFile.lines_of_file_exn (start_line, end_.pos.line) path
     |> List_.mapi (fun idx x -> (start_line + idx, x))
   in
 
@@ -259,8 +260,24 @@ let produce_ignored (matches : Core_result.processed_match list) :
   let matches, wide_errors =
     matches
     |> List_.map (fun (pm : Core_result.processed_match) ->
-           let is_ignored, errors = rule_match_nosem pm.pm in
-           ({ pm with is_ignored }, errors))
+           try
+             let is_ignored, errors = rule_match_nosem pm.pm in
+             ({ pm with is_ignored }, errors)
+           with
+           | (Time_limit.Timeout _ | Common.ErrorOnFile _) as exn ->
+               Exception.catch_and_reraise exn
+           | exn ->
+               (* let's rewrap the exn with ErrorOnFile *)
+               let e = Exception.catch exn in
+               let trace = Exception.get_trace e in
+               let msg = Printexc.to_string exn in
+               let exn =
+                 Common.ErrorOnFile
+                   ( spf "produce_ignored: %s" msg,
+                     pm.pm.path.internal_path_to_content )
+               in
+               let e = Exception.create exn trace in
+               Exception.reraise e)
     |> List_.split
   in
   (matches, List_.flatten wide_errors)
diff --git a/src/rpc/RPC.ml b/src/rpc/RPC.ml
index b6c8b287e581..ad7fb0003477 100644
--- a/src/rpc/RPC.ml
+++ b/src/rpc/RPC.ml
@@ -64,11 +64,13 @@ let handle_call (caps : < Cap.exec ; Cap.tmp >) :
   | `CallValidate path ->
       let valid = RPC_return.validate path in
       Ok (`RetValidate valid)
-  | `CallResolveDependencies manifests -> (
+  | `CallResolveDependencies dependency_sources -> (
       match !RPC_return.hook_resolve_dependencies with
       | Some resolve_dependencies ->
           let resolved =
-            resolve_dependencies (caps :> < Cap.exec ; Cap.tmp >) manifests
+            resolve_dependencies
+              (caps :> < Cap.exec ; Cap.tmp >)
+              dependency_sources
           in
           Ok (`RetResolveDependencies resolved)
       | None ->
diff --git a/src/rpc/RPC_return.mli b/src/rpc/RPC_return.mli
index 7e479d2d9d91..4dd7ae579de5 100644
--- a/src/rpc/RPC_return.mli
+++ b/src/rpc/RPC_return.mli
@@ -18,8 +18,8 @@ val validate : Out.fpath -> bool
 
 val hook_resolve_dependencies :
   (< Cap.exec ; Cap.tmp > ->
-  Out.manifest list ->
-  (Out.manifest * Out.resolution_result) list)
+  Out.dependency_source list ->
+  (Out.dependency_source * Out.resolution_result) list)
   option
   ref
 
diff --git a/src/rule/Rule.ml b/src/rule/Rule.ml
index b2c3e67c51fa..03f5f1140130 100644
--- a/src/rule/Rule.ml
+++ b/src/rule/Rule.ml
@@ -457,6 +457,7 @@ type aws_request = {
   secret_access_key : string;
   access_key_id : string;
   region : string;
+  session_token : string option;
 }
 [@@deriving show]
 
@@ -820,7 +821,7 @@ let kind_of_formula = function
   | Not _ ->
       "formula"
 
-let rec formula_of_mode (mode : mode) =
+let rec formulas_of_mode (mode : mode) : formula list =
   match mode with
   | `Search formula -> [ formula ]
   | `Taint { sources = _, sources; sanitizers; sinks = _, sinks; propagators }
@@ -835,7 +836,7 @@ let rec formula_of_mode (mode : mode) =
   | `Extract { formula; extract = _; _ } -> [ formula ]
   | `Steps steps ->
       List.concat_map
-        (fun step -> formula_of_mode (step.step_mode :> mode))
+        (fun step -> formulas_of_mode (step.step_mode :> mode))
         steps
   | `SCA _ -> []
 
diff --git a/src/rule/Visit_rule.ml b/src/rule/Visit_rule.ml
index 74f502fe4818..4ce5c91bd7f9 100644
--- a/src/rule/Visit_rule.ml
+++ b/src/rule/Visit_rule.ml
@@ -52,8 +52,8 @@ let visit_xpatterns (func : Xpattern.t -> inside:bool -> unit)
   aux func formula
 
 let xpatterns_of_rule (rule : Rule.t) : Xpattern.t list =
-  let formulae = formula_of_mode rule.mode in
+  let xs = formulas_of_mode rule.mode in
   let xpat_store = ref [] in
   let visit xpat ~inside:_ = xpat_store := xpat :: !xpat_store in
-  List.iter (visit_xpatterns visit) formulae;
+  List.iter (visit_xpatterns visit) xs;
   !xpat_store
diff --git a/src/target/Lockfile_kind.ml b/src/target/Lockfile_kind.ml
index e23a294efbc9..59a6ae045561 100644
--- a/src/target/Lockfile_kind.ml
+++ b/src/target/Lockfile_kind.ml
@@ -1,7 +1,22 @@
-type t = PackageLockJsonV3 [@@deriving show, eq, yojson]
+type t = Semgrep_output_v1_j.lockfile_kind [@@deriving show, eq, yojson]
 
 let to_ecosystem : t -> Semgrep_output_v1_t.ecosystem = function
-  | PackageLockJsonV3 -> `Npm
+  | PipRequirementsTxt -> `Pypi
+  | PoetryLock -> `Pypi
+  | PipfileLock -> `Pypi
+  | NpmPackageLockJson -> `Npm
+  | YarnLock -> `Npm
+  | PnpmLock -> `Npm
+  | GemfileLock -> `Gem
+  | GoMod -> `Gomod
+  | CargoLock -> `Cargo
+  | MavenDepTree -> `Maven
+  | GradleLockfile -> `Maven
+  | ComposerLock -> `Composer
+  | NugetPackagesLockJson -> `Nuget
+  | PubspecLock -> `Pub
+  | SwiftPackageResolved -> `SwiftPM
+  | MixLock -> `Hex
 
 let supported_lockfiles = String.concat "," [ "package-lock.json v3" ]
 
@@ -9,8 +24,23 @@ let unsupported_lockfile_message (lockfile_s : string) =
   Common.spf "unsupported lockfile: %s; supported lockfile tags are: %s"
     lockfile_s supported_lockfiles
 
-let of_string = function
-  | "PackageLockV3" -> PackageLockJsonV3
+let of_string : string -> t = function
+  | "PipRequirementsTxt" -> PipRequirementsTxt
+  | "PoetryLock" -> PoetryLock
+  | "PipfileLock" -> PipfileLock
+  | "NpmPackageLockJson" -> NpmPackageLockJson
+  | "YarnLock" -> YarnLock
+  | "PnpmLock" -> PnpmLock
+  | "GemfileLock" -> GemfileLock
+  | "GoMod" -> GoMod
+  | "CargoLock" -> CargoLock
+  | "MavenDepTree" -> MavenDepTree
+  | "GradleLockfile" -> GradleLockfile
+  | "ComposerLock" -> ComposerLock
+  | "NugetPackagesLockJson" -> NugetPackagesLockJson
+  | "PubspecLock" -> PubspecLock
+  | "SwiftPackageResolved" -> SwiftPackageResolved
+  | "MixLock" -> MixLock
   | s -> failwith (unsupported_lockfile_message s)
 
 let wrap = of_string
diff --git a/src/target/Lockfile_xtarget.ml b/src/target/Lockfile_xtarget.ml
index 101557854c66..345286200fcb 100644
--- a/src/target/Lockfile_xtarget.ml
+++ b/src/target/Lockfile_xtarget.ml
@@ -54,14 +54,18 @@ let resolve_manifest parser (target : Target.manifest) : manifest =
       lazy (parser target.kind target.path.internal_path_to_content);
   }
 
-let resolve manifest_parser parser (target : Target.lockfile) : t =
+let resolve manifest_parser parser (lockfile_target : Target.lockfile)
+    (manifest_target : Target.manifest option) : t =
   let manifest =
-    Option.map (resolve_manifest manifest_parser) target.manifest
+    Option.map (resolve_manifest manifest_parser) manifest_target
   in
   {
-    target;
+    target = lockfile_target;
     manifest;
-    lazy_content = lazy (UFile.read_file target.path.internal_path_to_content);
+    lazy_content =
+      lazy (UFile.read_file lockfile_target.path.internal_path_to_content);
     lazy_dependencies =
-      lazy (parser target.kind manifest target.path.internal_path_to_content);
+      lazy
+        (parser lockfile_target.kind manifest
+           lockfile_target.path.internal_path_to_content);
   }
diff --git a/src/target/Manifest_kind.ml b/src/target/Manifest_kind.ml
index 40e41a33e3c1..470cfdaf1cf5 100644
--- a/src/target/Manifest_kind.ml
+++ b/src/target/Manifest_kind.ml
@@ -8,6 +8,7 @@ let to_ecosystem : t -> Semgrep_output_v1_t.ecosystem = function
   | `CargoToml -> `Cargo
   | `PomXml -> `Maven
   | `BuildGradle -> `Maven
+  | `SettingsGradle -> `Maven
   | `ComposerJson -> `Composer
   | `NugetManifestJson -> `Nuget
   | `PubspecYaml -> `Pub
@@ -46,6 +47,3 @@ let of_string s =
 (* For use in Input_to_core.atd *)
 let wrap = of_string
 let unwrap = Semgrep_output_v1_j.show_manifest_kind
-
-let of_lockfile_kind = function
-  | Lockfile_kind.PackageLockJsonV3 -> `PackageJson
diff --git a/src/target/Target.ml b/src/target/Target.ml
index 92de37dddbe0..f0b714b77f47 100644
--- a/src/target/Target.ml
+++ b/src/target/Target.ml
@@ -35,13 +35,14 @@ type path = {
 type manifest = { path : path; kind : Manifest_kind.t }
 [@@deriving show, yojson]
 
-type lockfile = {
-  path : path;
-  kind : Lockfile_kind.t;
-  manifest : manifest option;
-}
+type lockfile = { path : path; kind : Lockfile_kind.t }
 [@@deriving show, yojson]
 
+type dependency_source =
+  | ManifestOnly of manifest
+  | LockfileOnly of lockfile
+  | ManifestAndLockfile of manifest * lockfile
+
 let pp_debug_lockfile f t =
   Format.fprintf f "%s" (t.path.internal_path_to_content |> Fpath.to_string)
 
@@ -76,6 +77,8 @@ type regular = {
   products : Out.product list;
       [@to_yojson out_product_list_to_yojson]
       [@of_yojson out_product_list_of_yojson]
+  (* TODO: (sca) associate each target with a dependency_source here rather than only a lockfile.
+   * We did not do this at the time that we added dependency_source because this code was unused at that point. *)
   lockfile : lockfile option;
 }
 [@@deriving show, yojson]
@@ -123,8 +126,8 @@ let path_of_origin (origin : Origin.t) : path =
 let mk_regular ?lockfile analyzer products (origin : Origin.t) : regular =
   { path = path_of_origin origin; analyzer; products; lockfile }
 
-let mk_lockfile ?manifest kind (origin : Origin.t) : lockfile =
-  { path = path_of_origin origin; kind; manifest }
+let mk_lockfile kind (origin : Origin.t) : lockfile =
+  { path = path_of_origin origin; kind }
 
 let mk_manifest kind (origin : Origin.t) : manifest =
   { path = path_of_origin origin; kind }
@@ -139,15 +142,9 @@ let mk_target (xlang : Xlang.t) (file : Fpath.t) : t =
 (* Input_to_core -> Target *)
 (*****************************************************************************)
 
-let manifest_target_of_input_to_core
-    ({ path; manifest_kind = kind } : In.manifest_target) : manifest =
-  mk_manifest kind (File (Fpath.v path))
-
 let lockfile_target_of_input_to_core
-    ({ path; lockfile_kind = kind; manifest_target } : In.lockfile_target) :
-    lockfile =
-  let manifest = Option.map manifest_target_of_input_to_core manifest_target in
-  mk_lockfile ?manifest kind (File (Fpath.v path))
+    ({ path; lockfile_kind = kind } : In.lockfile_target) : lockfile =
+  mk_lockfile kind (File (Fpath.v path))
 
 let code_target_location_of_input_to_core
     ({ path; analyzer; products; lockfile_target } : In.code_target) : regular =
@@ -159,6 +156,28 @@ let target_of_input_to_core (input : In.target) : t =
   | `CodeTarget x -> Regular (code_target_location_of_input_to_core x)
   | `LockfileTarget x -> Lockfile (lockfile_target_of_input_to_core x)
 
+(*****************************************************************************)
+(* Semgrep_output -> Target *)
+(*****************************************************************************)
+
+let manifest_of_semgrep_output ({ path; kind } : Out.manifest) : manifest =
+  mk_manifest kind (File path)
+
+let lockfile_of_semgrep_output ({ path; kind } : Out.lockfile) : lockfile =
+  mk_lockfile kind (File path)
+
+let dependency_source_of_semgrep_output (output_source : Out.dependency_source)
+    =
+  match output_source with
+  | ManifestOnlyDependencySource manifest ->
+      ManifestOnly (manifest_of_semgrep_output manifest)
+  | LockfileOnlyDependencySource lockfile ->
+      LockfileOnly (lockfile_of_semgrep_output lockfile)
+  | ManifestLockfileDependencySource (manifest, lockfile) ->
+      ManifestAndLockfile
+        ( manifest_of_semgrep_output manifest,
+          lockfile_of_semgrep_output lockfile )
+
 (*****************************************************************************)
 (* Accessors *)
 (*****************************************************************************)
diff --git a/src/target/Target.mli b/src/target/Target.mli
index 990e459da180..61d32eecee6f 100644
--- a/src/target/Target.mli
+++ b/src/target/Target.mli
@@ -45,13 +45,17 @@ type lockfile = {
   kind : Lockfile_kind.t;
       (** The type of lockfile this is. Analogous to analyzer for a source code
           target. *)
-  manifest : manifest option;
-      (** Optionally, a manifest file associated with this lockfile. *)
 }
 [@@deriving show, yojson]
 (** A lockfile to be used during matching. See also {!Lockfile_xtarget.t}, an
     augmented version with the contents of the lockfile. *)
 
+type dependency_source =
+  | ManifestOnly of manifest
+  | LockfileOnly of lockfile
+  | ManifestAndLockfile of manifest * lockfile
+      (** A source to resolve dependencies from. Can be either a lockfile or a manifest, or both. *)
+
 val pp_debug_lockfile : Format.formatter -> lockfile -> unit
 
 type regular = {
@@ -112,7 +116,7 @@ val mk_regular :
       a target from certain types of origins, such as generating a tempfile.
  *)
 
-val mk_lockfile : ?manifest:manifest -> Lockfile_kind.t -> Origin.t -> lockfile
+val mk_lockfile : Lockfile_kind.t -> Origin.t -> lockfile
 (** [mk_lockfile k origin] is the a {!lockfile} target
       originating from [origin] of kind [k]. If [manifest] is specified, it
       shall be used as the associated manifest.
@@ -139,6 +143,13 @@ val mk_target : Xlang.t -> Fpath.t -> t
 (*****************************************************************************)
 val target_of_input_to_core : Input_to_core_t.target -> t
 
+(*****************************************************************************)
+(* Semgrep_output -> Target *)
+(*****************************************************************************)
+
+val dependency_source_of_semgrep_output :
+  Semgrep_output_v1_t.dependency_source -> dependency_source
+
 (*****************************************************************************)
 (* Accessors *)
 (*****************************************************************************)


Test patch:
diff --git a/.github/workflows/build-test-manylinux-x86.jsonnet b/.github/workflows/build-test-manylinux-x86.jsonnet
index 1f2b767430b9..bb28b0a3f92e 100644
--- a/.github/workflows/build-test-manylinux-x86.jsonnet
+++ b/.github/workflows/build-test-manylinux-x86.jsonnet
@@ -142,6 +142,50 @@ local test_wheels_venv_job = {
   ],
 };
 
+local test_wheels_wsl_job = {
+  'runs-on': 'windows-latest',
+  needs: [
+    'build-wheels',
+  ],
+  steps: [
+    actions.download_artifact_step(wheel_name),
+    {
+      run: 'unzip dist.zip',
+    },
+    {
+      uses: 'Vampire/setup-wsl@v3',
+    },
+    {
+      name: 'Install Python',
+      shell: 'wsl-bash {0}',
+      run: |||
+        sudo apt update -y
+        sudo apt install -y make python3 python3-pip
+        sudo ln -s /usr/bin/python3 /usr/bin/python
+      |||,
+    },
+    {
+      name: "install package",
+      shell: 'wsl-bash {0}',
+      run: "python3 -m pip install dist/*.whl"
+    },
+    {
+      name: 'test package',
+      shell: 'wsl-bash {0}',
+      run: |||
+        semgrep --version
+      |||,
+    },
+    {
+      name: 'e2e semgrep-core test',
+      shell: 'wsl-bash {0}',
+      run: |||
+        echo '1 == 1' | semgrep -l python -e '$X == $X' -
+      |||,
+    },
+  ],
+};
+
 // ----------------------------------------------------------------------------
 // The Workflow
 // ----------------------------------------------------------------------------
@@ -162,5 +206,6 @@ local test_wheels_venv_job = {
     'build-wheels': build_wheels_job,
     'test-wheels': test_wheels_job,
     'test-wheels-venv': test_wheels_venv_job,
+    'test-wheels-wsl': test_wheels_wsl_job,
   },
 }
diff --git a/.github/workflows/build-test-manylinux-x86.yml b/.github/workflows/build-test-manylinux-x86.yml
index 0ab332aec3b8..bbd832660ec7 100644
--- a/.github/workflows/build-test-manylinux-x86.yml
+++ b/.github/workflows/build-test-manylinux-x86.yml
@@ -67,6 +67,33 @@ jobs:
       - name: e2e semgrep-core test
         run: |
           echo '1 == 1' | env/bin/semgrep -l python -e '$X == $X' -
+  test-wheels-wsl:
+    needs:
+      - build-wheels
+    runs-on: windows-latest
+    steps:
+      - uses: actions/download-artifact@v3
+        with:
+          name: manylinux-x86-wheel
+      - run: unzip dist.zip
+      - uses: Vampire/setup-wsl@v3
+      - name: Install Python
+        run: |
+          sudo apt update -y
+          sudo apt install -y make python3 python3-pip
+          sudo ln -s /usr/bin/python3 /usr/bin/python
+        shell: wsl-bash {0}
+      - name: install package
+        run: python3 -m pip install dist/*.whl
+        shell: wsl-bash {0}
+      - name: test package
+        run: |
+          semgrep --version
+        shell: wsl-bash {0}
+      - name: e2e semgrep-core test
+        run: |
+          echo '1 == 1' | semgrep -l python -e '$X == $X' -
+        shell: wsl-bash {0}
 name: build-test-manylinux-x86
 on:
   workflow_call: null
diff --git a/.github/workflows/build-test-osx-x86.jsonnet b/.github/workflows/build-test-osx-x86.jsonnet
index ded96d8460e0..7d39c8af8a81 100644
--- a/.github/workflows/build-test-osx-x86.jsonnet
+++ b/.github/workflows/build-test-osx-x86.jsonnet
@@ -15,7 +15,9 @@ local wheel_name = 'osx-x86-wheel';
 // See https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md
 // This already comes with Python installed so no need to
 // have a setup_python_step like in build-test-osx-arm64.jsonnet
-local runs_on = 'macos-12';
+// Macos 13 is the last version that has x86 + osx; macos-14 and above are arm
+// only
+local runs_on = 'macos-13';
 
 // This is reused in build-test-osx-arm64.jsonnet
 local test_semgrep_steps = [
diff --git a/.github/workflows/build-test-osx-x86.yml b/.github/workflows/build-test-osx-x86.yml
index 31ad15d6b7c3..1ae42852e433 100644
--- a/.github/workflows/build-test-osx-x86.yml
+++ b/.github/workflows/build-test-osx-x86.yml
@@ -1,7 +1,7 @@
 # AUTOGENERATED FROM build-test-osx-x86.jsonnet DO NOT MODIFY
 jobs:
   build-core:
-    runs-on: macos-12
+    runs-on: macos-13
     steps:
       - uses: actions/checkout@v3
         with:
@@ -30,7 +30,7 @@ jobs:
   build-wheels:
     needs:
       - build-core
-    runs-on: macos-12
+    runs-on: macos-13
     steps:
       - uses: actions/checkout@v3
         with:
@@ -49,7 +49,7 @@ jobs:
   test-wheels:
     needs:
       - build-wheels
-    runs-on: macos-12
+    runs-on: macos-13
     steps:
       - uses: actions/download-artifact@v3
         with:
diff --git a/.github/workflows/build-test-windows-x86.jsonnet b/.github/workflows/build-test-windows-x86.jsonnet
index b2a650b49989..2b7c894ac1ac 100644
--- a/.github/workflows/build-test-windows-x86.jsonnet
+++ b/.github/workflows/build-test-windows-x86.jsonnet
@@ -20,6 +20,11 @@ local defaults = {
 // The job
 // ----------------------------------------------------------------------------
 local build_core_job = {
+  // This job is currently disabled because it fails with the ocaml/setup-ocaml@v3
+  // github action, which we need for the latest cohttp and for OCaml 5. Currently,
+  // `ocamlfind` fails to build when we run this workflow in CI. The ticket for
+  // re-enabling the job is https://linear.app/semgrep/issue/SAF-1728/restore-windows-workflow
+  'if': 'false',
   'runs-on': runs_on,
   defaults: defaults,
   steps: [
diff --git a/.github/workflows/build-test-windows-x86.yml b/.github/workflows/build-test-windows-x86.yml
index b1471839e675..703c266bb5b1 100644
--- a/.github/workflows/build-test-windows-x86.yml
+++ b/.github/workflows/build-test-windows-x86.yml
@@ -4,6 +4,7 @@ jobs:
     defaults:
       run:
         shell: bash
+    if: "false"
     runs-on: windows-latest
     steps:
       - uses: actions/checkout@v3
diff --git a/cli/tests/conftest.py b/cli/tests/conftest.py
index ee30fd09fdfb..01579ba30cb1 100644
--- a/cli/tests/conftest.py
+++ b/cli/tests/conftest.py
@@ -23,6 +23,7 @@
 import re
 import shlex
 import subprocess
+import sys
 import tempfile
 from dataclasses import dataclass
 from functools import partial
@@ -44,6 +45,7 @@
 from ruamel.yaml import YAML
 from tests import fixtures
 from tests.semgrep_runner import SemgrepRunner
+from tests.semgrep_runner import USE_OSEMGREP
 
 from semgrep import __VERSION__
 from semgrep.cli import cli
@@ -351,9 +353,6 @@ def as_snapshot(self, mask: Optional[Maskers] = None):
         # This is a list of pairs (title, data) containing different
         # kinds of output to put into the snapshot.
         sections = {
-            "command": mask_variable_text(
-                self.command, mask, clean_fingerprint=self.clean_fingerprint
-            ),
             "exit code": self.exit_code,
             "stdout - plain": self.strip_color(stdout),
             "stderr - plain": self.strip_color(stderr),
@@ -374,16 +373,17 @@ def as_snapshot(self, mask: Optional[Maskers] = None):
 
     def print_debug_info(self) -> None:
         print(
-            "=== to reproduce (run with `pytest --pdb` to suspend while temp dirs exist)"
+            "=== to reproduce (run with `pytest --pdb` to suspend while temp dirs exist)",
+            file=sys.stderr,
         )
-        print(f"$ cd {os.getcwd()}")
-        print(f"$ {self.command}")
-        print("=== exit code")
-        print(self.exit_code)
-        print("=== stdout")
-        print(self.stdout)
-        print("=== stderr")
-        print(self.stderr)
+        print(f"$ cd {os.getcwd()}", file=sys.stderr)
+        print(f"$ {self.command}", file=sys.stderr)
+        print("=== exit code", file=sys.stderr)
+        print(self.exit_code, file=sys.stderr)
+        print("=== stdout", file=sys.stderr)
+        print(self.stdout, file=sys.stderr)
+        print("=== stderr", file=sys.stderr)
+        print(self.stderr, file=sys.stderr)
 
     def __iter__(self):
         """For backwards compat with usages like `stdout, stderr = run_semgrep(...)`"""
@@ -414,6 +414,7 @@ def _run_semgrep(
     prepare_workspace: Callable[[], None] = lambda: None,
     teardown_workspace: Callable[[], None] = lambda: None,
     context_manager: Optional[ContextManager] = None,
+    osemgrep_force_project_root: bool = False,
 ) -> SemgrepResult:
     """Run the semgrep CLI.
 
@@ -458,6 +459,21 @@ def _run_semgrep(
             if options is None:
                 options = []
 
+            # This is a hack to make osemgrep's new semgrepignore behavior
+            # compatible with pysemgrep when the current folder is not
+            # the project's root.
+            # - pysemgrep will use the .semgrepignore in the current folder
+            # - osemgrep will locate the project root and use all the
+            #   .semgrepignore and .gitignore files it finds in the project.
+            # In tests, we want to ignore the project-wide's semgrepignore.
+            # This is what the '--project-root .' option achieves.
+            if (
+                (subcommand is None or subcommand == "scan")
+                and USE_OSEMGREP
+                and osemgrep_force_project_root
+            ):
+                options.extend(["--project-root", "."])
+
             if strict:
                 options.append("--strict")
 
@@ -560,7 +576,11 @@ def unique_home_dir(monkeypatch: pytest.MonkeyPatch, tmp_path: Path):
 
 # Provide a run_semgrep function with alternate defaults
 _run_strict_semgrep_on_basic_targets_with_json_output: fixtures.RunSemgrep = partial(
-    _run_semgrep, strict=True, target_name="basic", output_format=OutputFormat.JSON
+    _run_semgrep,
+    strict=True,
+    target_name="basic",
+    output_format=OutputFormat.JSON,
+    osemgrep_force_project_root=True,
 )
 
 
diff --git a/cli/tests/default/e2e-other/parsers/test_poetry.py b/cli/tests/default/e2e-other/parsers/test_poetry.py
new file mode 100644
index 000000000000..73fb94c3fc3e
--- /dev/null
+++ b/cli/tests/default/e2e-other/parsers/test_poetry.py
@@ -0,0 +1,152 @@
+import textwrap
+
+import pytest
+
+from semdep.parsers import poetry
+
+
+@pytest.mark.quick
+@pytest.mark.parametrize(
+    "original, expected",
+    [
+        ("[foo, bar]", ""),
+        (
+            "[\nfoo,\nbar\n]",
+            "",
+        ),
+    ],
+)
+def test_poetry_list_value_parser(original: str, expected: str) -> None:
+    assert poetry.list_value.parse(original) == expected
+
+
+@pytest.mark.quick
+def test_poetry_object_value_parser() -> None:
+    original = """{version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}"""
+    expected = ""
+    assert poetry.object_value.parse(original) == expected
+
+
+@pytest.mark.quick
+@pytest.mark.parametrize(
+    "original, expected",
+    [
+        ('"foo"\n', ("foo", "\n")),
+        ('"foo[bar]"\n', ("foo[bar]", "\n")),
+    ],
+)
+def test_quoted_value_parser(original, expected) -> None:
+    assert poetry.quoted_value.parse_partial(original) == expected
+
+
+@pytest.mark.quick
+def test_multi_line_quoted_value_parser() -> None:
+    original = '''"""
+        foobar
+    """\n'''
+    expected = ""
+    assert poetry.multi_line_quoted_value.parse(original) == expected
+
+
+@pytest.mark.quick
+@pytest.mark.parametrize(
+    "original, expected",
+    [
+        ("foo = bar\n", (("foo", "bar"), "\n")),
+        ("foo = [bar, baz]\n", (("foo", ""), "\n")),
+    ],
+)
+def test_key_value_parser(original, expected) -> None:
+    assert poetry.key_value.parse_partial(original) == expected
+
+
+@pytest.mark.quick
+def test_manifest_deps_parser() -> None:
+    original = textwrap.dedent(
+        """        [tool.poetry.dependencies]
+        python = "^3.9"
+        toml = "^0.10.2"
+        requests = "^2.26.0"
+    """
+    )
+    expected = (["python", "toml", "requests"], "\n")
+    assert poetry.manifest_deps.parse_partial(original) == expected
+
+
+@pytest.mark.quick
+def test_manifest_sections_extra_parser() -> None:
+    original = textwrap.dedent(
+        """        [tool.black]
+        line-length = 120
+        include = '\.pyi?$'
+        exclude = '''
+        (
+        /(\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|_build|buck-out|build|dist)/
+        | .*/pippy/.*
+        | .*_pb2.py
+        | .*_pb2_grpc.py
+        )
+        '''
+    """
+    )
+    expected = (None, "")
+    assert poetry.manifest_sections_extra.parse_partial(original) == expected
+
+
+@pytest.mark.quick
+def test_poetry_dep_extra_parser() -> None:
+    original = textwrap.dedent(
+        """        [package.extras]
+        dev = ["coverage", "django", "flake8", "isort", "pillow", "sqlalchemy", "mongoengine", "wheel (>=0.32.0)", "tox", "zest.releaser"]
+        doc = ["sphinx", "sphinx-rtd-theme", "sphinxcontrib-spelling"]
+    """
+    )
+    expected = (None, "\n")
+    assert poetry.poetry_dep_extra.parse_partial(original) == expected
+
+
+@pytest.mark.quick
+def test_poetry_source_extra_parser() -> None:
+    original = textwrap.dedent(
+        """        [[tool.poetry.source]]
+        name = "semgrep"
+        url = "https://artifact.semgrep.com/"
+        secondary = False
+    """
+    )
+    expected = (None, "\n")
+    assert poetry.poetry_source_extra.parse_partial(original) == expected
+
+
+@pytest.mark.quick
+def test_manifest_parser() -> None:
+    original = textwrap.dedent(
+        """        [tool.poetry.dependencies]
+        python = "^3.9"
+        toml = "^0.10.2"
+        requests = "^2.26.0"
+
+        [tool.poetry.dev-dependencies]
+        pytest = "^6.2.4"
+        pytest-cov = "^2.12.1"
+        pytest-mock = "^3.6.1"
+        pytest-xdist = "^2.3.0"
+
+        [tool.black]
+        line-length = 120
+        include = '\.pyi?$'
+        exclude = '''
+        (
+        /(\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|_build|buck-out|build|dist)/
+        | .*/pippy/.*
+        | .*_pb2.py
+        | .*_pb2_grpc.py
+        )
+
+        [package.extras]
+        dev = ["coverage", "django", "flake8", "isort", "pillow", "sqlalchemy", "mongoengine", "wheel (>=0.32.0)", "tox", "zest.releaser"]
+        doc = ["sphinx", "sphinx-rtd-theme", "sphinxcontrib-spelling"]
+    """
+    )
+
+    assert poetry.manifest.parse_partial(original)[0] == {"toml", "python", "requests"}
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product0-/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product0-/results.txt
index 4789a8032b8c..5597ac8beb16 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product0-/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product0-/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product0-_foo.py/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product0-_foo.py/results.txt
index 4789a8032b8c..5597ac8beb16 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product0-_foo.py/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product0-_foo.py/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product0-foo.py/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product0-foo.py/results.txt
index 595d1a7bd509..982f973bdf2b 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product0-foo.py/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product0-foo.py/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product1-/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product1-/results.txt
index 4789a8032b8c..5597ac8beb16 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product1-/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product1-/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product1-_foo.py/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product1-_foo.py/results.txt
index 4789a8032b8c..5597ac8beb16 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product1-_foo.py/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product1-_foo.py/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product1-foo.py/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product1-foo.py/results.txt
index 4789a8032b8c..5597ac8beb16 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product1-foo.py/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_app_ignore/ignored_product1-foo.py/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_config_run/autofix/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_config_run/autofix/results.txt
index 263ec9a41817..d78a2acba721 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_config_run/autofix/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_config_run/autofix/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --config p/something
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_config_run/noautofix/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_config_run/noautofix/results.txt
index 263ec9a41817..d78a2acba721 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_config_run/noautofix/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_config_run/noautofix/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --config p/something
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_dryrun/None/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_dryrun/None/results.txt
index fa2b062e8c78..befe7339ff79 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_dryrun/None/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_dryrun/None/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --dry-run --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_existing_reachable_finding_deduplication/config/base_output.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_existing_reachable_finding_deduplication/config/base_output.txt
index fe6e8e74291d..e3d19dfc850c 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_existing_reachable_finding_deduplication/config/base_output.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_existing_reachable_finding_deduplication/config/base_output.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --baseline-commit <MASKED>
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_existing_reachable_finding_deduplication/config/new_output.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_existing_reachable_finding_deduplication/config/new_output.txt
index 137d94d3b8f9..2f241a1f1b92 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_existing_reachable_finding_deduplication/config/new_output.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_existing_reachable_finding_deduplication/config/new_output.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --baseline-commit <MASKED>
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_existing_supply_chain_finding/config/base_output.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_existing_supply_chain_finding/config/base_output.txt
index ad19fdceac5d..8089b0b06eed 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_existing_supply_chain_finding/config/base_output.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_existing_supply_chain_finding/config/base_output.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_existing_supply_chain_finding/config/new_output.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_existing_supply_chain_finding/config/new_output.txt
index 675253425969..7cffc3bfc3d4 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_existing_supply_chain_finding/config/new_output.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_existing_supply_chain_finding/config/new_output.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --baseline-commit <MASKED>
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_fail_on_historical_scan_without_secrets/output.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_fail_on_historical_scan_without_secrets/output.txt
index 513608b3d131..f88cae39fa83 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_fail_on_historical_scan_without_secrets/output.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_fail_on_historical_scan_without_secrets/output.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --historical-secrets --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 2
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-azure-pipelines-overwrite-autodetected-variables/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-azure-pipelines-overwrite-autodetected-variables/results.txt
index f5c48299384e..a135b56edb34 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-azure-pipelines-overwrite-autodetected-variables/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-azure-pipelines-overwrite-autodetected-variables/results.txt
@@ -1,7 +1,3 @@
-=== command
-BUILD_BUILDID="some_id" SEMGREP_REPO_NAME="a/repo/name" SEMGREP_REPO_URL="https://random.url.org/some/path" SEMGREP_BRANCH="branch/some-other-branch-name" SEMGREP_JOB_URL="https://another.random.url.org/some/path" SEMGREP_COMMIT="<some_random_commit>" SEMGREP_PR_ID="34566" BUILD_REPOSITORY_URI="https://github.com/project_name/project_name.git" SYSTEM_PULLREQUEST_SOURCEBRANCH="some/branch-name" SYSTEM_TEAMFOUNDATIONSERVERURI="https://azure.pipeline.url/" SYSTEM_TEAMPROJECTID="project_id" SYSTEM_JOBID="job_id" SYSTEM_TASKINSTANCEID="task_id" SYSTEM_PULLREQUEST_SOURCECOMMITID="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-azure-pipelines/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-azure-pipelines/results.txt
index 53f071fa1483..71b95bd293d6 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-azure-pipelines/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-azure-pipelines/results.txt
@@ -1,7 +1,3 @@
-=== command
-BUILD_BUILDID="some_id" BUILD_REPOSITORY_URI="https://github.com/project_name/project_name.git" SYSTEM_PULLREQUEST_SOURCEBRANCH="some/branch-name" SYSTEM_TEAMFOUNDATIONSERVERURI="https://azure.pipeline.url/" SYSTEM_TEAMPROJECTID="project_id" SYSTEM_JOBID="job_id" SYSTEM_TASKINSTANCEID="task_id" SYSTEM_PULLREQUEST_PULLREQUESTNUMBER="1234" SYSTEM_PULLREQUEST_SOURCECOMMITID="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-bitbucket-overwrite-autodetected-variables/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-bitbucket-overwrite-autodetected-variables/results.txt
index 9850a5eda519..8f50a737d0b8 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-bitbucket-overwrite-autodetected-variables/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-bitbucket-overwrite-autodetected-variables/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" BITBUCKET_BUILD_NUMBER="hi" SEMGREP_REPO_NAME="a/repo/name" SEMGREP_REPO_URL="https://random.url.org/some/path" SEMGREP_BRANCH="branch/some-other-branch-name" SEMGREP_JOB_URL="https://another.random.url.org/some/path" SEMGREP_COMMIT="<some_random_commit>" SEMGREP_PR_ID="99999" BITBUCKET_REPO_FULL_NAME="project_name/project_name" BITBUCKET_GIT_HTTP_ORIGIN="http://bitbucket.org/project_name/project_name" BITBUCKET_BRANCH="some/branch-name" BITBUCKET_PIPELINE_UUID="a-uuid" BITBUCKET_PR_ID="35" BITBUCKET_COMMIT="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-bitbucket/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-bitbucket/results.txt
index 9e84e34a32df..f8ac0f6c7179 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-bitbucket/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-bitbucket/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" BITBUCKET_BUILD_NUMBER="hi" BITBUCKET_REPO_FULL_NAME="project_name/project_name" BITBUCKET_GIT_HTTP_ORIGIN="http://bitbucket.org/project_name/project_name" BITBUCKET_BRANCH="some/branch-name" BITBUCKET_PIPELINE_UUID="a-uuid" BITBUCKET_PR_ID="35" BITBUCKET_COMMIT="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-buildkite-overwrite-autodetected-variables/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-buildkite-overwrite-autodetected-variables/results.txt
index 1ea074ce370d..179265e920b1 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-buildkite-overwrite-autodetected-variables/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-buildkite-overwrite-autodetected-variables/results.txt
@@ -1,7 +1,3 @@
-=== command
-BUILDKITE="true" SEMGREP_REPO_NAME="a/repo/name" SEMGREP_REPO_URL="https://random.url.org/some/path" SEMGREP_BRANCH="branch/some-other-branch-name" SEMGREP_JOB_URL="https://another.random.url.org/some/path" SEMGREP_COMMIT="<some_random_commit>" SEMGREP_PR_ID="99999" BUILDKITE_REPO="git@github.com/project_name/project_name.git" BUILDKITE_BRANCH="some/branch-name" BUILDKITE_BUILD_URL="https://buildkite.build.url/something" BUILDKITE_JOB_ID="42" BUILDKITE_PULL_REQUEST="35" BUILDKITE_BUILD_AUTHOR="Environment Test" BUILDKITE_BUILD_AUTHOR_EMAIL="test_environment@test.r2c.dev" BUILDKITE_MESSAGE="some: commit message! foo" BUILDKITE_COMMIT="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-buildkite/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-buildkite/results.txt
index 091e30dd710b..80c3dab52ac8 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-buildkite/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-buildkite/results.txt
@@ -1,7 +1,3 @@
-=== command
-BUILDKITE="true" BUILDKITE_REPO="git@github.com/project_name/project_name.git" BUILDKITE_BRANCH="some/branch-name" BUILDKITE_BUILD_URL="https://buildkite.build.url/something" BUILDKITE_JOB_ID="42" BUILDKITE_PULL_REQUEST="35" BUILDKITE_BUILD_AUTHOR="Environment Test" BUILDKITE_BUILD_AUTHOR_EMAIL="test_environment@test.r2c.dev" BUILDKITE_MESSAGE="some: commit message! foo" BUILDKITE_COMMIT="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-circleci-overwrite-autodetected-variables/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-circleci-overwrite-autodetected-variables/results.txt
index f1e5ca22f18f..52fa5abe49ea 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-circleci-overwrite-autodetected-variables/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-circleci-overwrite-autodetected-variables/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" SEMGREP_REPO_NAME="a/repo/name" SEMGREP_REPO_URL="https://random.url.org/some/path" SEMGREP_BRANCH="branch/some-other-branch-name" SEMGREP_JOB_URL="https://another.random.url.org/some/path" SEMGREP_COMMIT="<some_random_commit>" SEMGREP_PR_ID="99999" CIRCLECI="true" CIRCLE_PROJECT_USERNAME="project_name" CIRCLE_PROJECT_REPONAME="project_name" CIRCLE_REPOSITORY_URL="git@github.com/project_name/project_name.git" CIRCLE_BRANCH="some/branch-name" CIRCLE_BUILD_URL="https://circle.ci.build.url" CIRCLE_PULL_REQUEST="https://github.com/project_name/project_name/pull/35" CIRCLE_SHA1="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-circleci/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-circleci/results.txt
index 75bfbdc65705..6137265f4023 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-circleci/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-circleci/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" CIRCLECI="true" CIRCLE_PROJECT_USERNAME="project_name" CIRCLE_PROJECT_REPONAME="project_name" CIRCLE_REPOSITORY_URL="git@github.com/project_name/project_name.git" CIRCLE_BRANCH="some/branch-name" CIRCLE_BUILD_URL="https://circle.ci.build.url" CIRCLE_PULL_REQUEST="https://github.com/project_name/project_name/pull/35" CIRCLE_SHA1="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-enterprise/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-enterprise/results.txt
index 3bb897ccb8a2..db72bcc3ca93 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-enterprise/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-enterprise/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" GITHUB_ACTIONS="true" GITHUB_ACTOR="some_test_username" GITHUB_API_URL="https://api.github.com" GITHUB_REPOSITORY="project_name/project_name" GITHUB_RUN_ID="35" GITHUB_SERVER_URL="https://some.enterprise.url.com" GITHUB_WORKSPACE="/home/runner/work/actions-test/actions-test" GITHUB_REPOSITORY_ID="4" GITHUB_REPOSITORY_OWNER_ID="2" GITHUB_EVENT_NAME="push" GITHUB_REF="refs/heads/some/branch-name" GITHUB_BASE_REF="" GITHUB_HEAD_REF="" GITHUB_SHA="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-pr-semgrepconfig/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-pr-semgrepconfig/results.txt
index 8e37dd217543..68963e51419f 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-pr-semgrepconfig/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-pr-semgrepconfig/results.txt
@@ -1,10 +1,3 @@
-=== command
-CI="true" GITHUB_ACTIONS="true" GITHUB_ACTOR="some_test_username" GITHUB_API_URL="https://api.github.com" GITHUB_REPOSITORY="project_name/project_name" GITHUB_RUN_ID="35" GITHUB_SERVER_URL="https://github.com" GITHUB_WORKSPACE="/home/runner/work/actions-test/actions-test" GITHUB_REPOSITORY_ID="4" GITHUB_REPOSITORY_OWNER_ID="2" GITHUB_EVENT_NAME="pull_request" GITHUB_REF="refs/pull/123/merge" GITHUB_BASE_REF="main" GITHUB_HEAD_REF="some/branch-name" SEMGREP_PROJECT_CONFIG="tags:
-- tag1
-- tag_key:tag_val
-" GITHUB_SHA="<MASKED>" GITHUB_EVENT_PATH="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-pr/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-pr/results.txt
index 8e8af064b3ce..68963e51419f 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-pr/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-pr/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" GITHUB_ACTIONS="true" GITHUB_ACTOR="some_test_username" GITHUB_API_URL="https://api.github.com" GITHUB_REPOSITORY="project_name/project_name" GITHUB_RUN_ID="35" GITHUB_SERVER_URL="https://github.com" GITHUB_WORKSPACE="/home/runner/work/actions-test/actions-test" GITHUB_REPOSITORY_ID="4" GITHUB_REPOSITORY_OWNER_ID="2" GITHUB_EVENT_NAME="pull_request" GITHUB_REF="refs/pull/123/merge" GITHUB_BASE_REF="main" GITHUB_HEAD_REF="some/branch-name" GITHUB_SHA="<MASKED>" GITHUB_EVENT_PATH="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-push-special-env-vars/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-push-special-env-vars/results.txt
index 40e230319ebd..fd1233d11258 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-push-special-env-vars/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-push-special-env-vars/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" GITHUB_ACTIONS="true" GITHUB_ACTOR="some_test_username" GITHUB_API_URL="https://api.github.com" GITHUB_REPOSITORY="project_name/project_name" GITHUB_RUN_ID="35" GITHUB_SERVER_URL="https://github.com" GITHUB_WORKSPACE="/home/runner/work/actions-test/actions-test" GITHUB_REPOSITORY_ID="4" GITHUB_REPOSITORY_OWNER_ID="2" GITHUB_EVENT_NAME="push" GITHUB_REF="refs/heads/some/branch-name" GITHUB_BASE_REF="" GITHUB_HEAD_REF="" SEMGREP_REPO_NAME="project_name/project_name" SEMGREP_JOB_URL="customjoburl.com" SEMGREP_PR_ID="312" SEMGREP_PR_TITLE="PR_TITLE" SEMGREP_BRANCH="some/branch-name" GITHUB_SHA="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-push-with-app-url/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-push-with-app-url/results.txt
index c2bd55b3e69e..975f9e766a5c 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-push-with-app-url/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-push-with-app-url/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" GITHUB_ACTIONS="true" GITHUB_ACTOR="some_test_username" GITHUB_API_URL="https://api.github.com" GITHUB_REPOSITORY="project_name/project_name" GITHUB_RUN_ID="35" GITHUB_SERVER_URL="https://github.com" GITHUB_WORKSPACE="/home/runner/work/actions-test/actions-test" GITHUB_REPOSITORY_ID="4" GITHUB_REPOSITORY_OWNER_ID="2" GITHUB_EVENT_NAME="push" GITHUB_REF="refs/heads/some/branch-name" GITHUB_BASE_REF="" GITHUB_HEAD_REF="" SEMGREP_URL="https://tenantname.semgrep.dev" GITHUB_SHA="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-push/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-push/results.txt
index ba40e3e86791..db72bcc3ca93 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-push/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-github-push/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" GITHUB_ACTIONS="true" GITHUB_ACTOR="some_test_username" GITHUB_API_URL="https://api.github.com" GITHUB_REPOSITORY="project_name/project_name" GITHUB_RUN_ID="35" GITHUB_SERVER_URL="https://github.com" GITHUB_WORKSPACE="/home/runner/work/actions-test/actions-test" GITHUB_REPOSITORY_ID="4" GITHUB_REPOSITORY_OWNER_ID="2" GITHUB_EVENT_NAME="push" GITHUB_REF="refs/heads/some/branch-name" GITHUB_BASE_REF="" GITHUB_HEAD_REF="" GITHUB_SHA="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-gitlab-push/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-gitlab-push/results.txt
index 756e537e1f86..345b912acfc8 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-gitlab-push/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-gitlab-push/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" GITLAB_CI="true" CI_PROJECT_PATH="project_name/project_name" CI_PIPELINE_SOURCE="push" CI_JOB_TOKEN="some-token-test-placeholder" CI_COMMIT_REF_NAME="some/branch-name" CI_COMMIT_SHA="<MASKED>" CI_PROJECT_URL="https://example.com/gitlab-org/gitlab-foss" CI_JOB_URL="https://gitlab.com/gitlab-examples/ci-debug-trace/-/jobs/379424655" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-gitlab-special-env-vars/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-gitlab-special-env-vars/results.txt
index b400e1bee91b..7c09dade7086 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-gitlab-special-env-vars/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-gitlab-special-env-vars/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" GITLAB_CI="true" SEMGREP_REPO_NAME="project_name/project_name" CI_PIPELINE_SOURCE="merge_request_event" CI_MERGE_REQUEST_TARGET_BRANCH_NAME="main" CI_MERGE_REQUEST_PROJECT_URL="https://some.project.url.test.placeholder" CI_JOB_TOKEN="some-token-test-placeholder" CI_COMMIT_REF_NAME="some/branch-name" SEMGREP_COMMIT="unused-commit-test-placeholder" SEMGREP_REPO_URL="https://example.com/gitlab-org/gitlab-foss" SEMGREP_JOB_URL="https://gitlab.com/gitlab-examples/ci-debug-trace/-/jobs/379424655" SEMGREP_PR_ID="unused-iid-test-placeholder" CI_MERGE_REQUEST_DIFF_BASE_SHA="unused-commit-test-placeholder" SEMGREP_PR_TITLE="unused-merge-request-title-test-placeholder" CI_COMMIT_SHA="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-gitlab/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-gitlab/results.txt
index 2599236a49c1..7c09dade7086 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-gitlab/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-gitlab/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" GITLAB_CI="true" CI_PROJECT_PATH="project_name/project_name" CI_PIPELINE_SOURCE="merge_request_event" CI_MERGE_REQUEST_TARGET_BRANCH_NAME="main" CI_MERGE_REQUEST_PROJECT_URL="https://some.project.url.test.placeholder" CI_JOB_TOKEN="some-token-test-placeholder" CI_COMMIT_REF_NAME="some/branch-name" CI_COMMIT_SHA="<MASKED>" CI_PROJECT_URL="https://example.com/gitlab-org/gitlab-foss" CI_JOB_URL="https://gitlab.com/gitlab-examples/ci-debug-trace/-/jobs/379424655" CI_MERGE_REQUEST_IID="unused-iid-test-placeholder" CI_MERGE_REQUEST_DIFF_BASE_SHA="unused-commit-test-placeholder" CI_MERGE_REQUEST_TITLE="unused-merge-request-title-test-placeholder" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-jenkins-missing-vars/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-jenkins-missing-vars/results.txt
index 68fe5c706f6d..2819087f37c2 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-jenkins-missing-vars/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-jenkins-missing-vars/results.txt
@@ -1,7 +1,3 @@
-=== command
-JENKINS_URL="some_url" SEMGREP_REPO_URL="https://random.url.org/some/path" SEMGREP_REPO_NAME="a/repo/name" GIT_BRANCH="some/branch-name" BUILD_URL="https://jenkins.build.url" GIT_COMMIT="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-jenkins-overwrite-autodetected-variables/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-jenkins-overwrite-autodetected-variables/results.txt
index eb32d6445662..d7c667934991 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-jenkins-overwrite-autodetected-variables/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-jenkins-overwrite-autodetected-variables/results.txt
@@ -1,7 +1,3 @@
-=== command
-JENKINS_URL="some_url" SEMGREP_REPO_NAME="a/repo/name" SEMGREP_REPO_URL="https://random.url.org/some/path" SEMGREP_BRANCH="branch/some-other-branch-name" SEMGREP_JOB_URL="https://another.random.url.org/some/path" SEMGREP_COMMIT="<some_random_commit>" GIT_URL="https://github.com/org/repo.git/" GIT_BRANCH="some/branch-name" BUILD_URL="https://jenkins.build.url" GIT_COMMIT="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-jenkins/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-jenkins/results.txt
index 913239e64034..f4eeda636fb5 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-jenkins/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-jenkins/results.txt
@@ -1,7 +1,3 @@
-=== command
-JENKINS_URL="some_url" GIT_URL="https://github.com/org/repo.git/" GIT_BRANCH="some/branch-name" BUILD_URL="https://jenkins.build.url" GIT_COMMIT="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-local/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-local/results.txt
index 09afdfbbd2bb..4eccad416521 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-local/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-local/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_REPO_URL="git@github.com:example/fake.git" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-self-hosted/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-self-hosted/results.txt
index ace8490411e4..f25eb07daf5c 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-self-hosted/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-self-hosted/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" SEMGREP_REPO_NAME="org_name/project_name/project_name" SEMGREP_REPO_URL="https://some.enterprise.url.com/org_name/project_name/project_name" SEMGREP_PR_ID="35" SEMGREP_BRANCH="some/branch-name" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-travis-overwrite-autodetected-variables/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-travis-overwrite-autodetected-variables/results.txt
index 2db90152413c..329fc755f53b 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-travis-overwrite-autodetected-variables/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-travis-overwrite-autodetected-variables/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" TRAVIS="true" SEMGREP_REPO_NAME="a/repo/name" SEMGREP_REPO_URL="https://random.url.org/some/path" SEMGREP_BRANCH="branch/some-other-branch-name" SEMGREP_JOB_URL="https://another.random.url.org/some/path" SEMGREP_COMMIT="<some_random_commit>" SEMGREP_PR_ID="99999" TRAVIS_REPO_SLUG="project_name/project_name" TRAVIS_PULL_REQUEST_BRANCH="some/branch-name" TRAVIS_JOB_WEB_URL="https://travis.job.web.url/" TRAVIS_PULL_REQUEST="35" TRAVIS_COMMIT_MESSAGE="some: commit message! foo" TRAVIS_COMMIT="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-travis/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-travis/results.txt
index 9b213e5185f2..c08d2f4567e9 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-travis/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-travis/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" TRAVIS="true" TRAVIS_REPO_SLUG="project_name/project_name" TRAVIS_PULL_REQUEST_BRANCH="some/branch-name" TRAVIS_JOB_WEB_URL="https://travis.job.web.url/" TRAVIS_PULL_REQUEST="35" TRAVIS_COMMIT_MESSAGE="some: commit message! foo" TRAVIS_COMMIT="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-unparsable_url/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-unparsable_url/results.txt
index 6749421d8a2e..f25eb07daf5c 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-unparsable_url/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/autofix-unparsable_url/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" SEMGREP_REPO_NAME="org_name/project_name/project_name" SEMGREP_REPO_URL="https://gitlab.net/foo.bar/a-b/a-b-c-d" SEMGREP_PR_ID="35" SEMGREP_BRANCH="some/branch-name" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-azure-pipelines-overwrite-autodetected-variables/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-azure-pipelines-overwrite-autodetected-variables/results.txt
index c5d83008517d..568e9dc6e262 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-azure-pipelines-overwrite-autodetected-variables/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-azure-pipelines-overwrite-autodetected-variables/results.txt
@@ -1,7 +1,3 @@
-=== command
-BUILD_BUILDID="some_id" SEMGREP_REPO_NAME="a/repo/name" SEMGREP_REPO_URL="https://random.url.org/some/path" SEMGREP_BRANCH="branch/some-other-branch-name" SEMGREP_JOB_URL="https://another.random.url.org/some/path" SEMGREP_COMMIT="<some_random_commit>" SEMGREP_PR_ID="34566" BUILD_REPOSITORY_URI="https://github.com/project_name/project_name.git" SYSTEM_PULLREQUEST_SOURCEBRANCH="some/branch-name" SYSTEM_TEAMFOUNDATIONSERVERURI="https://azure.pipeline.url/" SYSTEM_TEAMPROJECTID="project_id" SYSTEM_JOBID="job_id" SYSTEM_TASKINSTANCEID="task_id" SYSTEM_PULLREQUEST_SOURCECOMMITID="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-azure-pipelines/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-azure-pipelines/results.txt
index 968385bb35ff..b07a64a3d83b 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-azure-pipelines/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-azure-pipelines/results.txt
@@ -1,7 +1,3 @@
-=== command
-BUILD_BUILDID="some_id" BUILD_REPOSITORY_URI="https://github.com/project_name/project_name.git" SYSTEM_PULLREQUEST_SOURCEBRANCH="some/branch-name" SYSTEM_TEAMFOUNDATIONSERVERURI="https://azure.pipeline.url/" SYSTEM_TEAMPROJECTID="project_id" SYSTEM_JOBID="job_id" SYSTEM_TASKINSTANCEID="task_id" SYSTEM_PULLREQUEST_PULLREQUESTNUMBER="1234" SYSTEM_PULLREQUEST_SOURCECOMMITID="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-bitbucket-overwrite-autodetected-variables/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-bitbucket-overwrite-autodetected-variables/results.txt
index 1ccd1abd8d56..1b426e02a4d1 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-bitbucket-overwrite-autodetected-variables/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-bitbucket-overwrite-autodetected-variables/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" BITBUCKET_BUILD_NUMBER="hi" SEMGREP_REPO_NAME="a/repo/name" SEMGREP_REPO_URL="https://random.url.org/some/path" SEMGREP_BRANCH="branch/some-other-branch-name" SEMGREP_JOB_URL="https://another.random.url.org/some/path" SEMGREP_COMMIT="<some_random_commit>" SEMGREP_PR_ID="99999" BITBUCKET_REPO_FULL_NAME="project_name/project_name" BITBUCKET_GIT_HTTP_ORIGIN="http://bitbucket.org/project_name/project_name" BITBUCKET_BRANCH="some/branch-name" BITBUCKET_PIPELINE_UUID="a-uuid" BITBUCKET_PR_ID="35" BITBUCKET_COMMIT="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-bitbucket/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-bitbucket/results.txt
index 195fa48b5719..354d15e4fa74 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-bitbucket/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-bitbucket/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" BITBUCKET_BUILD_NUMBER="hi" BITBUCKET_REPO_FULL_NAME="project_name/project_name" BITBUCKET_GIT_HTTP_ORIGIN="http://bitbucket.org/project_name/project_name" BITBUCKET_BRANCH="some/branch-name" BITBUCKET_PIPELINE_UUID="a-uuid" BITBUCKET_PR_ID="35" BITBUCKET_COMMIT="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-buildkite-overwrite-autodetected-variables/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-buildkite-overwrite-autodetected-variables/results.txt
index 74723dc6aa09..d44165ace96b 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-buildkite-overwrite-autodetected-variables/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-buildkite-overwrite-autodetected-variables/results.txt
@@ -1,7 +1,3 @@
-=== command
-BUILDKITE="true" SEMGREP_REPO_NAME="a/repo/name" SEMGREP_REPO_URL="https://random.url.org/some/path" SEMGREP_BRANCH="branch/some-other-branch-name" SEMGREP_JOB_URL="https://another.random.url.org/some/path" SEMGREP_COMMIT="<some_random_commit>" SEMGREP_PR_ID="99999" BUILDKITE_REPO="git@github.com/project_name/project_name.git" BUILDKITE_BRANCH="some/branch-name" BUILDKITE_BUILD_URL="https://buildkite.build.url/something" BUILDKITE_JOB_ID="42" BUILDKITE_PULL_REQUEST="35" BUILDKITE_BUILD_AUTHOR="Environment Test" BUILDKITE_BUILD_AUTHOR_EMAIL="test_environment@test.r2c.dev" BUILDKITE_MESSAGE="some: commit message! foo" BUILDKITE_COMMIT="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-buildkite/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-buildkite/results.txt
index 9036bade22bc..6e7a2040dee0 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-buildkite/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-buildkite/results.txt
@@ -1,7 +1,3 @@
-=== command
-BUILDKITE="true" BUILDKITE_REPO="git@github.com/project_name/project_name.git" BUILDKITE_BRANCH="some/branch-name" BUILDKITE_BUILD_URL="https://buildkite.build.url/something" BUILDKITE_JOB_ID="42" BUILDKITE_PULL_REQUEST="35" BUILDKITE_BUILD_AUTHOR="Environment Test" BUILDKITE_BUILD_AUTHOR_EMAIL="test_environment@test.r2c.dev" BUILDKITE_MESSAGE="some: commit message! foo" BUILDKITE_COMMIT="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-circleci-overwrite-autodetected-variables/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-circleci-overwrite-autodetected-variables/results.txt
index 58edb7fd4396..c79c8bd85b61 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-circleci-overwrite-autodetected-variables/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-circleci-overwrite-autodetected-variables/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" SEMGREP_REPO_NAME="a/repo/name" SEMGREP_REPO_URL="https://random.url.org/some/path" SEMGREP_BRANCH="branch/some-other-branch-name" SEMGREP_JOB_URL="https://another.random.url.org/some/path" SEMGREP_COMMIT="<some_random_commit>" SEMGREP_PR_ID="99999" CIRCLECI="true" CIRCLE_PROJECT_USERNAME="project_name" CIRCLE_PROJECT_REPONAME="project_name" CIRCLE_REPOSITORY_URL="git@github.com/project_name/project_name.git" CIRCLE_BRANCH="some/branch-name" CIRCLE_BUILD_URL="https://circle.ci.build.url" CIRCLE_PULL_REQUEST="https://github.com/project_name/project_name/pull/35" CIRCLE_SHA1="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-circleci/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-circleci/results.txt
index de0a2e6bf935..7fe143cffc80 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-circleci/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-circleci/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" CIRCLECI="true" CIRCLE_PROJECT_USERNAME="project_name" CIRCLE_PROJECT_REPONAME="project_name" CIRCLE_REPOSITORY_URL="git@github.com/project_name/project_name.git" CIRCLE_BRANCH="some/branch-name" CIRCLE_BUILD_URL="https://circle.ci.build.url" CIRCLE_PULL_REQUEST="https://github.com/project_name/project_name/pull/35" CIRCLE_SHA1="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-enterprise/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-enterprise/results.txt
index 74d81bf8a3c9..3d645abfd761 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-enterprise/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-enterprise/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" GITHUB_ACTIONS="true" GITHUB_ACTOR="some_test_username" GITHUB_API_URL="https://api.github.com" GITHUB_REPOSITORY="project_name/project_name" GITHUB_RUN_ID="35" GITHUB_SERVER_URL="https://some.enterprise.url.com" GITHUB_WORKSPACE="/home/runner/work/actions-test/actions-test" GITHUB_REPOSITORY_ID="4" GITHUB_REPOSITORY_OWNER_ID="2" GITHUB_EVENT_NAME="push" GITHUB_REF="refs/heads/some/branch-name" GITHUB_BASE_REF="" GITHUB_HEAD_REF="" GITHUB_SHA="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-pr-semgrepconfig/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-pr-semgrepconfig/results.txt
index d6d62c1cde0b..879e15b81de4 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-pr-semgrepconfig/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-pr-semgrepconfig/results.txt
@@ -1,10 +1,3 @@
-=== command
-CI="true" GITHUB_ACTIONS="true" GITHUB_ACTOR="some_test_username" GITHUB_API_URL="https://api.github.com" GITHUB_REPOSITORY="project_name/project_name" GITHUB_RUN_ID="35" GITHUB_SERVER_URL="https://github.com" GITHUB_WORKSPACE="/home/runner/work/actions-test/actions-test" GITHUB_REPOSITORY_ID="4" GITHUB_REPOSITORY_OWNER_ID="2" GITHUB_EVENT_NAME="pull_request" GITHUB_REF="refs/pull/123/merge" GITHUB_BASE_REF="main" GITHUB_HEAD_REF="some/branch-name" SEMGREP_PROJECT_CONFIG="tags:
-- tag1
-- tag_key:tag_val
-" GITHUB_SHA="<MASKED>" GITHUB_EVENT_PATH="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-pr/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-pr/results.txt
index 36a015e90563..879e15b81de4 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-pr/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-pr/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" GITHUB_ACTIONS="true" GITHUB_ACTOR="some_test_username" GITHUB_API_URL="https://api.github.com" GITHUB_REPOSITORY="project_name/project_name" GITHUB_RUN_ID="35" GITHUB_SERVER_URL="https://github.com" GITHUB_WORKSPACE="/home/runner/work/actions-test/actions-test" GITHUB_REPOSITORY_ID="4" GITHUB_REPOSITORY_OWNER_ID="2" GITHUB_EVENT_NAME="pull_request" GITHUB_REF="refs/pull/123/merge" GITHUB_BASE_REF="main" GITHUB_HEAD_REF="some/branch-name" GITHUB_SHA="<MASKED>" GITHUB_EVENT_PATH="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-push-special-env-vars/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-push-special-env-vars/results.txt
index b4dd42202895..fe32ca544ac0 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-push-special-env-vars/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-push-special-env-vars/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" GITHUB_ACTIONS="true" GITHUB_ACTOR="some_test_username" GITHUB_API_URL="https://api.github.com" GITHUB_REPOSITORY="project_name/project_name" GITHUB_RUN_ID="35" GITHUB_SERVER_URL="https://github.com" GITHUB_WORKSPACE="/home/runner/work/actions-test/actions-test" GITHUB_REPOSITORY_ID="4" GITHUB_REPOSITORY_OWNER_ID="2" GITHUB_EVENT_NAME="push" GITHUB_REF="refs/heads/some/branch-name" GITHUB_BASE_REF="" GITHUB_HEAD_REF="" SEMGREP_REPO_NAME="project_name/project_name" SEMGREP_JOB_URL="customjoburl.com" SEMGREP_PR_ID="312" SEMGREP_PR_TITLE="PR_TITLE" SEMGREP_BRANCH="some/branch-name" GITHUB_SHA="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-push-with-app-url/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-push-with-app-url/results.txt
index 1c4f65b4ba64..070b9fa30073 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-push-with-app-url/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-push-with-app-url/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" GITHUB_ACTIONS="true" GITHUB_ACTOR="some_test_username" GITHUB_API_URL="https://api.github.com" GITHUB_REPOSITORY="project_name/project_name" GITHUB_RUN_ID="35" GITHUB_SERVER_URL="https://github.com" GITHUB_WORKSPACE="/home/runner/work/actions-test/actions-test" GITHUB_REPOSITORY_ID="4" GITHUB_REPOSITORY_OWNER_ID="2" GITHUB_EVENT_NAME="push" GITHUB_REF="refs/heads/some/branch-name" GITHUB_BASE_REF="" GITHUB_HEAD_REF="" SEMGREP_URL="https://tenantname.semgrep.dev" GITHUB_SHA="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-push/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-push/results.txt
index 2bc39102ff8a..3d645abfd761 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-push/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-github-push/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" GITHUB_ACTIONS="true" GITHUB_ACTOR="some_test_username" GITHUB_API_URL="https://api.github.com" GITHUB_REPOSITORY="project_name/project_name" GITHUB_RUN_ID="35" GITHUB_SERVER_URL="https://github.com" GITHUB_WORKSPACE="/home/runner/work/actions-test/actions-test" GITHUB_REPOSITORY_ID="4" GITHUB_REPOSITORY_OWNER_ID="2" GITHUB_EVENT_NAME="push" GITHUB_REF="refs/heads/some/branch-name" GITHUB_BASE_REF="" GITHUB_HEAD_REF="" GITHUB_SHA="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-gitlab-push/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-gitlab-push/results.txt
index 88afadca99e6..353f280be093 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-gitlab-push/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-gitlab-push/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" GITLAB_CI="true" CI_PROJECT_PATH="project_name/project_name" CI_PIPELINE_SOURCE="push" CI_JOB_TOKEN="some-token-test-placeholder" CI_COMMIT_REF_NAME="some/branch-name" CI_COMMIT_SHA="<MASKED>" CI_PROJECT_URL="https://example.com/gitlab-org/gitlab-foss" CI_JOB_URL="https://gitlab.com/gitlab-examples/ci-debug-trace/-/jobs/379424655" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-gitlab-special-env-vars/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-gitlab-special-env-vars/results.txt
index dbe8ae4b4cd3..28c8a246678c 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-gitlab-special-env-vars/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-gitlab-special-env-vars/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" GITLAB_CI="true" SEMGREP_REPO_NAME="project_name/project_name" CI_PIPELINE_SOURCE="merge_request_event" CI_MERGE_REQUEST_TARGET_BRANCH_NAME="main" CI_MERGE_REQUEST_PROJECT_URL="https://some.project.url.test.placeholder" CI_JOB_TOKEN="some-token-test-placeholder" CI_COMMIT_REF_NAME="some/branch-name" SEMGREP_COMMIT="unused-commit-test-placeholder" SEMGREP_REPO_URL="https://example.com/gitlab-org/gitlab-foss" SEMGREP_JOB_URL="https://gitlab.com/gitlab-examples/ci-debug-trace/-/jobs/379424655" SEMGREP_PR_ID="unused-iid-test-placeholder" CI_MERGE_REQUEST_DIFF_BASE_SHA="unused-commit-test-placeholder" SEMGREP_PR_TITLE="unused-merge-request-title-test-placeholder" CI_COMMIT_SHA="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-gitlab/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-gitlab/results.txt
index 2bb96b031690..28c8a246678c 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-gitlab/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-gitlab/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" GITLAB_CI="true" CI_PROJECT_PATH="project_name/project_name" CI_PIPELINE_SOURCE="merge_request_event" CI_MERGE_REQUEST_TARGET_BRANCH_NAME="main" CI_MERGE_REQUEST_PROJECT_URL="https://some.project.url.test.placeholder" CI_JOB_TOKEN="some-token-test-placeholder" CI_COMMIT_REF_NAME="some/branch-name" CI_COMMIT_SHA="<MASKED>" CI_PROJECT_URL="https://example.com/gitlab-org/gitlab-foss" CI_JOB_URL="https://gitlab.com/gitlab-examples/ci-debug-trace/-/jobs/379424655" CI_MERGE_REQUEST_IID="unused-iid-test-placeholder" CI_MERGE_REQUEST_DIFF_BASE_SHA="unused-commit-test-placeholder" CI_MERGE_REQUEST_TITLE="unused-merge-request-title-test-placeholder" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-jenkins-missing-vars/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-jenkins-missing-vars/results.txt
index 83715d86c536..fdf96612578b 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-jenkins-missing-vars/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-jenkins-missing-vars/results.txt
@@ -1,7 +1,3 @@
-=== command
-JENKINS_URL="some_url" SEMGREP_REPO_URL="https://random.url.org/some/path" SEMGREP_REPO_NAME="a/repo/name" GIT_BRANCH="some/branch-name" BUILD_URL="https://jenkins.build.url" GIT_COMMIT="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-jenkins-overwrite-autodetected-variables/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-jenkins-overwrite-autodetected-variables/results.txt
index f818d2bc048d..d60d4544c059 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-jenkins-overwrite-autodetected-variables/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-jenkins-overwrite-autodetected-variables/results.txt
@@ -1,7 +1,3 @@
-=== command
-JENKINS_URL="some_url" SEMGREP_REPO_NAME="a/repo/name" SEMGREP_REPO_URL="https://random.url.org/some/path" SEMGREP_BRANCH="branch/some-other-branch-name" SEMGREP_JOB_URL="https://another.random.url.org/some/path" SEMGREP_COMMIT="<some_random_commit>" GIT_URL="https://github.com/org/repo.git/" GIT_BRANCH="some/branch-name" BUILD_URL="https://jenkins.build.url" GIT_COMMIT="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-jenkins/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-jenkins/results.txt
index 27d79ab9c129..1ed41f6ba34b 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-jenkins/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-jenkins/results.txt
@@ -1,7 +1,3 @@
-=== command
-JENKINS_URL="some_url" GIT_URL="https://github.com/org/repo.git/" GIT_BRANCH="some/branch-name" BUILD_URL="https://jenkins.build.url" GIT_COMMIT="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-local/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-local/results.txt
index 8678b390cc13..5597ac8beb16 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-local/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-local/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_REPO_URL="git@github.com:example/fake.git" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-self-hosted/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-self-hosted/results.txt
index 80d1389ab15b..4800f9526188 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-self-hosted/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-self-hosted/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" SEMGREP_REPO_NAME="org_name/project_name/project_name" SEMGREP_REPO_URL="https://some.enterprise.url.com/org_name/project_name/project_name" SEMGREP_PR_ID="35" SEMGREP_BRANCH="some/branch-name" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-travis-overwrite-autodetected-variables/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-travis-overwrite-autodetected-variables/results.txt
index d7c12c2c3013..ead024dca571 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-travis-overwrite-autodetected-variables/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-travis-overwrite-autodetected-variables/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" TRAVIS="true" SEMGREP_REPO_NAME="a/repo/name" SEMGREP_REPO_URL="https://random.url.org/some/path" SEMGREP_BRANCH="branch/some-other-branch-name" SEMGREP_JOB_URL="https://another.random.url.org/some/path" SEMGREP_COMMIT="<some_random_commit>" SEMGREP_PR_ID="99999" TRAVIS_REPO_SLUG="project_name/project_name" TRAVIS_PULL_REQUEST_BRANCH="some/branch-name" TRAVIS_JOB_WEB_URL="https://travis.job.web.url/" TRAVIS_PULL_REQUEST="35" TRAVIS_COMMIT_MESSAGE="some: commit message! foo" TRAVIS_COMMIT="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-travis/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-travis/results.txt
index 2c816dc5edef..0a3e01962a34 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-travis/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-travis/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" TRAVIS="true" TRAVIS_REPO_SLUG="project_name/project_name" TRAVIS_PULL_REQUEST_BRANCH="some/branch-name" TRAVIS_JOB_WEB_URL="https://travis.job.web.url/" TRAVIS_PULL_REQUEST="35" TRAVIS_COMMIT_MESSAGE="some: commit message! foo" TRAVIS_COMMIT="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-unparsable_url/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-unparsable_url/results.txt
index 82593527d92e..4800f9526188 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-unparsable_url/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_full_run/noautofix-unparsable_url/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" SEMGREP_REPO_NAME="org_name/project_name/project_name" SEMGREP_REPO_URL="https://gitlab.net/foo.bar/a-b/a-b-c-d" SEMGREP_PR_ID="35" SEMGREP_BRANCH="some/branch-name" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_generic_secrets_output/generic_secrets_and_real_rule/output.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_generic_secrets_output/generic_secrets_and_real_rule/output.txt
index 9f8b4c09028f..f26203b1ff1e 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_generic_secrets_output/generic_secrets_and_real_rule/output.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_generic_secrets_output/generic_secrets_and_real_rule/output.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_lockfile_parse_failure_reporting/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_lockfile_parse_failure_reporting/results.txt
index f74f5d28c87d..75639ea4cfc2 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_lockfile_parse_failure_reporting/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_lockfile_parse_failure_reporting/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_nosem/autofix---disable-nosem/output.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_nosem/autofix---disable-nosem/output.txt
index cf7fbcf7ae3e..294fe5ecf526 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_nosem/autofix---disable-nosem/output.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_nosem/autofix---disable-nosem/output.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --disable-nosem
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_nosem/autofix---enable-nosem/output.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_nosem/autofix---enable-nosem/output.txt
index 8ab9cca60a7c..4eccad416521 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_nosem/autofix---enable-nosem/output.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_nosem/autofix---enable-nosem/output.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --enable-nosem
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_nosem/noautofix---disable-nosem/output.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_nosem/noautofix---disable-nosem/output.txt
index f5a6b7f9cc48..0e958ad18744 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_nosem/noautofix---disable-nosem/output.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_nosem/noautofix---disable-nosem/output.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --disable-nosem
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_nosem/noautofix---enable-nosem/output.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_nosem/noautofix---enable-nosem/output.txt
index 69e335fad7cd..5597ac8beb16 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_nosem/noautofix---enable-nosem/output.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_nosem/noautofix---enable-nosem/output.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --enable-nosem
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---emacs/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---emacs/results.txt
index 942305b30620..bf4b152e038a 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---emacs/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---emacs/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --emacs
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---gitlab-sast/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---gitlab-sast/results.txt
index 6692e27e0e1f..8b68a5954367 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---gitlab-sast/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---gitlab-sast/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --gitlab-sast
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---gitlab-secrets/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---gitlab-secrets/results.txt
index 3b706f0c9f72..8a5e6800a6a9 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---gitlab-secrets/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---gitlab-secrets/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --gitlab-secrets
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---json/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---json/results.txt
index a098a8d86cc9..64406f307544 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---json/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---json/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --json
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---sarif/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---sarif/results.txt
index ed4194205faf..abde25bfec38 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---sarif/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---sarif/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --sarif
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---vim/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---vim/results.txt
index 9a9321e77361..e9967b013afb 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---vim/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/autofix---vim/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --vim
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---emacs/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---emacs/results.txt
index 942305b30620..bf4b152e038a 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---emacs/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---emacs/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --emacs
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---gitlab-sast/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---gitlab-sast/results.txt
index 6692e27e0e1f..8b68a5954367 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---gitlab-sast/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---gitlab-sast/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --gitlab-sast
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---gitlab-secrets/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---gitlab-secrets/results.txt
index 3b706f0c9f72..8a5e6800a6a9 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---gitlab-secrets/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---gitlab-secrets/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --gitlab-secrets
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---json/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---json/results.txt
index 45930da84258..e3480b86b5f8 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---json/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---json/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --json
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---sarif/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---sarif/results.txt
index f58e0da7007d..a5602c197ed5 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---sarif/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---sarif/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --sarif
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---vim/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---vim/results.txt
index 9a9321e77361..e9967b013afb 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---vim/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_outputs/noautofix---vim/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --vim
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_query_dependency/True-config/output.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_query_dependency/True-config/output.txt
index e40a0c45ede3..d54363597b73 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_query_dependency/True-config/output.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_query_dependency/True-config/output.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_reachable_and_unreachable_diff_scan_findings/config/base_output.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_reachable_and_unreachable_diff_scan_findings/config/base_output.txt
index d918680a0416..52c2d445981f 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_reachable_and_unreachable_diff_scan_findings/config/base_output.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_reachable_and_unreachable_diff_scan_findings/config/base_output.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_reachable_and_unreachable_diff_scan_findings/config/new_output.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_reachable_and_unreachable_diff_scan_findings/config/new_output.txt
index 04134b0fa139..b408f905d498 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_reachable_and_unreachable_diff_scan_findings/config/new_output.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_reachable_and_unreachable_diff_scan_findings/config/new_output.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --oss-only --baseline-commit <MASKED>
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_sarif_output_with_dataflow_traces/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_sarif_output_with_dataflow_traces/results.txt
index b6956340c107..2c8c1c85a097 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_sarif_output_with_dataflow_traces/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_sarif_output_with_dataflow_traces/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-suppress-errors --dataflow-traces --oss-only --sarif
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_shallow_wrong_merge_base/bad_results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_shallow_wrong_merge_base/bad_results.txt
index c7b2c9eeebca..34c58df8d325 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_shallow_wrong_merge_base/bad_results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_shallow_wrong_merge_base/bad_results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" GITHUB_ACTIONS="true" GITHUB_EVENT_NAME="pull_request" GITHUB_REPOSITORY="project_name/project_name" GITHUB_RUN_ID="35" GITHUB_ACTOR="some_test_username" GITHUB_REF="some/branch-name" GITHUB_EVENT_PATH="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-force-color --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_shallow_wrong_merge_base/results.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_shallow_wrong_merge_base/results.txt
index e373e92e65d0..43f9d283a1b2 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_shallow_wrong_merge_base/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_shallow_wrong_merge_base/results.txt
@@ -1,7 +1,3 @@
-=== command
-CI="true" GITHUB_ACTIONS="true" GITHUB_EVENT_NAME="pull_request" GITHUB_REPOSITORY="project_name/project_name" GITHUB_RUN_ID="35" GITHUB_ACTOR="some_test_username" GITHUB_REF="some/branch-name" GITHUB_EVENT_PATH="<MASKED>" SEMGREP_APP_TOKEN="fake-key-from-tests" SEMGREP_GHA_MIN_FETCH_DEPTH="100" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --no-force-color --no-suppress-errors --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/..-False/output.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/..-False/output.txt
index f69866e3fca7..8fbb44387f35 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/..-False/output.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/..-False/output.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --subdir .. --oss-only
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/..org-False/output.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/..org-False/output.txt
index 5cf029ce091e..8fbb44387f35 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/..org-False/output.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/..org-False/output.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --subdir ../org --oss-only
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/checkout_project_nameorgexamples-False/output.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/checkout_project_nameorgexamples-False/output.txt
index 4532363c52cf..8fbb44387f35 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/checkout_project_nameorgexamples-False/output.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/checkout_project_nameorgexamples-False/output.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --subdir /checkout_project_name/org/examples --oss-only
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/org-True/output.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/org-True/output.txt
index deaa5815b0f4..aca2cf83dcec 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/org-True/output.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/org-True/output.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --subdir org/ --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/orgexamples-True/output.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/orgexamples-True/output.txt
index 4590bde7f043..ae90dc80bb83 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/orgexamples-True/output.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/orgexamples-True/output.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --subdir org/examples --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/test..orgexamples-True/output.txt b/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/test..orgexamples-True/output.txt
index 9515f282d5ec..ae90dc80bb83 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/test..orgexamples-True/output.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ci/test_subdir/test..orgexamples-True/output.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_APP_TOKEN="fake_key" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep ci --subdir test/../org/examples/ --oss-only
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_match_based_id/test_value/rule_and_target0/results.json b/cli/tests/default/e2e-other/snapshots/test_match_based_id/test_value/rule_and_target0/results.json
new file mode 100644
index 000000000000..835e01f3a005
--- /dev/null
+++ b/cli/tests/default/e2e-other/snapshots/test_match_based_id/test_value/rule_and_target0/results.json
@@ -0,0 +1,55 @@
+{
+  "errors": [],
+  "interfile_languages_used": [],
+  "paths": {
+    "scanned": [
+      "targets/basic/stupid.py"
+    ]
+  },
+  "results": [
+    {
+      "check_id": "rules.eqeq-is-bad",
+      "end": {
+        "col": 26,
+        "line": 3,
+        "offset": 69
+      },
+      "extra": {
+        "engine_kind": "OSS",
+        "fingerprint": "62b4a09c4569768898c43c09fa0a5b95b7e93257ef3a0911a5c379b6265b4d49fa4aecd5782461632e9aef4779af02d7cad4405b9a5318a0e5ffe9a5bd8daeae_0",
+        "is_ignored": false,
+        "lines": "    return a + b == a + b",
+        "message": "useless comparison operation `a + b == a + b` or `a + b != a + b`",
+        "metadata": {
+          "shortlink": "https://sg.run/xyz1",
+          "source": "https://semgrep.dev/r/eqeq-bad"
+        },
+        "metavars": {
+          "$X": {
+            "abstract_content": "a+b",
+            "end": {
+              "col": 17,
+              "line": 3,
+              "offset": 60
+            },
+            "start": {
+              "col": 12,
+              "line": 3,
+              "offset": 55
+            }
+          }
+        },
+        "severity": "ERROR",
+        "validation_state": "NO_VALIDATOR"
+      },
+      "path": "targets/basic/stupid.py",
+      "start": {
+        "col": 12,
+        "line": 3,
+        "offset": 55
+      }
+    }
+  ],
+  "skipped_rules": [],
+  "version": "0.42"
+}
\ No newline at end of file
diff --git a/cli/tests/default/e2e-other/snapshots/test_match_based_id/test_value/rule_and_target1/results.json b/cli/tests/default/e2e-other/snapshots/test_match_based_id/test_value/rule_and_target1/results.json
new file mode 100644
index 000000000000..2a684dda3313
--- /dev/null
+++ b/cli/tests/default/e2e-other/snapshots/test_match_based_id/test_value/rule_and_target1/results.json
@@ -0,0 +1,129 @@
+{
+  "errors": [],
+  "interfile_languages_used": [],
+  "paths": {
+    "scanned": [
+      "targets/basic/stupid.py"
+    ]
+  },
+  "results": [
+    {
+      "check_id": "rules.rule-with-cwe-tag",
+      "end": {
+        "col": 17,
+        "line": 3,
+        "offset": 60
+      },
+      "extra": {
+        "engine_kind": "OSS",
+        "fingerprint": "c14751d4f27243ab3fc24c8e9993358c58673b1f8cb907fc90f66a33826c9183a0386cc160cf4457cf7e175c9efb083d250a3b52fe3154a003fc9b9955802118_0",
+        "is_ignored": false,
+        "lines": "    return a + b == a + b",
+        "message": "Fake message.",
+        "metadata": {
+          "cwe": [
+            "CWE-99999999: Fake CWE",
+            "CWE-99999999: Fake CWE",
+            "CWE-88888888: Another fake CWE"
+          ]
+        },
+        "metavars": {
+          "$X": {
+            "abstract_content": "a",
+            "end": {
+              "col": 13,
+              "line": 3,
+              "offset": 56
+            },
+            "start": {
+              "col": 12,
+              "line": 3,
+              "offset": 55
+            }
+          },
+          "$Y": {
+            "abstract_content": "b",
+            "end": {
+              "col": 17,
+              "line": 3,
+              "offset": 60
+            },
+            "start": {
+              "col": 16,
+              "line": 3,
+              "offset": 59
+            }
+          }
+        },
+        "severity": "ERROR",
+        "validation_state": "NO_VALIDATOR"
+      },
+      "path": "targets/basic/stupid.py",
+      "start": {
+        "col": 12,
+        "line": 3,
+        "offset": 55
+      }
+    },
+    {
+      "check_id": "rules.rule-with-cwe-tag",
+      "end": {
+        "col": 26,
+        "line": 3,
+        "offset": 69
+      },
+      "extra": {
+        "engine_kind": "OSS",
+        "fingerprint": "c14751d4f27243ab3fc24c8e9993358c58673b1f8cb907fc90f66a33826c9183a0386cc160cf4457cf7e175c9efb083d250a3b52fe3154a003fc9b9955802118_1",
+        "is_ignored": false,
+        "lines": "    return a + b == a + b",
+        "message": "Fake message.",
+        "metadata": {
+          "cwe": [
+            "CWE-99999999: Fake CWE",
+            "CWE-99999999: Fake CWE",
+            "CWE-88888888: Another fake CWE"
+          ]
+        },
+        "metavars": {
+          "$X": {
+            "abstract_content": "a",
+            "end": {
+              "col": 22,
+              "line": 3,
+              "offset": 65
+            },
+            "start": {
+              "col": 21,
+              "line": 3,
+              "offset": 64
+            }
+          },
+          "$Y": {
+            "abstract_content": "b",
+            "end": {
+              "col": 26,
+              "line": 3,
+              "offset": 69
+            },
+            "start": {
+              "col": 25,
+              "line": 3,
+              "offset": 68
+            }
+          }
+        },
+        "severity": "ERROR",
+        "validation_state": "NO_VALIDATOR"
+      },
+      "path": "targets/basic/stupid.py",
+      "start": {
+        "col": 21,
+        "line": 3,
+        "offset": 64
+      }
+    }
+  ],
+  "skipped_rules": [],
+  "version": "0.42"
+}
\ No newline at end of file
diff --git a/cli/tests/default/e2e-other/snapshots/test_match_based_id/test_value_osemfail/rule_and_target0/results.json b/cli/tests/default/e2e-other/snapshots/test_match_based_id/test_value_osemfail/rule_and_target0/results.json
new file mode 100644
index 000000000000..8937b9bdc955
--- /dev/null
+++ b/cli/tests/default/e2e-other/snapshots/test_match_based_id/test_value_osemfail/rule_and_target0/results.json
@@ -0,0 +1,157 @@
+{
+  "errors": [],
+  "interfile_languages_used": [],
+  "paths": {
+    "scanned": [
+      "targets/taint/taint_trace.cpp"
+    ]
+  },
+  "results": [
+    {
+      "check_id": "rules.taint-trace",
+      "end": {
+        "col": 14,
+        "line": 17,
+        "offset": 522
+      },
+      "extra": {
+        "dataflow_trace": {
+          "intermediate_vars": [
+            {
+              "content": "res1",
+              "location": {
+                "end": {
+                  "col": 11,
+                  "line": 10,
+                  "offset": 223
+                },
+                "path": "targets/taint/taint_trace.cpp",
+                "start": {
+                  "col": 7,
+                  "line": 10,
+                  "offset": 219
+                }
+              }
+            },
+            {
+              "content": "curBase",
+              "location": {
+                "end": {
+                  "col": 12,
+                  "line": 15,
+                  "offset": 427
+                },
+                "path": "targets/taint/taint_trace.cpp",
+                "start": {
+                  "col": 5,
+                  "line": 15,
+                  "offset": 420
+                }
+              }
+            },
+            {
+              "content": "res2",
+              "location": {
+                "end": {
+                  "col": 11,
+                  "line": 12,
+                  "offset": 311
+                },
+                "path": "targets/taint/taint_trace.cpp",
+                "start": {
+                  "col": 7,
+                  "line": 12,
+                  "offset": 307
+                }
+              }
+            }
+          ],
+          "taint_sink": [
+            "CliLoc",
+            [
+              {
+                "end": {
+                  "col": 14,
+                  "line": 17,
+                  "offset": 522
+                },
+                "path": "targets/taint/taint_trace.cpp",
+                "start": {
+                  "col": 10,
+                  "line": 17,
+                  "offset": 518
+                }
+              },
+              "res2"
+            ]
+          ],
+          "taint_source": [
+            "CliLoc",
+            [
+              {
+                "end": {
+                  "col": 22,
+                  "line": 10,
+                  "offset": 234
+                },
+                "path": "targets/taint/taint_trace.cpp",
+                "start": {
+                  "col": 14,
+                  "line": 10,
+                  "offset": 226
+                }
+              },
+              "SOURCE()"
+            ]
+          ]
+        },
+        "engine_kind": "OSS",
+        "fingerprint": "c103a760f6ce7176c2d5127a8c5afa47e83d41bef95586322d99febf0f148c2808bc6b168b31948e8fd10576f7f4edd69fb6d341c25ed9c259cde4e9164d7b96_0",
+        "is_ignored": false,
+        "lines": "    SINK(res2);                               // (5) sink res2",
+        "message": "found an error",
+        "metadata": {
+          "interfile": true
+        },
+        "metavars": {
+          "$RHS": {
+            "abstract_content": "res1",
+            "end": {
+              "col": 44,
+              "line": 15,
+              "offset": 459
+            },
+            "start": {
+              "col": 40,
+              "line": 15,
+              "offset": 455
+            }
+          },
+          "$SRC": {
+            "abstract_content": "res2",
+            "end": {
+              "col": 14,
+              "line": 17,
+              "offset": 522
+            },
+            "start": {
+              "col": 10,
+              "line": 17,
+              "offset": 518
+            }
+          }
+        },
+        "severity": "WARNING",
+        "validation_state": "NO_VALIDATOR"
+      },
+      "path": "targets/taint/taint_trace.cpp",
+      "start": {
+        "col": 10,
+        "line": 17,
+        "offset": 518
+      }
+    }
+  ],
+  "skipped_rules": [],
+  "version": "0.42"
+}
\ No newline at end of file
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_no_lockfiles/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_no_lockfiles/results.txt
index f73241a58811..54f7fbaab33a 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_no_lockfiles/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_no_lockfiles/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --config ../../rules/dependency_aware/js-sca.yaml stupid.js
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareansi-html.yaml-dependency_awareansi/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareansi-html.yaml-dependency_awareansi/results.txt
index 0cdce8d0b60c..855c9f597a54 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareansi-html.yaml-dependency_awareansi/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareansi-html.yaml-dependency_awareansi/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/ansi-html.yaml --json targets/dependency_aware/ansi
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareawscli_vuln.yaml-dependency_awareawscli-with-manifest/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareawscli_vuln.yaml-dependency_awareawscli-with-manifest/results.txt
index 5aca4b176817..6b2cfaf21d85 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareawscli_vuln.yaml-dependency_awareawscli-with-manifest/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareawscli_vuln.yaml-dependency_awareawscli-with-manifest/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/awscli_vuln.yaml --json targets/dependency_aware/awscli-with-manifest
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareawscli_vuln.yaml-dependency_awareawscli/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareawscli_vuln.yaml-dependency_awareawscli/results.txt
index 15eb0a1e9e3d..0acaa7f80267 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareawscli_vuln.yaml-dependency_awareawscli/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareawscli_vuln.yaml-dependency_awareawscli/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/awscli_vuln.yaml --json targets/dependency_aware/awscli
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaredart-parity.yaml-dependency_awaredart/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaredart-parity.yaml-dependency_awaredart/results.txt
index d57447841542..a6fb7721a2c1 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaredart-parity.yaml-dependency_awaredart/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaredart-parity.yaml-dependency_awaredart/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/dart-parity.yaml --json targets/dependency_aware/dart
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaregeneric-sca.yaml-dependency_awaregeneric/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaregeneric-sca.yaml-dependency_awaregeneric/results.txt
index 17b5ae8855cf..11a25a0ed90f 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaregeneric-sca.yaml-dependency_awaregeneric/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaregeneric-sca.yaml-dependency_awaregeneric/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/generic-sca.yaml --json targets/dependency_aware/generic
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarego-sca.yaml-dependency_awarego/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarego-sca.yaml-dependency_awarego/results.txt
index 22230f39f8ed..0d886c33bae3 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarego-sca.yaml-dependency_awarego/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarego-sca.yaml-dependency_awarego/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/go-sca.yaml --json targets/dependency_aware/go
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarego-sca.yaml-dependency_awarego_multi_newline/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarego-sca.yaml-dependency_awarego_multi_newline/results.txt
index 67cd04cb3ebe..16c3c1c2724e 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarego-sca.yaml-dependency_awarego_multi_newline/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarego-sca.yaml-dependency_awarego_multi_newline/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/go-sca.yaml --json targets/dependency_aware/go_multi_newline
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarego-sca.yaml-dependency_awarego_toolchain/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarego-sca.yaml-dependency_awarego_toolchain/results.txt
index 7134400b5bae..33a9e17564ea 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarego-sca.yaml-dependency_awarego_toolchain/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarego-sca.yaml-dependency_awarego_toolchain/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/go-sca.yaml --json targets/dependency_aware/go_toolchain
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaregradle-guava.yaml-dependency_awaregradle-direct/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaregradle-guava.yaml-dependency_awaregradle-direct/results.txt
index 55d2083d2e8b..7bfffe33c8eb 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaregradle-guava.yaml-dependency_awaregradle-direct/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaregradle-guava.yaml-dependency_awaregradle-direct/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/gradle-guava.yaml --json targets/dependency_aware/gradle-direct
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejava-gradle-sca.yaml-dependency_awaregradle-arbitrary-comment/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejava-gradle-sca.yaml-dependency_awaregradle-arbitrary-comment/results.txt
index 8a5ddddb453c..38d2043facda 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejava-gradle-sca.yaml-dependency_awaregradle-arbitrary-comment/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejava-gradle-sca.yaml-dependency_awaregradle-arbitrary-comment/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/java-gradle-sca.yaml --json targets/dependency_aware/gradle-arbitrary-comment
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejava-gradle-sca.yaml-dependency_awaregradle-no-comment/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejava-gradle-sca.yaml-dependency_awaregradle-no-comment/results.txt
index 2e78ae9bf994..ad33e1036747 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejava-gradle-sca.yaml-dependency_awaregradle-no-comment/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejava-gradle-sca.yaml-dependency_awaregradle-no-comment/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/java-gradle-sca.yaml --json targets/dependency_aware/gradle-no-comment
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejava-gradle-sca.yaml-dependency_awaregradle/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejava-gradle-sca.yaml-dependency_awaregradle/results.txt
index 90056f89eeee..cfbc1f38ca31 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejava-gradle-sca.yaml-dependency_awaregradle/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejava-gradle-sca.yaml-dependency_awaregradle/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/java-gradle-sca.yaml --json targets/dependency_aware/gradle
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejava-gradle-sca.yaml-dependency_awaregradle_empty/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejava-gradle-sca.yaml-dependency_awaregradle_empty/results.txt
index 64bc8ffac3b6..b9366b82422f 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejava-gradle-sca.yaml-dependency_awaregradle_empty/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejava-gradle-sca.yaml-dependency_awaregradle_empty/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/java-gradle-sca.yaml --json targets/dependency_aware/gradle_empty=
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejava-gradle-sca.yaml-dependency_awaregradle_trailing_newline/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejava-gradle-sca.yaml-dependency_awaregradle_trailing_newline/results.txt
index 5722f88c8c04..85bf9c3f6c1a 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejava-gradle-sca.yaml-dependency_awaregradle_trailing_newline/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejava-gradle-sca.yaml-dependency_awaregradle_trailing_newline/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/java-gradle-sca.yaml --json targets/dependency_aware/gradle_trailing_newline
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-pnpm-sca.yaml-dependency_awarepnpm-v6/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-pnpm-sca.yaml-dependency_awarepnpm-v6/results.txt
index a5fa1f8efe02..d345418d8c5d 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-pnpm-sca.yaml-dependency_awarepnpm-v6/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-pnpm-sca.yaml-dependency_awarepnpm-v6/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/js-pnpm-sca.yaml --json targets/dependency_aware/pnpm-v6
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-pnpm-sca.yaml-dependency_awarepnpm-v9/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-pnpm-sca.yaml-dependency_awarepnpm-v9/results.txt
index a47660e00627..e24f95d38d20 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-pnpm-sca.yaml-dependency_awarepnpm-v9/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-pnpm-sca.yaml-dependency_awarepnpm-v9/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/js-pnpm-sca.yaml --json targets/dependency_aware/pnpm-v9
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-pnpm-sca.yaml-dependency_awarepnpm-workspaces/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-pnpm-sca.yaml-dependency_awarepnpm-workspaces/results.txt
index b2235189e126..93a970bfaf55 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-pnpm-sca.yaml-dependency_awarepnpm-workspaces/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-pnpm-sca.yaml-dependency_awarepnpm-workspaces/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/js-pnpm-sca.yaml --json targets/dependency_aware/pnpm-workspaces
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-pnpm-sca.yaml-dependency_awarepnpm/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-pnpm-sca.yaml-dependency_awarepnpm/results.txt
index 914d43b35d55..e7660f2c0427 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-pnpm-sca.yaml-dependency_awarepnpm/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-pnpm-sca.yaml-dependency_awarepnpm/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/js-pnpm-sca.yaml --json targets/dependency_aware/pnpm
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-sca.yaml-dependency_awaredeeply_nested_package_lock/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-sca.yaml-dependency_awaredeeply_nested_package_lock/results.txt
index e2ba0f2ab5a4..5c5d047dd9c7 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-sca.yaml-dependency_awaredeeply_nested_package_lock/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-sca.yaml-dependency_awaredeeply_nested_package_lock/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/js-sca.yaml --json targets/dependency_aware/deeply_nested_package_lock
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-sca.yaml-dependency_awarejs/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-sca.yaml-dependency_awarejs/results.txt
index 2fa9be696b3f..4cf59da57ac2 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-sca.yaml-dependency_awarejs/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-sca.yaml-dependency_awarejs/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/js-sca.yaml --json targets/dependency_aware/js
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-sca.yaml-dependency_awarepackage-lock_resolved_false/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-sca.yaml-dependency_awarepackage-lock_resolved_false/results.txt
index 6b4524c16c71..ea32044bff13 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-sca.yaml-dependency_awarepackage-lock_resolved_false/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-sca.yaml-dependency_awarepackage-lock_resolved_false/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/js-sca.yaml --json targets/dependency_aware/package-lock_resolved_false
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-yarn2-sca.yaml-dependency_awarepackage-lock-v3/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-yarn2-sca.yaml-dependency_awarepackage-lock-v3/results.txt
index 4b3c601896b1..33b3d8599957 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-yarn2-sca.yaml-dependency_awarepackage-lock-v3/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-yarn2-sca.yaml-dependency_awarepackage-lock-v3/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/js-yarn2-sca.yaml --json targets/dependency_aware/package-lock-v3
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-yarn2-sca.yaml-dependency_awareyarn2/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-yarn2-sca.yaml-dependency_awareyarn2/results.txt
index 7accd3d8ba4e..74a69b7f502c 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-yarn2-sca.yaml-dependency_awareyarn2/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarejs-yarn2-sca.yaml-dependency_awareyarn2/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/js-yarn2-sca.yaml --json targets/dependency_aware/yarn2
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarelodash-4.17.19.yaml-dependency_awarelodash/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarelodash-4.17.19.yaml-dependency_awarelodash/results.txt
index 2f2f06e8f258..bead920102a6 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarelodash-4.17.19.yaml-dependency_awarelodash/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarelodash-4.17.19.yaml-dependency_awarelodash/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/lodash-4.17.19.yaml --json targets/dependency_aware/lodash
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarelog4shell.yaml-dependency_awarelog4j/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarelog4shell.yaml-dependency_awarelog4j/results.txt
index bf6d010b0b3e..6452f2813f1e 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarelog4shell.yaml-dependency_awarelog4j/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarelog4shell.yaml-dependency_awarelog4j/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/log4shell.yaml --json targets/dependency_aware/log4j
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaremaven-guice.yaml-dependency_awaremaven_dep_tree_extra_field/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaremaven-guice.yaml-dependency_awaremaven_dep_tree_extra_field/results.txt
index daeb856f0e36..00e6d7f1df94 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaremaven-guice.yaml-dependency_awaremaven_dep_tree_extra_field/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaremaven-guice.yaml-dependency_awaremaven_dep_tree_extra_field/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/maven-guice.yaml --json targets/dependency_aware/maven_dep_tree_extra_field
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaremaven-guice.yaml-dependency_awaremaven_dep_tree_joined/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaremaven-guice.yaml-dependency_awaremaven_dep_tree_joined/results.txt
index ed017c5b073f..d9022d042421 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaremaven-guice.yaml-dependency_awaremaven_dep_tree_joined/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaremaven-guice.yaml-dependency_awaremaven_dep_tree_joined/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/maven-guice.yaml --json targets/dependency_aware/maven_dep_tree_joined
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaremaven-guice.yaml-dependency_awaremaven_dep_tree_optional/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaremaven-guice.yaml-dependency_awaremaven_dep_tree_optional/results.txt
index 7dc34f929e7f..a2bf526c7e7b 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaremaven-guice.yaml-dependency_awaremaven_dep_tree_optional/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaremaven-guice.yaml-dependency_awaremaven_dep_tree_optional/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/maven-guice.yaml --json targets/dependency_aware/maven_dep_tree_optional
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaremaven-guice.yaml-dependency_awaremaven_dep_tree_release_version/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaremaven-guice.yaml-dependency_awaremaven_dep_tree_release_version/results.txt
index 43617b0cf929..0782b35cd850 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaremaven-guice.yaml-dependency_awaremaven_dep_tree_release_version/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaremaven-guice.yaml-dependency_awaremaven_dep_tree_release_version/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/maven-guice.yaml --json targets/dependency_aware/maven_dep_tree_release_version
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaremonorepo.yaml-dependency_awaremonorepo/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaremonorepo.yaml-dependency_awaremonorepo/results.txt
index f55664f61fe2..30545c64b502 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaremonorepo.yaml-dependency_awaremonorepo/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaremonorepo.yaml-dependency_awaremonorepo/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/monorepo.yaml --json targets/dependency_aware/monorepo
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarenested_package_lock.yaml-dependency_awarenested_package_lock/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarenested_package_lock.yaml-dependency_awarenested_package_lock/results.txt
index b06054bd4240..9f9fcb8984c2 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarenested_package_lock.yaml-dependency_awarenested_package_lock/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarenested_package_lock.yaml-dependency_awarenested_package_lock/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/nested_package_lock.yaml --json targets/dependency_aware/nested_package_lock
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareno-pattern.yaml-dependency_awareyarn-v1-without-version-constraint/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareno-pattern.yaml-dependency_awareyarn-v1-without-version-constraint/results.txt
index 9c396d08eb6c..53ecaeb3fe91 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareno-pattern.yaml-dependency_awareyarn-v1-without-version-constraint/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareno-pattern.yaml-dependency_awareyarn-v1-without-version-constraint/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/no-pattern.yaml --json targets/dependency_aware/yarn-v1-without-version-constraint
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareno-pattern.yaml-dependency_awareyarn/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareno-pattern.yaml-dependency_awareyarn/results.txt
index fc643303a500..2416bca1b5b0 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareno-pattern.yaml-dependency_awareyarn/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareno-pattern.yaml-dependency_awareyarn/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/no-pattern.yaml --json targets/dependency_aware/yarn
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareno-pattern.yaml-dependency_awareyarn_multi_hash/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareno-pattern.yaml-dependency_awareyarn_multi_hash/results.txt
index 2b8404f5fb39..c11adef22079 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareno-pattern.yaml-dependency_awareyarn_multi_hash/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareno-pattern.yaml-dependency_awareyarn_multi_hash/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/no-pattern.yaml --json targets/dependency_aware/yarn_multi_hash
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarenuget-sca-simple.yaml-dependency_awarenuget-large/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarenuget-sca-simple.yaml-dependency_awarenuget-large/results.txt
index f25a0267b758..aeeadb0b430b 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarenuget-sca-simple.yaml-dependency_awarenuget-large/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarenuget-sca-simple.yaml-dependency_awarenuget-large/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/nuget-sca-simple.yaml --json targets/dependency_aware/nuget-large
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarenuget-sca-simple.yaml-dependency_awarenuget/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarenuget-sca-simple.yaml-dependency_awarenuget/results.txt
index 590b2d9f6f59..1881e17d7caa 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarenuget-sca-simple.yaml-dependency_awarenuget/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarenuget-sca-simple.yaml-dependency_awarenuget/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/nuget-sca-simple.yaml --json targets/dependency_aware/nuget
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarephp-sca.yaml-dependency_awarephp/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarephp-sca.yaml-dependency_awarephp/results.txt
index 9f4b15716282..e9319bf0a89c 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarephp-sca.yaml-dependency_awarephp/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarephp-sca.yaml-dependency_awarephp/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/php-sca.yaml --json targets/dependency_aware/php
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-pipfile-pyhcl-sca.yaml-dependency_awarepipfile_with_vuln_in_dev-packages/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-pipfile-pyhcl-sca.yaml-dependency_awarepipfile_with_vuln_in_dev-packages/results.txt
index cfbc6ab71139..e0178bd0182d 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-pipfile-pyhcl-sca.yaml-dependency_awarepipfile_with_vuln_in_dev-packages/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-pipfile-pyhcl-sca.yaml-dependency_awarepipfile_with_vuln_in_dev-packages/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-pipfile-pyhcl-sca.yaml --json targets/dependency_aware/pipfile_with_vuln_in_dev-packages
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-pipfile-sca.yaml-dependency_awarepipfile/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-pipfile-sca.yaml-dependency_awarepipfile/results.txt
index 90feb239bdab..433b48716db6 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-pipfile-sca.yaml-dependency_awarepipfile/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-pipfile-sca.yaml-dependency_awarepipfile/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-pipfile-sca.yaml --json targets/dependency_aware/pipfile
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-pipfile-sca.yaml-dependency_awarepipfile_with_empty_dev-packages/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-pipfile-sca.yaml-dependency_awarepipfile_with_empty_dev-packages/results.txt
index d6c30507f3be..6ff66e9a8203 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-pipfile-sca.yaml-dependency_awarepipfile_with_empty_dev-packages/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-pipfile-sca.yaml-dependency_awarepipfile_with_empty_dev-packages/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-pipfile-sca.yaml --json targets/dependency_aware/pipfile_with_empty_dev-packages
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-pipfile-sca.yaml-dependency_awarepipfile_with_one_newline_between_sections/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-pipfile-sca.yaml-dependency_awarepipfile_with_one_newline_between_sections/results.txt
index cecc23f38907..2151b21fcff0 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-pipfile-sca.yaml-dependency_awarepipfile_with_one_newline_between_sections/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-pipfile-sca.yaml-dependency_awarepipfile_with_one_newline_between_sections/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-pipfile-sca.yaml --json targets/dependency_aware/pipfile_with_one_newline_between_sections
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-pipfile-sca.yaml-dependency_awarepipfile_with_uppercase_package_name/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-pipfile-sca.yaml-dependency_awarepipfile_with_uppercase_package_name/results.txt
index c71fbb15b66b..9fbe1e8e1271 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-pipfile-sca.yaml-dependency_awarepipfile_with_uppercase_package_name/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-pipfile-sca.yaml-dependency_awarepipfile_with_uppercase_package_name/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-pipfile-sca.yaml --json targets/dependency_aware/pipfile_with_uppercase_package_name
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awaremanifest_parse_error/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awaremanifest_parse_error/results.txt
index 221990d8c9a3..b9901e505c75 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awaremanifest_parse_error/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awaremanifest_parse_error/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-poetry-sca.yaml --json targets/dependency_aware/manifest_parse_error
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarenuget/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarenuget/results.txt
index d0efab0121d6..87c0ee8cd129 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarenuget/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarenuget/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-poetry-sca.yaml --json targets/dependency_aware/nuget
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry/results.txt
index 80dadba4c02e..767917b43df5 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-poetry-sca.yaml --json targets/dependency_aware/poetry
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_comments/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_comments/results.txt
index 8cd9647f279d..f3402b4e24bf 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_comments/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_comments/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-poetry-sca.yaml --json targets/dependency_aware/poetry_comments
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_empty_table/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_empty_table/results.txt
index a04e7dd0418c..febf98940d60 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_empty_table/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_empty_table/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-poetry-sca.yaml --json targets/dependency_aware/poetry_empty_table
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_quoted_key/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_quoted_key/results.txt
index 65660cf01e71..39afbc60adae 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_quoted_key/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_quoted_key/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-poetry-sca.yaml --json targets/dependency_aware/poetry_quoted_key
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_trailing_newlines/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_trailing_newlines/results.txt
index 5e5f4d8d2fbb..21289c6d4043 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_trailing_newlines/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_trailing_newlines/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-poetry-sca.yaml --json targets/dependency_aware/poetry_trailing_newlines
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_with_arbitrary_starting_comment/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_with_arbitrary_starting_comment/results.txt
index fa1bd2f437ad..99777bad9a58 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_with_arbitrary_starting_comment/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_with_arbitrary_starting_comment/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-poetry-sca.yaml --json targets/dependency_aware/poetry_with_arbitrary_starting_comment
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_with_uppercase_package_name/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_with_uppercase_package_name/results.txt
index ab30ac1706cb..21952ecc3975 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_with_uppercase_package_name/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-poetry-sca.yaml-dependency_awarepoetry_with_uppercase_package_name/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-poetry-sca.yaml --json targets/dependency_aware/poetry_with_uppercase_package_name
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements/results.txt
index 50f524bc87f9..b417c4ef8ada 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirements
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements3/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements3/results.txt
index 2702412a1694..bc9f1f5ad918 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements3/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements3/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirements3
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_with_uppercase_package_name/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_with_uppercase_package_name/results.txt
index e0af95c5f0bf..838e7bde5061 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_with_uppercase_package_name/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_with_uppercase_package_name/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirements_with_uppercase_package_name
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareruby-sca.yaml-dependency_awareruby-with-multiple-gem-sections/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareruby-sca.yaml-dependency_awareruby-with-multiple-gem-sections/results.txt
index 4ca927cbb0a3..0f4a2ae88ff0 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareruby-sca.yaml-dependency_awareruby-with-multiple-gem-sections/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareruby-sca.yaml-dependency_awareruby-with-multiple-gem-sections/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/ruby-sca.yaml --json targets/dependency_aware/ruby-with-multiple-gem-sections
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareruby-sca.yaml-dependency_awareruby-with-multiple-remotes/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareruby-sca.yaml-dependency_awareruby-with-multiple-remotes/results.txt
index ea9aaeedede6..911c24484379 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareruby-sca.yaml-dependency_awareruby-with-multiple-remotes/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareruby-sca.yaml-dependency_awareruby-with-multiple-remotes/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/ruby-sca.yaml --json targets/dependency_aware/ruby-with-multiple-remotes
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareruby-sca.yaml-dependency_awareruby/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareruby-sca.yaml-dependency_awareruby/results.txt
index 360c819bd50e..b992aa8e4d33 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareruby-sca.yaml-dependency_awareruby/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareruby-sca.yaml-dependency_awareruby/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/ruby-sca.yaml --json targets/dependency_aware/ruby
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarerust-sca.yaml-dependency_awarerust/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarerust-sca.yaml-dependency_awarerust/results.txt
index 6e49acd555e1..886048fb4d52 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarerust-sca.yaml-dependency_awarerust/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarerust-sca.yaml-dependency_awarerust/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/rust-sca.yaml --json targets/dependency_aware/rust
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarerust-sca.yaml-dependency_awarerust_short_lockfile/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarerust-sca.yaml-dependency_awarerust_short_lockfile/results.txt
index 89824a5181dc..2cad4c1cbd2e 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarerust-sca.yaml-dependency_awarerust_short_lockfile/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awarerust-sca.yaml-dependency_awarerust_short_lockfile/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/rust-sca.yaml --json targets/dependency_aware/rust_short_lockfile
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareswift-sca.yaml-dependency_awareswiftpm_missing_version/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareswift-sca.yaml-dependency_awareswiftpm_missing_version/results.txt
index 38afa88abd82..d48041e858db 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareswift-sca.yaml-dependency_awareswiftpm_missing_version/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareswift-sca.yaml-dependency_awareswiftpm_missing_version/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/swift-sca.yaml --json targets/dependency_aware/swiftpm_missing_version
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareswift-sca.yaml-dependency_awareswiftpmv1/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareswift-sca.yaml-dependency_awareswiftpmv1/results.txt
index c7a8b01c9c65..c642d09fc489 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareswift-sca.yaml-dependency_awareswiftpmv1/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareswift-sca.yaml-dependency_awareswiftpmv1/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/swift-sca.yaml --json targets/dependency_aware/swiftpm/v1
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareswift-sca.yaml-dependency_awareswiftpmv2/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareswift-sca.yaml-dependency_awareswiftpmv2/results.txt
index 48fe6007bbfa..2940976edbc4 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareswift-sca.yaml-dependency_awareswiftpmv2/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareswift-sca.yaml-dependency_awareswiftpmv2/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/swift-sca.yaml --json targets/dependency_aware/swiftpm/v2
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaretransitive_and_direct.yaml-dependency_awaretransitive_and_directdirect_reachable_transitive_unreachable/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaretransitive_and_direct.yaml-dependency_awaretransitive_and_directdirect_reachable_transitive_unreachable/results.txt
index 56002277c23f..a69c8def5232 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaretransitive_and_direct.yaml-dependency_awaretransitive_and_directdirect_reachable_transitive_unreachable/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaretransitive_and_direct.yaml-dependency_awaretransitive_and_directdirect_reachable_transitive_unreachable/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/transitive_and_direct.yaml --json targets/dependency_aware/transitive_and_direct/direct_reachable_transitive_unreachable
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaretransitive_and_direct.yaml-dependency_awaretransitive_and_directtransitive_not_reachable_if_direct/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaretransitive_and_direct.yaml-dependency_awaretransitive_and_directtransitive_not_reachable_if_direct/results.txt
index 29b8e19b3dbf..b135c14c2c21 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaretransitive_and_direct.yaml-dependency_awaretransitive_and_directtransitive_not_reachable_if_direct/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awaretransitive_and_direct.yaml-dependency_awaretransitive_and_directtransitive_not_reachable_if_direct/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/transitive_and_direct.yaml --json targets/dependency_aware/transitive_and_direct/transitive_not_reachable_if_direct
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareyarn-sass.yaml-dependency_awareyarn/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareyarn-sass.yaml-dependency_awareyarn/results.txt
index 720a017106d1..f0d9a8bf4ed1 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareyarn-sass.yaml-dependency_awareyarn/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareyarn-sass.yaml-dependency_awareyarn/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/yarn-sass.yaml --json targets/dependency_aware/yarn
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareyarn-sass.yaml-dependency_awareyarn_at_in_version/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareyarn-sass.yaml-dependency_awareyarn_at_in_version/results.txt
index 2e1ca9af8779..1d46d42bf8f2 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareyarn-sass.yaml-dependency_awareyarn_at_in_version/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc/rulesdependency_awareyarn-sass.yaml-dependency_awareyarn_at_in_version/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/yarn-sass.yaml --json targets/dependency_aware/yarn_at_in_version
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__pypi_package_name_lowercase/rulesdependency_awarepython-pipfile-case-insensitive-package.yaml-dependency_awarepipfile/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__pypi_package_name_lowercase/rulesdependency_awarepython-pipfile-case-insensitive-package.yaml-dependency_awarepipfile/results.txt
index 4b76efd8ea87..433b48716db6 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__pypi_package_name_lowercase/rulesdependency_awarepython-pipfile-case-insensitive-package.yaml-dependency_awarepipfile/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__pypi_package_name_lowercase/rulesdependency_awarepython-pipfile-case-insensitive-package.yaml-dependency_awarepipfile/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-pipfile-case-insensitive-package.yaml --json targets/dependency_aware/pipfile
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__pypi_package_name_lowercase/rulesdependency_awarepython-poetry-case-insensitive-package.yaml-dependency_awarepoetry/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__pypi_package_name_lowercase/rulesdependency_awarepython-poetry-case-insensitive-package.yaml-dependency_awarepoetry/results.txt
index a8f2927a6538..767917b43df5 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__pypi_package_name_lowercase/rulesdependency_awarepython-poetry-case-insensitive-package.yaml-dependency_awarepoetry/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__pypi_package_name_lowercase/rulesdependency_awarepython-poetry-case-insensitive-package.yaml-dependency_awarepoetry/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-poetry-case-insensitive-package.yaml --json targets/dependency_aware/poetry
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__pypi_package_name_lowercase/rulesdependency_awarepython-requirements-case-insensitive-package.yaml-dependency_awarerequirements/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__pypi_package_name_lowercase/rulesdependency_awarepython-requirements-case-insensitive-package.yaml-dependency_awarerequirements/results.txt
index 7d47843fad5a..b417c4ef8ada 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__pypi_package_name_lowercase/rulesdependency_awarepython-requirements-case-insensitive-package.yaml-dependency_awarerequirements/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__pypi_package_name_lowercase/rulesdependency_awarepython-requirements-case-insensitive-package.yaml-dependency_awarerequirements/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-case-insensitive-package.yaml --json targets/dependency_aware/requirements
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirement/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirement/results.txt
index 4aa08ef6bc13..297052ed4a8c 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirement/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirement/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirement
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirement_pip/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirement_pip/results.txt
index f0fb44436d7b..3df155f39355 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirement_pip/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirement_pip/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirement_pip
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_folder/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_folder/results.txt
index 45899a4635ea..b53b1793666f 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_folder/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_folder/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirements_folder
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_folder_dep_dupes/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_folder_dep_dupes/results.txt
index 449a1141dea0..d302940e5d14 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_folder_dep_dupes/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_folder_dep_dupes/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirements_folder_dep_dupes
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_folder_no_src/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_folder_no_src/results.txt
index 47a5cac51e72..eedae951dc2d 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_folder_no_src/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_folder_no_src/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirements_folder_no_src
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_folder_similar_deps/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_folder_similar_deps/results.txt
index 358cfb0c29aa..466eb56c239b 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_folder_similar_deps/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_folder_similar_deps/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirements_folder_similar_deps
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_multiple_lockfiles/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_multiple_lockfiles/results.txt
index ebc618a46b0b..bbbfe9cf1621 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_multiple_lockfiles/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_multiple_lockfiles/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirements_multiple_lockfiles
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_multiple_lockfiles_dep_dupes/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_multiple_lockfiles_dep_dupes/results.txt
index 6da3b4e3168d..c5c96f17c6ae 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_multiple_lockfiles_dep_dupes/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_multiple_lockfiles_dep_dupes/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirements_multiple_lockfiles_dep_dupes
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_multiple_lockfiles_dep_dupes_no_src/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_multiple_lockfiles_dep_dupes_no_src/results.txt
index 4b69d66619b3..1e42cf7ac4a5 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_multiple_lockfiles_dep_dupes_no_src/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_multiple_lockfiles_dep_dupes_no_src/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirements_multiple_lockfiles_dep_dupes_no_src
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_multiple_lockfiles_no_src/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_multiple_lockfiles_no_src/results.txt
index 45050361cc76..1a4e17947a8c 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_multiple_lockfiles_no_src/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_multiple_lockfiles_no_src/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirements_multiple_lockfiles_no_src
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_multiple_lockfiles_similar_deps/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_multiple_lockfiles_similar_deps/results.txt
index 834b57219a30..d0e550335f2e 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_multiple_lockfiles_similar_deps/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_multiple_lockfiles_similar_deps/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirements_multiple_lockfiles_similar_deps
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested/results.txt
index 8e2ea30bed2e..eb8dc49cf77d 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirements_nested
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested_dep_dupes/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested_dep_dupes/results.txt
index adecf608a676..fa2049b29396 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested_dep_dupes/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested_dep_dupes/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirements_nested_dep_dupes
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested_folder/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested_folder/results.txt
index faafdc3f9b65..8ccd67e27c0d 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested_folder/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested_folder/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirements_nested_folder
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested_folder_dep_dupes/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested_folder_dep_dupes/results.txt
index e30e6883755d..1349fb3397d1 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested_folder_dep_dupes/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested_folder_dep_dupes/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirements_nested_folder_dep_dupes
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested_folder_no_src/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested_folder_no_src/results.txt
index 813a22f0dbda..bf196a7b8655 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested_folder_no_src/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested_folder_no_src/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirements_nested_folder_no_src
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested_no_src/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested_no_src/results.txt
index 3577634a3a7f..d8c8b23196cc 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested_no_src/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_nested_no_src/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirements_nested_no_src
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_pip/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_pip/results.txt
index 11e9c363aae1..5865a97b7f88 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_pip/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_pip/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirements_pip
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_pip_folder/results.txt b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_pip_folder/results.txt
index db3db0511166..027650bea839 100644
--- a/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_pip_folder/results.txt
+++ b/cli/tests/default/e2e-other/snapshots/test_ssc/test_ssc__requirements_lockfiles/rulesdependency_awarepython-requirements-sca.yaml-dependency_awarerequirements_pip_folder/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/dependency_aware/python-requirements-sca.yaml --json targets/dependency_aware/requirements_pip_folder
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e-other/test_ci.py b/cli/tests/default/e2e-other/test_ci.py
index 285a6126159c..1405baf04227 100644
--- a/cli/tests/default/e2e-other/test_ci.py
+++ b/cli/tests/default/e2e-other/test_ci.py
@@ -1662,6 +1662,27 @@ def test_generic_secrets_output(
     assert "generic secrets rule message" not in result.raw_stdout
 
 
+@pytest.mark.osemfail
+def test_semgrep_managed_scan_id(run_semgrep: RunSemgrep, requests_mock):
+    MANAGED_SCAN_ID = "12321"
+    scan_create = requests_mock.post("https://semgrep.dev/api/cli/scans")
+    run_semgrep(
+        subcommand="ci",
+        options=["--no-suppress-errors", "--oss-only"],
+        target_name=None,
+        strict=False,
+        assert_exit_code=None,
+        env={
+            "SEMGREP_APP_TOKEN": "fake-key-from-tests",
+            "SEMGREP_MANAGED_SCAN_ID": MANAGED_SCAN_ID,
+        },
+        use_click_runner=True,  # TODO: probably because rely on some mocking
+    )
+    assert scan_create.call_count == 1
+    request_body = scan_create.request_history[-1].json()
+    assert request_body["scan_metadata"]["sms_scan_id"] == MANAGED_SCAN_ID
+
+
 @pytest.mark.parametrize("mocked_scan_id", [None])
 @pytest.mark.osemfail
 def test_dryrun(
diff --git a/cli/tests/default/e2e-other/test_match_based_id.py b/cli/tests/default/e2e-other/test_match_based_id.py
index 908db875e869..f927078eeed3 100644
--- a/cli/tests/default/e2e-other/test_match_based_id.py
+++ b/cli/tests/default/e2e-other/test_match_based_id.py
@@ -8,6 +8,50 @@
 from semgrep.constants import OutputFormat
 
 
+# It should generate the right *fixed* fingerprint based on the rule and match.
+# The fingerprint should not change across semgrep versions; it must remain
+# stable.
+@pytest.mark.kinda_slow
+@pytest.mark.parametrize(
+    "rule_and_target",
+    [
+        # similar to test_output_sarif.py
+        ("rules/eqeq.yaml", "basic/stupid.py"),
+        ("rules/cwe_tag.yaml", "basic/stupid.py"),
+    ],
+)
+@pytest.mark.kinda_slow
+def test_value(run_semgrep_in_tmp: RunSemgrep, snapshot, rule_and_target):
+    (rule, target) = rule_and_target
+    results, _errors = run_semgrep_in_tmp(
+        rule,
+        target_name=target,
+        output_format=OutputFormat.JSON,
+        clean_fingerprint=False,
+    )
+    snapshot.assert_match(results, "results.json")
+
+
+# TODO: this currently don't pass with osemgrep, mostly because
+# of rule.py formula_string() not being fully ported to osemgrep
+@pytest.mark.kinda_slow
+@pytest.mark.parametrize(
+    "rule_and_target",
+    [("rules/taint_trace.yaml", "taint/taint_trace.cpp")],
+)
+@pytest.mark.kinda_slow
+@pytest.mark.osemfail
+def test_value_osemfail(run_semgrep_in_tmp: RunSemgrep, snapshot, rule_and_target):
+    (rule, target) = rule_and_target
+    results, _errors = run_semgrep_in_tmp(
+        rule,
+        target_name=target,
+        output_format=OutputFormat.JSON,
+        clean_fingerprint=False,
+    )
+    snapshot.assert_match(results, "results.json")
+
+
 @pytest.mark.kinda_slow
 def test_duplicate_matches_indexing(run_semgrep_in_tmp: RunSemgrep, snapshot):
     results, _errors = run_semgrep_in_tmp(
diff --git a/cli/tests/default/e2e/snapshots/test_fixtest/test_fixtest_test4_json/results.txt b/cli/tests/default/e2e/snapshots/test_fixtest/test_fixtest_test4_json/results.txt
index 99ab6b99b40d..c820b659baf5 100644
--- a/cli/tests/default/e2e/snapshots/test_fixtest/test_fixtest_test4_json/results.txt
+++ b/cli/tests/default/e2e/snapshots/test_fixtest/test_fixtest_test4_json/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --test --strict --config rules/fixtest/other_pattern.yaml --json targets/fixtest/test4.py
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e/snapshots/test_fixtest/test_fixtest_test4_no_json/results.txt b/cli/tests/default/e2e/snapshots/test_fixtest/test_fixtest_test4_no_json/results.txt
index 978ba7e14095..5021d112ae80 100644
--- a/cli/tests/default/e2e/snapshots/test_fixtest/test_fixtest_test4_no_json/results.txt
+++ b/cli/tests/default/e2e/snapshots/test_fixtest/test_fixtest_test4_no_json/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --test --strict --config rules/fixtest/other_pattern.yaml targets/fixtest/test4.py
-=== end of command
-
 === exit code
 1
 === end of exit code
diff --git a/cli/tests/default/e2e/snapshots/test_help/test_help_text/--help/help.txt b/cli/tests/default/e2e/snapshots/test_help/test_help_text/--help/help.txt
index 0f31d08a1b6c..4b9c86900133 100644
--- a/cli/tests/default/e2e/snapshots/test_help/test_help_text/--help/help.txt
+++ b/cli/tests/default/e2e/snapshots/test_help/test_help_text/--help/help.txt
@@ -10,13 +10,13 @@
   -h, --help  Show this message and exit.
 
 [4mCommands[24m:
-  [36mci[39m                   The recommended way to run semgrep in CI
+  [36mci[39m                   Run Semgrep on a git diff (for use in CI)
   [36minstall-semgrep-pro[39m  Install the Semgrep Pro Engine
   [36mlogin[39m                Obtain and save credentials for [36m[4msemgrep.dev[39m[24m
   [36mlogout[39m               Remove locally stored credentials to [36m[4msemgrep.dev[39m[24m
   [36mlsp[39m                  Start the Semgrep LSP server (useful for IDEs)
   [36mpublish[39m              Upload rule to [36m[4msemgrep.dev[39m[24m
-  [36mscan[39m                 Run semgrep rules on files
-  [36mshow[39m                 Show various information about Semgrep
+  [36mscan[39m                 Run Semgrep rules on local folders or files
+  [36mshow[39m                 Show various types of information
   [36mtest[39m                 Test the rules
   [36mvalidate[39m             Validate the rules
diff --git a/cli/tests/default/e2e/snapshots/test_help/test_help_text/-h/help.txt b/cli/tests/default/e2e/snapshots/test_help/test_help_text/-h/help.txt
index 0f31d08a1b6c..4b9c86900133 100644
--- a/cli/tests/default/e2e/snapshots/test_help/test_help_text/-h/help.txt
+++ b/cli/tests/default/e2e/snapshots/test_help/test_help_text/-h/help.txt
@@ -10,13 +10,13 @@
   -h, --help  Show this message and exit.
 
 [4mCommands[24m:
-  [36mci[39m                   The recommended way to run semgrep in CI
+  [36mci[39m                   Run Semgrep on a git diff (for use in CI)
   [36minstall-semgrep-pro[39m  Install the Semgrep Pro Engine
   [36mlogin[39m                Obtain and save credentials for [36m[4msemgrep.dev[39m[24m
   [36mlogout[39m               Remove locally stored credentials to [36m[4msemgrep.dev[39m[24m
   [36mlsp[39m                  Start the Semgrep LSP server (useful for IDEs)
   [36mpublish[39m              Upload rule to [36m[4msemgrep.dev[39m[24m
-  [36mscan[39m                 Run semgrep rules on files
-  [36mshow[39m                 Show various information about Semgrep
+  [36mscan[39m                 Run Semgrep rules on local folders or files
+  [36mshow[39m                 Show various types of information
   [36mtest[39m                 Test the rules
   [36mvalidate[39m             Validate the rules
diff --git a/cli/tests/default/e2e/snapshots/test_ignores/test_file_not_relative_to_base_path/results.txt b/cli/tests/default/e2e/snapshots/test_ignores/test_file_not_relative_to_base_path/results.txt
index 048e6c498d19..207339206355 100644
--- a/cli/tests/default/e2e/snapshots/test_ignores/test_file_not_relative_to_base_path/results.txt
+++ b/cli/tests/default/e2e/snapshots/test_ignores/test_file_not_relative_to_base_path/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --json -e a --lang js -
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e/snapshots/test_misc/test_terminal_output/results.txt b/cli/tests/default/e2e/snapshots/test_misc/test_terminal_output/results.txt
index 81ee01bdfcf6..8d0ca457eb20 100644
--- a/cli/tests/default/e2e/snapshots/test_misc/test_terminal_output/results.txt
+++ b/cli/tests/default/e2e/snapshots/test_misc/test_terminal_output/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_FORCE_COLOR="true" FORCE_COLOR="1" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/eqeq.yaml targets/basic
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e/snapshots/test_misc/test_terminal_output/results_second.txt b/cli/tests/default/e2e/snapshots/test_misc/test_terminal_output/results_second.txt
index e6220ff81824..924a8b54a124 100644
--- a/cli/tests/default/e2e/snapshots/test_misc/test_terminal_output/results_second.txt
+++ b/cli/tests/default/e2e/snapshots/test_misc/test_terminal_output/results_second.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_FORCE_COLOR="true" FORCE_COLOR="1" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --config rules/eqeq.yaml targets/basic
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e/snapshots/test_misc/test_terminal_output_quiet/results.txt b/cli/tests/default/e2e/snapshots/test_misc/test_terminal_output_quiet/results.txt
index d59ab1df6f91..a7506d5dcbe1 100644
--- a/cli/tests/default/e2e/snapshots/test_misc/test_terminal_output_quiet/results.txt
+++ b/cli/tests/default/e2e/snapshots/test_misc/test_terminal_output_quiet/results.txt
@@ -1,7 +1,3 @@
-=== command
-SEMGREP_SETTINGS_FILE="<MASKED>" SEMGREP_FORCE_COLOR="true" FORCE_COLOR="1" SEMGREP_USER_AGENT_APPEND="pytest" SEMGREP_VERSION_CACHE_PATH="<MASKED>" SEMGREP_ENABLE_VERSION_CHECK="0" SEMGREP_SEND_METRICS="off" semgrep --strict --quiet --config rules/eqeq.yaml targets/basic
-=== end of command
-
 === exit code
 0
 === end of exit code
diff --git a/cli/tests/default/e2e/snapshots/test_target_selection/test_target_selection/git/files.list b/cli/tests/default/e2e/snapshots/test_target_selection/test_target_selection/git/files.list
deleted file mode 100644
index 6e8ce078a2c7..000000000000
--- a/cli/tests/default/e2e/snapshots/test_target_selection/test_target_selection/git/files.list
+++ /dev/null
@@ -1,5 +0,0 @@
-targets/target_selection/hello
-targets/target_selection/quote'/hello
-targets/target_selection/spaces !/hello
-targets/target_selection/~
-targets/target_selection/😊
diff --git a/cli/tests/default/e2e/snapshots/test_target_selection/test_target_selection/nongit/files.list b/cli/tests/default/e2e/snapshots/test_target_selection/test_target_selection/nongit/files.list
deleted file mode 100644
index 6e8ce078a2c7..000000000000
--- a/cli/tests/default/e2e/snapshots/test_target_selection/test_target_selection/nongit/files.list
+++ /dev/null
@@ -1,5 +0,0 @@
-targets/target_selection/hello
-targets/target_selection/quote'/hello
-targets/target_selection/spaces !/hello
-targets/target_selection/~
-targets/target_selection/😊
diff --git a/cli/tests/default/e2e/targets/target_selection/hello b/cli/tests/default/e2e/targets/target_selection/hello
deleted file mode 100644
index ce013625030b..000000000000
--- a/cli/tests/default/e2e/targets/target_selection/hello
+++ /dev/null
@@ -1,1 +0,0 @@
-hello
diff --git a/cli/tests/default/e2e/targets/target_selection/quote'/hello b/cli/tests/default/e2e/targets/target_selection/quote'/hello
deleted file mode 100644
index ce013625030b..000000000000
--- a/cli/tests/default/e2e/targets/target_selection/quote'/hello
+++ /dev/null
@@ -1,1 +0,0 @@
-hello
diff --git a/cli/tests/default/e2e/targets/target_selection/spaces !/hello b/cli/tests/default/e2e/targets/target_selection/spaces !/hello
deleted file mode 100644
index ce013625030b..000000000000
--- a/cli/tests/default/e2e/targets/target_selection/spaces !/hello
+++ /dev/null
@@ -1,1 +0,0 @@
-hello
diff --git a/cli/tests/default/e2e/targets/target_selection/~ b/cli/tests/default/e2e/targets/target_selection/~
deleted file mode 100644
index ce013625030b..000000000000
--- a/cli/tests/default/e2e/targets/target_selection/~
+++ /dev/null
@@ -1,1 +0,0 @@
-hello
diff --git "a/cli/tests/default/e2e/targets/target_selection/\360\237\230\212" "b/cli/tests/default/e2e/targets/target_selection/\360\237\230\212"
deleted file mode 100644
index ce013625030b..000000000000
--- "a/cli/tests/default/e2e/targets/target_selection/\360\237\230\212"
+++ /dev/null
@@ -1,1 +0,0 @@
-hello
diff --git a/cli/tests/default/e2e/test_target_selection.py b/cli/tests/default/e2e/test_target_selection.py
index e0a6ee00c160..9ff12301d506 100644
--- a/cli/tests/default/e2e/test_target_selection.py
+++ b/cli/tests/default/e2e/test_target_selection.py
@@ -1,32 +1,262 @@
 #
 # Test target selection before any rule or language-specific filtering
 #
+import os
+import shutil
+import sys
+from dataclasses import dataclass
+from dataclasses import field
+from pathlib import Path
+from typing import List
+from typing import Optional
+from typing import Set
+from typing import Tuple
+
 import pytest
-from tests.conftest import create_git_repo
 from tests.fixtures import RunSemgrep
 
 
-# Check that a variety of file and folder names are tolerated and selected
-# normally by semgrep scan.
+# The expectations regarding a particular target file path
+@dataclass
+class Expect:
+    selected: bool
+    selected_by_pysemgrep: Optional[bool] = None
+    selected_by_osemgrep: Optional[bool] = None
+    ignore_pysemgrep_result: bool = False
+    ignore_osemgrep_result: bool = False
+    paths: List[str] = field(default_factory=lambda: [])
+
+
+# Check whether a target path was selected or ignored by semgrep, depending
+# the expectation we have.
+def check_expectation(
+    expect: Expect,
+    is_running_osemgrep: bool,
+    is_git_project: bool,
+    selected_targets: Set[str],
+):
+    paths = expect.paths
+
+    if is_running_osemgrep and expect.ignore_osemgrep_result:
+        return
+    if not is_running_osemgrep and expect.ignore_pysemgrep_result:
+        return
+
+    expect_selected = expect.selected
+    if is_running_osemgrep and expect.selected_by_osemgrep is not None:
+        expect_selected = expect.selected_by_osemgrep
+    if not is_running_osemgrep and expect.selected_by_pysemgrep is not None:
+        expect_selected = expect.selected_by_pysemgrep
+
+    label = "[osemgrep]" if is_running_osemgrep else "[pysemgrep]"
+    label = label + (" [git project]" if is_git_project else " [nongit project]")
+    for path in paths:
+        if expect_selected:
+            print(
+                f"{label} check that target path was selected: {path}", file=sys.stderr
+            )
+            assert path in selected_targets
+        else:
+            print(
+                f"{label} check that target path was ignored: {path}", file=sys.stderr
+            )
+            assert path not in selected_targets
+
+
+# What we expect from semgrep when running with the most common invocation i.e.
 #
-# Compatibility note: Unix, Windows, and MacOS have different rules for
-# what's allowed as a file name. Here, we only test filenames that work
-# on all platforms.
+# - run semgrep from the project root
+# - run semgrep on the project root
+# - no optional command-line flags
+# - shared expectations in git and nongit projects
 #
-# At least one test file name should be quoted
-# by 'git ls-files' e.g. '😊' will be shown as '"\360\237\230\212"'
+# To cover a new test case, add a file to the test repo and specify
+# the expectations for the new path below.
 #
+COMMON_EXPECTATIONS = [
+    Expect(
+        selected=True,
+        paths=[
+            # Paths that are correctly selected by both pysemgrep and osemgrep
+            ".gitignore",
+            ".gitmodules",
+            ".semgrepignore",
+            "README.md",
+            "gitignored-only-in-src-and-below.py",
+            "gitignored-only-in-src.py",
+            "hello.py",
+            "img/hello.svg",
+            "semgrepignored/hello.py",
+            "semgrepignored-folder",
+            "semgrepignored-only-in-src-and-below.py",
+            "semgrepignored-only-in-src.py",
+            "sempignored-py-contents/hello.rb",
+            "src/.gitignore",
+            "src/.hidden.py",
+            "src/.semgrepignore",
+            "src/10KiB.py",
+            "src/hello.py",
+            "src/semgrepignored-root",
+            "src/semgrepignored-root-folder/hello.py",
+            "src/sempignored-py-contents/hello.rb",
+            "src/src/semgrepignored-anchored/hello.py",
+            "src/subdir/gitignored-only-in-src.py",
+            "src/subdir/semgrepignored-only-in-src.py",
+            "tests/hello.py",
+            # special characters in file names
+            "src/~",
+            "src/quote'/hello.py",
+            "src/space !/hello.py",
+            "src/🚀.py",
+        ],
+    ),
+    Expect(
+        selected=False,
+        paths=[
+            # Paths that are correctly ignored by both pysemgrep and osemgrep
+            "img/red.png",
+            "semgrepignored-everywhere/hello.py",
+            "semgrepignored-root",
+            "semgrepignored-root-folder/hello.py",
+            "src/broken-symlink.py",
+            "src/semgrepignored-everywhere/hello.py",
+            "src/semgrepignored-folder/hello.py",
+            "src/sempignored-py-contents/hello.py",
+            "src/symlink.py",
+        ],
+    ),
+    # accepted differences between pysemgrep and osemgrep
+    Expect(
+        # excluded by osemgrep, selected by pysemgrep
+        selected=False,
+        selected_by_pysemgrep=True,
+        paths=[
+            # pysemgrep doesn't consult .semgrepignore files in subfolders:
+            "src/semgrepignored-only-in-src-and-below.py",
+            "src/semgrepignored-only-in-src.py",
+        ],
+    ),
+    # pysemgrep bugs
+    Expect(
+        selected=False,
+        selected_by_pysemgrep=True,
+        paths=["sempignored-py-contents/hello.py"],
+    ),
+]
+
+GIT_PROJECT_EXPECTATIONS = [
+    # common expectations for a git project (but not for a nongit project)
+    Expect(
+        selected=False,
+        paths=[
+            # git submodule object (folder) listed by 'git ls-files'
+            "submodules/semgrep-test-project2",
+            # git submodule contents
+            "submodules/semgrep-test-project2/hello.py",
+        ],
+    ),
+    # accepted differences between pysemgrep and osemgrep
+    Expect(
+        selected=False,
+        selected_by_pysemgrep=True,
+        paths=[
+            # pysemgrep doesn't consult .gitignore files
+            "src/gitignored.py",
+            "src/gitignored-only-in-src-and-below.py",
+            "src/gitignored-only-in-src.py",
+        ],
+    ),
+]
+
+NONGIT_PROJECT_EXPECTATIONS = [
+    # common expectations for a nongit project (but not for a git project)
+    Expect(
+        selected=True,
+        paths=[
+            # regular file in what was a git submodule
+            "submodules/semgrep-test-project2/hello.py",
+            # we don't consult .gitignore files in nongit projects
+            "src/gitignored.py",
+            "src/gitignored-only-in-src-and-below.py",
+            "src/gitignored-only-in-src.py",
+        ],
+    ),
+    Expect(
+        selected=False,
+        paths=[
+            # folder, not a regular file
+            "submodules/semgrep-test-project2",
+        ],
+    ),
+]
+
+# This is an artificial git project that offers all the difficulties we could
+# think of for file targeting.
+#
+# Do we need to run on several projects?
+PROJECT: Tuple[str, str] = (
+    "semgrep-test-project1",
+    "https://github.com/semgrep/semgrep-test-project1.git",
+)
+
+
 @pytest.mark.kinda_slow
 @pytest.mark.parametrize(
-    "prepare_workspace", [lambda: None, create_git_repo], ids=["nongit", "git"]
+    # a list of extra semgrep CLI options and osemgrep-specific options
+    "is_git_project,options,osemgrep_options,expectations",
+    [
+        (True, [], [], COMMON_EXPECTATIONS + GIT_PROJECT_EXPECTATIONS),
+        (False, [], [], COMMON_EXPECTATIONS + NONGIT_PROJECT_EXPECTATIONS),
+    ],
+    ids=["git", "nongit"],
 )
-def test_target_selection(
-    run_semgrep_on_copied_files: RunSemgrep, prepare_workspace, snapshot
-):
-    stdout, stderr = run_semgrep_on_copied_files(
-        "rules/eqeq.yaml",  # ignored by --x-ls
-        options=["--x-ls"],
-        prepare_workspace=prepare_workspace,
-        target_name="target_selection",
+def test_project_target_selection(
+    monkeypatch: pytest.MonkeyPatch,
+    tmp_path: Path,
+    run_semgrep: RunSemgrep,
+    is_git_project: bool,
+    options: List[str],
+    osemgrep_options: List[str],
+    expectations: List[Expect],
+) -> None:
+    # Instead of copying or symlinking the git submodule that sits nicely
+    # in our semgrep repo, we clone it as standalone repo to avoid problems
+    # due to having the structure of a submodule but no parent git project.
+    project_name, project_url = PROJECT
+    print(f"cd into {tmp_path}", file=sys.stderr)
+    monkeypatch.chdir(tmp_path)
+    print(f"clone {project_url}", file=sys.stderr)
+    os.system(f"git clone {project_url} {project_name}")
+    print(f"cd into {project_name}", file=sys.stderr)
+    monkeypatch.chdir(Path(project_name))
+    print(f"check out submodules", file=sys.stderr)
+    os.system(f"git submodule update --init --recursive")
+
+    if not is_git_project:
+        print(f"remove .git to make this a non-git project", file=sys.stderr)
+        shutil.rmtree(".git")
+
+    is_running_osemgrep = True if os.environ.get("PYTEST_USE_OSEMGREP") else False
+
+    extra_options = options
+    if is_running_osemgrep:
+        extra_options += osemgrep_options
+
+    # Call semgrep to list the target files and print them on stdout,
+    # one per line.
+    stdout, stderr = run_semgrep(
+        # the '-e' and '--lang' options are to keep pysemgrep happy because
+        # it wants to load rules
+        options=["--x-ls", "-e", "hello", "--lang", "python"] + extra_options,
+        assume_targets_dir=False,
+        target_name=".",
     )
-    snapshot.assert_match(stdout, "files.list")
+    selected_targets: Set[str] = set(filter(lambda x: x, stdout.split("\n")))
+
+    print(f"selected target paths:", file=sys.stderr)
+    for path in sorted(list(selected_targets)):
+        print(f"  {path}", file=sys.stderr)
+
+    # Check the status of each file path we want to check.
+    for expect in expectations:
+        check_expectation(expect, is_running_osemgrep, is_git_project, selected_targets)
diff --git a/cli/tests/default/unit/test_filter_dependency_aware_rules.py b/cli/tests/default/unit/test_filter_dependency_aware_rules.py
index 9a9efe1de05c..2687320221aa 100644
--- a/cli/tests/default/unit/test_filter_dependency_aware_rules.py
+++ b/cli/tests/default/unit/test_filter_dependency_aware_rules.py
@@ -12,8 +12,7 @@
 from semgrep.semgrep_interfaces.semgrep_output_v1 import Pipfile_
 from semgrep.semgrep_interfaces.semgrep_output_v1 import Pypi
 from semgrep.semgrep_interfaces.semgrep_output_v1 import Transitivity
-from semgrep.subproject import LockfileDependencySource
-from semgrep.subproject import PackageManagerType
+from semgrep.subproject import ManifestLockfileDependencySource
 from semgrep.subproject import ResolutionMethod
 from semgrep.subproject import ResolvedSubproject
 from semgrep.subproject import Subproject
@@ -107,11 +106,12 @@ def sample_resolved_deps():
         ),
     ]
 
-    # Create LockfileDependencySource
-    dependency_source = LockfileDependencySource(
+    # Create dependency source
+    dependency_source = ManifestLockfileDependencySource(
         manifest=out.Manifest(ManifestKind(value=Pipfile_()), out.Fpath("Pipfile")),
-        package_manager_type=PackageManagerType.PIP,
-        lockfile_path=Path("Pipfile.lock"),
+        lockfile=out.Lockfile(
+            out.LockfileKind(value=out.PipfileLock()), out.Fpath("Pipfile.lock")
+        ),
     )
 
     resolution_method = ResolutionMethod.LOCKFILE_PARSING
diff --git a/cli/tests/default/unit/test_resolve_subprojects.py b/cli/tests/default/unit/test_resolve_subprojects.py
index 53190105f7b9..c8dd248ba730 100644
--- a/cli/tests/default/unit/test_resolve_subprojects.py
+++ b/cli/tests/default/unit/test_resolve_subprojects.py
@@ -10,9 +10,9 @@
 from semdep.subproject_matchers import SubprojectMatcher
 from semgrep.resolve_subprojects import _resolve_dependency_source
 from semgrep.resolve_subprojects import find_subprojects
-from semgrep.subproject import LockfileDependencySource
+from semgrep.subproject import LockfileOnlyDependencySource
+from semgrep.subproject import ManifestLockfileDependencySource
 from semgrep.subproject import ManifestOnlyDependencySource
-from semgrep.subproject import PackageManagerType
 from semgrep.subproject import Subproject
 
 
@@ -31,34 +31,37 @@
                 ExactLockfileManifestMatcher(
                     lockfile_name="requirements.txt",
                     manifest_name="requirements.in",
-                    package_manager_type=PackageManagerType.PIP,
+                    lockfile_kind=out.LockfileKind(value=out.PipRequirementsTxt()),
                     manifest_kind=out.ManifestKind(value=out.RequirementsIn()),
                 ),
                 ExactLockfileManifestMatcher(
                     lockfile_name="requirements3.txt",
                     manifest_name="requirements.in",
-                    package_manager_type=PackageManagerType.PIP,
+                    lockfile_kind=out.LockfileKind(value=out.PipRequirementsTxt()),
                     manifest_kind=out.ManifestKind(value=out.RequirementsIn()),
                 ),
             ],
             [
                 Subproject(
                     root_dir=Path(),
-                    dependency_source=LockfileDependencySource(
-                        package_manager_type=PackageManagerType.PIP,
+                    dependency_source=ManifestLockfileDependencySource(
                         manifest=out.Manifest(
                             out.ManifestKind(value=out.RequirementsIn()),
                             out.Fpath("requirements.in"),
                         ),
-                        lockfile_path=Path("requirements.txt"),
+                        lockfile=out.Lockfile(
+                            out.LockfileKind(out.PipRequirementsTxt()),
+                            out.Fpath("requirements.txt"),
+                        ),
                     ),
                 ),
                 Subproject(
                     root_dir=Path(),
-                    dependency_source=LockfileDependencySource(
-                        package_manager_type=PackageManagerType.PIP,
-                        manifest=None,
-                        lockfile_path=Path("requirements3.txt"),
+                    dependency_source=LockfileOnlyDependencySource(
+                        lockfile=out.Lockfile(
+                            out.LockfileKind(out.PipRequirementsTxt()),
+                            out.Fpath("requirements3.txt"),
+                        )
                     ),
                 ),
             ],
@@ -74,26 +77,28 @@
                 ExactLockfileManifestMatcher(
                     lockfile_name="requirements.txt",
                     manifest_name="requirements.in",
-                    package_manager_type=PackageManagerType.PIP,
+                    lockfile_kind=out.LockfileKind(value=out.PipRequirementsTxt()),
                     manifest_kind=out.ManifestKind(value=out.RequirementsIn()),
                 ),
                 ExactLockfileManifestMatcher(
                     lockfile_name="requirements3.txt",
                     manifest_name="requirements.in",
-                    package_manager_type=PackageManagerType.PIP,
+                    lockfile_kind=out.LockfileKind(value=out.PipRequirementsTxt()),
                     manifest_kind=out.ManifestKind(value=out.RequirementsIn()),
                 ),
             ],
             [
                 Subproject(
                     root_dir=Path(),
-                    dependency_source=LockfileDependencySource(
-                        package_manager_type=PackageManagerType.PIP,
+                    dependency_source=ManifestLockfileDependencySource(
                         manifest=out.Manifest(
                             out.ManifestKind(value=out.RequirementsIn()),
                             out.Fpath("requirements.in"),
                         ),
-                        lockfile_path=Path("requirements3.txt"),
+                        lockfile=out.Lockfile(
+                            out.LockfileKind(out.PipRequirementsTxt()),
+                            out.Fpath("requirements3.txt"),
+                        ),
                     ),
                 ),
             ],
@@ -115,22 +120,28 @@
                 Subproject(
                     root_dir=Path(),
                     dependency_source=ManifestOnlyDependencySource(
-                        manifest_path=Path("pom.xml"),
-                        manifest_kind=out.ManifestKind(out.PomXml()),
+                        manifest=out.Manifest(
+                            out.ManifestKind(out.PomXml()),
+                            out.Fpath("pom.xml"),
+                        )
                     ),
                 ),
                 Subproject(
                     root_dir=Path("child-a"),
                     dependency_source=ManifestOnlyDependencySource(
-                        manifest_path=Path("child-a/pom.xml"),
-                        manifest_kind=out.ManifestKind(out.PomXml()),
+                        manifest=out.Manifest(
+                            out.ManifestKind(out.PomXml()),
+                            out.Fpath("child-a/pom.xml"),
+                        )
                     ),
                 ),
                 Subproject(
                     root_dir=Path("child-b"),
                     dependency_source=ManifestOnlyDependencySource(
-                        manifest_path=Path("child-b/pom.xml"),
-                        manifest_kind=out.ManifestKind(out.PomXml()),
+                        manifest=out.Manifest(
+                            out.ManifestKind(out.PomXml()),
+                            out.Fpath("child-b/pom.xml"),
+                        )
                     ),
                 ),
             ],
@@ -161,13 +172,15 @@ def test_ptt_unconditionally_generates_dependency_graphs(
     lockfile_file.close()
 
     mock_dynamic_resolve.return_value = ["mock_ecosystem", [], [], []]
-    dep_source = LockfileDependencySource(
-        package_manager_type=PackageManagerType.PIP,
+    dep_source = ManifestLockfileDependencySource(
         manifest=out.Manifest(
             out.ManifestKind(value=out.RequirementsIn()),
             out.Fpath(str((tmp_path / "requirements.in"))),
         ),
-        lockfile_path=Path(tmp_path / "requirements.txt"),
+        lockfile=out.Lockfile(
+            out.LockfileKind(value=out.PipRequirementsTxt()),
+            out.Fpath(str(tmp_path / "requirements.txt")),
+        ),
     )
     _resolve_dependency_source(dep_source, True, True)
 
@@ -202,13 +215,15 @@ def test_ptt_unconditional_graph_generation_falls_back_on_lockfile_parsing(
         ],
         [],
     )
-    dep_source = LockfileDependencySource(
-        package_manager_type=PackageManagerType.PIP,
+    dep_source = ManifestLockfileDependencySource(
         manifest=out.Manifest(
             out.ManifestKind(value=out.RequirementsIn()),
             out.Fpath(str((tmp_path / "requirements.in"))),
         ),
-        lockfile_path=Path(tmp_path / "requirements.txt"),
+        lockfile=out.Lockfile(
+            out.LockfileKind(value=out.PipRequirementsTxt()),
+            out.Fpath(str(tmp_path / "requirements.txt")),
+        ),
     )
     _resolve_dependency_source(dep_source, True, True)
 
diff --git a/cli/tests/default/unit/test_subproject.py b/cli/tests/default/unit/test_subproject.py
index 3b2bed2db715..82697646a607 100644
--- a/cli/tests/default/unit/test_subproject.py
+++ b/cli/tests/default/unit/test_subproject.py
@@ -11,9 +11,9 @@
 from semgrep.semgrep_interfaces.semgrep_output_v1 import Transitivity
 from semgrep.semgrep_interfaces.semgrep_output_v1 import Unknown
 from semgrep.subproject import find_closest_subproject
-from semgrep.subproject import LockfileDependencySource
+from semgrep.subproject import LockfileOnlyDependencySource
+from semgrep.subproject import ManifestLockfileDependencySource
 from semgrep.subproject import MultiLockfileDependencySource
-from semgrep.subproject import PackageManagerType
 from semgrep.subproject import ResolutionMethod
 from semgrep.subproject import ResolvedDependencies
 from semgrep.subproject import ResolvedSubproject
@@ -39,13 +39,15 @@ def test_finds_subproject_in_same_directory(
         expected = ResolvedSubproject(
             root_dir=Path("a/b/c"),
             resolution_errors=[],
-            dependency_source=LockfileDependencySource(
-                lockfile_path=lockfile_path,
+            dependency_source=ManifestLockfileDependencySource(
                 manifest=out.Manifest(
                     out.ManifestKind(out.RequirementsIn()),
                     out.Fpath("a/b/c/requirements.in"),
                 ),
-                package_manager_type=PackageManagerType.PIP,
+                lockfile=out.Lockfile(
+                    out.LockfileKind(out.PipRequirementsTxt()),
+                    out.Fpath(str(lockfile_path)),
+                ),
             ),
             found_dependencies=ResolvedDependencies.from_found_dependencies([]),
             ecosystem=Ecosystem(Pypi()),
@@ -55,13 +57,15 @@ def test_finds_subproject_in_same_directory(
             ResolvedSubproject(
                 root_dir=Path("a/b"),
                 resolution_errors=[],
-                dependency_source=LockfileDependencySource(
-                    lockfile_path=extra_lockfile_path,
+                dependency_source=ManifestLockfileDependencySource(
                     manifest=out.Manifest(
                         out.ManifestKind(out.RequirementsIn()),
                         out.Fpath("a/b/requirements.in"),
                     ),
-                    package_manager_type=PackageManagerType.PIP,
+                    lockfile=out.Lockfile(
+                        out.LockfileKind(out.PipRequirementsTxt()),
+                        out.Fpath(str(extra_lockfile_path)),
+                    ),
                 ),
                 found_dependencies=ResolvedDependencies.from_found_dependencies([]),
                 ecosystem=Ecosystem(Pypi()),
@@ -88,13 +92,15 @@ def test_finds_subproject_for_requested_ecosystem(self, tmp_path, monkeypatch):
         expected = ResolvedSubproject(
             root_dir=Path("a/b"),
             resolution_errors=[],
-            dependency_source=LockfileDependencySource(
-                lockfile_path=lockfile_path,
+            dependency_source=ManifestLockfileDependencySource(
                 manifest=out.Manifest(
                     out.ManifestKind(out.RequirementsIn()),
                     out.Fpath("a/b/build.gradle"),
                 ),
-                package_manager_type=PackageManagerType.GRADLE,
+                lockfile=out.Lockfile(
+                    out.LockfileKind(out.GradleLockfile_()),
+                    out.Fpath(str(lockfile_path)),
+                ),
             ),
             found_dependencies=ResolvedDependencies.from_found_dependencies([]),
             ecosystem=Ecosystem(Maven()),
@@ -104,13 +110,15 @@ def test_finds_subproject_for_requested_ecosystem(self, tmp_path, monkeypatch):
             ResolvedSubproject(
                 root_dir=Path("a/b/c"),
                 resolution_errors=[],
-                dependency_source=LockfileDependencySource(
-                    lockfile_path=extra_lockfile_path,
+                dependency_source=ManifestLockfileDependencySource(
                     manifest=out.Manifest(
                         out.ManifestKind(out.RequirementsIn()),
                         out.Fpath("a/b/c/requirements.in"),
                     ),
-                    package_manager_type=PackageManagerType.PIP,
+                    lockfile=out.Lockfile(
+                        out.LockfileKind(out.PipRequirementsTxt()),
+                        out.Fpath(str(extra_lockfile_path)),
+                    ),
                 ),
                 found_dependencies=ResolvedDependencies.from_found_dependencies([]),
                 ecosystem=Ecosystem(Pypi()),
@@ -144,13 +152,15 @@ def test_base_case(self, lockfile_path: Path):
         subproject = ResolvedSubproject(
             root_dir=Path("a/b/c"),
             resolution_errors=[],
-            dependency_source=LockfileDependencySource(
-                lockfile_path=lockfile_path,
+            dependency_source=ManifestLockfileDependencySource(
                 manifest=out.Manifest(
                     out.ManifestKind(out.RequirementsIn()),
                     out.Fpath("a/b/c/requirements.in"),
                 ),
-                package_manager_type=PackageManagerType.PIP,
+                lockfile=out.Lockfile(
+                    out.LockfileKind(out.PipRequirementsTxt()),
+                    out.Fpath(str(lockfile_path)),
+                ),
             ),
             resolution_method=ResolutionMethod.LOCKFILE_PARSING,
             ecosystem=Ecosystem(Pypi()),
@@ -196,15 +206,17 @@ def test_multiple_lockfiles(self):
 
         multi_lockfile_source = MultiLockfileDependencySource(
             sources=(
-                LockfileDependencySource(
-                    lockfile_path=lockfile_path,
-                    manifest=None,
-                    package_manager_type=PackageManagerType.PIP,
+                LockfileOnlyDependencySource(
+                    lockfile=out.Lockfile(
+                        out.LockfileKind(out.PipRequirementsTxt()),
+                        out.Fpath(str(lockfile_path)),
+                    )
                 ),
-                LockfileDependencySource(
-                    lockfile_path=extra_lockfile_path,
-                    manifest=None,
-                    package_manager_type=PackageManagerType.PIP,
+                LockfileOnlyDependencySource(
+                    lockfile=out.Lockfile(
+                        out.LockfileKind(out.PipRequirementsTxt()),
+                        out.Fpath(str(extra_lockfile_path)),
+                    )
                 ),
             )
         )
@@ -249,13 +261,15 @@ def test_dep_missing_lockfile_path(self):
         subproject = ResolvedSubproject(
             root_dir=Path("a/b/c"),
             resolution_errors=[],
-            dependency_source=LockfileDependencySource(
-                lockfile_path=lockfile_path,
+            dependency_source=ManifestLockfileDependencySource(
                 manifest=out.Manifest(
                     out.ManifestKind(value=out.RequirementsIn()),
                     out.Fpath("a/b/c/requirements.in"),
                 ),
-                package_manager_type=PackageManagerType.PIP,
+                lockfile=out.Lockfile(
+                    out.LockfileKind(out.PipRequirementsTxt()),
+                    out.Fpath(str(lockfile_path)),
+                ),
             ),
             resolution_method=ResolutionMethod.LOCKFILE_PARSING,
             ecosystem=Ecosystem(Pypi()),
@@ -281,10 +295,11 @@ class TestLockfileDependencySource:
     def test_base_case(self):
         lockfile_path = Path("a/b/c/requirements.txt")
 
-        source = LockfileDependencySource(
-            lockfile_path=lockfile_path,
-            manifest=None,
-            package_manager_type=PackageManagerType.PIP,
+        source = LockfileOnlyDependencySource(
+            lockfile=out.Lockfile(
+                out.LockfileKind(out.PipRequirementsTxt()),
+                out.Fpath(str(lockfile_path)),
+            )
         )
 
         assert source.get_display_paths() == [
@@ -300,15 +315,17 @@ def test_base_case(self):
 
         source = MultiLockfileDependencySource(
             sources=(
-                LockfileDependencySource(
-                    lockfile_path=lockfile_path,
-                    manifest=None,
-                    package_manager_type=PackageManagerType.PIP,
+                LockfileOnlyDependencySource(
+                    lockfile=out.Lockfile(
+                        out.LockfileKind(out.PipRequirementsTxt()),
+                        out.Fpath(str(lockfile_path)),
+                    )
                 ),
-                LockfileDependencySource(
-                    lockfile_path=extra_lockfile_path,
-                    manifest=None,
-                    package_manager_type=PackageManagerType.PIP,
+                LockfileOnlyDependencySource(
+                    lockfile=out.Lockfile(
+                        out.LockfileKind(out.PipRequirementsTxt()),
+                        out.Fpath(str(extra_lockfile_path)),
+                    )
                 ),
             )
         )
diff --git a/cli/tests/default/unit/test_subproject_matchers.py b/cli/tests/default/unit/test_subproject_matchers.py
index b34dd8de5c2d..d694bcfdc444 100644
--- a/cli/tests/default/unit/test_subproject_matchers.py
+++ b/cli/tests/default/unit/test_subproject_matchers.py
@@ -9,10 +9,10 @@
 from semdep.matchers.gradle import GradleMatcher
 from semdep.matchers.pip_requirements import PipRequirementsMatcher
 from semdep.subproject_matchers import filter_dependency_source_files
-from semgrep.subproject import LockfileDependencySource
+from semgrep.subproject import LockfileOnlyDependencySource
+from semgrep.subproject import ManifestLockfileDependencySource
 from semgrep.subproject import ManifestOnlyDependencySource
 from semgrep.subproject import MultiLockfileDependencySource
-from semgrep.subproject import PackageManagerType
 from semgrep.subproject import Subproject
 
 
@@ -20,10 +20,10 @@ class TestExactLockfileMatcher:
     @pytest.mark.quick
     def test_lockfile_match(self):
         matcher = ExactLockfileManifestMatcher(
-            package_manager_type=PackageManagerType.PIPENV,
             lockfile_name="Pipfile.lock",
             manifest_name="Pipfile",
             manifest_kind=out.ManifestKind(value=out.Pipfile_()),
+            lockfile_kind=out.LockfileKind(value=out.PipfileLock()),
         )
 
         assert matcher.is_match(Path("Pipfile.lock")) is True
@@ -33,10 +33,10 @@ def test_lockfile_match(self):
     @pytest.mark.quick
     def test_manifest_match(self):
         matcher = ExactLockfileManifestMatcher(
-            package_manager_type=PackageManagerType.PIPENV,
             lockfile_name="Pipfile.lock",
             manifest_name="Pipfile",
             manifest_kind=out.ManifestKind(value=out.Pipfile_()),
+            lockfile_kind=out.LockfileKind(value=out.PipfileLock()),
         )
 
         assert matcher.is_match(Path("Pipfile")) is True
@@ -56,8 +56,8 @@ def test_make_subprojects(
         matcher = ExactLockfileManifestMatcher(
             lockfile_name="Pipfile.lock",
             manifest_name="Pipfile",
-            package_manager_type=PackageManagerType.PIPENV,
             manifest_kind=out.ManifestKind(value=out.Pipfile_()),
+            lockfile_kind=out.LockfileKind(value=out.PipfileLock()),
         )
 
         assert matcher.is_match(manifest_path)
@@ -70,21 +70,21 @@ def test_make_subprojects(
         assert len(subprojects) == 1
         subproject = subprojects[0]
         assert subproject.root_dir == tmp_path
-        assert isinstance(subproject.dependency_source, LockfileDependencySource)
-        assert subproject.dependency_source.lockfile_path == lockfile_path
-        subproject_manifest_path = (
-            subproject.dependency_source.manifest.path
-            if subproject.dependency_source.manifest is not None
-            else None
-        )
+        assert subproject.dependency_source.lockfile.path.value == str(lockfile_path)
 
         if create_manifest:
+            assert isinstance(
+                subproject.dependency_source, ManifestLockfileDependencySource
+            )
+            subproject_manifest_path = subproject.dependency_source.manifest.path
             assert (
                 subproject_manifest_path is not None
                 and Path(subproject_manifest_path.value) == manifest_path
             )
         else:
-            assert subproject_manifest_path is None
+            assert isinstance(
+                subproject.dependency_source, LockfileOnlyDependencySource
+            )
 
 
 class TestPatternLockfileMatcher:
@@ -93,7 +93,7 @@ def test_is_match(self):
         matcher = PatternLockfileMatcher(
             lockfile_pattern="*requirements*.txt",
             manifest_name="requirements.in",
-            package_manager_type=PackageManagerType.PIP,
+            lockfile_kind=out.LockfileKind(out.PipRequirementsTxt()),
             manifest_kind=out.ManifestKind(value=out.RequirementsIn()),
         )
 
@@ -148,8 +148,8 @@ def test_make_subprojects(
         matcher = PatternLockfileMatcher(
             lockfile_pattern="*requirements*.txt",
             manifest_name="requirements.in",
-            package_manager_type=PackageManagerType.PIP,
             manifest_kind=out.ManifestKind(value=out.RequirementsIn()),
+            lockfile_kind=out.LockfileKind(value=out.PipRequirementsTxt()),
         )
 
         # expect the matcher to create four subprojects, with or without manifests
@@ -164,23 +164,27 @@ def test_make_subprojects(
         assert used_files == files
         assert len(subprojects) == 4
         for subproject in subprojects:
-            assert isinstance(subproject.dependency_source, LockfileDependencySource)
-            manifest_path = (
-                subproject.dependency_source.manifest.path
-                if subproject.dependency_source.manifest is not None
-                else None
+            assert isinstance(
+                subproject.dependency_source,
+                (ManifestLockfileDependencySource, LockfileOnlyDependencySource),
             )
             expected_root, expected_manifest = test_data[
-                subproject.dependency_source.lockfile_path
+                Path(subproject.dependency_source.lockfile.path.value)
             ]
             assert subproject.root_dir == expected_root
             if with_manifest:
+                assert isinstance(
+                    subproject.dependency_source, ManifestLockfileDependencySource
+                )
+                manifest_path = subproject.dependency_source.manifest.path
                 assert (
                     manifest_path is not None
                     and Path(manifest_path.value) == expected_manifest
                 )
             else:
-                assert manifest_path is None
+                assert isinstance(
+                    subproject.dependency_source, LockfileOnlyDependencySource
+                )
 
 
 class TestRequirementsLockfileMatcher:
@@ -240,24 +244,28 @@ def test_is_match(self) -> None:
                 [
                     Subproject(
                         root_dir=Path(),
-                        dependency_source=LockfileDependencySource(
-                            package_manager_type=PackageManagerType.PIP,
+                        dependency_source=ManifestLockfileDependencySource(
                             manifest=out.Manifest(
                                 out.ManifestKind(out.RequirementsIn()),
                                 out.Fpath("requirements.in"),
                             ),
-                            lockfile_path=Path("requirements.txt"),
+                            lockfile=out.Lockfile(
+                                out.LockfileKind(out.PipRequirementsTxt()),
+                                out.Fpath("requirements.txt"),
+                            ),
                         ),
                     ),
                     Subproject(
                         root_dir=Path("a/b/c"),
-                        dependency_source=LockfileDependencySource(
-                            package_manager_type=PackageManagerType.PIP,
+                        dependency_source=ManifestLockfileDependencySource(
                             manifest=out.Manifest(
                                 out.ManifestKind(out.RequirementsIn()),
                                 out.Fpath("a/b/c/requirements.in"),
                             ),
-                            lockfile_path=Path("a/b/c/requirements.txt"),
+                            lockfile=out.Lockfile(
+                                out.LockfileKind(out.PipRequirementsTxt()),
+                                out.Fpath("a/b/c/requirements.txt"),
+                            ),
                         ),
                     ),
                 ],
@@ -272,24 +280,28 @@ def test_is_match(self) -> None:
                 [
                     Subproject(
                         root_dir=Path(),
-                        dependency_source=LockfileDependencySource(
-                            package_manager_type=PackageManagerType.PIP,
+                        dependency_source=ManifestLockfileDependencySource(
                             manifest=out.Manifest(
                                 out.ManifestKind(out.RequirementsIn()),
                                 out.Fpath("requirements3.in"),
                             ),
-                            lockfile_path=Path("requirements3.txt"),
+                            lockfile=out.Lockfile(
+                                out.LockfileKind(out.PipRequirementsTxt()),
+                                out.Fpath("requirements3.txt"),
+                            ),
                         ),
                     ),
                     Subproject(
                         root_dir=Path("a/b/c"),
-                        dependency_source=LockfileDependencySource(
-                            package_manager_type=PackageManagerType.PIP,
+                        dependency_source=ManifestLockfileDependencySource(
                             manifest=out.Manifest(
                                 out.ManifestKind(out.RequirementsIn()),
                                 out.Fpath("a/b/c/requirements.in"),
                             ),
-                            lockfile_path=Path("a/b/c/requirements3.txt"),
+                            lockfile=out.Lockfile(
+                                out.LockfileKind(out.PipRequirementsTxt()),
+                                out.Fpath("a/b/c/requirements3.txt"),
+                            ),
                         ),
                     ),
                 ],
@@ -305,21 +317,25 @@ def test_is_match(self) -> None:
                         root_dir=Path(),
                         dependency_source=MultiLockfileDependencySource(
                             sources=(
-                                LockfileDependencySource(
-                                    package_manager_type=PackageManagerType.PIP,
+                                ManifestLockfileDependencySource(
                                     manifest=out.Manifest(
                                         out.ManifestKind(out.RequirementsIn()),
                                         out.Fpath("requirements.in"),
                                     ),
-                                    lockfile_path=Path("requirements-dev.txt"),
+                                    lockfile=out.Lockfile(
+                                        out.LockfileKind(out.PipRequirementsTxt()),
+                                        out.Fpath("requirements-dev.txt"),
+                                    ),
                                 ),
-                                LockfileDependencySource(
-                                    package_manager_type=PackageManagerType.PIP,
+                                ManifestLockfileDependencySource(
                                     manifest=out.Manifest(
                                         out.ManifestKind(out.RequirementsIn()),
                                         out.Fpath("requirements.in"),
                                     ),
-                                    lockfile_path=Path("requirements-prod.txt"),
+                                    lockfile=out.Lockfile(
+                                        out.LockfileKind(out.PipRequirementsTxt()),
+                                        out.Fpath("requirements-prod.txt"),
+                                    ),
                                 ),
                             )
                         ),
@@ -337,21 +353,25 @@ def test_is_match(self) -> None:
                         root_dir=Path(),
                         dependency_source=MultiLockfileDependencySource(
                             sources=(
-                                LockfileDependencySource(
-                                    package_manager_type=PackageManagerType.PIP,
+                                ManifestLockfileDependencySource(
                                     manifest=out.Manifest(
                                         out.ManifestKind(out.RequirementsIn()),
                                         out.Fpath("requirements.in"),
                                     ),
-                                    lockfile_path=Path("dev-requirements.txt"),
+                                    lockfile=out.Lockfile(
+                                        out.LockfileKind(out.PipRequirementsTxt()),
+                                        out.Fpath("dev-requirements.txt"),
+                                    ),
                                 ),
-                                LockfileDependencySource(
-                                    package_manager_type=PackageManagerType.PIP,
+                                ManifestLockfileDependencySource(
                                     manifest=out.Manifest(
                                         out.ManifestKind(out.RequirementsIn()),
                                         out.Fpath("requirements.in"),
                                     ),
-                                    lockfile_path=Path("prod-requirements.txt"),
+                                    lockfile=out.Lockfile(
+                                        out.LockfileKind(out.PipRequirementsTxt()),
+                                        out.Fpath("prod-requirements.txt"),
+                                    ),
                                 ),
                             )
                         ),
@@ -363,13 +383,15 @@ def test_is_match(self) -> None:
                 [
                     Subproject(
                         root_dir=Path(),
-                        dependency_source=LockfileDependencySource(
-                            package_manager_type=PackageManagerType.PIP,
+                        dependency_source=ManifestLockfileDependencySource(
                             manifest=out.Manifest(
                                 out.ManifestKind(out.RequirementsIn()),
                                 out.Fpath("requirements.in"),
                             ),
-                            lockfile_path=Path("requirements_lock.txt"),
+                            lockfile=out.Lockfile(
+                                out.LockfileKind(out.PipRequirementsTxt()),
+                                out.Fpath("requirements_lock.txt"),
+                            ),
                         ),
                     )
                 ],
@@ -385,21 +407,25 @@ def test_is_match(self) -> None:
                         root_dir=Path(),
                         dependency_source=MultiLockfileDependencySource(
                             sources=(
-                                LockfileDependencySource(
-                                    package_manager_type=PackageManagerType.PIP,
+                                ManifestLockfileDependencySource(
                                     manifest=out.Manifest(
                                         out.ManifestKind(out.RequirementsIn()),
                                         out.Fpath("requirements.in"),
                                     ),
-                                    lockfile_path=Path("requirements/dev.txt"),
+                                    lockfile=out.Lockfile(
+                                        out.LockfileKind(out.PipRequirementsTxt()),
+                                        out.Fpath("requirements/dev.txt"),
+                                    ),
                                 ),
-                                LockfileDependencySource(
-                                    package_manager_type=PackageManagerType.PIP,
+                                ManifestLockfileDependencySource(
                                     manifest=out.Manifest(
                                         out.ManifestKind(out.RequirementsIn()),
                                         out.Fpath("requirements.in"),
                                     ),
-                                    lockfile_path=Path("requirements/prod.txt"),
+                                    lockfile=out.Lockfile(
+                                        out.LockfileKind(out.PipRequirementsTxt()),
+                                        out.Fpath("requirements/prod.txt"),
+                                    ),
                                 ),
                             )
                         ),
@@ -418,21 +444,25 @@ def test_is_match(self) -> None:
                         root_dir=Path(),
                         dependency_source=MultiLockfileDependencySource(
                             sources=(
-                                LockfileDependencySource(
-                                    package_manager_type=PackageManagerType.PIP,
+                                ManifestLockfileDependencySource(
                                     manifest=out.Manifest(
                                         out.ManifestKind(out.RequirementsIn()),
                                         out.Fpath("requirements.in"),
                                     ),
-                                    lockfile_path=Path("requirements/dev.txt"),
+                                    lockfile=out.Lockfile(
+                                        out.LockfileKind(out.PipRequirementsTxt()),
+                                        out.Fpath("requirements/dev.txt"),
+                                    ),
                                 ),
-                                LockfileDependencySource(
-                                    package_manager_type=PackageManagerType.PIP,
+                                ManifestLockfileDependencySource(
                                     manifest=out.Manifest(
                                         out.ManifestKind(out.RequirementsIn()),
                                         out.Fpath("requirements/prod.in"),
                                     ),
-                                    lockfile_path=Path("requirements/prod.txt"),
+                                    lockfile=out.Lockfile(
+                                        out.LockfileKind(out.PipRequirementsTxt()),
+                                        out.Fpath("requirements/prod.txt"),
+                                    ),
                                 ),
                             )
                         ),
@@ -449,15 +479,17 @@ def test_is_match(self) -> None:
                         root_dir=Path(),
                         dependency_source=MultiLockfileDependencySource(
                             sources=(
-                                LockfileDependencySource(
-                                    package_manager_type=PackageManagerType.PIP,
-                                    manifest=None,
-                                    lockfile_path=Path("requirements/dev.txt"),
+                                LockfileOnlyDependencySource(
+                                    lockfile=out.Lockfile(
+                                        out.LockfileKind(out.PipRequirementsTxt()),
+                                        out.Fpath("requirements/dev.txt"),
+                                    )
                                 ),
-                                LockfileDependencySource(
-                                    package_manager_type=PackageManagerType.PIP,
-                                    manifest=None,
-                                    lockfile_path=Path("requirements/prod.txt"),
+                                LockfileOnlyDependencySource(
+                                    lockfile=out.Lockfile(
+                                        out.LockfileKind(out.PipRequirementsTxt()),
+                                        out.Fpath("requirements/prod.txt"),
+                                    )
                                 ),
                             )
                         ),
@@ -534,15 +566,19 @@ def test_is_match(self, path: Path, expected_match: bool) -> None:
                     Subproject(
                         root_dir=Path(),
                         dependency_source=ManifestOnlyDependencySource(
-                            manifest_kind=out.ManifestKind(value=out.BuildGradle()),
-                            manifest_path=Path("build.gradle"),
+                            manifest=out.Manifest(
+                                out.ManifestKind(out.BuildGradle()),
+                                out.Fpath("build.gradle"),
+                            )
                         ),
                     ),
                     Subproject(
                         root_dir=Path("buildSrc"),
                         dependency_source=ManifestOnlyDependencySource(
-                            manifest_kind=out.ManifestKind(value=out.BuildGradle()),
-                            manifest_path=Path("buildSrc/build.gradle"),
+                            manifest=out.Manifest(
+                                out.ManifestKind(out.BuildGradle()),
+                                out.Fpath("buildSrc/build.gradle"),
+                            )
                         ),
                     ),
                 ],
@@ -565,20 +601,63 @@ def test_is_match(self, path: Path, expected_match: bool) -> None:
                 [
                     Subproject(
                         root_dir=Path(),
-                        dependency_source=LockfileDependencySource(
-                            package_manager_type=PackageManagerType.GRADLE,
+                        dependency_source=ManifestLockfileDependencySource(
                             manifest=out.Manifest(
                                 kind=out.ManifestKind(value=out.BuildGradle()),
                                 path=out.Fpath("build.gradle"),
                             ),
-                            lockfile_path=Path("gradle.lockfile"),
+                            lockfile=out.Lockfile(
+                                out.LockfileKind(out.GradleLockfile_()),
+                                out.Fpath("gradle.lockfile"),
+                            ),
                         ),
                     ),
                     Subproject(
                         root_dir=Path("buildSrc"),
                         dependency_source=ManifestOnlyDependencySource(
-                            manifest_kind=out.ManifestKind(value=out.BuildGradle()),
-                            manifest_path=Path("buildSrc/build.gradle"),
+                            manifest=out.Manifest(
+                                kind=out.ManifestKind(value=out.BuildGradle()),
+                                path=out.Fpath("buildSrc/build.gradle"),
+                            ),
+                        ),
+                    ),
+                ],
+                [
+                    Path("subdir_a/build.gradle"),
+                    Path("subdir_b/build.gradle"),
+                ],
+            ),
+            (
+                # a variant with no lockfile at the top level, but lockfile for one of the children
+                [
+                    Path("build.gradle"),
+                    Path("settings.gradle"),
+                    Path("buildSrc/build.gradle"),
+                    Path("buildSrc/gradle.lockfile"),
+                    Path("subdir_a/build.gradle"),
+                    Path("subdir_b/build.gradle"),
+                ],
+                [
+                    Subproject(
+                        root_dir=Path(),
+                        dependency_source=ManifestOnlyDependencySource(
+                            out.Manifest(
+                                kind=out.ManifestKind(value=out.BuildGradle()),
+                                path=out.Fpath("build.gradle"),
+                            )
+                        ),
+                    ),
+                    Subproject(
+                        root_dir=Path("buildSrc"),
+                        dependency_source=ManifestLockfileDependencySource(
+                            lockfile=out.Lockfile(
+                                kind=out.LockfileKind(out.GradleLockfile_()),
+                                path=out.Fpath("buildSrc/gradle.lockfile"),
+                            ),
+                            manifest=out.Manifest(
+                                kind=out.ManifestKind(value=out.BuildGradle()),
+                                path=out.Fpath("buildSrc/build.gradle"),
+                            ),
                         ),
                     ),
                 ],
@@ -600,20 +679,24 @@ def test_is_match(self, path: Path, expected_match: bool) -> None:
                 [
                     Subproject(
                         root_dir=Path(),
-                        dependency_source=LockfileDependencySource(
-                            package_manager_type=PackageManagerType.GRADLE,
+                        dependency_source=ManifestLockfileDependencySource(
                             manifest=out.Manifest(
                                 kind=out.ManifestKind(value=out.BuildGradle()),
                                 path=out.Fpath("build.gradle"),
                             ),
-                            lockfile_path=Path("gradle.lockfile"),
+                            lockfile=out.Lockfile(
+                                out.LockfileKind(out.GradleLockfile_()),
+                                out.Fpath("gradle.lockfile"),
+                            ),
                         ),
                     ),
                     Subproject(
                         root_dir=Path("buildSrc"),
                         dependency_source=ManifestOnlyDependencySource(
-                            manifest_kind=out.ManifestKind(value=out.BuildGradle()),
-                            manifest_path=Path("buildSrc/settings.gradle"),
+                            manifest=out.Manifest(
+                                kind=out.ManifestKind(value=out.SettingsGradle()),
+                                path=out.Fpath("buildSrc/settings.gradle"),
+                            ),
                         ),
                     ),
                 ],
@@ -631,8 +714,10 @@ def test_is_match(self, path: Path, expected_match: bool) -> None:
                     Subproject(
                         root_dir=Path(),
                         dependency_source=ManifestOnlyDependencySource(
-                            manifest_kind=out.ManifestKind(value=out.BuildGradle()),
-                            manifest_path=Path("build.gradle"),
+                            manifest=out.Manifest(
+                                kind=out.ManifestKind(value=out.BuildGradle()),
+                                path=out.Fpath("build.gradle"),
+                            ),
                         ),
                     ),
                 ],
@@ -659,8 +744,6 @@ def test_make_subprojects(
 
         # and expect the returned subprojects to match, ignoring order
         expected = set(expected_subprojects)
-        print("real subprojects", subprojects)
-        print("expected subprojects", expected_subprojects)
         assert len(subprojects) == len(expected_subprojects)
         for subproject in subprojects:
             assert subproject in expected
diff --git a/cli/tests/fixtures.py b/cli/tests/fixtures.py
index 31e1bcceef87..39c0791ffe09 100644
--- a/cli/tests/fixtures.py
+++ b/cli/tests/fixtures.py
@@ -58,5 +58,12 @@ def __call__(
         teardown_workspace: Callable[[], None] = lambda: None,
         # Context manager to wrap around the semgrep invocation:
         context_manager: Optional[ContextManager] = None,
+        # The --project-root option is used to prevent the .semgrepignore
+        # at the root of the git project to be taken into account when testing,
+        # which is a new behavior in osemgrep. It makes semgrep view the
+        # project as a non-git project. This option is only used to manage
+        # the transition from pysemgrep to osemgrep. Avoid using it if
+        # you can.
+        osemgrep_force_project_root: bool = False,
     ) -> SemgrepResult:
         ...
diff --git a/cli/tests/semgrep_runner.py b/cli/tests/semgrep_runner.py
index bcfc9fbfc6e1..c23e02e1b355 100644
--- a/cli/tests/semgrep_runner.py
+++ b/cli/tests/semgrep_runner.py
@@ -15,6 +15,7 @@
 # call-semgrep-in-a-subprocess does not provide yet.
 import os
 import shlex
+import sys
 from dataclasses import dataclass
 from pathlib import Path
 from subprocess import PIPE
@@ -37,11 +38,6 @@
 # The --experimental is to force the use of osemgrep.
 _OSEMGREP_EXTRA_ARGS = ["--experimental"]
 
-# The --project-root option is used to prevent the .semgrepignore
-# at the root of the git project to be taken into account when testing,
-# which is a new behavior in osemgrep.
-_OSEMGREP_SCAN_EXTRA_ARGS = _OSEMGREP_EXTRA_ARGS + ["--project-root", "."]
-
 _SEMGREP_PATH = str(
     (
         Path(__file__).parent.parent
@@ -57,7 +53,7 @@
 # For instance, SEMGREP_BASE_SCAN_COMMAND + ["logout"] will fail with osemgrep,
 # because the subcommand must come first.
 SEMGREP_BASE_SCAN_COMMAND: List[str] = (
-    [_SEMGREP_PATH] + _OSEMGREP_SCAN_EXTRA_ARGS if USE_OSEMGREP else [_SEMGREP_PATH]
+    [_SEMGREP_PATH] + _OSEMGREP_EXTRA_ARGS if USE_OSEMGREP else [_SEMGREP_PATH]
 )
 
 SEMGREP_BASE_SCAN_COMMAND_STR: str = " ".join(SEMGREP_BASE_SCAN_COMMAND)
@@ -124,6 +120,7 @@ def fork_semgrep(
     full_env = dict(os.environ, **env_dict)
 
     # let's fork and use a pipe to communicate with the external semgrep
+    print(f"[fork] semgrep command: {' '.join(argv)}", file=sys.stderr)
     proc = Popen(argv, stdout=PIPE, stderr=PIPE, env=full_env)
     stdout, stderr = proc.communicate()
     return Result(proc.returncode, stdout.decode("utf-8"), stderr.decode("utf-8"))
@@ -171,6 +168,9 @@ def invoke(
         if self._use_click_runner:
             if subcommand:
                 arg_list = [subcommand] + arg_list
+            print(
+                f"[click] semgrep command args: {' '.join(arg_list)}", file=sys.stderr
+            )
             result = self._runner.invoke(python_cli, arg_list, input=input, env=env)
             stderr = result.stderr if not self._mix_stderr else ""
             return Result(result.exit_code, result.stdout, stderr)
diff --git a/src/tests/Test.ml b/src/tests/Test.ml
index f9d703e417a1..3f526a0fa43b 100644
--- a/src/tests/Test.ml
+++ b/src/tests/Test.ml
@@ -137,6 +137,7 @@ let tests (caps : Cap.all_caps) =
       Unit_LS.tests (caps :> Session.caps);
       Unit_Login.tests caps;
       Unit_Fetching.tests (caps :> < Cap.network ; Cap.tmp >);
+      Unit_reporting.tests (caps :> < >);
       Test_is_blocking_helpers.tests;
       Test_login_subcommand.tests (caps :> < Cap.stdout ; Cap.network >);
       Test_scan_subcommand.tests
diff --git a/tests/semgrep-rules b/tests/semgrep-rules
index 178f46ebf490..3b05904d887f 160000
--- a/tests/semgrep-rules
+++ b/tests/semgrep-rules
@@ -1,1 +1,1 @@
-Subproject commit 178f46ebf4902fdbf748d0dcddb9d15401352312
+Subproject commit 3b05904d887fb274c494b2e19f5f47dc1d1933c6
diff --git a/tests/snapshots/semgrep-core/1b426baf9922/stdout b/tests/snapshots/semgrep-core/1b426baf9922/stdout
index 7c9fe77dff24..f8ca5e3215fc 100644
--- a/tests/snapshots/semgrep-core/1b426baf9922/stdout
+++ b/tests/snapshots/semgrep-core/1b426baf9922/stdout
@@ -1,4 +1,3 @@
-Snapshot for Test_show_subcommand.test_dump_pattern.(fun)
 E(
   Call(
     N(
diff --git a/tests/snapshots/semgrep-core/201a11c8d7a5/stdout b/tests/snapshots/semgrep-core/201a11c8d7a5/stdout
index da7873322c02..635efe59ece6 100644
--- a/tests/snapshots/semgrep-core/201a11c8d7a5/stdout
+++ b/tests/snapshots/semgrep-core/201a11c8d7a5/stdout
@@ -1,4 +1,3 @@
-Snapshot for Test_show_subcommand.test_dump_ast.(fun)
 --- begin input files ---
 foo.py
 --- end input files ---
diff --git a/tests/snapshots/semgrep-core/4f06f5cf1f9b/stdout b/tests/snapshots/semgrep-core/4f06f5cf1f9b/stdout
index 89ec60ba3547..0b2f569bdc5f 100644
--- a/tests/snapshots/semgrep-core/4f06f5cf1f9b/stdout
+++ b/tests/snapshots/semgrep-core/4f06f5cf1f9b/stdout
@@ -1,4 +1,3 @@
-Snapshot for Test_show_subcommand.test_dump_config.(fun)
 --- begin input files ---
 rule.yml
 --- end input files ---
diff --git a/tests/snapshots/semgrep-core/745a04125f26/stdout b/tests/snapshots/semgrep-core/745a04125f26/stdout
index 776084c27ffb..06818637e9c2 100644
--- a/tests/snapshots/semgrep-core/745a04125f26/stdout
+++ b/tests/snapshots/semgrep-core/745a04125f26/stdout
@@ -1,2 +1,1 @@
-Snapshot for Test_show_subcommand.test_supported_languages.(fun)
 supported languages are: apex, bash, c, c#, c++, cairo, circom, clojure, cpp, csharp, dart, docker, dockerfile, elixir, ex, generic, go, golang, hack, hcl, html, java, javascript, js, json, jsonnet, julia, kotlin, kt, lisp, lua, move_on_aptos, move_on_sui, none, ocaml, php, promql, proto, proto3, protobuf, py, python, python2, python3, ql, r, regex, ruby, rust, scala, scheme, sh, sol, solidity, swift, terraform, tf, ts, typescript, vue, xml, yaml
diff --git a/tests/snapshots/semgrep-core/7953814bd0ae/stdxxx b/tests/snapshots/semgrep-core/7953814bd0ae/stdxxx
index 19016e3c4cc2..6340632a7cee 100644
--- a/tests/snapshots/semgrep-core/7953814bd0ae/stdxxx
+++ b/tests/snapshots/semgrep-core/7953814bd0ae/stdxxx
@@ -1,4 +1,3 @@
-Snapshot for Test_show_subcommand.test_identity.(fun)
  SUCCESS  You are logged in as {"identity":"cli_fake_user_valid-from-fake-date"}
 --- begin input files ---
 settings.yml
diff --git a/tests/snapshots/semgrep-core/94a23aa13ed6/stdxxx b/tests/snapshots/semgrep-core/94a23aa13ed6/stdxxx
index 22573ef156cc..57b9db5bbea0 100644
--- a/tests/snapshots/semgrep-core/94a23aa13ed6/stdxxx
+++ b/tests/snapshots/semgrep-core/94a23aa13ed6/stdxxx
@@ -1,4 +1,3 @@
-Snapshot for Test_show_subcommand.test_deployment.(fun)
  SUCCESS  Your deployment info is {"id":42,"name":"fake_deployment","organization_id":0,"display_name":"","scm_name":"","slug":"","source_type":"","default_user_role":"","has_autofix":false,"has_deepsemgrep":false,"has_triage_via_comment":false,"has_dependency_query":false}
 --- begin input files ---
 settings.yml
diff --git a/tests/snapshots/semgrep-core/a5f8cca91ee4/stdout b/tests/snapshots/semgrep-core/a5f8cca91ee4/stdout
index ae4e22f51ccb..a90610e97088 100644
--- a/tests/snapshots/semgrep-core/a5f8cca91ee4/stdout
+++ b/tests/snapshots/semgrep-core/a5f8cca91ee4/stdout
@@ -1,4 +1,3 @@
-Snapshot for Test_show_subcommand.test_dump_rule_v2.(fun)
 --- begin input files ---
 rule.yml
 --- end input files ---
diff --git a/tests/snapshots/semgrep-core/b5c5c5717af7/name b/tests/snapshots/semgrep-core/b5c5c5717af7/name
new file mode 100644
index 000000000000..04f3efa0f488
--- /dev/null
+++ b/tests/snapshots/semgrep-core/b5c5c5717af7/name
@@ -0,0 +1,1 @@
+Osemgrep Show (e2e) > Test_show_subcommand.test_dump_ast_when_error
diff --git a/tests/snapshots/semgrep-core/b5c5c5717af7/stdxxx b/tests/snapshots/semgrep-core/b5c5c5717af7/stdxxx
new file mode 100644
index 000000000000..02793c72146d
--- /dev/null
+++ b/tests/snapshots/semgrep-core/b5c5c5717af7/stdxxx
@@ -0,0 +1,19 @@
+--- begin input files ---
+error.js
+--- end input files ---
+Pr([])
+[<MASKED TIMESTAMP>][ERROR]: errors=File error.js, line 1, characters 0-10:
+function (
+^^^^^^^^^^
+parent: program, no left sibling, first child: "function"
+node type: ERROR
+children: [
+  "function"
+  "("
+]
+Unrecognized construct
+tolerated errors=
+skipped={ str = "function (";
+  pos = { Pos.bytepos = 0; line = 1; column = 0; file = error.js } }
+inserted=
+ASSERT exit code