@codacy/tools-opengrep-1 0.23.1 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,20 +24,47 @@ Multi-language SAST (Static Application Security Testing) scanner using the [Ope
24
24
  | Strategy | CLI |
25
25
  | Languages | 30+ (Python, JavaScript, TypeScript, Java, Go, Ruby, C, C++, etc.) |
26
26
  | Binary | `opengrep` |
27
- | File patterns | `*` (auto-detects by language) |
27
+ | File patterns | ~70 extensions derived from the languages above, plus `Dockerfile` |
28
28
  | Pattern count | ~2500 |
29
29
  | Extra dependency | `yaml` (for YAML rule config parsing/generation) |
30
30
 
31
31
  ## Rule bundling
32
32
 
33
- Opengrep uses YAML rule files for detection. The adapter bundles ~2200 rules into `src/rules.yaml`, sourced from:
33
+ Opengrep uses YAML rule files for detection. The adapter bundles ~2500 rules into `src/rules.yaml`, sourced from:
34
34
 
35
- 1. **semgrep/semgrep-rules** — community rules (pinned at commit `4ccd3b9`)
36
- 2. **GitLab SAST rules** — MIT-licensed rules from `gitlab.com/gitlab-org/security-products/sast-rules`
37
- 3. **Codacy custom rules** — from `codacy/codacy-opengrep` wrapper repo
35
+ 1. **opengrep/opengrep-rules** — the registry rules, pinned at the same commit as the Codacy image
36
+ 2. **GitLab SAST rules** — MIT-licensed, from `gitlab.com/gitlab-org/security-products/sast-rules`
37
+ 3. **Codacy custom rules** — `docs/codacy-rules*.yaml` in `codacy/codacy-opengrep`
38
38
 
39
39
  The bundled `src/rules.yaml` is **committed to the repository** for consistency and reproducibility.
40
40
 
41
+ ### Keeping it in step with the Codacy image
42
+
43
+ All three sources, their pinned commits and their file filters are mirrored from
44
+ `codacy/codacy-opengrep`'s `internal/docgen/parsing.go`. Keep them that way: the image is the
45
+ reference implementation, and `scripts/fetch-rules.mjs` says which upstream function each piece
46
+ came from.
47
+
48
+ Three mistakes in that mirroring cost **296 of 2534 patterns (12%)**, every one of them silent —
49
+ the pattern stays in `patterns.json`, `init --auto` enables it, the config claims it is active, and
50
+ it simply never fires:
51
+
52
+ - **Rule ids were derived from the file path.** GitLab ids are written in the file and must be
53
+ taken verbatim. Path derivation looks right for the top-level tree (`java/crypto/rule-X.yml`
54
+ really does hold `java_crypto_rule-X`) and is wrong for the licence-tiered one, where
55
+ `rules/lgpl-cc/properties/spring/accesscontrol/rule-Y.yml` holds `properties_spring_rule-Y`. The
56
+ sharpest case is `rule-JwtDecodeWithoutVerify.yml`, whose id uses an underscore where the
57
+ filename uses a hyphen. 265 of 588 GitLab rules were lost this way.
58
+ - **The registry source was upstream `semgrep/semgrep-rules`**, not Opengrep's fork. The two share
59
+ most ids, so the output looks plausible while individual rules go missing.
60
+ - **YAML was read and rewritten line by line.** That corrupted multi-line flow scalars — the
61
+ generated file stopped parsing, and opengrep then reported _zero findings for the entire run_
62
+ rather than an error — and it skipped every `tests/` directory, where some shipped rules live.
63
+
64
+ `test/rules-coverage.test.ts` is the guard: it fails if any catalogued pattern has no rule, and if
65
+ the generated file does not parse. Run `pnpm fetch-rules` when it does; the script prints the
66
+ orphaned ids.
67
+
41
68
  ## Updating patterns
42
69
 
43
70
  ```bash
@@ -57,7 +84,8 @@ Pattern IDs follow the format `Semgrep_python.lang.security.audit.exec-detected.
57
84
  pnpm fetch-rules
58
85
 
59
86
  # This clones the rule repos, extracts matching rules, and writes src/rules.yaml
60
- # Only rules whose IDs match src/patterns.json are included
87
+ # Only rules whose IDs match src/patterns.json are included — and the script now
88
+ # warns loudly about the reverse, a catalogued pattern with no rule to run.
61
89
 
62
90
  # Commit the result
63
91
  git add src/rules.yaml
@@ -99,8 +127,9 @@ codacy-analysis analyze . --tool opengrep --install-dependencies
99
127
  ```
100
128
 
101
129
  `install()` downloads the matching statically-linked binary from GitHub releases
102
- (no archive to unpack) and `chmod +x`es it. The platform/arch/libc → asset
103
- mapping mirrors opengrep's official `install.sh`:
130
+ (no archive to unpack) and `chmod +x`es it (not on Windows). The
131
+ platform/arch/libc → asset mapping mirrors opengrep's official `install.sh` and
132
+ `install.ps1`:
104
133
 
105
134
  | Platform | Asset |
106
135
  | ------------------------- | ---------------------------- |
@@ -110,11 +139,15 @@ mapping mirrors opengrep's official `install.sh`:
110
139
  | Linux arm64 (glibc) | `opengrep_manylinux_aarch64` |
111
140
  | Linux x64 (musl/Alpine) | `opengrep_musllinux_x86` |
112
141
  | Linux arm64 (musl/Alpine) | `opengrep_musllinux_aarch64` |
142
+ | Windows x64 | `opengrep_windows_x86.exe` |
143
+ | Windows arm64 (emulated) | `opengrep_windows_x86.exe` |
113
144
 
114
145
  To install manually instead:
115
146
 
116
147
  ```bash
117
148
  curl -fsSL https://raw.githubusercontent.com/opengrep/opengrep/main/install.sh | bash
149
+ # Windows (PowerShell)
150
+ irm https://raw.githubusercontent.com/opengrep/opengrep/main/install.ps1 | iex
118
151
  ```
119
152
 
120
153
  ## Notes for maintainers
@@ -152,11 +185,11 @@ curl -fsSL https://raw.githubusercontent.com/opengrep/opengrep/main/install.sh |
152
185
 
153
186
  - **Tool ID is `Semgrep`** for legacy compatibility with the Codacy platform. The `displayName` is set to `Opengrep` for CLI display. Never change the tool ID without coordinating with the Codacy platform team.
154
187
  - **Config materialization**: When Codacy patterns are specified, the adapter loads `src/rules.yaml`, filters to enabled rules, and writes a filtered config to `.codacy/generated/opengrep-rules.yaml`.
155
- - **check_id prefix stripping**: Opengrep prepends a path-based namespace to check_ids derived from the config file path relative to cwd. The adapter computes and strips this prefix (e.g. `codacy.generated.`) plus any `tmp.` legacy prefix.
188
+ - **check_id is the bare rule ID**: every invocation passes `--no-rewrite-rule-ids`. Without it Opengrep prefixes `check_id` with the config file's directory relative to cwd (`.codacy/generated/x.yaml` → `codacy.generated.`), and the adapter used to reproduce that path math to strip the prefix again — never verified on Windows, where a mismatch would silently drop every finding at the enabled-pattern check. With the flag the rule ID maps to a pattern ID directly on every OS.
156
189
  - **Config file search**: `.semgrep.yaml`, `.opengrep.yaml`, `.semgrep.yml`, `.opengrep.yml` (in that order).
157
190
  - **Opengrep errors**: Parse errors (syntax errors, partial parsing) from Opengrep's JSON `errors` array are converted to structured `AnalysisError` objects with `filePath`. Stderr (scan status table) is logged at debug level only.
158
191
  - **Rule ID format**: Semgrep-rules use `{dir}.{filename}.{ruleId}` (dots), GitLab rules use `{dir}_{filename}` (underscores), Codacy custom rules keep their original IDs.
159
- - **Install / binary download**: `install()` downloads the bare per-platform binary from `https://github.com/opengrep/opengrep/releases/download/v{version}/{asset}` and `chmod +x`es it into `~/.codacy/tools/`. The release tag carries a `v` prefix (`v1.26.0`) even though `preferredVersion` does not. On Linux the libc flavour is detected with an `ldd /bin/sh` probe (`musl` → musllinux, else manylinux); detection failures fall back to glibc/manylinux. Unlike install.sh, the Codacy-managed install does **not** verify the cosign `.cert`/`.sig` signatures — if signature verification becomes a requirement, fetch those sibling assets and validate before chmod.
192
+ - **Install / binary download**: `install()` downloads the bare per-platform binary from `https://github.com/opengrep/opengrep/releases/download/v{version}/{asset}` and `chmod +x`es it into `~/.codacy/tools/`. The release tag carries a `v` prefix (`v1.26.0`) even though `preferredVersion` does not. On Linux the libc flavour is detected with an `ldd /bin/sh` probe (`musl` → musllinux, else manylinux); detection failures fall back to glibc/manylinux. On Windows the binary is saved as `opengrep.exe`; upstream builds only x64, which `install.ps1` also installs on ARM64 (emulated), so we do the same. **From opengrep 2.0.0 the Windows asset becomes `opengrep_windows_x86.zip`** (exe + DLLs) — bumping `preferredVersion` past 2.0 needs an extraction step. The argv chunk budget drops to `MAX_ARGV_BYTES` (30,000) on Windows, whose command line is capped at 32,767 characters. Unlike install.sh, the Codacy-managed install does **not** verify the cosign `.cert`/`.sig` signatures — if signature verification becomes a requirement, fetch those sibling assets and validate before chmod.
160
193
  - **Argv-size chunking (no directory walk)**: The adapter always passes Opengrep an **explicit file list** — the runner's already git-ignored, exclude-filtered target set — so Opengrep never walks the repository tree itself. `chunkFilesByArgvBudget()` splits that list into the **fewest, largest** chunks that each fit `ARGV_BYTE_BUDGET` (200 KB, well under `ARG_MAX`), and each chunk is scanned in its own `opengrep scan` invocation; findings and errors are merged across chunks. Chunk by **size, not count**: every invocation re-compiles `rules.yaml`, so fewer/larger chunks amortize that fixed cost. The earlier directory/“root” fallback was removed because on huge repos (a single tracked file at the repo root collapsed the minimal-cover dirs to `.`) it made Opengrep walk the entire tree — re-scanning excluded/generated/vendored files and timing out at the runner's 10-minute limit.
161
194
  - **File-size cap**: invocations pass `--max-target-bytes 1000000` (~1 MB). This restores Opengrep's default size cap — the adapter previously passed `0`, which **disables** the cap, so giant generated/minified bundles were scanned and dominated runtime. The cap applies to explicitly-listed files (verified on v1.16.4). Note: `--exclude-minified-files` is **not** used — that flag is rejected by the bundled binary unless `--experimental` (a different engine) is also passed, so we rely on the size cap instead. (The runner also enforces a tighter 150 KB per-file limit centrally before files reach the adapter, so this `--max-target-bytes` is now a secondary defense.)
162
195
  - **Partial results on timeout**: the per-chunk loop checks `ctx.signal` (the runner's timeout abort) between chunks and passes it to `spawnTool`, so an in-flight scan is killed promptly. On abort the adapter keeps the findings from completed chunks and adds a single `Timeout` error (→ `status: "partial"`) rather than losing everything. This is why chunking matters beyond ARG_MAX: it's the granularity at which a long scan can be stopped with results preserved.
package/dist/index.js CHANGED
@@ -30722,14 +30722,18 @@ var pattern_mappings_default = {
30722
30722
  // src/metadata.ts
30723
30723
  var descriptor = {
30724
30724
  id: "Semgrep",
30725
+ shortName: "opengrep",
30725
30726
  displayName: "Opengrep",
30726
30727
  preferredVersion: "1.26.0",
30728
+ // Canonical `KNOWN_LANGUAGES` names, which is what `init --auto` matches against
30729
+ // (`init.ts:882`). The engine's own names differ and must be translated here:
30730
+ // Bash/Shell and Vue/Javascript collapse onto one entry each, since `.sh` and
30731
+ // `.vue` are already those languages. Guarded by `descriptor-languages.test.ts`.
30727
30732
  languages: [
30728
30733
  "Apex",
30729
- "Bash",
30730
30734
  "C",
30731
- "C++",
30732
- "C#",
30735
+ "CPP",
30736
+ "CSharp",
30733
30737
  "Clojure",
30734
30738
  "Dart",
30735
30739
  "Dockerfile",
@@ -30737,7 +30741,7 @@ var descriptor = {
30737
30741
  "Go",
30738
30742
  "HTML",
30739
30743
  "Java",
30740
- "JavaScript",
30744
+ "Javascript",
30741
30745
  "JSON",
30742
30746
  "Kotlin",
30743
30747
  "Lua",
@@ -30750,13 +30754,111 @@ var descriptor = {
30750
30754
  "Swift",
30751
30755
  "Terraform",
30752
30756
  "TypeScript",
30753
- "Visual Basic",
30754
- "Vue",
30757
+ "VisualBasic",
30755
30758
  "XML",
30756
30759
  "YAML"
30757
30760
  ],
30758
- filePatterns: ["*"],
30759
- supportedOS: ["linux", "darwin"],
30761
+ // Derived from `languages` above, via `LANGUAGE_CONFIG`, plus the extensions
30762
+ // sibling adapters already route for those same languages (`.cjs`, `.hh`,
30763
+ // `.jsonc`, `.json5`, `.pyi`, `.dash`, `.ksh`) — `LANGUAGE_CONFIG` is a
30764
+ // detection map and does not claim to be exhaustive per tool.
30765
+ //
30766
+ // This was `["*"]`, which `routeFilesToTool` short-circuits to "every file", so
30767
+ // opengrep was handed the whole repository: on one 1774-file repo, 358 of them
30768
+ // (20%) were png/jpg/font assets. Opengrep resolves a language by extension and
30769
+ // skips what it cannot parse, so findings were never affected — but the paths
30770
+ // still cost a realpath+stat each, inflated the reported file count, and ate the
30771
+ // 200 KB argv budget, splitting one `opengrep scan` into two.
30772
+ //
30773
+ // Keep this a superset of `LANGUAGE_CONFIG`'s extensions for every declared
30774
+ // language; `opengrep-1/test/file-patterns.test.ts` fails if a language gains one
30775
+ // that is not routed here, because a missed extension is a silent coverage loss.
30776
+ filePatterns: [
30777
+ "**/*.bash",
30778
+ "**/*.c",
30779
+ "**/*.cc",
30780
+ "**/*.cjs",
30781
+ "**/*.clj",
30782
+ "**/*.cljc",
30783
+ "**/*.cljs",
30784
+ "**/*.cls",
30785
+ "**/*.cpp",
30786
+ "**/*.cs",
30787
+ "**/*.cxx",
30788
+ "**/*.dart",
30789
+ "**/*.dash",
30790
+ "**/*.dockerfile",
30791
+ "**/*.edn",
30792
+ "**/*.env",
30793
+ "**/*.ex",
30794
+ "**/*.exs",
30795
+ "**/*.gemspec",
30796
+ "**/*.go",
30797
+ "**/*.h",
30798
+ "**/*.hh",
30799
+ "**/*.hpp",
30800
+ "**/*.htm",
30801
+ "**/*.html",
30802
+ "**/*.ino",
30803
+ "**/*.java",
30804
+ "**/*.jbuilder",
30805
+ "**/*.js",
30806
+ "**/*.jsm",
30807
+ "**/*.json",
30808
+ "**/*.json5",
30809
+ "**/*.jsonc",
30810
+ "**/*.jsx",
30811
+ "**/*.ksh",
30812
+ "**/*.kt",
30813
+ "**/*.kts",
30814
+ "**/*.lua",
30815
+ "**/*.luau",
30816
+ "**/*.mjs",
30817
+ "**/*.opal",
30818
+ "**/*.php",
30819
+ "**/*.podspec",
30820
+ "**/*.pom",
30821
+ "**/*.py",
30822
+ "**/*.pyi",
30823
+ "**/*.rake",
30824
+ "**/*.rb",
30825
+ "**/*.rlib",
30826
+ "**/*.rs",
30827
+ "**/*.scala",
30828
+ "**/*.sh",
30829
+ "**/*.swift",
30830
+ "**/*.tf",
30831
+ "**/*.trigger",
30832
+ "**/*.ts",
30833
+ "**/*.tsx",
30834
+ "**/*.vb",
30835
+ "**/*.vue",
30836
+ "**/*.wsdl",
30837
+ "**/*.xhtml",
30838
+ "**/*.xml",
30839
+ "**/*.xsl",
30840
+ "**/*.yaml",
30841
+ "**/*.yml",
30842
+ // Source files with no extension. `LANGUAGE_CONFIG.files` is a *detection*
30843
+ // list — it is mostly lockfiles (`yarn.lock`, `go.sum`, `poetry.lock`), which
30844
+ // identify a language without being source in it — so only the entries
30845
+ // opengrep can actually parse are routed here: Dockerfiles, and Ruby's
30846
+ // extensionless DSL files.
30847
+ "**/Dockerfile",
30848
+ "**/Dockerfile.*",
30849
+ "**/Berksfile",
30850
+ "**/Capfile",
30851
+ "**/Cheffile",
30852
+ "**/Fastfile",
30853
+ "**/Gemfile",
30854
+ "**/Guardfile",
30855
+ "**/Podfile",
30856
+ "**/Rakefile",
30857
+ "**/Thorfile",
30858
+ "**/Vagrantfile",
30859
+ "**/config.ru"
30860
+ ],
30861
+ supportedOS: ["linux", "darwin", "win32"],
30760
30862
  supportedArch: ["x64", "arm64"],
30761
30863
  supportedRunners: ["local", "container"],
30762
30864
  frameworkMappings: pattern_mappings_default
@@ -30801,6 +30903,10 @@ function opengrepAssetName(osName, arch, muslLibc) {
30801
30903
  if (arch === "arm64") return `opengrep_${flavour}_aarch64`;
30802
30904
  return null;
30803
30905
  }
30906
+ if (osName === "win32") {
30907
+ if (arch === "x64" || arch === "arm64") return "opengrep_windows_x86.exe";
30908
+ return null;
30909
+ }
30804
30910
  return null;
30805
30911
  }
30806
30912
  async function isMuslLibc() {
@@ -30826,30 +30932,18 @@ async function getVersionFromBinary(binaryPath) {
30826
30932
  }
30827
30933
  }
30828
30934
  async function resolveBinaryPath(ctx) {
30829
- const codacyBin = import_path.default.join(ctx.toolInstallDir, BINARY);
30935
+ const codacyBin = import_path.default.join(ctx.toolInstallDir, (0, import_tooling.exeName)(BINARY));
30830
30936
  const version = await getVersionFromBinary(codacyBin);
30831
30937
  if (version) return codacyBin;
30832
30938
  return BINARY;
30833
30939
  }
30834
- function computeConfigPrefix(configPath, cwd) {
30835
- const relPath = import_path.default.relative(cwd, configPath);
30836
- const dir = import_path.default.dirname(relPath);
30837
- if (dir === ".") return "";
30838
- const components = dir.split(import_path.default.sep).map((c) => c.replace(/^\.+/, ""));
30839
- return components.filter(Boolean).join(".") + ".";
30840
- }
30841
- function stripCheckIdPrefix(checkId, configPrefix) {
30842
- let stripped = checkId.startsWith("tmp.") ? checkId.slice(4) : checkId;
30843
- if (configPrefix && stripped.startsWith(configPrefix)) {
30844
- stripped = stripped.slice(configPrefix.length);
30845
- }
30846
- return stripped;
30847
- }
30848
- function truncate(str, maxLen) {
30849
- if (str.length <= maxLen) return str;
30850
- return str.slice(0, maxLen - 3) + "...";
30940
+ function firstSentence(message) {
30941
+ const oneLine = message.replace(/\n/g, " ");
30942
+ const match = /(^.*?[a-z]{2,}[.!?])\s+\W*[A-Z]/.exec(oneLine);
30943
+ if (match) return match[1];
30944
+ return oneLine.length > 500 ? oneLine.slice(0, 500) : oneLine;
30851
30945
  }
30852
- var ARGV_BYTE_BUDGET = 200 * 1024;
30946
+ var ARGV_BYTE_BUDGET = import_tooling.IS_WINDOWS ? import_tooling.MAX_ARGV_BYTES : 200 * 1024;
30853
30947
  function chunkFilesByArgvBudget(files) {
30854
30948
  const chunks = [];
30855
30949
  let current = [];
@@ -30969,7 +31063,7 @@ var adapter = {
30969
31063
  */
30970
31064
  async checkAvailability(ctx) {
30971
31065
  ctx.logger.debug("Checking availability", { toolId: TOOL_ID });
30972
- const codacyBin = import_path.default.join(ctx.toolInstallDir, BINARY);
31066
+ const codacyBin = import_path.default.join(ctx.toolInstallDir, (0, import_tooling.exeName)(BINARY));
30973
31067
  const codacyVersion = await getVersionFromBinary(codacyBin);
30974
31068
  if (codacyVersion) {
30975
31069
  ctx.logger.debug("Found opengrep in codacy install dir", {
@@ -30999,21 +31093,22 @@ var adapter = {
30999
31093
  return {
31000
31094
  available: false,
31001
31095
  reason: "opengrep binary not found in PATH or Codacy install directory",
31002
- installHint: "codacy-analysis analyze . --install-dependencies # auto-install into ~/.codacy\ncurl -fsSL https://raw.githubusercontent.com/opengrep/opengrep/main/install.sh | bash # manual"
31096
+ installHint: "codacy-analysis analyze . --install-dependencies # auto-install into ~/.codacy\n" + (import_tooling.IS_WINDOWS ? "irm https://raw.githubusercontent.com/opengrep/opengrep/main/install.ps1 | iex # manual (PowerShell)" : "curl -fsSL https://raw.githubusercontent.com/opengrep/opengrep/main/install.sh | bash # manual")
31003
31097
  };
31004
31098
  },
31005
31099
  /**
31006
31100
  * Downloads the opengrep binary from GitHub releases into `ctx.toolInstallDir`.
31007
31101
  *
31008
31102
  * Opengrep distributes a single statically-linked executable per platform
31009
- * (no archive), so install is just a download + `chmod +x`. The
31010
- * platform/arch/libc → asset mapping mirrors opengrep's official install.sh
31011
- * (see {@link opengrepAssetName}). The binary lands at
31012
- * `ctx.toolInstallDir/opengrep`, which `checkAvailability` probes first — so
31013
- * the runner's post-install re-check picks up the managed install.
31103
+ * (no archive), so install is just a download + `chmod +x` (no chmod on
31104
+ * Windows). The platform/arch/libc → asset mapping mirrors opengrep's
31105
+ * official install.sh / install.ps1 (see {@link opengrepAssetName}). The
31106
+ * binary lands at `ctx.toolInstallDir/opengrep` (`opengrep.exe` on Windows),
31107
+ * which `checkAvailability` probes first — so the runner's post-install
31108
+ * re-check picks up the managed install.
31014
31109
  *
31015
31110
  * Returns an `UnsupportedPlatform` error for targets opengrep has no binary
31016
- * for (e.g. Windows), and a `DownloadError` if the fetch fails.
31111
+ * for, and a `DownloadError` if the fetch fails.
31017
31112
  */
31018
31113
  async install(ctx) {
31019
31114
  ctx.logger.info("Starting install", { toolId: TOOL_ID });
@@ -31028,17 +31123,17 @@ var adapter = {
31028
31123
  toolId: TOOL_ID,
31029
31124
  phase: "toolInstall",
31030
31125
  kind: "UnsupportedPlatform",
31031
- message: `Unsupported platform: ${platformKey}. Opengrep provides binaries for darwin-x64, darwin-arm64, linux-x64, and linux-arm64.`,
31126
+ message: `Unsupported platform: ${platformKey}. Opengrep provides binaries for darwin-x64, darwin-arm64, linux-x64, linux-arm64, and win32-x64 (also used on win32-arm64).`,
31032
31127
  level: "error"
31033
31128
  }
31034
31129
  };
31035
31130
  }
31036
31131
  const url = `https://github.com/opengrep/opengrep/releases/download/v${version}/${assetName}`;
31037
- const destPath = import_path.default.join(ctx.toolInstallDir, BINARY);
31132
+ const destPath = import_path.default.join(ctx.toolInstallDir, (0, import_tooling.exeName)(BINARY));
31038
31133
  try {
31039
31134
  ctx.logger.info(`Downloading Opengrep v${version} for ${platformKey}...`);
31040
31135
  await (0, import_tooling.downloadFile)(url, destPath, ctx.logger);
31041
- await import_promises.default.chmod(destPath, 493);
31136
+ if (!import_tooling.IS_WINDOWS) await import_promises.default.chmod(destPath, 493);
31042
31137
  ctx.logger.info(`Installed opengrep v${version} to ${destPath}`);
31043
31138
  return { success: true, installedPath: destPath, installedVersion: version };
31044
31139
  } catch (err) {
@@ -31086,8 +31181,9 @@ var adapter = {
31086
31181
  * the enabled rules and write a generated config.
31087
31182
  * 3. If no patterns and no local config, use the full bundled rules.yaml.
31088
31183
  *
31089
- * The `tmp.` prefix that opengrep adds to check IDs when using inline
31090
- * configs is stripped before building pattern IDs.
31184
+ * Every invocation passes `--no-rewrite-rule-ids`, so `check_id` is the rule
31185
+ * ID exactly as written in the config and maps to a pattern ID directly (see
31186
+ * the note on `baseArgs`).
31091
31187
  *
31092
31188
  * Exit codes:
31093
31189
  * - 0: success (findings may or may not exist)
@@ -31160,6 +31256,7 @@ var adapter = {
31160
31256
  "--json",
31161
31257
  "--config",
31162
31258
  configFile,
31259
+ "--no-rewrite-rule-ids",
31163
31260
  "--timeout",
31164
31261
  "5",
31165
31262
  "--timeout-threshold",
@@ -31220,12 +31317,14 @@ var adapter = {
31220
31317
  kind: errorType,
31221
31318
  message,
31222
31319
  level,
31223
- filePath
31320
+ filePath,
31321
+ // Opengrep only reports a path on errors that stopped it scanning
31322
+ // that file, so a path here means the file was not analysed.
31323
+ ...filePath ? { fileNotAnalyzed: true } : {}
31224
31324
  });
31225
31325
  }
31226
31326
  }
31227
31327
  const enabledPatterns = new Set(codacyConfig.patterns.map((p) => p.patternId));
31228
- const configPrefix = configFile ? computeConfigPrefix(configFile, ctx.repositoryRoot) : "";
31229
31328
  const findingsByFile = /* @__PURE__ */ new Map();
31230
31329
  for (const finding of allFindings) {
31231
31330
  if (finding.extra.is_ignored) continue;
@@ -31243,7 +31342,7 @@ var adapter = {
31243
31342
  const lines = await (0, import_tooling.readFileLines)(absoluteFilePath);
31244
31343
  const relPath = import_path.default.relative(ctx.repositoryRoot, absoluteFilePath);
31245
31344
  for (const finding of fileFindings) {
31246
- const ruleId = stripCheckIdPrefix(finding.check_id, configPrefix);
31345
+ const ruleId = finding.check_id;
31247
31346
  const patternId = `${TOOL_ID}_${ruleId}`;
31248
31347
  if (enabledPatterns.size > 0 && !enabledPatterns.has(patternId)) {
31249
31348
  continue;
@@ -31257,7 +31356,7 @@ var adapter = {
31257
31356
  column: finding.start.col,
31258
31357
  endLine: finding.end.line,
31259
31358
  endColumn: finding.end.col,
31260
- message: truncate(finding.extra.message, 250),
31359
+ message: firstSentence(finding.extra.message ?? ""),
31261
31360
  lineContent: (0, import_tooling.getLineContent)(lines, finding.start.line),
31262
31361
  multilineContent: (0, import_tooling.getMultilineContent)(lines, finding.start.line, finding.end.line),
31263
31362
  category: meta.category,
package/dist/metadata.js CHANGED
@@ -255,14 +255,18 @@ var pattern_mappings_default = {
255
255
  // src/metadata.ts
256
256
  var descriptor = {
257
257
  id: "Semgrep",
258
+ shortName: "opengrep",
258
259
  displayName: "Opengrep",
259
260
  preferredVersion: "1.26.0",
261
+ // Canonical `KNOWN_LANGUAGES` names, which is what `init --auto` matches against
262
+ // (`init.ts:882`). The engine's own names differ and must be translated here:
263
+ // Bash/Shell and Vue/Javascript collapse onto one entry each, since `.sh` and
264
+ // `.vue` are already those languages. Guarded by `descriptor-languages.test.ts`.
260
265
  languages: [
261
266
  "Apex",
262
- "Bash",
263
267
  "C",
264
- "C++",
265
- "C#",
268
+ "CPP",
269
+ "CSharp",
266
270
  "Clojure",
267
271
  "Dart",
268
272
  "Dockerfile",
@@ -270,7 +274,7 @@ var descriptor = {
270
274
  "Go",
271
275
  "HTML",
272
276
  "Java",
273
- "JavaScript",
277
+ "Javascript",
274
278
  "JSON",
275
279
  "Kotlin",
276
280
  "Lua",
@@ -283,13 +287,111 @@ var descriptor = {
283
287
  "Swift",
284
288
  "Terraform",
285
289
  "TypeScript",
286
- "Visual Basic",
287
- "Vue",
290
+ "VisualBasic",
288
291
  "XML",
289
292
  "YAML"
290
293
  ],
291
- filePatterns: ["*"],
292
- supportedOS: ["linux", "darwin"],
294
+ // Derived from `languages` above, via `LANGUAGE_CONFIG`, plus the extensions
295
+ // sibling adapters already route for those same languages (`.cjs`, `.hh`,
296
+ // `.jsonc`, `.json5`, `.pyi`, `.dash`, `.ksh`) — `LANGUAGE_CONFIG` is a
297
+ // detection map and does not claim to be exhaustive per tool.
298
+ //
299
+ // This was `["*"]`, which `routeFilesToTool` short-circuits to "every file", so
300
+ // opengrep was handed the whole repository: on one 1774-file repo, 358 of them
301
+ // (20%) were png/jpg/font assets. Opengrep resolves a language by extension and
302
+ // skips what it cannot parse, so findings were never affected — but the paths
303
+ // still cost a realpath+stat each, inflated the reported file count, and ate the
304
+ // 200 KB argv budget, splitting one `opengrep scan` into two.
305
+ //
306
+ // Keep this a superset of `LANGUAGE_CONFIG`'s extensions for every declared
307
+ // language; `opengrep-1/test/file-patterns.test.ts` fails if a language gains one
308
+ // that is not routed here, because a missed extension is a silent coverage loss.
309
+ filePatterns: [
310
+ "**/*.bash",
311
+ "**/*.c",
312
+ "**/*.cc",
313
+ "**/*.cjs",
314
+ "**/*.clj",
315
+ "**/*.cljc",
316
+ "**/*.cljs",
317
+ "**/*.cls",
318
+ "**/*.cpp",
319
+ "**/*.cs",
320
+ "**/*.cxx",
321
+ "**/*.dart",
322
+ "**/*.dash",
323
+ "**/*.dockerfile",
324
+ "**/*.edn",
325
+ "**/*.env",
326
+ "**/*.ex",
327
+ "**/*.exs",
328
+ "**/*.gemspec",
329
+ "**/*.go",
330
+ "**/*.h",
331
+ "**/*.hh",
332
+ "**/*.hpp",
333
+ "**/*.htm",
334
+ "**/*.html",
335
+ "**/*.ino",
336
+ "**/*.java",
337
+ "**/*.jbuilder",
338
+ "**/*.js",
339
+ "**/*.jsm",
340
+ "**/*.json",
341
+ "**/*.json5",
342
+ "**/*.jsonc",
343
+ "**/*.jsx",
344
+ "**/*.ksh",
345
+ "**/*.kt",
346
+ "**/*.kts",
347
+ "**/*.lua",
348
+ "**/*.luau",
349
+ "**/*.mjs",
350
+ "**/*.opal",
351
+ "**/*.php",
352
+ "**/*.podspec",
353
+ "**/*.pom",
354
+ "**/*.py",
355
+ "**/*.pyi",
356
+ "**/*.rake",
357
+ "**/*.rb",
358
+ "**/*.rlib",
359
+ "**/*.rs",
360
+ "**/*.scala",
361
+ "**/*.sh",
362
+ "**/*.swift",
363
+ "**/*.tf",
364
+ "**/*.trigger",
365
+ "**/*.ts",
366
+ "**/*.tsx",
367
+ "**/*.vb",
368
+ "**/*.vue",
369
+ "**/*.wsdl",
370
+ "**/*.xhtml",
371
+ "**/*.xml",
372
+ "**/*.xsl",
373
+ "**/*.yaml",
374
+ "**/*.yml",
375
+ // Source files with no extension. `LANGUAGE_CONFIG.files` is a *detection*
376
+ // list — it is mostly lockfiles (`yarn.lock`, `go.sum`, `poetry.lock`), which
377
+ // identify a language without being source in it — so only the entries
378
+ // opengrep can actually parse are routed here: Dockerfiles, and Ruby's
379
+ // extensionless DSL files.
380
+ "**/Dockerfile",
381
+ "**/Dockerfile.*",
382
+ "**/Berksfile",
383
+ "**/Capfile",
384
+ "**/Cheffile",
385
+ "**/Fastfile",
386
+ "**/Gemfile",
387
+ "**/Guardfile",
388
+ "**/Podfile",
389
+ "**/Rakefile",
390
+ "**/Thorfile",
391
+ "**/Vagrantfile",
392
+ "**/config.ru"
393
+ ],
394
+ supportedOS: ["linux", "darwin", "win32"],
293
395
  supportedArch: ["x64", "arm64"],
294
396
  supportedRunners: ["local", "container"],
295
397
  frameworkMappings: pattern_mappings_default
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codacy/tools-opengrep-1",
3
- "version": "0.23.1",
3
+ "version": "0.25.0",
4
4
  "description": "Opengrep adapter — CLI-mode multi-language SAST scanner",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -10,7 +10,7 @@
10
10
  ],
11
11
  "dependencies": {
12
12
  "yaml": "^2.6.0",
13
- "@codacy/tooling": "0.23.1"
13
+ "@codacy/tooling": "0.25.0"
14
14
  },
15
15
  "license": "MIT",
16
16
  "author": "Codacy <support@codacy.com> (https://www.codacy.com)",