Skip to main content

Read-only scope and the deny paths

Objective

Explain how a read-only scope (the one this course models) shapes which actions a tool can decide, and extend the lab policy so a mutating action is denied under that model.

Concept

For this course we model a Herald tool that holds a read-only scope. Treat that as a teaching model, a deliberate containment choice we adopt for the lab, not a stated product tier or a specific OAuth grant. Under this model a Herald tool can answer authorization questions about read-style access (read, query) but is not authorized to decide, or effect, anything that mutates. An agent can ask "may I read this dataset" and get a governed answer; it cannot get the tool to bless a write, an export, or a delete, because in our model the token behind the tool never carried write scope in the first place.

There are two layers of "no" here, and they are worth separating:

  • The scope layer. In our model the tool holds a read-only scope, so a write-style action is out of bounds before any policy runs. The tool simply is not authorized to act on mutations.
  • The policy layer. Even for read-style actions, the policy still enforces the federated conditions from lesson 2. Read-only scope is not read-anything: an active analyst can still be denied a restricted read because the SIEM risk is high or the ITSM ticket is missing.

The lab reflects both layers. The policy defines a set of read actions and only ever allows those; a mutating action falls straight through to the default deny, which is the policy-level echo of the read-only scope. This is the honest way to model the scope in Rego: we do not invent an enforceauth run or a scope flag; we model that the tool's decisions are confined to read-style actions and everything else denies.

Default deny does the heavy lifting. Every path that is not an explicit, fully-satisfied allow is a deny. That is what makes the deny tests the important ones: they prove the policy withholds access when any required signal is missing, and they are what catch a false allow if someone loosens a rule.

Hands-on lab

Open ../lab/policy.rego. It ships as a starter: the two allow rules are stubbed out behind a # TODO(learner) block, so nothing is allowed yet and three tests fail. Your job is to author the allow paths until the suite is green.

Run the suite from the cloned university-labs repo:

git clone https://github.com/EnforceAuth/university-labs.git
cd university-labs
opa test courses/evaluate-with-herald -v

On the starter you will see FAIL: 3/8 (the three allow tests). Implement the TODO in policy.rego:

  1. A non-restricted read rule: allow a read-action, active, authorized subject on a public or internal dataset.
  2. A restricted read rule: the same, but for a restricted dataset also require a low/medium SIEM risk level and an approved ITSM change ticket.

Re-run until you reach PASS: 8/8. Keep iterating until opa test is green. The path for the grader and README is courses/evaluate-with-herald.

Note test_write_action_denied_read_only_scope in ../lab/policy_test.rego: it sends a fully legitimate subject (active, in an authorized group) but with a mutating action, and asserts the decision is deny. If your allow rules are written correctly it stays green, because a mutating action never satisfies the read-action guard. That test is the policy-level stand-in for the read-only scope we model.

Once you are green, extend the lab:

  1. Add an export action to the scenario and write a deny test proving that even an active, authorized analyst is denied export, because it is not a read action.
  2. Add a second read action name your team uses (for example search) to the policy's read-action set, then add one allow test for a search on an internal dataset by an active analyst.
  3. Keep the suite green. If a change lets a mutating action through, a deny test must fail. That is the point: the deny tests are the guardrail on the read-only scope.

Do not add a rule that allows a write or export to pass; that would break the read-only scope we model. The exercise is to widen the read surface, not to break containment.

Check for understanding

  1. Under the read-only scope this course models, an agent asks a Herald tool to approve a data export. What happens, and which layer (scope or policy) is the primary reason?
  2. "Read-only scope means any read is allowed." Why is that wrong, using the restricted-dataset rule as your example?
  3. In the lab, how does default deny make a mutating action fail without any explicit "deny write" rule?