Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The CLI is the core of the product. The extension is a thin UI layer that calls
## Core Concepts

- **Perimeter**: the top-level policy boundary for a repository
- **File Rule**: a file, folder, or glob pattern protected by an access policy. Standard rules (any member) or guardian rules (guardian/admin only)
- **File Rule**: a file, folder, or glob pattern protected by an access policy. Standard rules (any member) or elevated rules (elevated/admin only)
- **Pass**: a temporary access grant allowing an agent to write to a protected file. Configured with a duration
- **Demarcation**: a registered declaration of what an agent is currently working on, visible to the team via CodeLens and the demarcations panel

Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The CLI is the core of the product. The extension is a thin UI layer that calls
## Core Concepts

- **Perimeter**: the top-level policy boundary for a repository
- **File Rule**: a file, folder, or glob pattern protected by an access policy. Standard rules (any member) or guardian rules (guardian/admin only)
- **File Rule**: a file, folder, or glob pattern protected by an access policy. Standard rules (any member) or elevated rules (elevated/admin only)
- **Pass**: a temporary access grant allowing an agent to write to a protected file. Configured with a duration
- **Demarcation**: a registered declaration of what an agent is currently working on, visible to the team via CodeLens and the demarcations panel

Expand Down
4 changes: 2 additions & 2 deletions cli/cmd/command/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ func runCommandAdd(cmd *cobra.Command, args []string) error {
if r.RuleType == "allow" {
ruleLabel = "allow command rule"
}
if r.RuleAuthority == "guardian" {
ruleLabel += " (guardian)"
if r.RuleAuthority == "elevated" {
ruleLabel += " (elevated)"
}
fmt.Printf("added %s: %s\n", ruleLabel, r.Pattern)
return nil
Expand Down
11 changes: 6 additions & 5 deletions cli/cmd/file/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
)

var (
guardian bool
elevated bool
preventRead bool
allow bool
)
Expand All @@ -30,6 +30,7 @@ var addCmd = &cobra.Command{
func init() {
addCmd.Flags().BoolVar(&preventRead, "prevent-read", false, "Also block agent read access (e.g. for credential files)")
addCmd.Flags().BoolVar(&allow, "allow", false, "Create an allow file rule (permits access, overrides deny rules)")
addCmd.Flags().BoolVar(&elevated, "elevated", false, "Create an elevated-authority rule (requires elevated/admin permissions to remove)")
}

type fileAddResult struct {
Expand Down Expand Up @@ -71,8 +72,8 @@ func runFileAdd(cmd *cobra.Command, args []string) error {
fileAccess = "allow"
}
fileAuthority := "standard"
if guardian {
fileAuthority = "guardian"
if elevated {
fileAuthority = "elevated"
}

user := store.CurrentOSUser()
Expand Down Expand Up @@ -127,8 +128,8 @@ func runFileAdd(cmd *cobra.Command, args []string) error {
if f.FileType == "allow" {
ruleLabel = "allow rule"
}
if f.FileAuthority == "guardian" {
ruleLabel += " (guardian)"
if f.FileAuthority == "elevated" {
ruleLabel += " (elevated)"
}
readLabel := ""
if f.PreventRead {
Expand Down
59 changes: 33 additions & 26 deletions cli/cmd/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,57 +57,59 @@ var hookCmd = &cobra.Command{
// fails open — it returns (true, "") so the hook allows the write and logs the
// failure. This matches Cordon's fail-open design principle.
func buildPolicyChecker() hook.PolicyChecker {
return func(filePath, cwd string) (allowed bool, passID string) {
return func(filePath, cwd string) (allowed bool, passID string, notify bool) {
absRoot, err := resolveRepoRoot(cwd)
if err != nil {
fmt.Fprintf(os.Stderr, "cordon: policy check: resolve repo root: %v\n", err)
return true, "" // fail-open
return true, "", false // fail-open
}

policyDB, err := store.OpenPolicyDB(absRoot)
if err != nil {
fmt.Fprintf(os.Stderr, "cordon: policy check: open policy db: %v\n", err)
return true, "" // fail-open
return true, "", false // fail-open
}
defer policyDB.Close()

if err := store.MigratePolicyDB(policyDB); err != nil {
fmt.Fprintf(os.Stderr, "cordon: policy check: migrate policy db: %v\n", err)
return true, "" // fail-open
return true, "", false // fail-open
}

rule, err := store.FileRuleForPath(policyDB, filePath, absRoot)
if err != nil {
fmt.Fprintf(os.Stderr, "cordon: policy check: file rule lookup: %v\n", err)
return true, "" // fail-open
return true, "", false // fail-open
}
if rule == nil {
// File is not covered by any file rule — allow.
return true, ""
return true, "", false
}

notify = rule.Notify

// File is covered by a file rule. Check for an active pass in the data database.
dataDB, err := store.OpenDataDB(absRoot)
if err != nil {
fmt.Fprintf(os.Stderr, "cordon: policy check: open data db: %v\n", err)
return false, "" // has file rule, data DB unavailable — deny
return false, "", notify // has file rule, data DB unavailable — deny
}
defer dataDB.Close()

if err := store.MigrateDataDB(dataDB); err != nil {
fmt.Fprintf(os.Stderr, "cordon: policy check: migrate data db: %v\n", err)
return false, "" // has file rule, data DB unavailable — deny
return false, "", notify // has file rule, data DB unavailable — deny
}

pass, err := store.ActivePassForPath(dataDB, filePath, absRoot)
if err != nil {
fmt.Fprintf(os.Stderr, "cordon: policy check: pass lookup: %v\n", err)
return false, "" // has file rule, pass lookup failed — deny
return false, "", notify // has file rule, pass lookup failed — deny
}
if pass == nil {
return false, "" // has file rule, no active pass — deny
return false, "", notify // has file rule, no active pass — deny
}
return true, pass.ID // has file rule, active pass — allow
return true, pass.ID, notify // has file rule, active pass — allow
}
}

Expand All @@ -116,43 +118,45 @@ func buildPolicyChecker() hook.PolicyChecker {
//
// Fails open on any infrastructure error.
func buildReadChecker() hook.ReadChecker {
return func(filePath, cwd string) (allowed bool, passID string) {
return func(filePath, cwd string) (allowed bool, passID string, notify bool) {
absRoot, err := resolveRepoRoot(cwd)
if err != nil {
return true, "" // fail-open
return true, "", false // fail-open
}

policyDB, err := store.OpenPolicyDB(absRoot)
if err != nil {
return true, "" // fail-open
return true, "", false // fail-open
}
defer policyDB.Close()

if err := store.MigratePolicyDB(policyDB); err != nil {
return true, "" // fail-open
return true, "", false // fail-open
}

rule, err := store.FileRuleForPath(policyDB, filePath, absRoot)
if err != nil || rule == nil || !rule.PreventRead {
return true, "" // fail-open or not in a prevent-read file rule
return true, "", false // fail-open or not in a prevent-read file rule
}

notify = rule.Notify

// File is in a prevent-read file rule. Check for an active pass.
dataDB, err := store.OpenDataDB(absRoot)
if err != nil {
return false, "" // has file rule, data DB unavailable — deny
return false, "", notify // has file rule, data DB unavailable — deny
}
defer dataDB.Close()

if err := store.MigrateDataDB(dataDB); err != nil {
return false, "" // has file rule, data DB unavailable — deny
return false, "", notify // has file rule, data DB unavailable — deny
}

pass, err := store.ActivePassForPath(dataDB, filePath, absRoot)
if err != nil || pass == nil {
return false, "" // has file rule, no active pass — deny
return false, "", notify // has file rule, no active pass — deny
}
return true, pass.ID
return true, pass.ID, notify
}
}

Expand All @@ -162,32 +166,34 @@ func buildReadChecker() hook.ReadChecker {
//
// Fails open on any infrastructure error.
func buildCommandChecker() hook.CommandChecker {
return func(command, cwd string) (allowed bool, matched *hook.MatchedRule) {
return func(command, cwd string) (allowed bool, matched *hook.MatchedRule, notify bool) {
absRoot, err := resolveRepoRoot(cwd)
if err != nil {
return true, nil // fail-open
return true, nil, false // fail-open
}

policyDB, err := store.OpenPolicyDB(absRoot)
if err != nil {
return true, nil // fail-open
return true, nil, false // fail-open
}
defer policyDB.Close()

if err := store.MigratePolicyDB(policyDB); err != nil {
return true, nil // fail-open
return true, nil, false // fail-open
}

rule, err := store.MatchCommandRule(policyDB, command)
if err != nil || rule == nil {
return true, nil // fail-open or no match
return true, nil, false // fail-open or no match
}

notify = rule.Notify

return false, &hook.MatchedRule{
Pattern: rule.Pattern,
RuleType: rule.RuleType,
RuleAuthority: rule.RuleAuthority,
}
}, notify
}
}

Expand Down Expand Up @@ -221,6 +227,7 @@ func logHookEvent(event *hook.Event) {
OSUser: store.CurrentOSUser(),
Agent: hookAgent,
PassID: event.PassID,
Notify: event.Notify,
}

if err := store.InsertHookLog(db, entry); err != nil {
Expand Down
4 changes: 2 additions & 2 deletions cli/internal/codexpolicy/codexpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ func buildContent(rules []store.FileRule) string {
continue // allow rules permit access; omit from deny list
}
label := ""
if f.FileAuthority == "guardian" {
label = " *(guardian rule — requires guardian/admin pass)*"
if f.FileAuthority == "elevated" {
label = " *(elevated rule — requires elevated/admin pass)*"
}
fmt.Fprintf(&b, "- `%s`%s\n", f.Pattern, label)
}
Expand Down
10 changes: 5 additions & 5 deletions cli/internal/hook/commandrule.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
type MatchedRule struct {
Pattern string
RuleType string // "deny" or "allow"
RuleAuthority string // "standard" or "guardian"
RuleAuthority string // "standard" or "elevated"
}

// CommandChecker checks whether a bash command segment is allowed by command rules.
Expand All @@ -18,11 +18,11 @@ type MatchedRule struct {
// cwd is the agent working directory used to locate the policy database.
//
// Return values:
// - true, nil — command is allowed
// - false, rule — command is blocked; rule describes the matching rule
// - true, nil, false — command is allowed
// - false, rule, notify — command is blocked; rule describes the matching rule
//
// A nil CommandChecker allows all commands (fail-open).
type CommandChecker func(command, cwd string) (allowed bool, matched *MatchedRule)
type CommandChecker func(command, cwd string) (allowed bool, matched *MatchedRule, notify bool)

// builtinRule is a command rule compiled into the binary.
type builtinRule struct {
Expand Down Expand Up @@ -59,7 +59,7 @@ func CheckBuiltinRules(command string) *MatchedRule {
firstDeny = &MatchedRule{
Pattern: r.Pattern,
RuleType: "deny",
RuleAuthority: "guardian",
RuleAuthority: "elevated",
}
}
}
Expand Down
Loading
Loading