52 Commits

Author SHA1 Message Date
Stephan Renatus cb54e9c14f runtime: rule labels metadata processing follow-ups (#8613)
 We now parse rego metadata annotations by default.

Rule annotations now support a `labels` field. During policy eval,
labels from all successfully evaluated rules are collected and included
in each decision log entry as a top-level `rule_labels` array. Each
element preserves the label map from one evaluated rule. Exact
duplicates are omitted.

```rego
# METADATA
# labels:
#   severity: low
#   team: platform
allow if input.role == "admin"
```

The resulting decision log entry will contain:

```json
{"rule_labels": [{"severity": "low", "team": "platform"}]}
```

---------

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2026-05-08 15:00:26 +00:00
Stephan Renatus 6f113ba009 ci: golangci-lint bump to v2.12.2 (#8623)
https://github.com/golangci/golangci-lint/releases/tag/v2.12.2

Mostly because I couldn't make sense of the failures in
https://github.com/open-policy-agent/opa/pull/8622

---------

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2026-05-08 13:15:11 +02:00
Stephan Renatus 2cf57ca6d3 introduce rule IDs, include in decision logs and response payloads (#8606)
Rules can now be annotated with a metadata `id` field. When any
metadata `id` annotations are present in the rego (scope: rule), the IDs
of successfully evaluated rules are included in decision log events.
Additionally, the Data API supports a `?id` query parameter to
include evaluated rule IDs directly in the response payload.

```rego
# METADATA
# id: allow-admin
allow if input.role == "admin"
```

Modules containing `id` annotations will have metadata parsing enabled
automatically.

Fixes #2089

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2026-05-06 09:38:34 +02:00
Stephan Renatus 13e9488921 server+topdown+logs: feed arbitrary extra info from Data API to topdown and back (#8570)
Wrapping projects can now attach custom metadata to Data API requests
and have evaluation produce response metadata.

Introduce two distinct metadata paths:

  - Request (incoming) metadata: parsed from extra top-level keys in the request
body, made available to builtins via `BuiltinContext.RequestMetadata`.
    Logged in the decision log under `Custom["request_metadata"]`.

- Response (outgoing) metadata: a separate map (`BuiltinContext.ResponseMetadata`)
    that builtins can populate during evaluation. Only included in the
    API response and decision log (`Custom["response_metadata"]`)
    if non-empty.

In vanilla OPA, no builtins write response metadata, so responses are
unchanged. The request metadata map is only allocated when the request
carries extra fields; the outgoing map is one empty map per request.

To avoid conflicts with future OPA top-level keys, callers should use a
namespaced key: `{"input": {...}, "com.example.opa/md": {...}}`.

```mermaid
flowchart LR
    req["POST /v1/data\n{input, com.example.opa/md}"]
    parse["readInputPostV1"]
    eval["topdown eval"]
    resp["API response"]
    dl["decision log"]

    req --> parse
    parse -- "reqMetadata" --> eval
    parse -- "reqMetadata" --> dl
    eval -- "respMetadata\n(if non-empty)" --> resp
    eval -- "respMetadata\n(if non-empty)" --> dl
    eval -. "BuiltinContext\n.RequestMetadata\n.ResponseMetadata" .-> eval
```

---------

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2026-04-28 12:58:18 +00:00
Stephan Renatus a61146254a server: only avoid chmod on linux (macos/win don't have abstract sockets) (#8549)
Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2026-04-22 10:38:47 +00:00
bakayolo 6e1e935a45 server: Skip chmod for abstract Unix domain sockets (#8536)
Abstract Unix sockets (paths prefixed with @) exist only in the
kernel socket namespace and have no filesystem representation.
Calling os.Chmod on them fails with "no such file or directory".

The --unix-socket-perm flag (added in v0.53.0 via PR #5888) defaults
to "755" and always triggers a chmod on the socket path after the
listener is created. This makes it impossible to use abstract Unix
sockets with OPA >= v0.53.0.

The fix adds a guard to skip chmod when the socket path starts with
"@", matching the existing guard that already skips os.Remove for
abstract sockets a few lines above.





Amp-Thread-ID: https://ampcode.com/threads/T-019d9906-2625-774e-8f1c-a0c288630be4

Signed-off-by: Ben Abderrazak <babderrazak@squareup.com>
Signed-off-by: Ben Apprederisse <bena@squareup.com>
Co-authored-by: Amp <amp@ampcode.com>
2026-04-20 19:36:49 +00:00
Johan Fylling 4e104b0c94 server: Drop HTML index page (#8478)
Fixes: #8477

Signed-off-by: Johan Fylling <johan.dev@fylling.se>
2026-04-16 12:59:28 +02:00
Stephan Renatus e0d66617c3 plugins/rest: various changes re: TLS, *http.Client caching (#8376)
* plugins/rest: cache *http.Client and auth plugin

This will require further changes to cert TLS and token auth methods to
stay compatible with the previous behaviour.

* plugins/rest: configurable re-read interval for TLS cert+key

Defaulting to re-reading all the time, more or less like we did before.

(I write "more or less" because we now do it in `GetClientCertificate()`.)

* plugins/rest: document change (code comments, CHANGELOG)
* plugins/rest: set minimum TLS version where `&tls.Config{}` is used
* plugins/rest: ensure min TLS version and ciphersuites are used

...as  configured with the server.


Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2026-03-17 08:30:57 +01:00
Anders Eknert 037101cd7c Linter configuration cleanup (#8397)
And enable more staticcheck linters. I saw staticcheck failures
mentioned in another PR, so thought I'd check it out.

- `WriteString(fmt.Sprintf)` -> `fmt.Fprintf`
- Rewrite calls to deprecated `*Rule.Path()`
- Don't use `==` to compare `time.Time`
- Use inline ignores over config exclusions of paths
- Remove 'varcheck' ignores as no longer used
- Remove v0 topdown/graphql.go (!)

Signed-off-by: Anders Eknert <anders.eknert@apple.com>
2026-03-06 22:07:35 +00:00
Anders Eknert fc55be83e6 perf: json.patch + interning improvements (#8289)
The `json.patch` built-in is quite versatile, and compared to
patching via e.g. `object.union` et. al. often communicates
intent better, IMO. But while it uses some fairly advanced
logic for complex patch operations, it doesn't perform all that
great on simple ones. This is a first and pretty basic attempt
to improve that somewhat by picking the most low-hangig performance
fruits, like avoiding repeated allocations of temporary term pointers.

The main allocation source is the creation of EditTree's, and this
remains a problem. I have created a sync pool but only managed to
get the outermost edit tree to recycle, as I found it really hard
to track where it's safe to release those created in the deeply
nested calls. Additionally, I managed to trigger stack overflows
trying to recycle child trees, so there seems to be some circular
refs? Or I just did something wrong.

If someone wants to look into this and pick up where
I left, that'd be great!

- Add InternedIntRange for testing, primarily
- Intern keys used in json.patch patches
- Clean up json.X built-in benchmarks
- Reduce allocations in edit tree function
- Avoid using intermediate data structures
  for JSON patches
- Some unrelated interning fixes to reduce noise
  in tests and benchmarks (e.g. do less stuff in
  var inits)

Selected benchmark that I used while working on this:

**Before**
```
BenchmarkJSONPatchAddShallowScalar/object-10-16    147853      8008 ns/op    9667 B/op    206 allocs/op
BenchmarkJSONPatchAddShallowScalar/array-10-16     201704      5889 ns/op    7256 B/op    173 allocs/op
BenchmarkJSONPatchAddShallowScalar/set-10-16       182566      6733 ns/op    8103 B/op    156 allocs/op
```

**After**
```
BenchmarkJSONPatchAddShallowScalar/object-10-16    197414      6066 ns/op    7256 B/op    133 allocs/op
BenchmarkJSONPatchAddShallowScalar/array-10-16     278121      4427 ns/op    5285 B/op    100 allocs/op
BenchmarkJSONPatchAddShallowScalar/set-10-16       233884      4839 ns/op    6243 B/op    113 allocs/op
```

Signed-off-by: Anders Eknert <anders.eknert@apple.com>
2026-02-10 20:57:12 +00:00
Anders Eknert 037c247b73 compiler: Smarter template string rewriting (#8310)
Avoid wrapping expressions in comprehensions if they are known
to be defined. References that can't be undefined does not need
to be wrapped in a comprehension for safety, and as this incurs
a runtime cost, we should avoid it if we can! The goal is of course
to make interpolated strings the most performant option of all!

We're not there yet, as the recursive eval implementation remains
slower than e.g. sprintf or concat. We'll get there next! This PR
ensures that there's at least no runtime penalty passed from compilation
by not rewriting:

- A reference to a rule with default assignment
- A reference to a "constant" rule — single definition, ground value (`pi := 3.14`)
- A plain variable reference (`x`, not `x.y`)

In the case of constant rules, we could go one step further next,
and skip even a local assignment in favor of adding their value
directly to the interpolation, e.g:

```rego
x := 1
r := $"{x} + 2"
```

Would simply be rewritten like:
```rego
internal.template_string(["1" " + 2"], __local0__)
```

But saving that for another day.

Signed-off-by: Anders Eknert <anders.eknert@apple.com>
2026-02-10 12:33:01 +00:00
Stephan Renatus e426fd7c76 run: support --h2c with unix domain socket (UDS)
When `--h2c` is passed, HTTP2 will also be used on the unix domain socket.
Previously, it had no effect on UDS, only on TCP connections.

Fixes #8282.

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2026-02-02 15:47:13 +01:00
Anders Eknert 3e1af3bd6a perf: String() implementations using appenders (#8192)
Some work I did during the holidays as part of improving the performance
of interpolated strings. This change is however not isolated to those, but
updates the `String()` implementation of all AST node types (term values
and policy components). This change also lays the groundwork for migrating
OPA to the `json/v2` package once that's stable. The `json/v2` package
provides low-level functions for zero alloc marshalling via appenders — and
well, here they are. The appenders here should be usable for that purpose with
only a few tweaks needed for the few cases where our `String()` implementations
aren't also valid JSON.

Creating perfectly sized buffers requires knowing the expected length beforehand.
In order to do this, each component now implements not only `encoding.AppendText`
but a new custom `StringLengther` interface, which allows asking any AST node about
its `StringLength()` before `make`ing a buffer of that length.

We could definitely consider adding these to e.g. the `Value` or `Node` interfaces,
but I've left that out of this PR as it's an easy thing to do later should we want
to, and I guess there's always some concerns about changing public interfaces even
when they're not meant to be implemented by external code.

While no `Value` appenders allocate and almost none of the policy appenders do either,
one notable exception is `Module` when there are annotations present, as they are
a bit of a (YAML) special case. It's doable, but as serializing full modules isn't
on a hot path anywhere, I have chosen to defer that work to the future.

Signed-off-by: Anders Eknert <anders.eknert@apple.com>
2026-01-08 10:14:53 +01:00
Ville Vesilehto cace2d3c35 fix(util): enforce gzip decompression limits
Replace blind trust of gzip trailer size with io.LimitReader to
prevent memory exhaustion from forged payloads. Add a regression
test, and refactor to use test cases.

Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
2025-12-16 21:13:26 +01:00
Anders Eknert e03ac2f200 Bump golangci-lint, more gocritic linters (#8052)
- Bump golangci-lint -> 2.6.2
- Fix all `deprecatedComment` "notices should be in a dedicated paragraph, separated from the rest" reports
- Enable `appendCombine` and fix all "appendCombine: can combine chain of X appends into one" notices
- Enable `preferFprint` and fix the few reported issues
- Fix various issues reported only once or twice, like `zeroByteRepeat`

Signed-off-by: Anders Eknert <anders.eknert@apple.com>
2025-11-17 11:08:39 +01:00
Stephan Renatus 9a864c6398 compile: add support for "any value at all", as IS NOT NULL (#7998)
* compile: support "equal to whatever" as "IS NOT NULL"

Fixes https://github.com/open-policy-agent/opa/issues/7996.

This only deals with SQL targets for starters.

This issue also came out of the discussion with @huntkalio.

* compile: expand support for "_ = <unknown>" to prisma

It seems like the translation is pretty straightforward here, so
let's include it.

Reference: https://www.prisma.io/docs/orm/prisma-client/queries/filtering-and-sorting#filter-for-non-null-fields

* docs/compile: mention fragment addition

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2025-10-29 13:38:28 +01:00
Stephan Renatus 39044ba6b5 server/failtracer: don't assume only being fed two-elem calls
This was overly specific before, and quickly failed when fail events had
not been on of the right sort. Now, we're more lenient.

These would happen, as in https://github.com/orgs/open-policy-agent/discussions/722#discussioncomment-14812737,
when a fail event happened that's unrelated to unknowns lookup, but a
valid failure nonetheless. So this will not lead to a hint. Only fail
events of the previously-expected form could yield hints.

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2025-10-29 10:57:41 +01:00
Anders Eknert d3f34a3387 Modernize analyzer fixes (#7965)
Have done this some time in the past, but there was a few
new issues this would highlight now that we're on Go 1.24.

Mostly:
- Use `b.Loop()` in benchmarks
- Use `strings.SplitSeq` where possible
- Remove `omitempty` tag for types that can't be empty

Signed-off-by: Anders Eknert <anders@eknert.com>
2025-10-10 17:51:02 +02:00
Stephan Renatus 85a0e2a28c Compile API: switch to compile annotation key (#7936)
* ast: add compile annotation key

This used to be

```
  custom:
    unknowns: [ ... ]
    mask_rule: ...
```

and now becomes
```
  compile:
    unknowns: [ ... ]
    mask_rule: ...
```

* server: adapt compile handler annotations processing

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2025-09-24 10:53:17 +02:00
Stephan Renatus 4ab2ac0c61 Port Compile API extensions from EOPA (#7887)
* server: port compile API

Also adds e2e tests: These include coverage for ucast in the prisma
setting, and thus require some JS runtime.

* e2e: selectively skip e2e Compile API tests

...for macos runs, and for the go-compat suites.

* server: accept timer_rego_external_resolve_ns metrics with value 0

When running the tests in a loop for a while, I would see values of 0ns
for this metric. However, comparing with its non-zero values, which are
often 41 or 42ns, it seems like this is just not happening in this code
path. So if "almost nothing" actually goes below 1ns, it's OK.

* e2e: split dep-heavy e2e tests into their own go module
* Makefile: export DOCKER_RUNNING (make e2e read it)

---------

Co-authored-by: Philip Conrad <philip@chariot-chaser.net>
Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2025-09-22 12:11:06 +02:00
Charlie Egan 00feaff858 handlers: add thread-safe initialization for gzipPool (#7828)
Fixes race condition in initGzipPool() that was causing go race test failures
when multiple tests run concurrently. The package-level gzipPool variable
was being unsafely reassigned across without synchronization.

RWMutex ensures that the gzipPool is only initialized once.

Signed-off-by: Charlie Egan <charlieegan3@users.noreply.github.com>
2025-08-28 02:54:48 +02:00
Ville Vesilehto f77322b3fb build: bump Go version requirement to 1.24 (#7839)
Go 1.23 is no longer supported as per Go release policy.

Changes:

- Use Go v1.24.6 as the project SDK requirement
- Apply lint fixes for Go 1.24
- Fix "non-constant format string in call" issues as seen in CI.

Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
2025-08-24 09:02:09 +02:00
Charlie Egan 11e52c4df6 v1/plugins: Address race in config access (#7825)
* v1/plugins: Address race in config access

I ran into this race condition on another PR:

https://github.com/open-policy-agent/opa/actions/runs/16655603110/job/47139789057

I have tried to make all manager.Config access thread-safe by adding new
getters for used values. GetConfig is regrettably based on a JSON
roundtrip deep copy of the config. This us used in tests (fine) but also
in the discovery plugin:

https://github.com/open-policy-agent/opa/blob/2d014a89bbbc307d7204817220146ffae992e838/v1/plugins/discovery/discovery.go#L122

getPluginSet is very tightly coupled to the manager.Config and because
of it's dependencies on status and the other plugins packages, it's hard
to break out.

So, for now, I think this is an improvement and worth getting a second
opinion on before more refactoring.

Signed-off-by: Charlie Egan <charlie@styra.com>

* v1/config: Use add Clone to config

This makes the use of the manager's config more thread-safe and
consistent without more API changes.

Signed-off-by: Charlie Egan <charlie@styra.com>

* topdown: Add clone() funcs for config structs

NamedValueCacheConfig.Clone, InterQueryBuiltinValueCacheConfig.Clone and
InterQueryBuiltinCacheConfig.Clone have been added.

All Clone methods return a deep copy of the struct. This is tested for
missed new fields using PopulateAllFields, a generic function that
stuffs structs with values for all fields.

Signed-off-by: Charlie Egan <charlie@styra.com>

* plugins: Clone new config

Signed-off-by: Charlie Egan <charlie@styra.com>

---------

Signed-off-by: Charlie Egan <charlie@styra.com>
2025-08-13 12:24:16 +00:00
Charlie Egan 7046ea87a1 server_test: Make TestCertReloading less verbose (#7823)
This commit reduces the verbosity of the TestCertReloading in the case
where it passes. This test had some debug logging that could be
confusing or noisy when running the test suite.

Signed-off-by: Charlie Egan <charlie@styra.com>
2025-08-05 17:02:50 +01:00
Philip Conrad 8190f997af plugins/logs: Add experimental intermediate results field.
This commit adds an experimental "intermediate results" field to
decision logs, and provides some basic plumbing in the server package
for attaching the intermediate results of an eval to the request
context.

Co-authored-by: Teemu Koponen <koponen@styra.com>
Signed-off-by: Philip Conrad <philip@chariot-chaser.net>
2025-07-24 07:01:55 +02:00
Stephan Renatus 62f48aa185 decisionlogs: add custom fields grab bag
Signed-off-by: Stephan Renatus <stephan@styra.com>
2025-07-23 23:01:37 +02:00
Philip Conrad 5c312800e8 server/authorizer: Allow adding paths to validator. (#7792)
This commit provides an extension mechanism for the server authorizer,
allowing plugins and other server extensions to inform the authorizer
about the methods and paths where it should expect and parse request
bodies.

Signed-off-by: Philip Conrad <philip@chariot-chaser.net>
2025-07-23 20:36:03 +00:00
Philip Conrad 614f3cdac0 server+logging: Add BatchDecisionID field to Decision Logs.
This commit adds a new field to Decision Log entries, allowing batches
of decisions to be correlated together later.

Signed-off-by: Philip Conrad <philip@chariot-chaser.net>
2025-07-23 20:23:25 +02:00
Stephan Renatus e4feab26f8 server+plugins: allow plugins to inject http handler middlewares
Before we had introduced `http.ServeMux` as "the router", we had been
using github.com/gorilla/mux. Using the latter, it was possible to
inject middlewares using the mux's `.Use()` method. This mechanism
allowed global middlewares to be injected from `runtime.Params`, for
example.

With `http.ServeMux`, that's no longer possible. However, it was never
an intentionally supported feature in the first place.

So this commit introduces HTTP handler middlewares as extension points.
It's modelled after `(*plugins.Manager).ExtraRoute()`.

Signed-off-by: Stephan Renatus <stephan@styra.com>
2025-07-23 19:03:51 +02:00
Stephan Renatus 84778e203d server: add hooks wiring + new hooks for inter-query caches
When an plugin http handler or some other mechanism wants to do rego
evaluations, too, it's beneficial to share the caches with the server.

This change introduces two new hook types to allow retrieving those
caches during server startup.

Signed-off-by: Stephan Renatus <stephan@styra.com>
2025-07-15 20:31:16 +02:00
Stephan Renatus ba2e6a021b plugins: allow registering handlerfuncs with name+path
This way, the extra handler functions are still covered by prometheus
metrics and opentelemetry spans.

The previous method of directly registering routes with the router
bypassed the server's handler wrapping.

Signed-off-by: Stephan Renatus <stephan@styra.com>
2025-07-14 18:17:30 +02:00
Anders Eknert dfb4d0bdcc Replace gorilla/mux dependency with http.ServeMux (#7676)
I was curious to see how much work this would entail, and it turned out to
be... some :) Particularly porting some of the features exposed as settings
by gorilla mux, like removing trailing slashes, or escaping `/` in matched
paths.

This change is breaking by necessity, as some public functions previously
accepted arguments straight from the mux library. I don't really see any
way around that if we want to get rid of the dependency. I don't think
that too many external projects use code from the server directly though,
so I'm thinking the impact should be minimal? Happy to hear what others
think.

Signed-off-by: Anders Eknert <anders@styra.com>
2025-06-10 01:47:34 +02:00
Philip Conrad 1c3fa4aa85 v1/server: Fix deferred metrics timers. (#7671)
The /v1/data endpoint's PUT, PATCH, and DELETE methods all reported
incorrect timer information (zeroed out timers). This was caused by the
Timer.Stop() calls being deferred in those method handlers.

The problem is that the timers do not record a time value until you stop
them. So, when the metrics are reported, all of those timers are still
storing their initial zero values.

The fix was manually calling Timer.Stop() right before metrics collection
on each endpoint, similar to how we handle many other endpoints in the
server.

Signed-off-by: Philip Conrad <philip@chariot-chaser.net>
2025-06-09 16:33:35 +00:00
Anders Eknert b1ce92c459 perf: more efficient data/v1 POST handler (#7673)
Since we don't use the HTTP API in Regal, I hadn't looked at this from
a performance POV before, and it was a fun side quest :)

A pretty decent reduction of the baseline cost for the most common
request type, v1/data POST. Changes:

- Add NoOp implementation of `Metrics` for when metrics aren't needed
- Cheaper `metrics.New` instantiation avoiding unnecessary lock
- Avoid cost of decision logging if decision logging isn't enabled
- Only call request.URL.Query() if URL.RawQuery isn't empty, avoiding
  allocating an empty map with each request

While the target was v1/data POST handling, some of the changes above
have a positive impact on all or most handlers. All changes have been
run through the existing tests, and a new benchmark to measure the impact
of the fixes have been added, showing:

```
13063 ns/op	   15162 B/op	     195 allocs/op - main
12796 ns/op	   14856 B/op	     189 allocs/op - avoid r.URL.Query() when no query provided
12541 ns/op	   14483 B/op	     187 allocs/op - decisionLogger.Log early exit if not enabled
12133 ns/op	   14235 B/op	     180 allocs/op - get revisions and init logger only if needed
11098 ns/op	   14207 B/op	     180 allocs/op - more efficient metrics.New() (without locking)
10683 ns/op	   13171 B/op	     169 allocs/op - use no-op metrics implementation when metrics aren't requested
```

Even if the impact is pretty good, it's worth noting that most of the improvements
above are only seen when decision logging is turned off. While this is the common case
for development, it's not in production. Getting the baseline cost down is important still
as there should be no cost paid for features unused.

Signed-off-by: Anders Eknert <anders@styra.com>
2025-06-09 12:33:48 +02:00
Oleg Koshmeliuk d615732efb fix: query url when opa is served not from root path (#7644)
Signed-off-by: Oleg Koshmeliuk <oleg.koshmeliuk@gmail.com>
2025-05-30 12:29:00 -05:00
Sebastian Spaink ec5714ec67 test: mark TestCertReloading as slow (#7624)
Signed-off-by: sspaink <sspaink@styra.com>
2025-05-27 13:42:55 +02:00
Anders Eknert 8ba08ac80c Apply modernize linter fixes (#7599)
Following up on #7566, and now applying the more exciting
modernizations. fmt.Appendf was new to me! But especially
the contains checks are so much better IMHO. I have reviewed
all changes myself and did a few manual changes where it
became obvious that things could be improved a little further.

(the modernize analyzer still has some issues running against
OPA, and I have manually worked around those for the time being)

Signed-off-by: Anders Eknert <anders@styra.com>
2025-05-20 23:12:13 +02:00
Anders Eknert e43ef0a979 Use any in place of interface{} (#7566)
Earlier this evening I tried to run the Go
[modernize](https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/modernize)
analyzer on OPA. That didn't go as planned:

- https://github.com/golang/go/issues/73661
- https://github.com/golang/go/issues/73663

While we wait for that to be fixed, I figured an old-fashioned
search-and-replace across the repo may work for at least the
`interface{}` to `any` conversion. That should help make it easier
to see the other fixes as applied by the modernize tool once it has
had those issues resolved.

Signed-off-by: Anders Eknert <anders@styra.com>
2025-05-12 13:57:48 +02:00
Johan Fylling ad2063247a Merge commit from fork
to disable code injection attacks, where Rego code can be injected into the constructed evaluation query.

See Security Advisory: https://github.com/open-policy-agent/opa/security/advisories/GHSA-6m8w-jc87-6cr7

Fixes: #GHSA-6m8w-jc87-6cr7
2025-05-01 16:26:52 +02:00
Shiqi Yang c8febc8625 feat: add more distributed tracing options (#7421)
Resolves: #7412

Signed-off-by: Shiqi Yang <syang482@bloomberg.net>
2025-03-24 19:29:00 +01:00
Anders Eknert bd5ceb5142 Enable unused-receiver linter (revive) (#7448)
Signed-off-by: Anders Eknert <anders@styra.com>
2025-03-14 11:41:25 +01:00
Anders Eknert afb30d3f9d Add gocritic linter, fix a bunch of stuff (#7377)
Brace yourselves! For there are many touched files here. No changes
in semantics however.

Spent a long time trying out the various optional rules gocritic
provides, and settled for a few of them. There are more I really
like, but that would take many hours to address across the codebase.

Perhaps others find gocritic too pedantic? If so, we can merge the
fixes without enabling the rule.

Signed-off-by: Anders Eknert <anders@styra.com>
2025-02-24 16:28:41 +01:00
Anders Eknert 772a29fc72 perf: use GetByValue to avoid boxing to interface{} (#7372)
And make shorthand types boxed types to avoid allocations at runtime.

Below stats show the difference running Regal lint with all rules disabled
(to better highlight compilation costs). @srenatus will be pleased by this
format, I'm sure.

```
goos: darwin
goarch: arm64
pkg: github.com/styrainc/regal/pkg/linter
cpu: Apple M4 Pro
                       │  main.txt   │              pr.txt               │
                       │   sec/op    │   sec/op     vs base              │
RegalNoEnabledRules-12   190.8m ± 2%   181.8m ± 2%  -4.72% (p=0.002 n=6)

                       │   main.txt   │               pr.txt               │
                       │     B/op     │     B/op      vs base              │
RegalNoEnabledRules-12   474.4Mi ± 0%   471.3Mi ± 0%  -0.65% (p=0.002 n=6)

                       │  main.txt   │              pr.txt               │
                       │  allocs/op  │  allocs/op   vs base              │
RegalNoEnabledRules-12   9.657M ± 0%   9.476M ± 0%  -1.88% (p=0.002 n=6)
```

Signed-off-by: Anders Eknert <anders@styra.com>
2025-02-18 16:56:53 +01:00
Anders Eknert bfd09256b2 Add make test-short task (#7364)
By tagging the worst offenders, we can make use of `go test -short` to
avoid them for a quicker dev-test cycle. Compare:

```
make test       200.69s user 209.81s system 170% cpu 4:01.20 total
```
```
make test-short  70.32s user  29.17s system 350% cpu 28.367 total
```

From 4 minutes down to under 30 seconds. The short tests can either
be run with `go test -short ./...` or `make test-short`.

We'll still run the full test suite in CI, naturally.

Also:
- Remove section on benchmarking that linked to a no longer used resource.

Signed-off-by: Anders Eknert <anders@styra.com>
2025-02-14 14:27:47 +01:00
Brian Cullen 08f98e9527 Add Additional Resource Attributes for OpenTelemetry
Adds the "deployment.environment" resource attribute to those that can
be configured for OpenTelemetry. This was done as some collectors,
including Datadog, require this value to properly classify traces.

Note: the "deployment.environment" attribute is being deprecated in
future versions of the OTel schemas and this may need to be
updated when that library is upgraded.

Fixes #7322

Signed-off-by: Brian Cullen <brianc@kahoot.com>
2025-02-04 11:35:29 -08:00
Anders Eknert 55e87e79ae Add perfsprint linter (#7334)
And update code to conform to the rule.

- Replace unnecessary fmt.Sprintf with string concatenation
- Replace fmt.Sprint with more efficient strconv.Itoa
- Replace static fmt.Errorf calls with more efficient errors.New

Thanks @srenatus for pushing me down this rabbit hole!

Signed-off-by: Anders Eknert <anders@styra.com>
2025-01-31 20:24:05 +01:00
Anders Eknert b942136a4a Use Go 1.22+ int ranges (#7328)
With "some" help from `golangci-lint run --fix ./...`

Signed-off-by: Anders Eknert <anders@styra.com>
2025-01-30 09:57:27 +01:00
Stephan Renatus 4b8a1382d0 topdown+rego+server: allow opt-in for evaluating non-det builtins in PE (#7313)
* topdown+rego: allow opt-in for evaluating non-det builtins in PE

Some use cases of PE, notably generating queries that are to be translated
into filters of some sort (think SQL), require the evaluation of non-deterministic
builtins. This is because the result of the builtin informs what queries are
returned.

Imagine that the user associated with a request is known at PE-time, but we need
extra information from an HTTP API to determine the filters that should be applied.

Previously, that was just impossible to do. Now, we can opt-in to evaluate non-det
builtins during PE from the Rego API.

Note that it would probably make sense to include this in the inlining controls, as
sent to the Compile API. (Considered out of scope for this PR.)

Also note that this will take highest precedence over the `ast.IgnoreDuringPartialEval`
map and the "Nondeterministic" value of the registered builtin. If the new option is
provided, both of these are ignored.

Signed-off-by: Stephan Renatus <stephan@styra.com>

* server+rego: expose nondeterministicBuiltins via inlining controls

With `foo.rego` as

```rego
package ex

include if input.fruits.name == object.get(http.send(input.req).body, input.path, "unknown")
```

the following queries show the difference:

```interactive
$ curl -v http://127.0.0.1:8181/v1/compile \
  -d '{"input": {"req": {"url": "https://httpbin.org/json", "method":"GET"}, "path": ["slideshow", "title"]}, "query": "data.ex.include", "unknowns": ["input.fruits"]}'
{
  "result": {
    "queries": [
      [
        {
          "index": 0,
          "terms": [
            {
              "type": "ref",
              "value": [
                {
                  "type": "var",
                  "value": "http"
                },
                {
                  "type": "string",
                  "value": "send"
                }
              ]
            },
            {
              "type": "object",
              "value": [
                [
                  {
                    "type": "string",
                    "value": "method"
                  },
                  {
                    "type": "string",
                    "value": "GET"
                  }
                ],
                [
                  {
                    "type": "string",
                    "value": "url"
                  },
                  {
                    "type": "string",
                    "value": "https://httpbin.org/json"
                  }
                ]
              ]
            },
            {
              "type": "var",
              "value": "__local0__1"
            }
          ]
        },
        {
          "index": 1,
          "terms": [
            {
              "type": "ref",
              "value": [
                {
                  "type": "var",
                  "value": "eq"
                }
              ]
            },
            {
              "type": "ref",
              "value": [
                {
                  "type": "var",
                  "value": "input"
                },
                {
                  "type": "string",
                  "value": "fruits"
                },
                {
                  "type": "string",
                  "value": "name"
                }
              ]
            },
            {
              "type": "call",
              "value": [
                {
                  "type": "ref",
                  "value": [
                    {
                      "type": "var",
                      "value": "object"
                    },
                    {
                      "type": "string",
                      "value": "get"
                    }
                  ]
                },
                {
                  "type": "ref",
                  "value": [
                    {
                      "type": "var",
                      "value": "__local0__1"
                    },
                    {
                      "type": "string",
                      "value": "body"
                    }
                  ]
                },
                {
                  "type": "array",
                  "value": [
                    {
                      "type": "string",
                      "value": "slideshow"
                    },
                    {
                      "type": "string",
                      "value": "title"
                    }
                  ]
                },
                {
                  "type": "string",
                  "value": "unknown"
                }
              ]
            }
          ]
        }
      ]
    ]
  }
}
```
Here, the builtin call to http.send is preserved.

If we also pass `nondeterminsticBuiltins: true` to the options, we get this:
```interactive
$ curl http://127.0.0.1:8181/v1/compile \
  -d '{"input": {"req": {"url": "https://httpbin.org/json", "method":"GET"}, "path": ["slideshow", "title"]}, "query": "data.ex.include", "unknowns": ["input.fruits"], "options": {"nondeterministicBuiltins": true}}'
{
  "result": {
    "queries": [
      [
        {
          "index": 0,
          "terms": [
            {
              "type": "ref",
              "value": [
                {
                  "type": "var",
                  "value": "eq"
                }
              ]
            },
            {
              "type": "ref",
              "value": [
                {
                  "type": "var",
                  "value": "input"
                },
                {
                  "type": "string",
                  "value": "fruits"
                },
                {
                  "type": "string",
                  "value": "name"
                }
              ]
            },
            {
              "type": "string",
              "value": "Sample Slide Show"
            }
          ]
        }
      ]
    ]
  }
}
```
Here, all args to http.send have been known at PE time and the call was fully
evaluated.

Signed-off-by: Stephan Renatus <stephan@styra.com>

* cmd/eval: expose --nondeterminstic-builtins for new PE control

```interactive
$ echo '{"req": {"url": "https://httpbin.org/json", "method":"GET"}, "path": ["slideshow", "title"]}'| ./opa_darwin_amd64 eval -fpretty -p -I -d foo.rego -u input.fruits data.ex.include
+---------+-------------------------------------------------------------------------------------+
| Query 1 | http.send({"method": "GET", "url": "https://httpbin.org/json"}, __local0__1)        |
|         | input.fruits.name = object.get(__local0__1.body, ["slideshow", "title"], "unknown") |
+---------+-------------------------------------------------------------------------------------+
$ echo '{"req": {"url": "https://httpbin.org/json", "method":"GET"}, "path": ["slideshow", "title"]}'| ./opa_darwin_amd64 eval -fpretty -p -I -d foo.rego -u input.fruits data.ex.include --nondeterminstic-builtins
+---------+-----------------------------------------+
| Query 1 | input.fruits.name = "Sample Slide Show" |
+---------+-----------------------------------------+
```

Signed-off-by: Stephan Renatus <stephan@styra.com>

---------

Signed-off-by: Stephan Renatus <stephan@styra.com>
2025-01-27 13:10:37 +01:00
Anders Eknert b0100a66cd testing: replace reflect.DeepEqual where possible (#7286)
And a few other small fixes in tests. This i not so much
about performance but about choosing the best tool for a
given task :) But that the alternatives are also faster
doesn't hurt either.

Signed-off-by: Anders Eknert <anders@styra.com>
2025-01-21 10:34:53 +01:00
Anders Eknert 622434d113 Add util.Keys and util.KeysSorted (#7285)
And use them to reduce imperative boilerplate throughout
the codebase.

Additionally, replace use of sort.Slice with slices.SortFunc
which is more efficient since it is generic and as such avoids
allocations related to `interface{}` casts.

Also a few performance-related minor fixes, but not the main
theme of this PR.

```
BenchmarkRegalLintingItself-10 before / after
1832684458 ns/op    3453470360 B/op    66125422 allocs/op
1826601250 ns/op    3449619024 B/op    65999164 allocs/op
````

Signed-off-by: Anders Eknert <anders@styra.com>
2025-01-21 08:30:19 +01:00