forked from lix-project/lix
Don't allow arbitrary computations in flake attributes
E.g. you can write 'edition = 201909' but not 'edition = 201909 + 0'. Fixes #3075.
This commit is contained in:
parent
2fa7f2a56a
commit
c87840ae14
|
@ -141,12 +141,12 @@ const Value *getPrimOp(const Value &v) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string showType(const Value & v)
|
string showType(ValueType type)
|
||||||
{
|
{
|
||||||
switch (v.type) {
|
switch (type) {
|
||||||
case tInt: return "an integer";
|
case tInt: return "an integer";
|
||||||
case tBool: return "a boolean";
|
case tBool: return "a boolean";
|
||||||
case tString: return v.string.context ? "a string with context" : "a string";
|
case tString: return "a string";
|
||||||
case tPath: return "a path";
|
case tPath: return "a path";
|
||||||
case tNull: return "null";
|
case tNull: return "null";
|
||||||
case tAttrs: return "a set";
|
case tAttrs: return "a set";
|
||||||
|
@ -155,14 +155,27 @@ string showType(const Value & v)
|
||||||
case tApp: return "a function application";
|
case tApp: return "a function application";
|
||||||
case tLambda: return "a function";
|
case tLambda: return "a function";
|
||||||
case tBlackhole: return "a black hole";
|
case tBlackhole: return "a black hole";
|
||||||
|
case tPrimOp: return "a built-in function";
|
||||||
|
case tPrimOpApp: return "a partially applied built-in function";
|
||||||
|
case tExternal: return "an external value";
|
||||||
|
case tFloat: return "a float";
|
||||||
|
}
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string showType(const Value & v)
|
||||||
|
{
|
||||||
|
switch (v.type) {
|
||||||
|
case tString: return v.string.context ? "a string with context" : "a string";
|
||||||
case tPrimOp:
|
case tPrimOp:
|
||||||
return fmt("the built-in function '%s'", string(v.primOp->name));
|
return fmt("the built-in function '%s'", string(v.primOp->name));
|
||||||
case tPrimOpApp:
|
case tPrimOpApp:
|
||||||
return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name));
|
return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name));
|
||||||
case tExternal: return v.external->showType();
|
case tExternal: return v.external->showType();
|
||||||
case tFloat: return "a float";
|
default:
|
||||||
|
return showType(v.type);
|
||||||
}
|
}
|
||||||
abort();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -338,6 +338,7 @@ private:
|
||||||
|
|
||||||
|
|
||||||
/* Return a string representing the type of the value `v'. */
|
/* Return a string representing the type of the value `v'. */
|
||||||
|
string showType(ValueType type);
|
||||||
string showType(const Value & v);
|
string showType(const Value & v);
|
||||||
|
|
||||||
/* Decode a context string ‘!<name>!<path>’ into a pair <path,
|
/* Decode a context string ‘!<name>!<path>’ into a pair <path,
|
||||||
|
|
|
@ -195,6 +195,18 @@ static SourceInfo fetchFlake(EvalState & state, const FlakeRef & resolvedRef)
|
||||||
else abort();
|
else abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void expectType(EvalState & state, ValueType type,
|
||||||
|
Value & value, const Pos & pos)
|
||||||
|
{
|
||||||
|
if (value.type == tThunk &&
|
||||||
|
((type == tAttrs && dynamic_cast<ExprAttrs *>(value.thunk.expr)) ||
|
||||||
|
(type == tLambda && dynamic_cast<ExprLambda *>(value.thunk.expr))))
|
||||||
|
state.forceValue(value, pos);
|
||||||
|
if (value.type != type)
|
||||||
|
throw Error("expected %s but got %s at %s",
|
||||||
|
showType(type), showType(value.type), pos);
|
||||||
|
}
|
||||||
|
|
||||||
Flake getFlake(EvalState & state, const FlakeRef & flakeRef)
|
Flake getFlake(EvalState & state, const FlakeRef & flakeRef)
|
||||||
{
|
{
|
||||||
SourceInfo sourceInfo = fetchFlake(state, flakeRef);
|
SourceInfo sourceInfo = fetchFlake(state, flakeRef);
|
||||||
|
@ -219,9 +231,10 @@ Flake getFlake(EvalState & state, const FlakeRef & flakeRef)
|
||||||
throw Error("source tree referenced by '%s' does not contain a '%s/flake.nix' file", resolvedRef, resolvedRef.subdir);
|
throw Error("source tree referenced by '%s' does not contain a '%s/flake.nix' file", resolvedRef, resolvedRef.subdir);
|
||||||
|
|
||||||
Value vInfo;
|
Value vInfo;
|
||||||
|
// FIXME: don't evaluate vInfo.
|
||||||
state.evalFile(realFlakeFile, vInfo); // FIXME: symlink attack
|
state.evalFile(realFlakeFile, vInfo); // FIXME: symlink attack
|
||||||
|
|
||||||
state.forceAttrs(vInfo);
|
expectType(state, tAttrs, vInfo, Pos(state.symbols.create(realFlakeFile), 0, 0));
|
||||||
|
|
||||||
auto sEdition = state.symbols.create("edition");
|
auto sEdition = state.symbols.create("edition");
|
||||||
auto sEpoch = state.symbols.create("epoch"); // FIXME: remove soon
|
auto sEpoch = state.symbols.create("epoch"); // FIXME: remove soon
|
||||||
|
@ -231,7 +244,8 @@ Flake getFlake(EvalState & state, const FlakeRef & flakeRef)
|
||||||
edition = vInfo.attrs->get(sEpoch);
|
edition = vInfo.attrs->get(sEpoch);
|
||||||
|
|
||||||
if (edition) {
|
if (edition) {
|
||||||
flake.edition = state.forceInt(*(**edition).value, *(**edition).pos);
|
expectType(state, tInt, *(**edition).value, *(**edition).pos);
|
||||||
|
flake.edition = (**edition).value->integer;
|
||||||
if (flake.edition > 201909)
|
if (flake.edition > 201909)
|
||||||
throw Error("flake '%s' requires unsupported edition %d; please upgrade Nix", flakeRef, flake.edition);
|
throw Error("flake '%s' requires unsupported edition %d; please upgrade Nix", flakeRef, flake.edition);
|
||||||
if (flake.edition < 201909)
|
if (flake.edition < 201909)
|
||||||
|
@ -239,26 +253,30 @@ Flake getFlake(EvalState & state, const FlakeRef & flakeRef)
|
||||||
} else
|
} else
|
||||||
throw Error("flake '%s' lacks attribute 'edition'", flakeRef);
|
throw Error("flake '%s' lacks attribute 'edition'", flakeRef);
|
||||||
|
|
||||||
if (auto description = vInfo.attrs->get(state.sDescription))
|
if (auto description = vInfo.attrs->get(state.sDescription)) {
|
||||||
flake.description = state.forceStringNoCtx(*(**description).value, *(**description).pos);
|
expectType(state, tString, *(**description).value, *(**description).pos);
|
||||||
|
flake.description = (**description).value->string.s;
|
||||||
|
}
|
||||||
|
|
||||||
auto sInputs = state.symbols.create("inputs");
|
auto sInputs = state.symbols.create("inputs");
|
||||||
auto sUri = state.symbols.create("uri");
|
auto sUri = state.symbols.create("uri");
|
||||||
auto sFlake = state.symbols.create("flake");
|
auto sFlake = state.symbols.create("flake");
|
||||||
|
|
||||||
if (std::optional<Attr *> inputs = vInfo.attrs->get(sInputs)) {
|
if (std::optional<Attr *> inputs = vInfo.attrs->get(sInputs)) {
|
||||||
state.forceAttrs(*(**inputs).value, *(**inputs).pos);
|
expectType(state, tAttrs, *(**inputs).value, *(**inputs).pos);
|
||||||
|
|
||||||
for (Attr inputAttr : *(*(**inputs).value).attrs) {
|
for (Attr inputAttr : *(*(**inputs).value).attrs) {
|
||||||
state.forceAttrs(*inputAttr.value, *inputAttr.pos);
|
expectType(state, tAttrs, *inputAttr.value, *inputAttr.pos);
|
||||||
|
|
||||||
FlakeInput input(FlakeRef(inputAttr.name));
|
FlakeInput input(FlakeRef(inputAttr.name));
|
||||||
|
|
||||||
for (Attr attr : *(inputAttr.value->attrs)) {
|
for (Attr attr : *(inputAttr.value->attrs)) {
|
||||||
if (attr.name == sUri) {
|
if (attr.name == sUri) {
|
||||||
input.ref = state.forceStringNoCtx(*attr.value, *attr.pos);
|
expectType(state, tString, *attr.value, *attr.pos);
|
||||||
|
input.ref = std::string(attr.value->string.s);
|
||||||
} else if (attr.name == sFlake) {
|
} else if (attr.name == sFlake) {
|
||||||
input.isFlake = state.forceBool(*attr.value, *attr.pos);
|
expectType(state, tBool, *attr.value, *attr.pos);
|
||||||
|
input.isFlake = attr.value->boolean;
|
||||||
} else
|
} else
|
||||||
throw Error("flake input '%s' has an unsupported attribute '%s', at %s",
|
throw Error("flake input '%s' has an unsupported attribute '%s', at %s",
|
||||||
inputAttr.name, attr.name, *attr.pos);
|
inputAttr.name, attr.name, *attr.pos);
|
||||||
|
@ -271,7 +289,7 @@ Flake getFlake(EvalState & state, const FlakeRef & flakeRef)
|
||||||
auto sOutputs = state.symbols.create("outputs");
|
auto sOutputs = state.symbols.create("outputs");
|
||||||
|
|
||||||
if (auto outputs = vInfo.attrs->get(sOutputs)) {
|
if (auto outputs = vInfo.attrs->get(sOutputs)) {
|
||||||
state.forceFunction(*(**outputs).value, *(**outputs).pos);
|
expectType(state, tLambda, *(**outputs).value, *(**outputs).pos);
|
||||||
flake.vOutputs = (**outputs).value;
|
flake.vOutputs = (**outputs).value;
|
||||||
|
|
||||||
if (flake.vOutputs->lambda.fun->matchAttrs) {
|
if (flake.vOutputs->lambda.fun->matchAttrs) {
|
||||||
|
|
Loading…
Reference in a new issue