Skip to content

Lease glob "/prefix/**" strips the boundary slash, authorizing sibling paths (§9.2, §9.3) #72

@nficano

Description

@nficano

Category: security Severity: major
Location: src/Arcp.Runtime/Leases/LeaseManager.cs:154-158
Spec: ARCP v1.1 §9.2, §9.3

What

GlobMatch handles a "…/**" pattern by stripping the last three characters (/**), which removes the trailing path-separator boundary, then does a bare StartsWith. A lease grant of fs.read: ["/workspace/myapp/**"] therefore authorizes any path that merely begins with the string /workspace/myapp, including sibling directories like /workspace/myapp-private/secrets or /workspace/myapp.bak. This is an over-broad authorization / path-confusion vulnerability: the enforcement boundary (§9.3 "MUST be covered by a capability") is wider than the operator intended.

Evidence

if (pattern.EndsWith("/**", StringComparison.Ordinal))
{
    var prefix = pattern[..^3];           // "/workspace/myapp/**" -> "/workspace/myapp"
    return input.StartsWith(prefix, StringComparison.Ordinal);  // matches "/workspace/myapp-private/x"
}

For pattern "/workspace/myapp/**", prefix == "/workspace/myapp" (no trailing /), so GlobMatch("/workspace/myapp-private/secret", "/workspace/myapp/**") returns true.

Proposed fix

In LeaseManager.GlobMatch, for the "/**" case strip only ** (keep the separator) and require the boundary:

if (pattern.EndsWith("/**", StringComparison.Ordinal))
{
    var dir = pattern[..^2];               // keep trailing "/": "/workspace/myapp/"
    return input.StartsWith(dir, StringComparison.Ordinal)
        || input.Equals(dir[..^1], StringComparison.Ordinal); // allow the dir itself
}

Add unit tests asserting "/workspace/myapp/**" matches /workspace/myapp/a and /workspace/myapp but not /workspace/myapp-private/x.

Acceptance criteria

  • GlobMatch("/workspace/myapp-private/secret", "/workspace/myapp/**") returns false.
  • GlobMatch("/workspace/myapp/src/a.cs", "/workspace/myapp/**") returns true.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions