Home

boring / v2

publicBoring-Software-Inc/v2
Overview Code History Branches Pull requestsIssuesInsights Campfire Docs
main
HomeOverview Code PRsIssues

#170 [Detail Bug] Honeypot glob patterns with spaces over-match unrelated paths

open

Opened by detail-app[bot] · 6 days ago

detail-app[bot]opened this issueAuthor· 6 days ago

Sign in to comment.

Linked pull requests

No linked pull requests yet.

Detail Bug Report

app.detail.dev/org_0f605249-55f3-46eb-880c-20ae3e45ae99/…/bug_b096d1ba-a021-4eb2-b3c9-d963b36d2fba

Introduced in #10 by @ripgrim on Jul 22, 2026

Summary

  • Context: packages/sdk/src/glob.ts implements the glob-lite matcher used by the honeypot rule to decide whether a contributor's changed paths touch bait/protected files — both the rule's pass/fail verdict and its "touched" evidence come from this one function.
  • Bug: globMatch uses a literal space character as the temporary placeholder for **, so any literal space in a user-configured pattern is silently converted into the wildcard .* (which also spans /, since JS . matches /).
  • Actual vs. expected: A pattern like docs/My Folder/** matches docs/MyXFolder/secret.yml (it compiles to ^docs/My.*Folder/.*$); it should only match paths under the literal directory docs/My Folder/.
  • Impact: A maintainer-configured honeypot pattern containing a space silently expands the rule's block scope and "touched" evidence to unrelated paths, blocking legitimate change requests that never touched the honey pot. The over-match is strictly fail-closed: because .* is a superset of the literal space, a genuine honeypot touch is never missed — the bug causes false positives on unrelated paths/PRs, never a security gap.

Code with Bug

export function globMatch(pattern: string, path: string): boolean {
	const regex = new RegExp(
		`^${pattern
			.replaceAll(/[.+^${}()|[\]\\]/g, "\\$&")
			.replaceAll("**", " ")      // <-- BUG 🔴 '**' replaced with a literal SPACE marker
			.replaceAll("*", "[^/]*")
			.replaceAll(" ", ".*")}$`,  // <-- BUG 🔴 literal spaces become '.*' wildcard (also spans '/')
	);
	return regex.test(path);
}

Explanation

globMatch constructs a regex by first escaping special regex characters, then translating glob tokens. To handle **, it temporarily replaces ** with a marker, later expanding the marker into .*.

The implementation uses a literal space (" ") as that marker. Because spaces are not escaped and can appear in user-provided patterns (e.g., real folder names like My Folder), the final .replaceAll(" ", ".*") converts every literal space in the pattern into .*. This causes broad over-matching, including across path separators.

Codebase Inconsistency

Earlier honeypot matcher code used a raw NUL byte ("\0") as the ** placeholder, which cannot collide with user input. The regression was introduced when the matcher was moved into the SDK and the placeholder was changed from "\0" to a space while comments claimed it was “moved verbatim”.

Recommended Fix

Use a placeholder that cannot appear in user input (e.g. restore the NUL byte marker or avoid a sentinel entirely):

.replaceAll("**", "\0")
.replaceAll("*", "[^/]*")
.replaceAll("\0", ".*")

Add coverage for patterns containing spaces, e.g.:

  • globMatch("docs/My Folder/**", "docs/MyXFolder/secret.yml") === false
  • globMatch("docs/My Folder/**", "docs/My Folder/file.yml") === true

History

This bug was introduced in commit 2459c9d0 ("phase 3"). That commit extracted the honeypot's glob matcher into a new packages/sdk/src/glob.ts and swapped the ** placeholder from a raw NUL byte ("\0") to a literal space (" ").