From Runtime Behavior to Enforced Policy

Learn how Endura derives least-privilege policy from real build behavior, validates it without disruption, and moves pipelines safely from observation into kernel-level enforcement.

September 10, 2026

6 Min Reads

Endura Security

Learn how Endura derives least-privilege policy from real build behavior, validates it without disruption, and moves pipelines safely from observation into kernel-level enforcement.

In the previous article, we connected a GitHub Actions pipeline to Endura, deployed Runtime Sensor, and captured a baseline from a real build. That gave us visibility into what the build actually does.

Now comes the more important question:

Which of those behaviors should the build be allowed to do?

A build may legitimately compile code, execute scripts, read dependencies, write artifacts, and connect to package repositories. But observed behavior is not automatically trusted behavior. A dependency could perform an unexpected network request. A setup script might access a file once and never need it again. A build path might vary between runners.

Endura's policy workflow is designed around that reality. You start with observed behavior, refine it into an intentional security boundary, validate that boundary without disrupting builds, and only then enable enforcement.

The progression is simple:

  1. Derive
  2. Refine
  3. Observe
  4. Enforce

Each stage answers a different question.

Start With What the Build Actually Did

An Endura policy in derive mode can be as simple as:

{
 "mode": "derive"
}

Run representative builds with Runtime Sensor active, then stop the Endura job when the work is complete. Runtime Sensor records the security-relevant behavior it observed and produces the rules needed to represent that activity. Depending on the build, those rules may describe file access, program execution, network connections, process operations, container activity, memory behavior, kernel operations, and other protected actions. This gives you a starting point based on evidence instead of assumptions. But it is only a starting point.

Observed Does Not Mean Approved

This is the most important idea in the policy lifecycle.

Derivation answers: What happened?

It does not answer: What should be allowed?

Endura cannot know whether every action observed during derivation was intentional.

Suppose a build runs npm install and one dependency unexpectedly attempts to connect to an external host. If that happens during derivation, the connection may appear in the derived policy because Runtime Sensor observed it.

That does not mean you should keep it.

The opposite problem also exists. If a legitimate branch did not execute during derivation, the derived policy cannot contain behavior it never saw. For example, perhaps your normal build ran successfully, but the error-handling path never executed. Or your release workflow was not triggered. Or an optional integration was not used. A good derivation process therefore includes more than one happy-path build. Run representative workloads, including optional paths and failure handling where practical.

The goal is not to blindly accept generated policy. The goal is to start policy review with a record of reality.

Step 1: Review the Derived Policy

Before assigning a derived policy to a pipeline, read it.

You are looking for three kinds of rules:

Expected behavior

These are operations the build clearly needs.

Examples might include:

  • a compiler reading source files
  • a build tool writing into the workspace
  • a package manager connecting to an expected registry
  • a test runner executing project binaries
  • a build script creating artifacts

These rules are candidates to remain in the policy.

Unexpected behavior

These deserve investigation.

Examples might include:

  • connections to unfamiliar external addresses
  • reads of SSH keys or credential files
  • unexpected process execution
  • access outside the build workspace
  • privileged container operations
  • raw or packet socket activity
  • kernel operations the build should not require

Do not simply add permissions because the build performed an operation.

Ask why it performed it.

Environment-specific noise

Some behavior is legitimate but too specific to one execution. Temporary filenames, runner paths, dynamically generated directories, individual service IP addresses, and other values may change from build to build. These usually need refinement rather than deletion.

Step 2: Turn Runtime Noise Into Stable Rules

A useful security policy needs to survive normal variation without becoming unnecessarily broad. Endura provides several policy patterns to help do that.

Use Workspace-Aware Paths

CI/CD workspaces often move between runners or change from one execution to another. Instead of hardcoding a path such as: /home/runner/work/my-project/my-project/build/output

Endura policies can use the %workspace% variable: %workspace%/build/output

%workspace% expands to the current Endura job's workspace, which makes the policy portable across builds without granting access to unrelated paths.

Other policy variables can represent values such as the user's home directory, host name, user ID, username, and temporary directory when available. The goal is not to make the rule vague. It is to describe the legitimate resource in a way that remains stable.

Step 3: Consolidate Dynamic Paths Carefully

Build tools often create filenames that include timestamps, hashes, random identifiers, or temporary values.

Imagine derivation produces several similar paths:

/opt/app/tmp/script-1842

/opt/app/tmp/script-7309

/opt/app/tmp/script-9124

You probably do not want a growing policy containing every temporary filename ever generated.

If /opt/app/tmp is the legitimate boundary, the policy can instead allow the stable parent directory.

