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:

  1. Subject: is the decision about one configured target, a selected graph closure, an output artifact, or one invocation?
  2. Evidence: which facts are required, and does missing evidence mean unknown or a fail-closed rejection?
  3. Enforcement moment: must analysis stop, must an ordinary build fail, or may a later release workflow reject promotion?
  4. 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

PlacementEvidence availableHow it can matterCompleteness limit
ProviderTyped facts deliberately returned for one configured targetSupplies evidence to a rule, aspect, or actionDoes not traverse or enforce anything by itself
AspectProviders and direct rule attributes on each visited configured target; recursively produced aspect state along selected attr_aspects edgesMay fail analysis, produce a report, or register actionsCommand-line application is opt-in, and recursion covers only selected roots, attributes, and configurations
Metadata or report ruleDeclared metadata labels and providers aggregated from named rootsA requested target may fail analysis or build a policy reportAnnotation gaps and invocations that do not request the gate remain outside it
Validation actionDeclared input files materialized for an execution-phase checkFailure makes an ordinary build failThe 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 consumerEmitted invocation events, statuses, named outputs, and retained linked evidenceCan report or reject a later workflow stepIt 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, explicit unknown, 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.

think

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.

key takeaway

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

Answers
Provider
Aspect
Validation action
External BEP/BES consumer

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

0 of 4 answered

Footnotes

  1. Aspects — direct ctx.rule.attr access, selected attr_aspects propagation, and command-line versus rule-propagated application

  2. Common definitions — the nonconfigurable package_metadata attribute and metadata-producing labels

  3. rules_license — legacy separation of package metadata collection from organization-specific license policy

  4. supply-chain — successor metadata and graph collection design, including its current API-stability warning

  5. Rules — providers, actions, the _validation output group, validation skip paths, and --run_validations

  6. Build Event Protocol — event-graph completeness, BES transport, and availability of referenced files