6.7.9 Bazel Policy and Compliance Checks
A Bazel policy check is credible only when its placement matches the evidence it can observe and the moment at which a violation must matter. Providers and aspects work with the configured target graph, validation actions can inspect declared files during execution, and BEP/BES consumers see invocation events after Bazel emits them. None of these boundaries, by itself, proves that an organization or legal policy has been satisfied.
Define the evidence contract first
Before choosing an extension point, write down four things:
- Subject: is the decision about one configured target, a selected graph closure, an output artifact, or one invocation?
- Evidence: which facts are required, and does missing evidence mean
unknownor a fail-closed rejection? - Enforcement moment: must analysis stop, must an ordinary build fail, or may a later release workflow reject promotion?
- Completeness boundary: which roots, attributes, configurations, actions, files, or events can bypass the collector?
The result should name its subject, collector and policy versions, evidence
evaluated, missing dimensions, and decision. A bare PASS cannot be reproduced
when the evidence later turns out to have been partial.
Keep evidence separate from interpretation. 6.7.6 Build Provenance and Attestations may authenticate source, builder, invocation, and subject claims, but its presence does not decide whether those claims meet a policy. 6.7.7 Generating SBOMs from Bazel Builds may inventory components, but a valid SBOM does not establish that graph and artifact coverage are complete.
Place the check where its evidence exists
| Placement | Evidence available | How it can matter | Completeness limit |
|---|---|---|---|
| Provider | Typed facts deliberately returned for one configured target | Supplies evidence to a rule, aspect, or action | Does not traverse or enforce anything by itself |
| Aspect | Providers and direct rule attributes on each visited configured target; recursively produced aspect state along selected attr_aspects edges | May fail analysis, produce a report, or register actions | Command-line application is opt-in, and recursion covers only selected roots, attributes, and configurations |
| Metadata or report rule | Declared metadata labels and providers aggregated from named roots | A requested target may fail analysis or build a policy report | Annotation gaps and invocations that do not request the gate remain outside it |
| Validation action | Declared input files materialized for an execution-phase check | Failure makes an ordinary build fail | The ruleset must wire the action; Bazel skips validations for tools, implicit dependencies, and exec-configuration targets, and the flag can disable them |
| External BEP/BES consumer | Emitted invocation events, statuses, named outputs, and retained linked evidence | Can report or reject a later workflow step | It cannot infer arbitrary graph facts or unread artifact bytes, and incomplete event delivery bounds its verdict |
Providers form the typed vocabulary for graph-integrated checks. An aspect can
consume them across many rule kinds without wrapping each target, but its
attr_aspects list selects the dependency attributes along which the aspect
propagates recursively. This does not make other direct attributes invisible:
the implementation on a visited target may still inspect a direct target in
ctx.rule.attr.data. What is absent is an aspect application—and therefore any
aspect-produced transitive state—for the data target when only deps is in
attr_aspects. The implementation must decide whether direct attributes are
evidence, deliberately ignored, or an error.1 Target visibility is a
separate dependency-authorization mechanism; it does not expand an aspect's
traversal or prove evidence coverage.
Application mode is another boundary. A command-line aspect runs only when the
invocation supplies --aspects; omitting the flag omits the check. A
rule-propagated aspect is attached to an attribute of a consuming rule and runs
when that consumer is analyzed, but it is not automatically repository-wide.
For a mandatory release gate, make a dedicated rule-propagated gate or an
equivalent fixed entry point part of a trusted CI invocation, and admit only
artifacts tied to that checked invocation. A command developers may freely run
without the aspect is useful evidence, not mandatory enforcement.
Declarative metadata is an input mechanism, not an enforcer. Bazel's common
package_metadata attribute associates metadata-producing labels with a
target, and package defaults can supply those labels, but some consumer must
still read their providers and make a decision.2 The predecessor
rules_license demonstrates this split between metadata collection and an
organization-specific policy checker. Active work moved to
bazel-contrib/supply-chain, whose public modules are still described as being
under initial development; use it as an architecture reference, not as a
promise that preview APIs are stable.3,4
Validation actions are the most direct build-blocking choice when a check needs
declared file contents. A rule returns their outputs in the special
_validation output group, so Bazel requests them with the target and gives
them normal incremental and caching behavior. The boundary is intentionally not
universal: validation actions do not run when the validated target is reached
as a tool, through an implicit dependency, or in the execution configuration,
and --norun_validations disables them.5 4.8.3 Validation Actions vs Aspects
develops the implementation trade-off between validation actions and aspects.
An explicit report rule makes the release roots and invocation of the gate
reviewable. That is useful when policy evaluation should be a named step, but a
successful ordinary bazel build //app says nothing about a separate
//policy:release_gate that was never requested. The trusted CI or release
boundary must request that entry point and bind its result to promotion.
Choose an external consumer when the subject is invocation-wide or when the
decision requires service-side identity and evidence from several systems. The
BEP is an event graph, and announced child events may remain missing after a
crash or failed upload. BES transports the events but treats their contents as
opaque; files named by a remote record also need a storage path the consumer can
actually read.6 Such a consumer may reject promotion after the build, but it
cannot retroactively prevent actions that already ran. Preserve the delivery
limits from 6.5.8 Complete CI Results with BEP/BES rather than translating a partial stream
into pass.
Prove detection and the bypass
The policy-check-boundaries
snippet makes two limits executable.
Its aspect propagates over deps but not data; its rule also attaches a
validation action to an executable target. With Bazel 9.1.0, these are the five
controls; selected output lines are shown:
$ bazel build //graph:violation
INFO: Build completed successfully
$ bazel build //graph:violation --aspects=//policy:rules.bzl%policy_aspect
Error in fail: policy aspect denied @@//graph:blocked
ERROR: Build did NOT complete successfully
$ bazel build //graph:attribute_bypass --aspects=//policy:rules.bzl%policy_aspect
INFO: Build completed successfully
$ bazel build //validation:denied_tool
policy validation denied validation/denied.policy
ERROR: Build did NOT complete successfully
$ bazel build //validation:uses_denied_tool
INFO: Build completed successfully
The three successful builds are not endorsements of the blocked target. They are
negative controls. The first omits the opt-in command-line aspect. In the second,
the aspect reaches data_bridge through deps; that visited implementation
could inspect its direct data target, but the fixture deliberately ignores
that attribute, and
no aspect-produced state propagates from //graph:blocked. The third reaches the
denied target as an execution-configured tool, where validation actions are
skipped.
A production design must close a bypass with another collector, surface
unknown and fail closed, or accept it only through a governed waiver. The
waiver's owner, scope, compensating control, and expiry belong to the platform
governance process described in H.8.4 Policy Exceptions; merely writing
“out of scope” in check documentation is not an enforcement decision.
Use the same test pattern for every claimed placement:
- seed a violation that the mechanism must detect;
- seed an omitted invocation flag plus one omitted root, propagation edge, configuration, action input, artifact member, or event;
- assert
reject, explicitunknown, or a governed waiver; and - verify that the decision record identifies the exact configured target or immutable artifact subject.
Graph evidence and artifact composition remain different boundaries. An aspect may correctly pass every dependency it traversed while a packaging action adds a vendored archive that no rule exposed through a provider. That is a passing graph check and an unknown artifact decision—not a passing release.
Design: A release gate needs provider metadata from the configured dependency graph and must also inspect the bytes of a generated manifest before upload. Developers may run advisory checks locally, but promotion must never depend on remembering an optional flag. Where would you place collection, byte inspection, and mandatory enforcement?
Reveal
Use an aspect to collect the provider metadata, but attach it to a dedicated gate
through a rule-propagated aspect rather than relying only on --aspects. Have the
gate wire the generated manifest into a validation action for byte inspection.
Then require the gate target in trusted CI and allow promotion only for the
subject and invocation carrying its successful result. Test omission of the
gate, an unpropagated edge, missing manifest input, validation's tool/exec skip
paths, and any approved waiver. An external BEP/BES consumer is useful only if a
later workflow decision also needs delivered invocation evidence; it does not
replace the graph or file collectors.
Choose a Bazel policy-check boundary by evidence visibility and enforcement moment. Providers and metadata carry typed facts; aspects collect them only over selected recursive edges, while their implementation can inspect direct rule attributes. Command-line aspects are opt-in; rule-propagated aspects attach to a consumer but still require that consumer to be analyzed. Validation actions inspect wired files but have documented skip paths; explicit report rules work only when requested; and BEP/BES consumers can gate later workflow using only delivered, readable invocation evidence.
For every claimed boundary, prove one violation and one bypass. Record the
subject, evidence, missing dimensions, collector and policy versions, and
decision. Mandatory enforcement needs a trusted invocation or CI boundary that
cannot silently omit the gate. Missing coverage is unknown, a fail-closed
rejection, or an explicitly governed waiver—never proof of legal or
organizational compliance.
Check your understanding · 4 questions
1.Match each policy-check placement to the boundary it actually provides:
Drag each answer onto the matching prompt, or click an answer and then click a prompt
2.Which successful outcomes still leave a demonstrated policy boundary incomplete? Select all that apply.
Select all that apply
3.A target's validation action rejects a denied policy file when the target is built directly. A consumer then uses the same target as an executable tool, and the consumer build succeeds. What is the correct interpretation?
Select one answer
4.A release policy currently runs only when developers remember to pass a command-line aspect. What change makes it a credible mandatory gate?
Select one answer
Footnotes
-
Aspects — direct
ctx.rule.attraccess, selectedattr_aspectspropagation, and command-line versus rule-propagated application ↩ -
Common definitions — the nonconfigurable
package_metadataattribute and metadata-producing labels ↩ -
rules_license — legacy separation of package metadata collection from organization-specific license policy ↩
-
supply-chain — successor metadata and graph collection design, including its current API-stability warning ↩
-
Rules — providers, actions, the
_validationoutput group, validation skip paths, and--run_validations↩ -
Build Event Protocol — event-graph completeness, BES transport, and availability of referenced files ↩