Skip to content

Bug: deep content search discards all ripgrep matches because ./ is treated as a hidden path #359

Description

@rdos14

Bug: deep content search discards all ripgrep matches because ./ is treated as a hidden path

Bug description

Deep content search is enabled and ripgrep is available, but searches inside file contents return no results. Filename searches work correctly.

The issue is caused by the path returned by ripgrep when searching from the current directory. ripgrep returns paths with a ./ prefix, for example:

./FSQ-001-LUM-5-20260627.md

The search route passes that path directly to hiddenFiles.isHiddenPath(). The leading . is interpreted as a hidden-file prefix, so every content match is discarded before the access check and response formatting.

Version and environment

  • NextExplorer: v2.2.7
  • Repository commit tested: bac2e07
  • Deployment: Docker
  • Architecture: Linux ARM64 / aarch64
  • Search configuration:
    • SEARCH_DEEP=true
    • SEARCH_RIPGREP=true
  • ripgrep: 15.1.0

Steps to reproduce

  1. Enable deep search:

    SEARCH_DEEP=true
    SEARCH_RIPGREP=true
    
  2. Mount a directory containing a Markdown file. For example:

    HermesReports/FSQ-001-LUM-5-20260627.md
    
  3. Put a unique term in the file, such as IBOP.

  4. Search through the API:

    GET /api/search?q=IBOP&path=HermesReports
    
  5. Actual result:

    {"items":[]}
  6. Search by filename instead, for example FSQ. Filename results are returned correctly.

Expected behavior

The content search should return the matching Markdown file, including the matched line and line number.

Direct ripgrep evidence

Inside the same container and as the application user, ripgrep finds the match:

./FSQ-001-LUM-5-20260627.md:16:... Saver Sub ... IBOP mecánico ...

The API returns no results because the match is discarded by the hidden-path check.

Root cause

In backend/src/routes/search.js, the content-search path currently performs the hidden-file check on the raw ripgrep path:

const filePath = data.data?.path?.text;
if (!filePath) continue;
if (!includeHiddenFiles && hiddenFiles.isHiddenPath(filePath)) continue;

For a normal file returned as ./file.md, this incorrectly treats the . from ./ as a hidden-file marker.

Proposed fix

Normalize the relative path before checking whether it is hidden:

 const filePath = data.data?.path?.text;
 if (!filePath) continue;
-if (!includeHiddenFiles && hiddenFiles.isHiddenPath(filePath)) continue;
+const normalizedFilePath = filePath.replace(/^\.\/+/, '');
+if (!includeHiddenFiles && hiddenFiles.isHiddenPath(normalizedFilePath)) continue;

An equivalent normalization using the existing path utility would also be acceptable.

Verification

I built an ARM64 test image with this two-line change and reproduced the same API request. It returned:

{
  "items": [
    {
      "name": "FSQ-001-LUM-5-20260627.md",
      "path": "HermesReports",
      "kind": "file",
      "matchLineNumber": 16
    }
  ]
}

The production container now uses a locally built image containing this fix, and its health check remains healthy.

Additional notes

This affects content matches returned by ripgrep. Filename matching is unaffected. The bug is reproducible with Markdown files and should also affect other text-searchable file types.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions