least-privilege · git:20260716.fe90232 · 2026-07-16 · sha256 71f864c22125e2c9

least-privilege git:20260716.fe90232A

Immutable. This exact content is served forever at /api/v1/blob/71f864c22125e2c9.

---
name: least-privilege
description: Verify the receipt ids behind a normalized authority-usage summary, compare that evidence with granted scopes, and propose the narrowest grant the evidence supports.
runx:
  category: security
---

# Least Privilege Auditor

Turn granted authority plus attributable observed usage into a bounded
attenuation proposal.

This skill resolves every supporting receipt id through the native `ledger`
runner before it compares a normalized usage summary with the current grant.
Native history proves that the cited receipts exist and records their status and
verification posture; the caller remains responsible for the normalized scope
observations because the history projection does not expose hydrated receipt
bodies. Missing receipt proof defers every scope. The output is a reviewable
attenuation proposal, not an automatic change.

## What this skill does

1. Diff granted authority against receipt-backed usage.
2. Classify each granted scope as `keep`, `narrow`, `remove`, or `defer`.
3. Propose the narrowest grant that still covers observed usage.
4. State residual risk after attenuation.
5. Emit a receipt-quality report a reviewer can apply or reject.

## When to use this skill

- Periodic least-privilege review of a skill, grant, or principal before
  publish, renewal, or maturity promotion.
- After an incident, to identify authority that can be safely removed without
  breaking observed behavior.
- Before expanding distribution of a public skill, to prove its grant is
  minimal against real receipts.
- When a reviewer asks for a scope-by-scope evidence trail, not just a summary.

## When not to use this skill

- To grant new authority. This skill only narrows; widening is a human
  decision.
- When no usable receipt evidence exists. Return `needs_more_evidence` rather
  than guessing a grant down to nothing.
- For secret material handling or credential exposure. Use the appropriate
  secret-leak triage flow instead of scope review.
- When the user asks for automatic permission changes. Produce a proposal and
  stop unless a separate approved delivery lane exists.
- When grant semantics are unknown and cannot be normalized. Return
  `needs_input` with the exact syntax or policy question.

## Procedure

1. Scope the audit target.
   - Identify `subject`, grant source, receipt ids or receipt window, and
     whether receipts are from the same principal or skill version.
   - Gate: if the subject, grant list, or usage source is ambiguous, stop with
     `needs_input`.
   - Evidence expected: subject id or label, granted scope list, receipt ids or
     an explicit statement that no receipts were available.

2. Normalize granted scopes.
   - Parse each scope into verb, resource, path or namespace, conditions, and
     wildcard breadth.
   - Preserve original scope strings. Do not rewrite policy syntax casually.
   - Gate: if a scope cannot be parsed, keep it as `defer` and request the
     missing policy semantics instead of treating it as unused.

3. Build the usage model from attributable evidence.
   - Resolve each supporting receipt id through `ledger read`.
   - Read exercised verbs and resources from the supplied normalized usage
     summary and preserve its receipt references.
   - Count successful use separately from denied or dry-run checks.
   - Do not infer scope usage from a successful high-level task alone; cite the
     receipt step or policy check that exercised the authority.

4. Classify every granted scope.
   - `keep`: at least one observed successful use requires the granted scope as
     written, or a reserved/break-glass policy explicitly requires it.
   - `narrow`: all observed uses fit a strictly smaller verb, resource,
     namespace, condition, or path.
   - `remove`: no observed use, denied check, or documented reserved purpose
     supports the scope.
   - `defer`: evidence is conflicting, receipt attribution is weak, or policy
     semantics are unknown.

5. Propose attenuation.
   - Remove scopes classified as `remove`.
   - Downgrade scopes classified as `narrow` only when every observed use fits
     the narrower grant.
   - Leave `keep` and `defer` scopes unchanged in the proposed grant.
   - Gate: never produce a proposal narrower than the evidence supports. A
     scope used once is used.

