libexpr/primops: reject __json in structured attrs derivations

In structured attrs derivations, __json is used to serialize the `env`
attribute into a JSON object.

If the derivation enables structured attributes AND specify its own
`__json`, one of the field will take precedence (the reader can have fun
by guessing which one).

To reduce underspecification, we disallow `__json` in structured
attributes derivations.

Fixes #380.

Change-Id: I51c3b2af1ff9449471ba81d4c72df6a7a263eef7
Signed-off-by: Raito Bezarius <raito@lix.systems>
This commit is contained in:
Raito Bezarius
2026-03-01 19:05:54 +00:00
parent 12cee5cb22
commit 74300fd0a7
4 changed files with 47 additions and 0 deletions
@@ -0,0 +1,19 @@
---
synopsis: "Reject `__json` in structured attributes derivations"
cls: [5286]
issues: [fj#380]
category: "Improvements"
credits: [raito]
---
In structured attributes derivations, `__json` is used internally to store the
JSON representation of the `env` attribute field that users can set.
Unfortunately, a user can set `__json` *and* enable structured attributes,
resulting in a broken derivation from a semantic point of view.
As no user can benefit from setting `__json` *and* enable structured attributes,
we disallow that possibility and throw an error from now on.
This is not seen as a breaking change because there's no user code that can
benefit from this behavior, hence, it's an improvement to user experience.
+8
View File
@@ -903,6 +903,14 @@ drvName, Bindings * attrs, Value & v)
"attribute passed to builtins.derivationStrict"
))
{
if (attrs->get(state.ctx.symbols.sym___json)) {
state.ctx.errors
.make<EvalError>(
"a `__json` attribute cannot be passed to builtins.derivationStrict when structured "
"attributes are enabled"
)
.debugThrow();
}
jsonObject = JSON::object();
}
+1
View File
@@ -217,6 +217,7 @@ public:
const Symbol sym___structuredAttrs = create("__structuredAttrs");
const Symbol sym___contentAddressed = create("__contentAddressed");
const Symbol sym___impure = create("__impure");
const Symbol sym___json = create("__json");
/* Derivation */
const Symbol sym_outPath = create("outPath");
const Symbol sym_drvPath = create("drvPath");
@@ -0,0 +1,19 @@
from testlib.fixtures.nix import Nix
evil_drv = """
builtins.derivation {
name = "foo";
builder = "/bin/sh";
system = builtins.currentSystem;
__structuredAttrs = true;
__json.hello = "1";
env.hello = "2";
}
"""
def test_improper_structured_attrs_drv(nix: Nix):
result = nix.nix_build(["--expr", evil_drv]).run().expect(1)
assert "a `__json` attribute cannot be passed" in result.stderr_s