Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add mechanism for handling meta.sourceProvenance attributes #161098

Merged
merged 7 commits into from
May 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions doc/stdenv/meta.chapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,3 +249,31 @@ Unfree package that cannot be redistributed. You can build it yourself, but you
### `lib.licenses.unfreeRedistributableFirmware`, `"unfree-redistributable-firmware"` {#lib.licenses.unfreeredistributablefirmware-unfree-redistributable-firmware}

This package supplies unfree, redistributable firmware. This is a separate value from `unfree-redistributable` because not everybody cares whether firmware is free.

## Source provenance {#sec-meta-sourceProvenance}

The value of a package's `meta.sourceProvenance` attribute specifies the provenance of the package's derivation outputs.

If a package contains elements that are not built from the original source by a nixpkgs derivation, the `meta.sourceProvenance` attribute should be a list containing one or more value from `lib.sourceTypes` defined in [`nixpkgs/lib/source-types.nix`](https://github.com/NixOS/nixpkgs/blob/master/lib/source-types.nix).

Adding this information helps users who have needs related to build transparency and supply-chain security to gain some visibility into their installed software or set policy to allow or disallow installation based on source provenance.

The presence of a particular `sourceType` in a package's `meta.sourceProvenance` list indicates that the package contains some components falling into that category, though the *absence* of that `sourceType` does not *guarantee* the absence of that category of `sourceType` in the package's contents. A package with no `meta.sourceProvenance` set implies it has no *known* `sourceType`s other than `fromSource`.

The meaning of the `meta.sourceProvenance` attribute does not depend on the value of the `meta.license` attribute.

### `lib.sourceTypes.fromSource` {#lib.sourceTypes.fromSource}

Package elements which are produced by a nixpkgs derivation which builds them from source code.

### `lib.sourceTypes.binaryNativeCode` {#lib.sourceTypes.binaryNativeCode}

Native code to be executed on the target system's CPU, built by a third party. This includes packages which wrap a downloaded AppImage or Debian package.

### `lib.sourceTypes.binaryFirmware` {#lib.sourceTypes.binaryFirmware}

Code to be executed on a peripheral device or embedded controller, built by a third party.

### `lib.sourceTypes.binaryBytecode` {#lib.sourceTypes.binaryBytecode}

Code to run on a VM interpreter or JIT compiled into bytecode by a third party. This includes packages which download Java `.jar` files from another source.
1 change: 1 addition & 0 deletions lib/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ let

# constants
licenses = callLibs ./licenses.nix;
sourceTypes = callLibs ./source-types.nix;
systems = callLibs ./systems;

# serialization
Expand Down
19 changes: 19 additions & 0 deletions lib/source-types.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{ lib }:

let
defaultSourceType = tname: {
shortName = tname;
isSource = false;
};
in lib.mapAttrs (tname: tset: defaultSourceType tname // tset) {

fromSource = {
isSource = true;
};

binaryNativeCode = {};

binaryBytecode = {};

binaryFirmware = {};
}
44 changes: 40 additions & 4 deletions pkgs/stdenv/generic/check-meta.nix
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ let
allowUnfree = config.allowUnfree
|| builtins.getEnv "NIXPKGS_ALLOW_UNFREE" == "1";

allowNonSource = config.allowNonSource or true
|| builtins.getEnv "NIXPKGS_ALLOW_NONSOURCE" == "1";

allowlist = config.allowlistedLicenses or config.whitelistedLicenses or [];
blocklist = config.blocklistedLicenses or config.blacklistedLicenses or [];

Expand Down Expand Up @@ -86,12 +89,39 @@ let
allowInsecurePredicate attrs ||
builtins.getEnv "NIXPKGS_ALLOW_INSECURE" == "1";

showLicense = license: toString (map (l: l.shortName or "unknown") (lib.lists.toList license));

isNonSource = sourceTypes: lib.lists.any (t: !t.isSource) sourceTypes;

hasNonSourceProvenance = attrs:
(attrs ? meta.sourceProvenance) &&
isNonSource (lib.lists.toList attrs.meta.sourceProvenance);

# Allow granular checks to allow only some non-source-built packages
# Example:
# {pkgs, ...}:
# {
# allowNonSource = false;
# allowNonSourcePredicate = with lib.lists; pkg: !(any (p: !p.isSource && p!=lib.sourceTypes.binaryFirmware) (toList pkg.meta.sourceProvenance));
# }
risicle marked this conversation as resolved.
Show resolved Hide resolved
allowNonSourcePredicate = config.allowNonSourcePredicate or (x: false);

# Check whether non-source packages are allowed and if not, whether the
# package has non-source provenance and is not explicitly allowed by the
# `allowNonSourcePredicate` function.
hasDeniedNonSourceProvenance = attrs:
hasNonSourceProvenance attrs &&
!allowNonSource &&
!allowNonSourcePredicate attrs;

showLicenseOrSourceType = value: toString (map (v: v.shortName or "unknown") (lib.lists.toList value));
aforemny marked this conversation as resolved.
Show resolved Hide resolved
showLicense = showLicenseOrSourceType;
showSourceType = showLicenseOrSourceType;

pos_str = meta: meta.position or "«unknown-file»";

remediation = {
unfree = remediate_allowlist "Unfree" remediate_unfree_predicate;
unfree = remediate_allowlist "Unfree" (remediate_predicate "allowUnfreePredicate");
non-source = remediate_allowlist "NonSource" (remediate_predicate "allowNonSourcePredicate");
broken = remediate_allowlist "Broken" (x: "");
unsupported = remediate_allowlist "UnsupportedSystem" (x: "");
blocklisted = x: "";
Expand All @@ -104,17 +134,19 @@ let
Unfree = "NIXPKGS_ALLOW_UNFREE";
Broken = "NIXPKGS_ALLOW_BROKEN";
UnsupportedSystem = "NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM";
NonSource = "NIXPKGS_ALLOW_NONSOURCE";
}.${allow_attr};
remediation_phrase = allow_attr: {
Unfree = "unfree packages";
Broken = "broken packages";
UnsupportedSystem = "packages that are unsupported for this system";
NonSource = "packages not built from source";
}.${allow_attr};
remediate_unfree_predicate = attrs:
remediate_predicate = predicateConfigAttr: attrs:
''

Alternatively you can configure a predicate to allow specific packages:
{ nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
{ nixpkgs.config.${predicateConfigAttr} = pkg: builtins.elem (lib.getName pkg) [
"${lib.getName attrs}"
];
}
Expand Down Expand Up @@ -226,6 +258,7 @@ let
downloadPage = str;
changelog = either (listOf str) str;
license = either (listOf lib.types.attrs) (either lib.types.attrs str);
sourceProvenance = either (listOf lib.types.attrs) lib.types.attrs;
maintainers = listOf (attrsOf str);
priority = int;
platforms = listOf str;
Expand Down Expand Up @@ -288,6 +321,7 @@ let
checkValidity = attrs:
{
unfree = hasUnfreeLicense attrs;
nonSource = hasNonSourceProvenance attrs;
broken = isMarkedBroken attrs;
unsupported = hasUnsupportedPlatform attrs;
insecure = isMarkedInsecure attrs;
Expand All @@ -296,6 +330,8 @@ let
{ valid = "no"; reason = "unfree"; errormsg = "has an unfree license (‘${showLicense attrs.meta.license}’)"; }
else if hasBlocklistedLicense attrs then
{ valid = "no"; reason = "blocklisted"; errormsg = "has a blocklisted license (‘${showLicense attrs.meta.license}’)"; }
else if hasDeniedNonSourceProvenance attrs then
{ valid = "no"; reason = "non-source"; errormsg = "contains elements not built from source (‘${showSourceType attrs.meta.sourceProvenance}’)"; }
else if !allowBroken && attrs.meta.broken or false then
{ valid = "no"; reason = "broken"; errormsg = "is marked as broken"; }
else if !allowUnsupportedSystem && hasUnsupportedPlatform attrs then
Expand Down