Skip to content

fix(engine): pre-expand brace patterns with path separators in ctx.glob()#422

Merged
rhuanbarreto merged 2 commits into
mainfrom
fix/glob-brace-expansion-421
Jun 22, 2026
Merged

fix(engine): pre-expand brace patterns with path separators in ctx.glob()#422
rhuanbarreto merged 2 commits into
mainfrom
fix/glob-brace-expansion-421

Conversation

@rhuanbarreto

Copy link
Copy Markdown
Contributor

Summary

  • Work around oven-sh/bun#32596: Bun.Glob.scan() silently returns empty results for brace patterns whose alternatives contain path separators (e.g. svc/{src/env.ts,env.ts}), while match() handles them correctly
  • Add expandBracePattern() that detects {alt1,alt2} groups containing / and expands them into separate patterns before scanning — simple braces without / pass through unchanged
  • Applied in ctx.glob(), ctx.grepFiles(), and resolveScopedFiles() so rule authors and ADR files: globs work correctly with brace expansion

Closes #421

Test plan

  • Unit tests for expandBracePattern() — no braces, simple braces (no /), braces with /, multiple alternatives, suffix handling, no-prefix patterns
  • Integration tests for ctx.glob() and ctx.grepFiles() with brace patterns containing path separators
  • Integration test for resolveScopedFiles() with brace patterns containing path separators
  • Regression test that simple braces (no /) still work natively via Bun.Glob
  • bun run validate passes (lint, typecheck, format, 1303 tests, 39/39 ADR rules, knip, build)

https://claude.ai/code/session_01C3VSm9YHmfhZ9kLw2fkLE4

…ob()

Bun.Glob.scan() silently returns empty results for brace patterns whose
alternatives contain path separators (e.g. `svc/{src/env.ts,env.ts}`),
while Bun.Glob.match() handles them correctly. This caused ctx.glob()
and ctx.grepFiles() in .rules.ts files to silently find nothing, making
rule authoring errors very hard to diagnose.

Add expandBracePattern() that detects {alt1,alt2} groups where at least
one alternative contains `/` and expands them into separate patterns
before scanning. Simple braces without `/` pass through unchanged since
Bun.Glob handles those natively.

Applied in ctx.glob(), ctx.grepFiles(), and resolveScopedFiles().

Upstream: oven-sh/bun#32596
Closes #421

Claude-Session: https://claude.ai/code/session_01C3VSm9YHmfhZ9kLw2fkLE4
Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 22, 2026

Copy link
Copy Markdown

Deploying archgate-cli with  Cloudflare Pages  Cloudflare Pages

Latest commit: 66ce3cd
Status: ✅  Deploy successful!
Preview URL: https://9a5be120.archgate-cli.pages.dev
Branch Preview URL: https://fix-glob-brace-expansion-421.archgate-cli.pages.dev

View logs

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Code Coverage

Metric Value
Lines 90.4% (6888 / 7616)
Threshold 90% minimum — met
Platforms Linux + Windows

Full HTML report available in workflow artifacts.

