mirror of
https://github.com/ryan4yin/nix-config.git
synced 2026-11-13 12:13:46 +01:00
refactor(nixpak): split policy checks into per-app suites (#493)
* refactor(nixpak): split policy checks into per-app suites Make hardening/tests/policy.nix a small loader that auto-discovers one suite per app under hardening/tests/policy. Shared checks live in policy-lib.nix and common expectations are generated by mkAppPolicy; app files only declare differences and special cases. The loader also discovers wrappers from hardening/nixpaks and fails when wrapper/suite inventory or check names diverge. Assisted-by: DeepSeek Harness (deepseek-flash) * fix(nixpak): reject duplicate per-app policy checks mkAppPolicy merges generated and custom checks, so an extraChecks key could silently override a standard assertion. Fail explicitly when a per-app check name is defined twice. Assisted-by: DeepSeek Harness (deepseek-flash)
This commit is contained in:
1 parent
7a00fead7d
commit
090a7b622b
13 files changed
+335
-221
No files matched your search
+1
-1
@@ -66,7 +66,7 @@ All browsers get read-only NSS certificates, printing, native notifications and
|
||||
- WeChat uses the native nixpkgs-patched package with nixpak as the only bwrap layer. Build/policy
|
||||
pass; interactive Wayland validation is still pending.
|
||||
- Evidence and repeat commands: [runtime validation](tests/runtime-validation.md),
|
||||
[policy test](tests/policy.nix).
|
||||
[policy test](tests/policy.nix), which auto-loads every suite under [tests/policy](tests/policy/).
|
||||
|
||||
## Code map
|
||||
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
{ pkgs, mkNixPak }:
|
||||
let
|
||||
inherit (pkgs) lib;
|
||||
|
||||
load = name: (pkgs.callPackage (../nixpaks + "/${name}.nix") { inherit mkNixPak; }).config;
|
||||
|
||||
# Wrapper files are the source of truth for the app inventory. The test
|
||||
# loader compares this with the per-app suites under ./policy.
|
||||
appNames = map (lib.removeSuffix ".nix") (
|
||||
builtins.attrNames (
|
||||
lib.filterAttrs (
|
||||
name: type: type == "regular" && name != "default.nix" && lib.hasSuffix ".nix" name
|
||||
) (builtins.readDir ../nixpaks)
|
||||
)
|
||||
);
|
||||
apps = map load appNames;
|
||||
|
||||
mountStrings =
|
||||
app: builtins.filter builtins.isString (lib.flatten (with app.bubblewrap.bind; rw ++ ro ++ dev));
|
||||
|
||||
forbiddenDbus =
|
||||
argument:
|
||||
lib.any (name: lib.hasInfix name argument) [
|
||||
"org.freedesktop.portal.Usb"
|
||||
"org.freedesktop.portal.RemoteDesktop"
|
||||
"org.gnome.Shell.Screencast"
|
||||
]
|
||||
|| argument == "--talk=org.freedesktop.portal.Desktop";
|
||||
|
||||
withoutSecretPortal =
|
||||
calls:
|
||||
calls
|
||||
// {
|
||||
"org.freedesktop.portal.Desktop" = lib.filter (
|
||||
arg: !lib.hasInfix "org.freedesktop.portal.Secret" arg
|
||||
) calls."org.freedesktop.portal.Desktop";
|
||||
};
|
||||
|
||||
forbiddenMount =
|
||||
path:
|
||||
path == "/dev"
|
||||
|| lib.hasPrefix "/dev/video" path
|
||||
|| lib.hasPrefix "/dev/bus/usb" path
|
||||
|| path == "/run/dbus"
|
||||
|| path == "/run/dbus/system_bus_socket";
|
||||
|
||||
hasPrivateTmp =
|
||||
app:
|
||||
let
|
||||
tmpPath = lib.findFirst (
|
||||
entry: builtins.isList entry && builtins.length entry == 2 && builtins.elem "/tmp" entry
|
||||
) null app.bubblewrap.bind.rw;
|
||||
in
|
||||
builtins.isList tmpPath && builtins.length tmpPath == 2;
|
||||
|
||||
hasDbusFragment = app: fragment: lib.any (argument: lib.hasInfix fragment argument) app.dbus.args;
|
||||
|
||||
networkCheck =
|
||||
app: mode:
|
||||
if mode == "host" then
|
||||
app.bubblewrap.network && !app.pasta.enable
|
||||
else if mode == "pasta" then
|
||||
app.bubblewrap.network
|
||||
&& app.pasta.enable
|
||||
&& app.pasta.mode == "isolate"
|
||||
&& lib.elem "--no-map-gw" app.pasta.args
|
||||
else if mode == "none" then
|
||||
!app.bubblewrap.network && !app.pasta.enable
|
||||
else
|
||||
throw "unknown network mode for ${app.flatpak.appId}: ${mode}";
|
||||
|
||||
mediaCheck =
|
||||
app: expected:
|
||||
if expected then
|
||||
app.bubblewrap.sockets.pulse && app.portalGroups.camera && app.portalGroups.screenCast
|
||||
else
|
||||
!app.bubblewrap.sockets.pulse
|
||||
&& !app.bubblewrap.sockets.pipewire
|
||||
&& !app.portalGroups.camera
|
||||
&& !app.portalGroups.screenCast
|
||||
&& !(hasDbusFragment app "org.freedesktop.portal.Camera")
|
||||
&& !(hasDbusFragment app "org.freedesktop.portal.ScreenCast");
|
||||
|
||||
secretPortalCheck =
|
||||
app: expected:
|
||||
if expected then
|
||||
(app.browserPermissions.secretPortal or false)
|
||||
&& lib.any (
|
||||
argument:
|
||||
argument
|
||||
== "--call=org.freedesktop.portal.Desktop=org.freedesktop.portal.Secret.RetrieveSecret@/org/freedesktop/portal/desktop"
|
||||
) app.dbus.args
|
||||
else
|
||||
!(app.browserPermissions.secretPortal or false)
|
||||
&& !(hasDbusFragment app "org.freedesktop.portal.Secret");
|
||||
|
||||
trayCheck =
|
||||
app: expected:
|
||||
if expected then
|
||||
app.desktopIntegration.tray
|
||||
&& (app.dbus.policies."org.freedesktop.DBus" or null) == "talk"
|
||||
&& (app.dbus.policies."org.kde.StatusNotifierWatcher" or null) == "talk"
|
||||
else
|
||||
!app.desktopIntegration.tray;
|
||||
|
||||
mkAppPolicy =
|
||||
name: spec:
|
||||
let
|
||||
app = load name;
|
||||
allowedVideoDevices = spec.allowedVideoDevices or [ ];
|
||||
forbiddenMountFor = path: forbiddenMount path && !lib.elem path allowedVideoDevices;
|
||||
checks = {
|
||||
"${name}/clearEnv" = app.bubblewrap.clearEnv;
|
||||
"${name}/dbus" = app.dbus.enable;
|
||||
"${name}/noRawPipewire" = !app.bubblewrap.sockets.pipewire;
|
||||
"${name}/noForbiddenDbus" = !lib.any forbiddenDbus app.dbus.args;
|
||||
"${name}/noBroadKeyring" =
|
||||
!lib.any (argument: argument == "--talk=org.freedesktop.secrets") app.dbus.args;
|
||||
"${name}/noForbiddenMounts" = !lib.any forbiddenMountFor (mountStrings app);
|
||||
"${name}/privateTmp" = hasPrivateTmp app;
|
||||
"${name}/network" = networkCheck app spec.network;
|
||||
"${name}/x11" = app.bubblewrap.sockets.x11 == (spec.x11 or false);
|
||||
"${name}/media" = mediaCheck app (spec.media or false);
|
||||
"${name}/mpris" = app.desktopIntegration.mpris == (spec.mpris or false);
|
||||
"${name}/notifications" = app.desktopIntegration.notifications == (spec.notifications or true);
|
||||
"${name}/tray" = trayCheck app (spec.tray or false);
|
||||
"${name}/browserpass" =
|
||||
(app.browserPermissions.browserpass or false) == (spec.browserpass or false);
|
||||
"${name}/personalFiles" =
|
||||
(app.browserPermissions.personalFiles or false) == (spec.personalFiles or false);
|
||||
"${name}/secretPortal" = secretPortalCheck app (spec.secretPortal or false);
|
||||
};
|
||||
extraChecks = lib.mapAttrs' (checkName: value: {
|
||||
name = "${name}/${checkName}";
|
||||
inherit value;
|
||||
}) (spec.extraChecks or { });
|
||||
duplicates = lib.attrNames (lib.intersectAttrs checks extraChecks);
|
||||
in
|
||||
if duplicates != [ ] then
|
||||
throw "duplicate nixpak policy checks for ${name}: ${lib.concatStringsSep ", " duplicates}"
|
||||
else
|
||||
checks // extraChecks;
|
||||
|
||||
globalChecks =
|
||||
let
|
||||
paths = map (app: app.bubblewrap.bind.rw) apps;
|
||||
tmpPaths = map (
|
||||
bind:
|
||||
lib.findFirst (
|
||||
entry: builtins.isList entry && builtins.length entry == 2 && builtins.elem "/tmp" entry
|
||||
) null bind
|
||||
) paths;
|
||||
in
|
||||
{
|
||||
appTmpIsolation =
|
||||
lib.all (path: builtins.isList path && builtins.length path == 2) tmpPaths
|
||||
&& lib.length (lib.unique (map builtins.head tmpPaths)) == builtins.length apps;
|
||||
};
|
||||
in
|
||||
{
|
||||
inherit
|
||||
lib
|
||||
appNames
|
||||
load
|
||||
mountStrings
|
||||
withoutSecretPortal
|
||||
mkAppPolicy
|
||||
globalChecks
|
||||
;
|
||||
}
|
||||
+33
-218
@@ -1,221 +1,36 @@
|
||||
# Evaluate with the same pkgs/mkNixPak as the wrappers. Returns true or identifies
|
||||
# the violated policy boundary; does not build or launch an application.
|
||||
{ pkgs, mkNixPak }:
|
||||
# Evaluate with the same pkgs/mkNixPak as the wrappers. Every file under
|
||||
# ./policy is imported as one app policy suite. Wrapper files are auto-discovered
|
||||
# from ../nixpaks, so a wrapper and its suite must be added together.
|
||||
{
|
||||
pkgs,
|
||||
mkNixPak,
|
||||
mylib,
|
||||
}:
|
||||
let
|
||||
inherit (pkgs) lib;
|
||||
load = name: (pkgs.callPackage (../nixpaks + "/${name}.nix") { inherit mkNixPak; }).config;
|
||||
firefox = load "firefox";
|
||||
chromium = load "chromium";
|
||||
chrome = load "google-chrome";
|
||||
restrictedBrowsers = map load [
|
||||
"brave-origin"
|
||||
"microsoft-edge"
|
||||
];
|
||||
chromiumFamily = map load [
|
||||
"chromium"
|
||||
"google-chrome"
|
||||
"brave-origin"
|
||||
"microsoft-edge"
|
||||
];
|
||||
browsers = map load [
|
||||
"firefox"
|
||||
"chromium"
|
||||
"google-chrome"
|
||||
"brave-origin"
|
||||
"microsoft-edge"
|
||||
];
|
||||
nonBrowserApps = map load [
|
||||
"qq"
|
||||
"telegram-desktop"
|
||||
"zoom"
|
||||
"wechat"
|
||||
];
|
||||
browserAndChatApps =
|
||||
browsers
|
||||
++ map load [
|
||||
"qq"
|
||||
"telegram-desktop"
|
||||
];
|
||||
apps = browsers ++ nonBrowserApps;
|
||||
# Inspect effective proxy arguments, not just capability switches: an extra
|
||||
# direct service grant could otherwise bypass a disabled portal group.
|
||||
forbiddenDbus =
|
||||
argument:
|
||||
lib.any (name: lib.hasInfix name argument) [
|
||||
"org.freedesktop.portal.Usb"
|
||||
"org.freedesktop.portal.RemoteDesktop"
|
||||
"org.gnome.Shell.Screencast"
|
||||
]
|
||||
|| argument == "--talk=org.freedesktop.portal.Desktop";
|
||||
withoutSecretPortal =
|
||||
calls:
|
||||
calls
|
||||
// {
|
||||
"org.freedesktop.portal.Desktop" = lib.filter (
|
||||
arg: !lib.hasInfix "org.freedesktop.portal.Secret" arg
|
||||
) calls."org.freedesktop.portal.Desktop";
|
||||
};
|
||||
mountStrings =
|
||||
app: builtins.filter builtins.isString (lib.flatten (with app.bubblewrap.bind; rw ++ ro ++ dev));
|
||||
forbiddenMount =
|
||||
path:
|
||||
path == "/dev"
|
||||
|| lib.hasPrefix "/dev/video" path
|
||||
|| lib.hasPrefix "/dev/bus/usb" path
|
||||
|| path == "/run/dbus"
|
||||
|| path == "/run/dbus/system_bus_socket";
|
||||
checks = {
|
||||
personalBrowserCapabilitiesMatch =
|
||||
firefox.browserPermissions.media == chromium.browserPermissions.media
|
||||
&& firefox.browserPermissions.personalFiles == chromium.browserPermissions.personalFiles
|
||||
&& firefox.browserPermissions.browserpass == chromium.browserPermissions.browserpass
|
||||
&&
|
||||
(removeAttrs firefox.portalGroups [ "secret" ]) == (removeAttrs chromium.portalGroups [ "secret" ])
|
||||
&& firefox.desktopIntegration == chromium.desktopIntegration;
|
||||
personalBrowserPortalCallsMatch =
|
||||
withoutSecretPortal firefox.dbus.rules.call == withoutSecretPortal chromium.dbus.rules.call;
|
||||
personalBrowserHostLoopback = lib.all (app: app.bubblewrap.network && !app.pasta.enable) [
|
||||
firefox
|
||||
chromium
|
||||
];
|
||||
workBrowserIsolatedLoopback =
|
||||
chrome.bubblewrap.network
|
||||
&& chrome.pasta.enable
|
||||
&& chrome.pasta.mode == "isolate"
|
||||
&& lib.elem "--no-map-gw" chrome.pasta.args;
|
||||
zoomDrmSysfs = lib.elem "/sys/class/drm" (load "zoom").bubblewrap.bind.ro;
|
||||
zoomCameraDevices =
|
||||
let
|
||||
zoom = load "zoom";
|
||||
in
|
||||
lib.all (dev: lib.elem dev zoom.bubblewrap.bind.dev) [
|
||||
"/dev/video0"
|
||||
"/dev/video1"
|
||||
];
|
||||
fhsTrayIntegration =
|
||||
lib.all
|
||||
(
|
||||
app:
|
||||
app.desktopIntegration.tray
|
||||
&& app.dbus.policies."org.freedesktop.DBus" == "talk"
|
||||
&& app.dbus.policies."org.kde.StatusNotifierWatcher" == "talk"
|
||||
&& app.dbus.policies."org.kde.StatusNotifierItem-2-1" == "own"
|
||||
&& app.dbus.policies."org.kde.StatusNotifierItem-2-2" == "own"
|
||||
)
|
||||
(
|
||||
map load [
|
||||
"wechat"
|
||||
"zoom"
|
||||
]
|
||||
);
|
||||
appTmpIsolation =
|
||||
let
|
||||
tmpPath =
|
||||
app:
|
||||
lib.findFirst (
|
||||
entry: builtins.isList entry && builtins.length entry == 2 && builtins.elem "/tmp" entry
|
||||
) null app.bubblewrap.bind.rw;
|
||||
paths = map tmpPath apps;
|
||||
in
|
||||
lib.all (path: builtins.isList path && builtins.length path == 2) paths
|
||||
&& lib.length (lib.unique (map builtins.head paths)) == builtins.length apps;
|
||||
workBrowserMedia =
|
||||
chrome.portalGroups.camera
|
||||
&& chrome.portalGroups.screenCast
|
||||
&& chrome.bubblewrap.sockets.pulse
|
||||
&& chrome.gpu.enable
|
||||
&& lib.elem "--call=org.freedesktop.portal.Desktop=org.freedesktop.portal.Camera.OpenPipeWireRemote@/org/freedesktop/portal/desktop" chrome.dbus.args
|
||||
&& lib.elem "--call=org.freedesktop.portal.Desktop=org.freedesktop.portal.ScreenCast.OpenPipeWireRemote@/org/freedesktop/portal/desktop" chrome.dbus.args;
|
||||
secretPortalScope =
|
||||
lib.all (
|
||||
app:
|
||||
app.browserPermissions.secretPortal
|
||||
&& lib.any (
|
||||
arg:
|
||||
arg
|
||||
== "--call=org.freedesktop.portal.Desktop=org.freedesktop.portal.Secret.RetrieveSecret@/org/freedesktop/portal/desktop"
|
||||
) app.dbus.args
|
||||
&& !lib.any (arg: arg == "--talk=org.freedesktop.secrets") app.dbus.args
|
||||
) chromiumFamily
|
||||
&& !firefox.browserPermissions.secretPortal
|
||||
&& lib.all (
|
||||
app:
|
||||
!(app.browserPermissions.secretPortal or false)
|
||||
&& !lib.any (arg: lib.hasInfix "org.freedesktop.portal.Secret" arg) app.dbus.args
|
||||
) nonBrowserApps;
|
||||
mediaScope =
|
||||
lib.all
|
||||
(
|
||||
app:
|
||||
app.browserPermissions.media
|
||||
&& app.bubblewrap.sockets.pulse
|
||||
&& app.portalGroups.camera
|
||||
&& app.portalGroups.screenCast
|
||||
)
|
||||
[
|
||||
firefox
|
||||
chromium
|
||||
chrome
|
||||
];
|
||||
restrictedBrowserMedia = lib.all (
|
||||
app:
|
||||
!app.browserPermissions.media
|
||||
&& !app.bubblewrap.sockets.pulse
|
||||
&& !app.bubblewrap.sockets.pipewire
|
||||
&& !app.portalGroups.camera
|
||||
&& !app.portalGroups.screenCast
|
||||
&& !app.desktopIntegration.mpris
|
||||
&& !lib.any (
|
||||
arg:
|
||||
lib.hasInfix "org.freedesktop.portal.Camera" arg
|
||||
|| lib.hasInfix "org.freedesktop.portal.ScreenCast" arg
|
||||
|| lib.hasInfix "org.mpris.MediaPlayer2" arg
|
||||
) app.dbus.args
|
||||
&& !lib.any (path: lib.hasPrefix "/dev/snd" path) (mountStrings app)
|
||||
) restrictedBrowsers;
|
||||
personalFileScope =
|
||||
firefox.browserPermissions.personalFiles
|
||||
&& chromium.browserPermissions.personalFiles
|
||||
&& !chrome.browserPermissions.personalFiles;
|
||||
browserpassScope =
|
||||
lib.all (app: app.browserPermissions.browserpass) [
|
||||
firefox
|
||||
chromium
|
||||
chrome
|
||||
]
|
||||
&& lib.all (app: !app.browserPermissions.browserpass) (
|
||||
map load [
|
||||
"brave-origin"
|
||||
"microsoft-edge"
|
||||
]
|
||||
);
|
||||
deniedServices = lib.all (app: !lib.any forbiddenDbus app.dbus.args) apps;
|
||||
noBroadKeyring = lib.all (
|
||||
app: !lib.any (arg: arg == "--talk=org.freedesktop.secrets") app.dbus.args
|
||||
) apps;
|
||||
deniedDevicesAndSystemBus =
|
||||
let
|
||||
zoomVideoDevices = [
|
||||
"/dev/video0"
|
||||
"/dev/video1"
|
||||
];
|
||||
forbiddenMountFor =
|
||||
app:
|
||||
if app.flatpak.appId == "us.zoom.Zoom" then
|
||||
path: forbiddenMount path && !lib.elem path zoomVideoDevices
|
||||
else
|
||||
forbiddenMount;
|
||||
in
|
||||
lib.all (app: !lib.any (forbiddenMountFor app) (mountStrings app)) apps;
|
||||
filteredEnvironmentAndMedia = lib.all (
|
||||
app:
|
||||
app.bubblewrap.clearEnv
|
||||
&& app.dbus.enable
|
||||
&& !app.bubblewrap.sockets.pipewire
|
||||
&& !app.bubblewrap.sockets.x11
|
||||
) browserAndChatApps;
|
||||
};
|
||||
ctx = import ./policy-lib.nix { inherit pkgs mkNixPak; };
|
||||
suitePaths = mylib.scanPaths ./policy;
|
||||
suiteNames = map (path: lib.removeSuffix ".nix" (baseNameOf (toString path))) suitePaths;
|
||||
suites = map (path: import path ctx) suitePaths;
|
||||
merge =
|
||||
acc: suite:
|
||||
let
|
||||
duplicates = lib.attrNames (lib.intersectAttrs acc suite);
|
||||
in
|
||||
if duplicates != [ ] then
|
||||
throw "duplicate nixpak policy checks: ${lib.concatStringsSep ", " duplicates}"
|
||||
else
|
||||
acc // suite;
|
||||
checks = ctx.globalChecks // lib.foldl' merge { } suites;
|
||||
missing = lib.subtractLists suiteNames ctx.appNames;
|
||||
orphans = lib.subtractLists ctx.appNames suiteNames;
|
||||
failures = builtins.filter (name: !(checks.${name} or false)) (builtins.attrNames checks);
|
||||
problems =
|
||||
lib.optional (missing != [ ]) "missing policy suites: ${lib.concatStringsSep ", " missing}"
|
||||
++ lib.optional (orphans != [ ]) "orphan policy suites: ${lib.concatStringsSep ", " orphans}"
|
||||
++ lib.optional (failures != [ ]) "failed checks:\n - ${lib.concatStringsSep "\n - " failures}";
|
||||
in
|
||||
lib.all (name: checks.${name} || throw "nixpak policy check failed: ${name}") (
|
||||
builtins.attrNames checks
|
||||
)
|
||||
if problems == [ ] then
|
||||
true
|
||||
else
|
||||
throw "nixpak policy check failed:\n${lib.concatStringsSep "\n" problems}"
|
||||
@@ -0,0 +1,10 @@
|
||||
{ mkAppPolicy, ... }:
|
||||
mkAppPolicy "brave-origin" {
|
||||
network = "pasta";
|
||||
media = false;
|
||||
mpris = false;
|
||||
notifications = true;
|
||||
personalFiles = false;
|
||||
browserpass = false;
|
||||
secretPortal = true;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{ mkAppPolicy, ... }:
|
||||
mkAppPolicy "chromium" {
|
||||
network = "host";
|
||||
media = true;
|
||||
mpris = true;
|
||||
notifications = true;
|
||||
personalFiles = true;
|
||||
browserpass = true;
|
||||
secretPortal = true;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
lib,
|
||||
load,
|
||||
mkAppPolicy,
|
||||
withoutSecretPortal,
|
||||
...
|
||||
}:
|
||||
let
|
||||
firefox = load "firefox";
|
||||
chromium = load "chromium";
|
||||
in
|
||||
mkAppPolicy "firefox" {
|
||||
network = "host";
|
||||
media = true;
|
||||
mpris = true;
|
||||
notifications = true;
|
||||
personalFiles = true;
|
||||
browserpass = true;
|
||||
secretPortal = false;
|
||||
extraChecks = {
|
||||
capabilitiesMatchChromium =
|
||||
firefox.browserPermissions.media == chromium.browserPermissions.media
|
||||
&& firefox.browserPermissions.personalFiles == chromium.browserPermissions.personalFiles
|
||||
&& firefox.browserPermissions.browserpass == chromium.browserPermissions.browserpass
|
||||
&&
|
||||
(removeAttrs firefox.portalGroups [ "secret" ]) == (removeAttrs chromium.portalGroups [ "secret" ])
|
||||
&& firefox.desktopIntegration == chromium.desktopIntegration;
|
||||
portalCallsMatchChromium =
|
||||
withoutSecretPortal firefox.dbus.rules.call == withoutSecretPortal chromium.dbus.rules.call;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{ mkAppPolicy, ... }:
|
||||
mkAppPolicy "google-chrome" {
|
||||
network = "pasta";
|
||||
media = true;
|
||||
mpris = true;
|
||||
notifications = true;
|
||||
personalFiles = false;
|
||||
browserpass = true;
|
||||
secretPortal = true;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{ mkAppPolicy, ... }:
|
||||
mkAppPolicy "microsoft-edge" {
|
||||
network = "pasta";
|
||||
media = false;
|
||||
mpris = false;
|
||||
notifications = true;
|
||||
personalFiles = false;
|
||||
browserpass = false;
|
||||
secretPortal = true;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{ mkAppPolicy, ... }:
|
||||
mkAppPolicy "qq" {
|
||||
network = "pasta";
|
||||
media = true;
|
||||
mpris = true;
|
||||
notifications = true;
|
||||
tray = true;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{ mkAppPolicy, ... }:
|
||||
mkAppPolicy "telegram-desktop" {
|
||||
network = "pasta";
|
||||
media = true;
|
||||
mpris = true;
|
||||
notifications = true;
|
||||
tray = true;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{ mkAppPolicy, ... }:
|
||||
mkAppPolicy "wechat" {
|
||||
network = "pasta";
|
||||
media = true;
|
||||
mpris = false;
|
||||
notifications = false;
|
||||
x11 = true;
|
||||
tray = true;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
lib,
|
||||
load,
|
||||
mkAppPolicy,
|
||||
...
|
||||
}:
|
||||
let
|
||||
zoom = load "zoom";
|
||||
in
|
||||
mkAppPolicy "zoom" {
|
||||
network = "pasta";
|
||||
media = true;
|
||||
mpris = false;
|
||||
notifications = false;
|
||||
x11 = true;
|
||||
tray = true;
|
||||
allowedVideoDevices = [
|
||||
"/dev/video0"
|
||||
"/dev/video1"
|
||||
];
|
||||
extraChecks = {
|
||||
drmSysfs = lib.elem "/sys/class/drm" zoom.bubblewrap.bind.ro;
|
||||
videoDevices = lib.all (dev: lib.elem dev zoom.bubblewrap.bind.dev) [
|
||||
"/dev/video0"
|
||||
"/dev/video1"
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
{ inputs, outputs, ... }:
|
||||
{
|
||||
inputs,
|
||||
mylib,
|
||||
outputs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
pkgs = outputs.nixosConfigurations.ai-niri.pkgs;
|
||||
in
|
||||
import ../../../../hardening/tests/policy.nix {
|
||||
inherit pkgs;
|
||||
inherit pkgs mylib;
|
||||
mkNixPak = inputs.nixpak.lib.nixpak {
|
||||
inherit pkgs;
|
||||
inherit (pkgs) lib;
|
||||
|
||||
Reference in new issue
Block a user