Integrating TruffleHog secret detection patterns#16
Integrating TruffleHog secret detection patterns#16amrelsagaei wants to merge 5 commits intomainfrom
Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
| }, | ||
| "devDependencies": { | ||
| "@caido-community/dev": "^0.1.3", | ||
| "@caido/eslint-config": "^0.9.0", |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
📝 WalkthroughWalkthroughThis PR introduces batch secret pattern searching, comprehensive test infrastructure (ESLint configuration and Vitest), secret pattern catalogs with validation tests, and refactors nullable semantics to use optional/undefined throughout the backend and frontend codebase. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Form
participant Dialog
participant batchSearchStore
participant grepStore
participant grepRepository
participant Backend
User->>Form: Clicks "Search All Secrets"
Form->>batchSearchStore: startBatchSearch(options)
batchSearchStore->>Dialog: showWarningDialog = true
Dialog->>User: Display warning with pattern count
User->>Dialog: Clicks "Start Search"
Dialog->>batchSearchStore: confirmAndStart()
batchSearchStore->>batchSearchStore: Initialize search state<br/>(isSearching=true, reset counts)
loop For each valid SECRET_PATTERN
batchSearchStore->>grepStore: Update pattern & currentPatternName
batchSearchStore->>grepRepository: searchGrepRequests(pattern)
grepRepository->>Backend: grep API call
Backend->>grepRepository: Returns matches
grepRepository->>batchSearchStore: result.data (matches)
alt Matches found
batchSearchStore->>batchSearchStore: Tag matches with pattern.category<br/>in matchCategoryMap
batchSearchStore->>batchSearchStore: Update patternsCompleted,<br/>totalPatterns, activeCategorySet
else Pattern request error
batchSearchStore->>batchSearchStore: Continue to next pattern
end
alt User cancelled
batchSearchStore->>batchSearchStore: Break loop, set cancelled=true
batchSearchStore->>grepRepository: stopGrep()
end
end
batchSearchStore->>grepStore: Set searchTime, clear searching flags
batchSearchStore->>User: Show completion toast<br/>(success/warning based on results)
sequenceDiagram
participant Results as Results.vue
participant grepStore
participant batchSearchStore
participant UI as Match Display
Results->>grepStore: Read filteredAndSortedResults
grepStore->>grepStore: Compute filteredAndSortedResults<br/>from searchResults
alt Batch search active
Results->>batchSearchStore: Read selectedResultCategory
Results->>grepStore: Check categoryFilterOptions
grepStore->>batchSearchStore: For each match: getMatchCategory()
batchSearchStore->>grepStore: Return pattern.category
grepStore->>grepStore: Filter results by category<br/>if selectedResultCategory !== "all"
end
grepStore->>Results: Return filtered + sorted results
Results->>UI: Render matches with category badge<br/>and count ("Found X from Y")
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/backend/src/services/storage.ts (1)
13-24:⚠️ Potential issue | 🟠 MajorDon’t silently suppress storage directory initialization failures.
Line 22 currently swallows all errors, which can hide real filesystem failures and make later errors harder to diagnose.
💡 Proposed fix
constructor(sdk: CaidoBackendSDK) { this.sdk = sdk; this.regexesDir = path.join(this.sdk.meta.path(), "regexes"); - this.ensureRegexesDirectory(); } private async ensureRegexesDirectory(): Promise<void> { - try { - await mkdir(this.regexesDir, { recursive: true }); - } catch { - // Directory might already exist - } + await mkdir(this.regexesDir, { recursive: true }); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/backend/src/services/storage.ts` around lines 13 - 24, The ensureRegexesDirectory method is swallowing all errors which hides real filesystem failures; update ensureRegexesDirectory (used from the constructor) to handle errors explicitly by catching the error into a variable and either rethrowing or logging it with context (including this.regexesDir and the caught error) instead of an empty catch block; ensure the chosen behavior surfaces initialization failures to callers (eg. rethrow after logging) so the constructor/SDK initialization won't silently proceed on real disk errors.
🧹 Nitpick comments (4)
packages/frontend/src/components/results/None.vue (1)
19-29: Optional: extract repeated “no results” condition into a computed flag.The same predicate appears twice, which makes future tweaks easier to miss.
♻️ Suggested refactor
<script setup lang="ts"> import Card from "primevue/card"; +import { computed } from "vue"; import { useGrepStore } from "@/stores"; const store = useGrepStore(); +const hasNoResults = computed( + () => !!store.pattern.trim() && store.results.searchResults?.length === 0, +); </script>- <i - v-if=" - store.pattern.trim() && store.results.searchResults?.length === 0 - " - class="fas fa-search-minus text-gray-400 text-3xl" - ></i> + <i v-if="hasNoResults" class="fas fa-search-minus text-gray-400 text-3xl"></i> <i v-else class="fas fa-search text-gray-400 text-3xl"></i> - <div - v-if=" - store.pattern.trim() && store.results.searchResults?.length === 0 - " - class="text-gray-400 text-md" - > + <div v-if="hasNoResults" class="text-gray-400 text-md"> No results found </div>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/frontend/src/components/results/None.vue` around lines 19 - 29, The template repeats the predicate store.pattern.trim() && store.results.searchResults?.length === 0 twice; add a computed property (e.g., hasNoResults or isNoResults) in the None.vue component that returns that boolean and replace both occurrences in the template with this computed flag to centralize the logic and make future changes simpler; update references to use the new computed name and ensure it imports/uses the same store instance as the template..mise.toml (1)
8-8: Use a deterministic install command in validation.
pnpm ican mask lockfile drift. Prefer a frozen-lockfile install for reproducible validation runs.♻️ Proposed change
[tasks.validate] description = "Run all checks: install, lint, typecheck, test, build" run = """ -pnpm i +pnpm install --frozen-lockfile pnpm lint pnpm typecheck pnpm test pnpm build """🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.mise.toml at line 8, The validation currently runs the non-deterministic command "pnpm i" in .mise.toml; replace it with a frozen-lockfile install (e.g., "pnpm install --frozen-lockfile" or the equivalent CI-safe command for your pnpm version) so the validation fails on lockfile drift and becomes reproducible; update the command string in .mise.toml to the frozen-lockfile variant and ensure any CI/validation runner that references the "pnpm i" entry now uses that updated command.packages/frontend/src/components/search/TransformScriptDialog.vue (1)
24-28: Minor: Avoid callingtrim()twice.
localScript.value.trim()is evaluated twice when the script is non-empty. Consider storing the trimmed value first.♻️ Proposed fix
const save = () => { - grepStore.options.transformScript = - localScript.value.trim() === "" ? undefined : localScript.value.trim(); + const trimmed = localScript.value.trim(); + grepStore.options.transformScript = trimmed === "" ? undefined : trimmed; visible.value = false; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/frontend/src/components/search/TransformScriptDialog.vue` around lines 24 - 28, In save(), avoid calling localScript.value.trim() twice: compute a single const trimmed = localScript.value.trim() and then set grepStore.options.transformScript = trimmed === "" ? undefined : trimmed, finally set visible.value = false; this updates the function save and uses the variables localScript.value, grepStore.options.transformScript, and visible.value.packages/frontend/src/components/search/patterns/PatternsList.vue (1)
38-47: Prefer a stable id for custom-pattern actions.Edit/delete are keyed off
pattern.name, not a stable identifier. If custom names ever collide, both actions will resolve the first match, and the delete path can fall back to"". Thread the custom pattern id throughDisplayPatternand use that here directly.Also applies to: 155-166
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/frontend/src/components/search/patterns/PatternsList.vue` around lines 38 - 47, The edit/delete flows currently look up custom patterns by name (getCustomPatternById and editCustomPattern) which is fragile — change these to use the pattern's stable id: thread the custom pattern id through DisplayPattern, update the functions (e.g., getCustomPatternById -> getCustomPatternById(id: string) to find by p.id, and editCustomPattern to accept id and call patternsStore.openCustomRegexDialog(foundPattern)), and update the corresponding delete path (the code around the other occurrence at lines 155-166) to use the id instead of name so actions always target the exact custom pattern.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@eslint.config.js`:
- Line 22: Add a final newline at the end of the file so the trailing "];" line
ends with a newline to satisfy Prettier/formatting checks; locate the closing
array/semicolon token (the "];" line in eslint.config.js) and ensure the file
ends with a single newline character.
In `@packages/backend/src/services/grep.ts`:
- Line 152: Replace the native RegExp instantiation with the RE2 implementation:
import RE2 from 're2' at the top of packages/backend/src/services/grep.ts and
change the line that creates regex (currently const regex = new RegExp(pattern,
"is")) to use new RE2(pattern, "is") so user-supplied patterns are executed by
re2 (polynomial-time) instead of the native engine; keep the same variable name
(regex) and types where used so the rest of the grep logic (pattern matching
over request/response bodies) continues to work.
In `@packages/frontend/src/components/results/Results.vue`:
- Around line 170-175: The UI flips store.status.isSearching and
store.results.cancelled before the async stopGrep() completes, causing the UI to
show a stopped scan even if the backend stop fails; change stopSearch so it
awaits stopGrep() (or handles its Promise) and only set store.status.isSearching
= false and store.results.cancelled = true after stopGrep() resolves
successfully, and on rejection keep the searching state and surface the error
(so the Stop button remains available for retry); update the stopSearch function
to await stopGrep() and move the state mutations into the success path.
In `@packages/frontend/src/components/search/batch/BatchSearchDialog.vue`:
- Around line 12-18: The Dialog close paths must route through
batchSearchStore.closeWarningDialog so pendingOptions is always cleared; update
the Dialog in BatchSearchDialog.vue to disable built-in closing and bind the
hide event by setting :closable="false" and :close-on-escape="false" and adding
`@hide`="batchSearchStore.closeWarningDialog" (the v-model can remain bound to
batchSearchStore.showWarningDialog) so any user close (button/escape/other)
calls closeWarningDialog() and keeps startBatchSearch() consistent.
In `@packages/frontend/src/components/search/Form.vue`:
- Around line 33-35: handleSearch currently unconditionally calls
batchSearchStore.resetBatchState() and grepStore.searchGrepRequests(), which
allows a normal search to interrupt a running batch (and vice versa) because
both mutate the same grepStore; update handleSearch to first check a running
flag (e.g., grepStore.isRunning or batchSearchStore.isBatchRunning) and return
or disable starting a normal search if a batch run is active, and likewise
modify the batch-start path (the batch button handler referenced around lines
64-70) to check the same running flag and avoid calling
batchSearchStore.resetBatchState() or grepStore.searchGrepRequests() when the
other flow is active; use a single canonical running/mode flag on grepStore (or
batchSearchStore) to coordinate and prevent concurrent starts.
In `@packages/frontend/src/components/search/Search.vue`:
- Around line 8-12: The InputText for the search pattern needs to be disabled
during searches because handlePatternInput currently allows keystrokes to clear
batch search progress; update the InputText element to bind its disabled state
to the combined search statuses (use :disabled="grepStore.status.isSearching ||
batchSearchStore.status.isSearching") and leave handlePatternInput (which sets
grepStore.pattern and clears grepStore.currentPatternName) unchanged so user
input cannot interrupt in-progress searches.
In `@packages/frontend/src/data/secret-patterns.test.ts`:
- Around line 9-13: The testMatch helper ignores each pattern's configured
matchGroups and always prefers capture group 1; update testMatch to consult the
SecretPattern.matchGroups (e.g., use the first configured group index) when
selecting the returned capture: compute an index like const groupIndex =
Array.isArray(pattern.matchGroups) && pattern.matchGroups.length ?
pattern.matchGroups[0] : 1, validate it is a number within match.length, then
return match?.[groupIndex] ?? match?.[0] (falling back to the whole match if the
group is missing).
In `@packages/frontend/src/data/secret-patterns.ts`:
- Around line 166-168: The "JFrog Artifactory Key" detector (pattern
"\\b([a-zA-Z0-9]{64,73})\\b") and several other entries use overly broad,
context-free patterns (bare 64–73 alnum tokens, semver-like \d+\.\d+\.\d+, 44–80
char tokens, 6-digit numbers) that produce many false positives; update those
entries to either remove them or tighten them by requiring provider-specific
context/prefixes (e.g., repository/hostname, header names, known token prefixes)
or replace with stricter regexes that include surrounding keywords, or drop the
entries entirely—apply this change to the "JFrog Artifactory Key" entry and the
other context-free detectors referenced in the file so that only
provider-specific or contextual patterns remain.
In `@packages/frontend/src/stores/batchSearchStore.ts`:
- Around line 27-29: matchCategoryMap is keyed only by match.value which causes
collisions across requests and overlapping patterns; change the map to use a
stable per-match key (e.g., a generated stableMatchId or a composite key like
`${requestId}:${matchIndex}` or an inherent match.id) wherever entries are
created/updated (see matchCategoryMap usage and the code that populates it
around the existing mapping sites and the other occurrences noted). Update the
places that read/write this map (including the logic that sets and reads
selectedResultCategory) to use this stable key so categories are deterministic
across requests and overlaps, and ensure any helpers that produce keys are
consistently used in the three locations currently using match.value as the key.
- Around line 148-153: The cancelSearch() function currently flips
status.cancelled, grepStore.status.isSearching, and grepStore.results.cancelled
before calling grepRepository.stopGrep(); change this to await the asynchronous
stop operation first (await grepRepository.stopGrep()), and only set
grepStore.status.isSearching = false and grepStore.results.cancelled = true
after stopGrep resolves; also keep status.cancelled set immediately (or set it
before awaiting) so intent is recorded, and add a catch around await
grepRepository.stopGrep() to restore/adjust UI state
(grepStore.status.isSearching) or surface an error if stopping fails.
In `@packages/frontend/src/stores/grepStore.ts`:
- Around line 43-45: The code clears currentPatternName.value at the start of a
search which wipes out the label set by patternsStore; remove the line that sets
currentPatternName.value = "" so pattern-driven searches retain their label, but
keep the other initializations (results.searchResults = undefined and
status.isSearching = true) intact; locate and edit the search-starting code
where currentPatternName, results.searchResults, and status.isSearching are set
and delete only the currentPatternName reset.
- Around line 98-107: The truncation logic incorrectly treats exactly 25,000
results as truncated; update the check in the block that assigns
results.searchResults (the newResults/truncatedResults handling) to only treat
cases where newResults.length > 25000 as truncated, then slice to 25,000 and
append the synthetic warning row; ensure the condition uses > 25000 (not >=
25000) so an exact 25,000-result set is left unchanged and does not receive the
warning entry.
---
Outside diff comments:
In `@packages/backend/src/services/storage.ts`:
- Around line 13-24: The ensureRegexesDirectory method is swallowing all errors
which hides real filesystem failures; update ensureRegexesDirectory (used from
the constructor) to handle errors explicitly by catching the error into a
variable and either rethrowing or logging it with context (including
this.regexesDir and the caught error) instead of an empty catch block; ensure
the chosen behavior surfaces initialization failures to callers (eg. rethrow
after logging) so the constructor/SDK initialization won't silently proceed on
real disk errors.
---
Nitpick comments:
In @.mise.toml:
- Line 8: The validation currently runs the non-deterministic command "pnpm i"
in .mise.toml; replace it with a frozen-lockfile install (e.g., "pnpm install
--frozen-lockfile" or the equivalent CI-safe command for your pnpm version) so
the validation fails on lockfile drift and becomes reproducible; update the
command string in .mise.toml to the frozen-lockfile variant and ensure any
CI/validation runner that references the "pnpm i" entry now uses that updated
command.
In `@packages/frontend/src/components/results/None.vue`:
- Around line 19-29: The template repeats the predicate store.pattern.trim() &&
store.results.searchResults?.length === 0 twice; add a computed property (e.g.,
hasNoResults or isNoResults) in the None.vue component that returns that boolean
and replace both occurrences in the template with this computed flag to
centralize the logic and make future changes simpler; update references to use
the new computed name and ensure it imports/uses the same store instance as the
template.
In `@packages/frontend/src/components/search/patterns/PatternsList.vue`:
- Around line 38-47: The edit/delete flows currently look up custom patterns by
name (getCustomPatternById and editCustomPattern) which is fragile — change
these to use the pattern's stable id: thread the custom pattern id through
DisplayPattern, update the functions (e.g., getCustomPatternById ->
getCustomPatternById(id: string) to find by p.id, and editCustomPattern to
accept id and call patternsStore.openCustomRegexDialog(foundPattern)), and
update the corresponding delete path (the code around the other occurrence at
lines 155-166) to use the id instead of name so actions always target the exact
custom pattern.
In `@packages/frontend/src/components/search/TransformScriptDialog.vue`:
- Around line 24-28: In save(), avoid calling localScript.value.trim() twice:
compute a single const trimmed = localScript.value.trim() and then set
grepStore.options.transformScript = trimmed === "" ? undefined : trimmed,
finally set visible.value = false; this updates the function save and uses the
variables localScript.value, grepStore.options.transformScript, and
visible.value.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e922e980-0a33-453b-8154-9a555aa1583b
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (48)
.mise.tomlcaido.config.tseslint.config.jspackage.jsonpackages/backend/src/api/index.tspackages/backend/src/index.tspackages/backend/src/services/grep.test.tspackages/backend/src/services/grep.tspackages/backend/src/services/storage.tspackages/backend/src/types.tspackages/backend/src/utils/grep.tspackages/backend/src/validation/schemas.tspackages/frontend/package.jsonpackages/frontend/src/components/guide/Container.vuepackages/frontend/src/components/index.tspackages/frontend/src/components/results/Container.vuepackages/frontend/src/components/results/MatchViewer.vuepackages/frontend/src/components/results/None.vuepackages/frontend/src/components/results/Results.vuepackages/frontend/src/components/results/Searching.vuepackages/frontend/src/components/search/Container.vuepackages/frontend/src/components/search/Form.vuepackages/frontend/src/components/search/Options.vuepackages/frontend/src/components/search/Search.vuepackages/frontend/src/components/search/TransformScriptDialog.vuepackages/frontend/src/components/search/ai-dialog/AIDialog.vuepackages/frontend/src/components/search/ai-dialog/Container.vuepackages/frontend/src/components/search/batch/BatchSearchDialog.vuepackages/frontend/src/components/search/patterns/Container.vuepackages/frontend/src/components/search/patterns/CustomRegexDialog.vuepackages/frontend/src/components/search/patterns/PatternsList.vuepackages/frontend/src/data/secret-patterns.test.tspackages/frontend/src/data/secret-patterns.tspackages/frontend/src/index.tspackages/frontend/src/plugins/sdk.tspackages/frontend/src/repositories/customRegex.tspackages/frontend/src/repositories/grep.tspackages/frontend/src/stores/aiStore.tspackages/frontend/src/stores/batchSearchStore.tspackages/frontend/src/stores/grepStore.tspackages/frontend/src/stores/index.tspackages/frontend/src/stores/patternsStore.tspackages/frontend/src/types.tspackages/frontend/src/utils/ai.tspackages/frontend/src/utils/clipboard.tspackages/frontend/src/views/App.vuepackages/frontend/vitest.config.tspackages/shared/src/results.ts
| "packages/frontend/vitest.config.ts", | ||
| ], | ||
| }, | ||
| ]; No newline at end of file |
There was a problem hiding this comment.
Fix trailing newline to satisfy Prettier.
Line 22 currently fails formatting checks due to missing final newline.
🧹 Minimal fix
export default [
...defaultConfig({
compat: false,
}),
{
files: ["packages/frontend/**/*.{ts,vue}"],
languageOptions: {
globals: {
...globals.browser,
},
},
},
{
ignores: [
"packages/backend/vitest.config.ts",
"packages/frontend/vitest.config.ts",
],
},
];
+📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ]; | |
| export default [ | |
| ...defaultConfig({ | |
| compat: false, | |
| }), | |
| { | |
| files: ["packages/frontend/**/*.{ts,vue}"], | |
| languageOptions: { | |
| globals: { | |
| ...globals.browser, | |
| }, | |
| }, | |
| }, | |
| { | |
| ignores: [ | |
| "packages/backend/vitest.config.ts", | |
| "packages/frontend/vitest.config.ts", | |
| ], | |
| }, | |
| ]; | |
🧰 Tools
🪛 ESLint
[error] 22-22: Insert ⏎
(prettier/prettier)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@eslint.config.js` at line 22, Add a final newline at the end of the file so
the trailing "];" line ends with a newline to satisfy Prettier/formatting
checks; locate the closing array/semicolon token (the "];" line in
eslint.config.js) and ensure the file ends with a single newline character.
| return { error: "No requests found" }; | ||
| } | ||
|
|
||
| const regex = new RegExp(pattern, "is"); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -n -C3 'new RegExp\(pattern, "is"\)|GrepPatternSchema' packages
fd -HI '^package\.json$' -x rg -n '"(re2|safe-regex|safe-regex2)"' {}Repository: caido-community/data-grep
Length of output: 1995
🏁 Script executed:
# Get the full GrepPatternSchema definition
sed -n '46,70p' packages/backend/src/validation/schemas.ts
# Check the imports and implementation in grep.ts
head -30 packages/backend/src/services/grep.ts
# Search for re2 imports and usage
rg -n "re2|import.*re2" packagesRepository: caido-community/data-grep
Length of output: 1186
🏁 Script executed:
# Check if re2 is actually imported anywhere in the backend
rg -n "import.*re2|require.*re2" packages/backend
# Check the full grep.ts to understand the scope of pattern execution
wc -l packages/backend/src/services/grep.ts
# Look at the extractMatches and buildRegexFilter utilities
cat -n packages/backend/src/utils/grep.ts | head -80Repository: caido-community/data-grep
Length of output: 2412
🏁 Script executed:
# Check the exact usage of regex at line 152 - see what happens after construction
sed -n '145,170p' packages/backend/src/services/grep.ts
# See if there's any usage of the regex object after construction
sed -n '152,250p' packages/backend/src/services/grep.ts | head -50Repository: caido-community/data-grep
Length of output: 1709
Use the re2 library for regex execution instead of native RegExp to prevent ReDoS attacks.
This service compiles user-supplied patterns with native JavaScript RegExp and executes them over request/response bodies. A catastrophic backtracking pattern can hang the grep worker and block the entire operation. The GrepPatternSchema only validates syntax, not safety. Since re2 (^1.21.4) is already in your dependencies, replace new RegExp(pattern, "is") at line 152 with re2 to enforce polynomial-time regex matching.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/backend/src/services/grep.ts` at line 152, Replace the native RegExp
instantiation with the RE2 implementation: import RE2 from 're2' at the top of
packages/backend/src/services/grep.ts and change the line that creates regex
(currently const regex = new RegExp(pattern, "is")) to use new RE2(pattern,
"is") so user-supplied patterns are executed by re2 (polynomial-time) instead of
the native engine; keep the same variable name (regex) and types where used so
the rest of the grep logic (pattern matching over request/response bodies)
continues to work.
| const stopSearch = () => { | ||
| // Update UI immediately so user sees the stop | ||
| store.status.isSearching = false; | ||
| store.results.cancelled = true; | ||
| // Fire-and-forget — backend stop resolves asynchronously | ||
| stopGrep(); |
There was a problem hiding this comment.
Don’t mark the scan cancelled before stopGrep() succeeds.
stopGrep() is async and already reports backend errors. Flipping store.status.isSearching and store.results.cancelled first makes the UI say the scan stopped even when the backend rejects the stop request, and it removes the Stop button before the user can retry.
Suggested fix
-const stopSearch = () => {
- // Update UI immediately so user sees the stop
- store.status.isSearching = false;
- store.results.cancelled = true;
- // Fire-and-forget — backend stop resolves asynchronously
- stopGrep();
+const stopSearch = async () => {
+ const stopped = await stopGrep();
+ if (!stopped) return;
+
+ store.status.isSearching = false;
+ store.results.cancelled = true;
};🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/frontend/src/components/results/Results.vue` around lines 170 - 175,
The UI flips store.status.isSearching and store.results.cancelled before the
async stopGrep() completes, causing the UI to show a stopped scan even if the
backend stop fails; change stopSearch so it awaits stopGrep() (or handles its
Promise) and only set store.status.isSearching = false and
store.results.cancelled = true after stopGrep() resolves successfully, and on
rejection keep the searching state and surface the error (so the Stop button
remains available for retry); update the stopSearch function to await stopGrep()
and move the state mutations into the success path.
| <Dialog | ||
| v-model:visible="batchSearchStore.showWarningDialog" | ||
| modal | ||
| header="Search All Secrets" | ||
| :style="{ width: '500px' }" | ||
| :dismissable-mask="false" | ||
| > |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "BatchSearchDialog.vue" -o -name "batchSearchStore.ts" | head -20Repository: caido-community/data-grep
Length of output: 189
🏁 Script executed:
cat -n ./packages/frontend/src/components/search/batch/BatchSearchDialog.vue | head -50Repository: caido-community/data-grep
Length of output: 2055
🏁 Script executed:
cat -n ./packages/frontend/src/components/search/batch/BatchSearchDialog.vue | tail -20Repository: caido-community/data-grep
Length of output: 673
🏁 Script executed:
cat -n ./packages/frontend/src/stores/batchSearchStore.tsRepository: caido-community/data-grep
Length of output: 6894
🏁 Script executed:
# Let me check PrimeVue Dialog documentation behavior with a web search
# to confirm the default close mechanismsRepository: caido-community/data-grep
Length of output: 51
🌐 Web query:
PrimeVue Dialog component default close button escape key behavior
💡 Result:
PrimeVue Dialog close button / Escape key behavior (default): 1) Close (X) button visibility - The “X” close button is rendered only when the dialog is closable. In the Dialog source, the close button slot/button is wrapped in v-if="closable". - Therefore, if you set :closable="false", the close button is not shown. 2) Escape key behavior - PrimeVue’s Dialog supports closing on Escape via the closeOnEscape prop: pressing Escape calls this.close only when closeOnEscape is true. - From the Dialog documentation’s accessibility keyboard support table: escape “Closes the dialog if closeOnEscape is true.” 3) Interaction between closable=false and Escape - There have been reported issues/edge cases where Escape did not work when closable was false, indicating historical coupling/bug. For example, PrimeVue issue reports state that closeOnEscape was not working when closable is false. - In the component implementation, Escape handling is tied to closeOnEscape, not to closable; however the presence/absence of the close button affects the real-world behavior/focus in those reported cases. Practical answer for your question (“default close button escape key behavior”): - By default, if the dialog is closable (close button visible) and closeOnEscape is enabled, Escape closes the dialog. - If you disable the close button (closable=false), Escape should still close when closeOnEscape=true, but PrimeVue has had bugs/regressions in this exact scenario—so verify on your PrimeVue version, especially around 4.1–4.3 where issues were reported. If you share your PrimeVue version and whether you set closeOnEscape / closable, I can tell you the most likely behavior for that exact version.
Citations:
- 1: https://primevue.org/dialog/
- 2: https://github.com/primefaces/primevue/blob/master/packages/primevue/src/dialog/Dialog.vue
- 3: Dialog: closeOnEscape is not working when closable is false primefaces/primevue#7867
- 4: Dialog:
closeOnEscapeis not working whenclosableis false primefaces/primevue#6922
🏁 Script executed:
# Check PrimeVue version in package.json
grep -A2 '"primevue"' ./package.jsonRepository: caido-community/data-grep
Length of output: 51
🏁 Script executed:
find . -name "package.json" -type f | xargs grep -l "primevue" | head -5Repository: caido-community/data-grep
Length of output: 101
🏁 Script executed:
cat ./packages/frontend/package.json | grep -A5 '"primevue"'Repository: caido-community/data-grep
Length of output: 194
Route every dialog close path through closeWarningDialog().
By default, PrimeVue Dialog has closable=true and closeOnEscape=true. When users close the dialog via the close button or Escape key, only showWarningDialog is updated through the v-model binding. The closeWarningDialog() method is not called, leaving pendingOptions set (see packages/frontend/src/stores/batchSearchStore.ts:155-158). This creates inconsistent state if startBatchSearch() is invoked again. Add :closable="false", :close-on-escape="false", and @hide="batchSearchStore.closeWarningDialog" to ensure all close paths clear pending options.
Suggested fix
<Dialog
v-model:visible="batchSearchStore.showWarningDialog"
modal
header="Search All Secrets"
:style="{ width: '500px' }"
:dismissable-mask="false"
+ :closable="false"
+ :close-on-escape="false"
+ `@hide`="batchSearchStore.closeWarningDialog"
>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/frontend/src/components/search/batch/BatchSearchDialog.vue` around
lines 12 - 18, The Dialog close paths must route through
batchSearchStore.closeWarningDialog so pendingOptions is always cleared; update
the Dialog in BatchSearchDialog.vue to disable built-in closing and bind the
hide event by setting :closable="false" and :close-on-escape="false" and adding
`@hide`="batchSearchStore.closeWarningDialog" (the v-model can remain bound to
batchSearchStore.showWarningDialog) so any user close (button/escape/other)
calls closeWarningDialog() and keeps startBatchSearch() consistent.
| const handleSearch = () => { | ||
| batchSearchStore.resetBatchState(); | ||
| grepStore.searchGrepRequests(); |
There was a problem hiding this comment.
Prevent normal and batch searches from starting over each other.
Both flows mutate the same grepStore state. handleSearch() unconditionally clears batch state and starts a normal grep, while the batch button can still kick off a batch run that reinitializes grepStore for accumulation. If either path is triggered mid-run, results/progress for the active search get overwritten.
Also applies to: 64-70
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/frontend/src/components/search/Form.vue` around lines 33 - 35,
handleSearch currently unconditionally calls batchSearchStore.resetBatchState()
and grepStore.searchGrepRequests(), which allows a normal search to interrupt a
running batch (and vice versa) because both mutate the same grepStore; update
handleSearch to first check a running flag (e.g., grepStore.isRunning or
batchSearchStore.isBatchRunning) and return or disable starting a normal search
if a batch run is active, and likewise modify the batch-start path (the batch
button handler referenced around lines 64-70) to check the same running flag and
avoid calling batchSearchStore.resetBatchState() or
grepStore.searchGrepRequests() when the other flow is active; use a single
canonical running/mode flag on grepStore (or batchSearchStore) to coordinate and
prevent concurrent starts.
| name: "JFrog Artifactory Key", | ||
| pattern: "\\b([a-zA-Z0-9]{64,73})\\b", | ||
| category: "CI/CD & DevOps", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cd packages/frontend && wc -l src/data/secret-patterns.tsRepository: caido-community/data-grep
Length of output: 100
🏁 Script executed:
head -350 packages/frontend/src/data/secret-patterns.ts | tail -200Repository: caido-community/data-grep
Length of output: 5646
🏁 Script executed:
cat -n packages/frontend/src/data/secret-patterns.ts | sed -n '160,175p'Repository: caido-community/data-grep
Length of output: 629
🏁 Script executed:
cat -n packages/frontend/src/data/secret-patterns.ts | sed -n '190,205p'Repository: caido-community/data-grep
Length of output: 529
🏁 Script executed:
cat -n packages/frontend/src/data/secret-patterns.ts | sed -n '265,275p'Repository: caido-community/data-grep
Length of output: 494
🏁 Script executed:
cat -n packages/frontend/src/data/secret-patterns.ts | sed -n '315,330p'Repository: caido-community/data-grep
Length of output: 554
🏁 Script executed:
cat -n packages/frontend/src/stores/batchSearchStore.tsRepository: caido-community/data-grep
Length of output: 6894
Tighten or remove the context-free secret detectors.
These patterns are so broad that they will dominate batch search with false positives: a bare 64–73 char alnum token, semver-like \d+\.\d+\.\d+, any 44–80 char token, and any 6-digit number are all common non-secret text. In this UI, that means "Search All Secrets" will routinely swamp real findings. Please add provider-specific prefixes/context or drop these entries.
Also applies to: lines 197-199, 269-270, 321-323
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/frontend/src/data/secret-patterns.ts` around lines 166 - 168, The
"JFrog Artifactory Key" detector (pattern "\\b([a-zA-Z0-9]{64,73})\\b") and
several other entries use overly broad, context-free patterns (bare 64–73 alnum
tokens, semver-like \d+\.\d+\.\d+, 44–80 char tokens, 6-digit numbers) that
produce many false positives; update those entries to either remove them or
tighten them by requiring provider-specific context/prefixes (e.g.,
repository/hostname, header names, known token prefixes) or replace with
stricter regexes that include surrounding keywords, or drop the entries
entirely—apply this change to the "JFrog Artifactory Key" entry and the other
context-free detectors referenced in the file so that only provider-specific or
contextual patterns remain.
| // Maps match value -> category for filtering results | ||
| const matchCategoryMap = ref(new Map<string, SecretPatternCategory>()); | ||
| const selectedResultCategory = ref<SecretPatternCategory | "all">("all"); |
There was a problem hiding this comment.
Use a stable per-match key for category mapping.
This map is keyed only by match.value, so the same string found in different requests or matched by overlapping patterns will overwrite previous categories. Once that happens, category filtering becomes nondeterministic. Key this by a stable match identity instead of the raw value alone.
🔧 Suggested fix
-import type { GrepOptions } from "shared";
+import type { GrepOptions, MatchResult } from "shared";
@@
- const matchCategoryMap = ref(new Map<string, SecretPatternCategory>());
+ const matchCategoryMap = ref(new Map<string, SecretPatternCategory>());
+
+ const getMatchKey = (match: MatchResult) =>
+ `${match.requestId}:${match.source}:${match.startIndex}:${match.endIndex}:${match.value}`;
@@
- matchCategoryMap.value.set(match.value, pattern.category);
+ matchCategoryMap.value.set(getMatchKey(match), pattern.category);
@@
- function getMatchCategory(value: string): SecretPatternCategory | undefined {
- return matchCategoryMap.value.get(value);
+ function getMatchCategory(
+ match: MatchResult,
+ ): SecretPatternCategory | undefined {
+ return matchCategoryMap.value.get(getMatchKey(match));
}Also applies to: 104-110, 166-168
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/frontend/src/stores/batchSearchStore.ts` around lines 27 - 29,
matchCategoryMap is keyed only by match.value which causes collisions across
requests and overlapping patterns; change the map to use a stable per-match key
(e.g., a generated stableMatchId or a composite key like
`${requestId}:${matchIndex}` or an inherent match.id) wherever entries are
created/updated (see matchCategoryMap usage and the code that populates it
around the existing mapping sites and the other occurrences noted). Update the
places that read/write this map (including the logic that sets and reads
selectedResultCategory) to use this stable key so categories are deterministic
across requests and overlaps, and ensure any helpers that produce keys are
consistently used in the three locations currently using match.value as the key.
| function cancelSearch() { | ||
| status.cancelled = true; | ||
| grepStore.status.isSearching = false; | ||
| grepStore.results.cancelled = true; | ||
| grepRepository.stopGrep(); | ||
| } |
There was a problem hiding this comment.
Await the stop before marking the UI idle.
cancelSearch() flips both stores out of the searching state before the backend confirms the grep has actually stopped. If the stop is slow or fails, the UI looks idle while the next search can still hit “A grep scan is already running”.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/frontend/src/stores/batchSearchStore.ts` around lines 148 - 153, The
cancelSearch() function currently flips status.cancelled,
grepStore.status.isSearching, and grepStore.results.cancelled before calling
grepRepository.stopGrep(); change this to await the asynchronous stop operation
first (await grepRepository.stopGrep()), and only set
grepStore.status.isSearching = false and grepStore.results.cancelled = true
after stopGrep resolves; also keep status.cancelled set immediately (or set it
before awaiting) so intent is recorded, and add a catch around await
grepRepository.stopGrep() to restore/adjust UI state
(grepStore.status.isSearching) or surface an error if stopping fails.
| currentPatternName.value = ""; | ||
| results.searchResults = undefined; | ||
| status.isSearching = true; |
There was a problem hiding this comment.
Don’t clear currentPatternName at search start.
packages/frontend/src/stores/patternsStore.ts sets this immediately before launching a saved/secret pattern search. Resetting it here strips the label from normal pattern-driven searches, so the results view can no longer tell users which pattern produced the matches.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/frontend/src/stores/grepStore.ts` around lines 43 - 45, The code
clears currentPatternName.value at the start of a search which wipes out the
label set by patternsStore; remove the line that sets currentPatternName.value =
"" so pattern-driven searches retain their label, but keep the other
initializations (results.searchResults = undefined and status.isSearching =
true) intact; locate and edit the search-starting code where currentPatternName,
results.searchResults, and status.isSearching are set and delete only the
currentPatternName reset.
| if (newResults.length >= 25000) { | ||
| const truncatedResults = newResults.slice(0, 25000); | ||
| truncatedResults.push({ | ||
| value: "!!! Results truncated to 25K. Export to view more", | ||
| requestId: "", | ||
| source: "request", | ||
| startIndex: 0, | ||
| endIndex: 0, | ||
| }); | ||
| results.searchResults = truncatedResults; |
There was a problem hiding this comment.
Fix the truncation off-by-one.
With >= 25000, an exact 25,000-result search gets marked as truncated and receives the synthetic warning row even though nothing was dropped.
🔧 Suggested fix
- if (newResults.length >= 25000) {
+ if (newResults.length > 25000) {
const truncatedResults = newResults.slice(0, 25000);
truncatedResults.push({
value: "!!! Results truncated to 25K. Export to view more",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (newResults.length >= 25000) { | |
| const truncatedResults = newResults.slice(0, 25000); | |
| truncatedResults.push({ | |
| value: "!!! Results truncated to 25K. Export to view more", | |
| requestId: "", | |
| source: "request", | |
| startIndex: 0, | |
| endIndex: 0, | |
| }); | |
| results.searchResults = truncatedResults; | |
| if (newResults.length > 25000) { | |
| const truncatedResults = newResults.slice(0, 25000); | |
| truncatedResults.push({ | |
| value: "!!! Results truncated to 25K. Export to view more", | |
| requestId: "", | |
| source: "request", | |
| startIndex: 0, | |
| endIndex: 0, | |
| }); | |
| results.searchResults = truncatedResults; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/frontend/src/stores/grepStore.ts` around lines 98 - 107, The
truncation logic incorrectly treats exactly 25,000 results as truncated; update
the check in the block that assigns results.searchResults (the
newResults/truncatedResults handling) to only treat cases where
newResults.length > 25000 as truncated, then slice to 25,000 and append the
synthetic warning row; ensure the condition uses > 25000 (not >= 25000) so an
exact 25,000-result set is left unchanged and does not receive the warning
entry.
Summary by CodeRabbit
Release Notes v1.2.4
New Features
Bug Fixes
Tests
Chores