Repository: finos/git-proxy

Issue:
Blocked string literals in commitConfig.diff.block.literals incorrectly handled as RegExp
**Describe the bug**
scanDiff is handling string literals that should be blocked in diffs from the configuration as RegExp without escaping them:

https://github.com/finos/git-proxy/blob/292ecadf3794afb253cbd1ee0e8f73a9e00f0028/src/proxy/processors/push-action/scanDiff.ts#L81

The same is NOT true of the handling for literals in commit messages, where a lowercase literal comparison is made via the string `includes` function:

https://github.com/finos/git-proxy/blob/292ecadf3794afb253cbd1ee0e8f73a9e00f0028/src/proxy/processors/push-action/checkCommitMessages.ts#L29

A property for patterns is already provided so these literals do not need to be / should not be interpreted as Reg Exp.

**To Reproduce**
Steps to reproduce the behavior:

1. Configure an array of blocked string literals in the config's `commitConfig.diff.block.literals` 
    - for testing purposes they should be interpretable as a regex that does not match the literal string that it defines, e.g.: 
    - `a{3}`  will not match "a{3}" but will match "aaa"
    - `./some/path` - will not parse as a regex and should throw an error
    -  `^something` will not match "^something" or "more^something" but will match "something"
3. Create a commit whose diff contains the phrases that should be detected.
4. Push into the proxy and check whether the push is automatically blocked due to the literal matches. 

**Expected behavior**
`commitConfig.diff.block.literals` are matched as literals, rather than RegExp

Discussion:
@kriswest: Fixing this may be as simple as escaping the literal (i.e. `match: new RegExp(RegExp.escape(literal), 'gi'),`, but I was getting type errors for that for some reason...@kriswest: The fix *is* as simple as that if you are in node 24, until then there is a lib to use (that was adopted into es2025 to provide this feature)

Solution patch:
diff --git a/.gitignore b/.gitignore
index 1937e19d..c6076f1a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -270,5 +270,8 @@ website/.docusaurus
 # Jetbrains IDE
 .idea
 
+# VS COde IDE
+.vscode/settings.json
+
 # Generated from testing
 /test/fixtures/test-package/package-lock.json
diff --git a/package-lock.json b/package-lock.json
index 3a3df5b8..4e2549c3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24,6 +24,7 @@
         "cors": "^2.8.5",
         "diff2html": "^3.4.52",
         "env-paths": "^3.0.0",
+        "escape-string-regexp": "^5.0.0",
         "express": "^4.21.2",
         "express-http-proxy": "^2.1.2",
         "express-rate-limit": "^8.1.0",
@@ -5245,11 +5246,13 @@
       "license": "MIT"
     },
     "node_modules/escape-string-regexp": {
-      "version": "1.0.5",
-      "dev": true,
+      "version": "5.0.0",
       "license": "MIT",
       "engines": {
-        "node": ">=0.8.0"
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/eslint": {
@@ -5918,6 +5921,14 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/figures/node_modules/escape-string-regexp": {
+      "version": "1.0.5",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
     "node_modules/file-entry-cache": {
       "version": "8.0.0",
       "dev": true,
diff --git a/package.json b/package.json
index d6bebacc..6b4e9dbf 100644
--- a/package.json
+++ b/package.json
@@ -55,6 +55,7 @@
     "cors": "^2.8.5",
     "diff2html": "^3.4.52",
     "env-paths": "^3.0.0",
+    "escape-string-regexp": "^5.0.0",
     "express": "^4.21.2",
     "express-http-proxy": "^2.1.2",
     "express-rate-limit": "^8.1.0",
@@ -108,8 +109,8 @@
     "@types/passport-local": "^1.0.38",
     "@types/react-dom": "^17.0.26",
     "@types/react-html-parser": "^2.0.7",
-    "@types/validator": "^13.15.3",
     "@types/sinon": "^17.0.4",
+    "@types/validator": "^13.15.3",
     "@types/yargs": "^17.0.33",
     "@vitejs/plugin-react": "^4.7.0",
     "chai": "^4.5.0",
diff --git a/src/proxy/processors/push-action/scanDiff.ts b/src/proxy/processors/push-action/scanDiff.ts
index b1e3152b..56f3ddc1 100644
--- a/src/proxy/processors/push-action/scanDiff.ts
+++ b/src/proxy/processors/push-action/scanDiff.ts
@@ -1,6 +1,7 @@
 import { Action, Step } from '../../actions';
 import { getCommitConfig, getPrivateOrganizations } from '../../../config';
 import parseDiff, { File } from 'parse-diff';
+import escapeStringRegexp from 'escape-string-regexp';
 
 const commitConfig = getCommitConfig();
 const privateOrganizations = getPrivateOrganizations();
@@ -75,10 +76,11 @@ const combineMatches = (organization: string) => {
       : Object.entries(commitConfig?.diff?.block?.providers ?? []);
 
   // Combine all matches (literals, patterns)
+
   const combinedMatches = [
     ...blockedLiterals.map((literal) => ({
       type: BLOCK_TYPE.LITERAL,
-      match: new RegExp(literal, 'gi'),
+      match: new RegExp(escapeStringRegexp(literal), 'gi'), //TODO: swap out escapeStringRegexp() for RegExp.escape() when we require node 24
     })),
     ...blockedPatterns.map((pattern) => ({
       type: BLOCK_TYPE.PATTERN,



Test patch:
diff --git a/test/processors/scanDiff.test.js b/test/processors/scanDiff.test.js
index bd8afd99..28d94938 100644
--- a/test/processors/scanDiff.test.js
+++ b/test/processors/scanDiff.test.js
@@ -53,14 +53,15 @@ index 8b97e49..de18d43 100644
  Project to test gitproxy
 +AKIAIOSFODNN7EXAMPLE
 +AKIAIOSFODNN8EXAMPLE
-+blockedTestLiteral
++emdedded_blocked.Te$t.Literal?
 `;
 };
 describe('Scan commit diff...', async () => {
   privateOrganizations[0] = 'private-org-test';
   commitConfig.diff = {
     block: {
-      literals: ['blockedTestLiteral'],
+      //n.b. the example literal includes special chars that would be interpreted as RegEx if not escaped properly
+      literals: ['blocked.Te$t.Literal?'],
       patterns: [],
       providers: {
         'AWS (Amazon Web Services) Access Key ID':