Endura supports directory-prefix matching for file path arguments, so a rule targeting a directory can cover paths beneath it. This is an important distinction. You are broadening the policy enough to accommodate expected variation, but only within a boundary you have reviewed.

Step 4: Use Brace Expansion Instead of Duplicate Rules

Policies also support brace expansion for related values.

For example, instead of maintaining separate rules for:

/usr/bin/curl|api.example.com|80

/usr/bin/curl|api.example.com|443

/usr/bin/curl|api.example.com|8080

you can express the same intent more cleanly as:

/usr/bin/curl|api.example.com|{80,443,8080}

The same approach can help consolidate executable paths, scripts, ports, and other repeated values. Cleaner policy is easier to understand. And policy that is easier to understand is easier to review safely.

Step 5: Be Deliberate About Network Access

Network behavior deserves particular attention in CI/CD. Build environments frequently hold valuable credentials. A compromised dependency does not need to persist on the runner if it can read a credential and send it somewhere else during the build. Endura can control outbound connections based on the executable, destination, and port.

A rule might permit an expected tool to reach a specific service:

/usr/bin/curl|api.example.com|443

or allow a verified network range: all|140.82.112.0/20|443

Endura can use hostnames in connection rules, although enforcement ultimately operates at the IP layer. CIDR ranges may provide more reliable behavior for services whose addresses change frequently. Derived policies commonly capture specific addresses. Resist the temptation to immediately replace several addresses with a very broad CIDR. First verify that the entire range belongs to the service you intend to trust.

The question should always be: What destination does this build actually need?

Not: What network can I allow so this violation goes away?

Step 6: Treat all as a Decision

Endura policy rules support the value: all

It means exactly what it sounds like. It matches anything in that position. There are cases where broad access may be legitimate. But every use of all should be intentional.

For example:

{
 "ip": {
   "connect": ["all"]
 }
}

allows every covered outbound connection. That might make a policy easy to deploy, but it also removes the network boundary you were trying to establish. The same principle applies to file access, execution, containers, and other operations. Start with the narrowest rule that represents the legitimate behavior you understand. Broaden it when the workload requires it, not simply because broad rules are easier.

Step 7: Account for Behavior You Did Not Derive

Derivation is only as complete as the builds you ran. Before moving forward, think about legitimate paths that may not have been exercised.

Does the pipeline:

  • run differently on pull requests?
  • publish artifacts only from the default branch?
  • perform release signing?
  • run additional tests on scheduled builds?
  • execute cleanup after failures?
  • access a separate package repository in release builds?
  • use a different tool when a cache misses?
  • run database migrations only during deployment?

If the answer is yes, either exercise those paths during derivation or deliberately add the permissions they require. This is one reason moving directly from derive to enforce is risky. You want policy to represent expected behavior across the pipeline, not one successful execution.

Step 8: Move the Policy to Observe Mode

Once you have reviewed and refined the baseline, change the policy mode to:

{
 "mode": "observe"
}

Observe mode evaluates the policy but does not block covered operations.

If the build performs something outside the policy, Endura records a violation and allows the operation to continue. This is where you find out whether the policy you designed matches the behavior of real builds. Run normal workloads. Run pull requests. Run tests. Run builds with and without caches. Run release paths if they behave differently. Let developers work normally. Then review what Endura reports.

A Violation Is a Question

When a violation appears in observe mode, do not immediately treat it as either an attack or a false positive.

Treat it as a question: Why did the build need to do this?

There are usually three possibilities.

The behavior is legitimate

Perhaps a valid workflow branch was missing from derivation. Update the policy with the narrowest rule that permits the behavior.

The behavior is legitimate, but the policy is too specific

Maybe the build wrote to a new temporary filename or reached another approved service address. Refine the existing rule so it represents the stable boundary.

The behavior should not occur

This is the most interesting outcome. The application may not need that network connection. The package manager may not need access to that credential path. The build tool may not need to execute that binary. In that case, do not change the policy. Change the build, dependency, configuration, or workflow instead. This is how observe mode turns policy development into a useful security review rather than a simple allowlist exercise.

Step 9: Manage and Assign the Policy in Team Server

Team Server centralizes security policy management for connected pipelines. To create a policy, open Policies, provide a name, description, and version, then add the policy definition. Once the policy is ready, assign it to the pipeline or pipelines it should govern. Policy owners and administrators can manage policy changes, assignments, versions, and related actions from Team Server.

For the first pipeline, keep the scope simple: one pipeline, one reviewed policy. A representative set of observe-mode builds; the objective is confidence, not rollout speed.

Step 10: Decide When the Policy Is Ready

There is no magic number of observe-mode builds that makes a policy safe. What matters is coverage.