Per-directory breakdown
Directory Coverage Lines
src/commands/ 88.1% 1874 / 2128
src/engine/ 93.3% 1418 / 1520
src/formats/ 100.0% 142 / 142
src/helpers/ 90.3% 3454 / 3826

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR fixes a silent Bun.Glob.scan() failure where brace patterns containing path separators (e.g., svc/{src/env.ts,env.ts}) return zero matches. A new exported expandBracePattern() function is added to runner.ts; it detects brace groups whose alternatives include / and pre-expands them into separate flat patterns. This expansion is applied in the glob() and grepFiles() rule-context methods in runner.ts, and also in resolveScopedFiles() in git-files.ts. Both call sites now deduplicate results via a Set. Unit tests cover all expandBracePattern edge cases, and regression tests validate the fix for ctx.glob, ctx.grepFiles, and resolveScopedFiles. Agent memory notes document the root cause and a related lint false-positive.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: fixing a bug where ctx.glob() fails with brace patterns containing path separators by pre-expanding them.
Description check ✅ Passed The description is directly related to the changeset, explaining the Bun.Glob bug, the expandBracePattern solution, and its three application points with comprehensive testing.
Linked Issues check ✅ Passed The PR fully addresses issue #421 by implementing brace expansion support in ctx.glob(), ctx.grepFiles(), and resolveScopedFiles() to eliminate the silent failure when patterns contain path separators.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing and testing the brace pattern expansion fix; documentation additions and test coverage are all aligned with the stated objectives.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/glob-brace-expansion-421

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/engine/runner.ts`:
- Around line 87-98: The expandBracePattern function returns the pattern
unchanged when the first brace group lacks a forward slash, but this skips
checking for subsequent brace groups that may contain slashes and need
expansion. Instead of the early return when `!alternatives.includes("/")`,
recursively process the suffix by calling expandBracePattern on the suffix
parameter and combine each result with the prefix to properly handle multi-brace
patterns like `a/{b,c}/{d/e,f}` where later brace groups contain slashes even
though earlier ones do not.

In `@tests/engine/expand-brace-pattern.test.ts`:
- Around line 14-46: The test suite for expandBracePattern is missing coverage
for patterns with multiple brace groups at the same nesting level. Add a new
test case within the describe("expandBracePattern", ...) block that tests a
pattern like "a/{b,c}/{d/e,f}" to document the current behavior and serve as a
regression check. This test should verify how the function handles multiple
independent brace groups in a single pattern and will establish baseline
behavior for this scenario.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ebfdba52-ef57-45a7-99ee-31a6b198f799

📥 Commits

Reviewing files that changed from the base of the PR and between aa436e6 and 31bff05.

📒 Files selected for processing (5)
  • .claude/agent-memory/archgate-developer/MEMORY.md
  • src/engine/git-files.ts
  • src/engine/runner.ts
  • tests/engine/expand-brace-pattern.test.ts
  • tests/engine/git-files.test.ts

Comment thread src/engine/runner.ts
Comment thread tests/engine/expand-brace-pattern.test.ts
When the first brace group has no path separators but a later one does
(e.g. `a/{b,c}/{d/e,f}`), the function was returning the pattern
unchanged, silently reintroducing the Bun.Glob.scan() bug. Now
recursively checks the suffix for additional brace groups that need
expansion.

Claude-Session: https://claude.ai/code/session_01C3VSm9YHmfhZ9kLw2fkLE4
Signed-off-by: Rhuan Barreto <rhuan@barreto.work>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/engine/runner.ts (1)

159-163: ⚠️ Potential issue | 🔴 Critical

Add re-validation of expanded patterns to prevent absolute-path bypass.

safeGlob() runs only on the raw input pattern, but expandBracePattern() can produce absolute patterns that were never validated. For example, a pattern like {/absolute/path/*,relative/*} bypasses the initial check because isAbsolute() on the raw string returns false (it starts with {), yet the expansion yields an absolute pattern that Bun.Glob may not sandbox to the project root.

Suggested fix
@@
       const patterns = expandBracePattern(pattern);
       const seen = new Set<string>();

       for (const p of patterns) {
+        safeGlob(p);
         const g = new Bun.Glob(p);
@@
       const globs = expandBracePattern(fileGlob);

@@
       const seen = new Set<string>();
       for (const p of globs) {
+        safeGlob(p);
         const g = new Bun.Glob(p);

Apply at both locations: lines 161 and 207.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/engine/runner.ts` around lines 159 - 163, The expandBracePattern()
function can produce absolute paths that bypass the initial safeGlob()
validation since the raw input pattern may not be absolute (e.g., starting with
a brace character). Add validation of each expanded pattern in the patterns
array after expandBracePattern() is called but before they are used with
Bun.Glob. Check each pattern returned by expandBracePattern() with isAbsolute()
to ensure no absolute paths are processed, and apply this same validation at
both occurrences where expanded patterns are used (around line 161 in the loop
iteration and at line 207).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/engine/runner.ts`:
- Around line 159-163: The expandBracePattern() function can produce absolute
paths that bypass the initial safeGlob() validation since the raw input pattern
may not be absolute (e.g., starting with a brace character). Add validation of
each expanded pattern in the patterns array after expandBracePattern() is called
but before they are used with Bun.Glob. Check each pattern returned by
expandBracePattern() with isAbsolute() to ensure no absolute paths are
processed, and apply this same validation at both occurrences where expanded
patterns are used (around line 161 in the loop iteration and at line 207).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c8502793-518b-4711-86c7-7fe8949f4916

📥 Commits

Reviewing files that changed from the base of the PR and between 31bff05 and 66ce3cd.

📒 Files selected for processing (2)
  • src/engine/runner.ts
  • tests/engine/expand-brace-pattern.test.ts

@rhuanbarreto rhuanbarreto merged commit 6eade7a into main Jun 22, 2026
19 checks passed
@rhuanbarreto rhuanbarreto deleted the fix/glob-brace-expansion-421 branch June 22, 2026 17:44
@archgatebot archgatebot Bot mentioned this pull request Jun 22, 2026
rhuanbarreto added a commit that referenced this pull request Jun 22, 2026
## Summary

- `expandBracePattern()` can produce absolute paths that bypass the
initial `safeGlob()` check (e.g. `{/etc/passwd,foo}` starts with `{` so
`isAbsolute()` returns false, but expands to `/etc/passwd`)
- Add `safeGlob()` validation on each expanded pattern before passing to
`Bun.Glob` in both `ctx.glob()` and `ctx.grepFiles()`
- Add security test verifying that absolute paths produced by brace
expansion are blocked

Follow-up to #422, addressing [CodeRabbit review
feedback](#422 (review)).

## Test plan

- [x] New test: `"blocks absolute paths produced by brace expansion"` in
`runner-security.test.ts`
- [x] All existing tests pass (1305 pass, 0 fail)
- [x] `bun run validate` passes (lint, typecheck, format, test, 39/39
ADR rules, knip, build)

https://claude.ai/code/session_01C3VSm9YHmfhZ9kLw2fkLE4

---------

Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
rhuanbarreto pushed a commit that referenced this pull request Jun 22, 2026
# archgate

## [0.45.6](v0.45.5...v0.45.6)
(2026-06-22)

### Bug Fixes

* **engine:** pre-expand brace patterns with path separators in
ctx.glob() ([#422](#422))
([6eade7a](6eade7a)),
closes [#421](#421)
* **engine:** re-validate expanded brace patterns against safeGlob
([#424](#424))
([09593e7](09593e7)),
closes [#422](#422)

---
This PR was generated with
[simple-release](https://github.com/TrigenSoftware/simple-release).

<details>
<summary>📄 Cheatsheet</summary>
<br>



You can configure the bot's behavior through a pull request comment
using the `!simple-release/set-options` command.

### Command Format

````md
!simple-release/set-options

```json
{
  "bump": {},
  "publish": {}
}
```
````

### Useful Parameters

#### Bump

| Parameter | Type | Description |
|-----------|------|-------------|
| `version` | `string` | Force set specific version |
| `as` | `'major' \| 'minor' \| 'patch' \| 'prerelease'` | Release type
|
| `prerelease` | `string` | Pre-release identifier (e.g., "alpha",
"beta") |
| `firstRelease` | `boolean` | Whether this is the first release |
| `skip` | `boolean` | Skip version bump |
| `byProject` | `Record<string, object>` | Per-project bump options for
monorepos |

#### Publish

| Parameter | Type | Description |
|-----------|------|-------------|
| `skip` | `boolean` | Skip publishing |
| `access` | `'public' \| 'restricted'` | Package access level |
| `tag` | `string` | Tag for npm publication |

### Usage Examples

#### Force specific version

````md
!simple-release/set-options

```json
{
  "bump": {
    "version": "2.0.0"
  }
}
```
````

#### Force major bump

````md
!simple-release/set-options

```json
{
  "bump": {
    "as": "major"
  }
}
```
````

#### Create alpha pre-release

````md
!simple-release/set-options

```json
{
  "bump": {
    "prerelease": "alpha"
  }
}
```
````

#### Publish with specific access and tag

````md
!simple-release/set-options

```json
{
  "bump": {
    "prerelease": "beta"
  },
  "publish": {
    "access": "public",
    "tag": "beta"
  }
}
```
````

### Access Restrictions

The command can only be used by users with permissions:
- repository owner
- organization member
- collaborator

### Notes

- The last comment with `!simple-release/set-options` command takes
priority
- JSON must be valid, otherwise the command will be ignored
- Parameters apply only to the current release execution
- The command can be updated by editing the comment or adding a new one


</details>

<!--
  Please do not edit this comment.
  simple-release-pull-request: true
  simple-release-branch-from: release
  simple-release-branch-to: main
-->

Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ctx.glob() silently returns empty results for brace expansion patterns

1 participant