6. State residual risk and reviewer action.
   - Name what the proposed grant can still do.
   - Name any broad scope kept despite thin evidence and why.
   - Separate `applyable now` from `needs human policy decision`.

7. Emit receipt expectations.
   - A valid receipt for this skill should record input grant count, receipt
     sources, classification counts, proposed removals or narrowings, stop
     status, and unresolved questions.

## Edge cases and stop conditions

- Empty or unattributable usage evidence: return `needs_more_evidence`; do not
  remove all scopes by default.
- Missing granted scopes: return `needs_input`; there is no baseline to diff.
- Receipt subject mismatch: return `needs_input` with the mismatched subject or
  version.
- Conflicting receipts: classify affected scopes as `defer` and return
  `needs_human` if the conflict changes the proposal.
- Wildcard grants: narrow only to observed resource prefixes when receipt
  coverage is representative; otherwise keep and flag residual risk.
- Reserved, compliance, or break-glass scopes: keep unless the operator
  provides explicit policy authority to remove them.
- Dry-run-only use: do not count as successful exercised authority unless the
  grant exists solely for validation.
- Grant already matches usage: return `no_change` with the evidence summary.
- User asks to hide or omit unused authority: refuse that part and report the
  complete scope diff.

## Output schema

Return a structured report with these fields:

```yaml
status: attenuation_proposed | no_change | needs_more_evidence | needs_input | needs_human | refused
subject: string
evidence:
  receipt_ids: [string]
  receipt_window: string | null
  grant_source: string | null
  limitations: [string]
scope_diff:
  - granted_scope: string
    normalized:
      verb: string | null
      resource: string | null
      conditions: object | null
    observed_use:
      count: number
      verbs: [string]
      resources: [string]
      receipt_refs: [string]
    classification: keep | narrow | remove | defer
    proposal: string | null
    rationale: string
attenuated_grant: [string]
removed_scopes: [string]
narrowed_scopes:
  - from: string
    to: string
kept_scopes: [string]
deferred_scopes: [string]
residual_risk: [string]
reviewer_action: applyable_now | needs_policy_decision | gather_more_receipts | none
receipt_expectations:
  classification_counts: object
  stop_status: string
  unresolved_questions: [string]
```

## Worked example

Input:

```yaml
subject: skills/report-exporter
granted_scopes:
  - drive.files.read:/reports/*
  - drive.files.write:/reports/*
  - drive.files.delete:/reports/*
receipt_ids: [rx_101, rx_102]
usage_summary:
  observed:
    - scope: drive.files.read:/reports/*
      count: 8
      refs: [rx_101:step_3, rx_102:step_2]
    - scope: drive.files.write:/reports/*
      count: 2
      refs: [rx_101:step_6, rx_102:step_5]
```

Output:

```yaml
status: attenuation_proposed
subject: skills/report-exporter
removed_scopes:
  - drive.files.delete:/reports/*
narrowed_scopes: []
kept_scopes:
  - drive.files.read:/reports/*
  - drive.files.write:/reports/*
attenuated_grant:
  - drive.files.read:/reports/*
  - drive.files.write:/reports/*
residual_risk:
  - The skill can still read and write any file under /reports/*.
reviewer_action: applyable_now
```

The delete scope is removable because no cited receipt exercised delete
authority. The read and write scopes stay because each was used at least once.

## Inputs

- `subject` (optional): skill id, grant id, principal, or other label for what
  is being audited.
- `granted_scopes` (required): the current scopes granted to the subject,
  preferably in canonical policy syntax.
- `receipt_ids` (required): exact receipt ids supporting the usage summary.
- `usage_summary` (required): normalized receipt-derived usage with an
  `observed` array of scope, count, and receipt refs.
- `receipt_rows` (optional): native-projection rows for deterministic replay;
  live runs resolve `receipt_ids` from the configured receipt store.
- `objective` (optional): operator intent that focuses the review, such as
  "prepare for public publish" or "post-incident attenuation".
- `policy_notes` (optional): reserved scopes, compliance constraints, or
  human-approved exceptions that affect removal decisions.