You should have enough representative executions to be comfortable that:

  • normal builds complete without unexplained violations
  • optional workflow paths have been exercised
  • failure handling has been considered
  • expected network destinations are covered
  • file access boundaries are stable
  • legitimate executables are represented
  • dynamic behavior has been generalized carefully
  • every remaining broad rule is intentional

The Endura documentation recommends moving to enforcement after representative observe-mode runs complete without unexplained violations. That last word matters.

Unexplained.

You do not need a policy that never produces information. You need a policy where you understand why the allowed behavior is allowed.

Step 11: Enable Enforcement

When you are satisfied with the policy, change:

{
 "mode": "observe"
}

to:

{
 "mode": "enforce"
}

The meaning of the policy has now changed. In observe mode, an operation outside policy creates a violation. In enforce mode, a covered operation without a matching rule is denied. Endura policies are deny-by-default, so omitted covered operations are not implicitly permitted. Runtime Sensor evaluates security-relevant Linux operations at the kernel boundary using eBPF. In enforce mode, unauthorized covered behavior is blocked before it completes. A policy violation can therefore stop the offending operation and cause the build to fail rather than simply producing an alert after the activity has already occurred. That is the point where the behavioral baseline becomes an actual security boundary.

What Enforcement Changes

Consider a pipeline whose policy permits:

  • execution of its normal build toolchain
  • reads from required source and dependency locations
  • writes to the workspace and artifact directory
  • outbound HTTPS access to approved package and source services

Now imagine compromised code attempts to:

  • read an SSH private key
  • connect to an unfamiliar external address
  • execute an unexpected binary
  • access the Docker socket
  • attach to another process
  • create a raw network socket
  • load an eBPF program
  • escalate privileges

If the relevant operation is covered by Endura and no policy rule allows it, enforcement denies it. The build does not need a known malware signature. The package does not need to appear on a blocklist. Someone does not need to identify the dependency as malicious first. The behavior is outside the boundary. That is enough.

Keep Reviewing After Enforcement

Enforcement is not the end of policy management: build systems change, dependencies change, toolchains change, cloud providers change infrastructure, developers add workflows, etc.

When legitimate behavior changes, an enforced policy may begin producing violations. Continue reviewing them. The difference is that once you have reached enforcement, unexpected behavior does not automatically get the benefit of the doubt. Endura blocks the covered operation until policy permits it. That creates a useful forcing function. Instead of silently expanding what a pipeline can do over time, changes to the runtime boundary become visible and reviewable.

Common Mistakes to Avoid

Deriving Once and Immediately Enforcing

A single successful build rarely exercises every legitimate branch. Use derivation to start the policy, not to finish it.

Keeping Everything That Was Derived

If behavior looks strange, investigate it. Derivation records what happened, including behavior you may not want to permit.

Using Broad Rules to Eliminate Violations

A policy that allows everything will certainly produce fewer violations. It will also provide very little protection. Use broad rules only when they reflect an intentional security decision.

Overgeneralizing Network Ranges

Several observed IP addresses do not automatically justify access to an entire subnet. Verify ownership and required service ranges before broadening network rules.

Ignoring Failure and Release Paths

A policy based only on normal development builds may break when the pipeline encounters an error or performs a release. Include meaningful alternate paths in validation.

Treating Every Violation as Noise

A violation means runtime behavior fell outside the boundary you defined. Sometimes the correct response is to change the policy. Sometimes the correct response is to change the build. That distinction is where much of the security value comes from.

What a Good Endura Policy Looks Like

There is no universal Endura policy.

A Node.js application, Rust project, container build, kernel package, and infrastructure deployment all behave differently. That is exactly why behavior-based derivation is useful. A good policy is not the shortest policy possible. It is not the policy with the most rules either. A good policy is one your team can explain.

You should be able to look at the important permissions and understand:

  • why that executable runs
  • why that process accesses that path
  • why that network destination is required
  • why that container capability exists
  • why that privileged operation is necessary

If nobody can explain a permission, that permission deserves another look.

From Visibility to Control

The progression from derive to enforce is designed to make least privilege practical in environments that are often too complex to model by hand. That process gives you something more useful than a static description of your pipeline. It gives you an operating boundary based on how the pipeline actually behaves. And once that boundary is enforced at the kernel, software running inside the build no longer gets unlimited freedom simply because it made it into the pipeline.

It gets exactly the access the build requires, and nothing more.

Ready to put this into practice?

See how Endura applies runtime security to your CI/CD environment.
const next = await fetch("https://www.endurasecurity.com/next");
Black and white grid pattern with black dots at the intersections, forming a repeating checkered design.