mirror of
https://github.com/nix-community/home-manager.git
synced 2025-12-06 09:01:04 +01:00
firefox: add extension permissions (#7402)
Adds extension permissions as suggested in https://github.com/nix-community/home-manager/issues/7001. Adds the 'profiles.<name>.extensions.settings.<name>.permissions' to Firefox derivatives. If set, this option adds an assertion that fails if an extension package requests permissions that weren't added to the permissions option. In order to not require 'profiles.<name>.extensions.force' to be set when only permissions, but no extension settings were defined, the relevant assertions were changed. They now check whether any 'extensions.settings.<name>.settings' was set instead of checking whether 'extensions.settings' was set. --------- Co-authored-by: Robert Helgesson <robert@rycee.net> Co-authored-by: awwpotato <awwpotato@voidq.com>
This commit is contained in:
parent
fb12dbbce3
commit
d52da303ef
3 changed files with 214 additions and 102 deletions
|
|
@ -80,6 +80,9 @@ let
|
||||||
if lib.isBool pref || lib.isInt pref || lib.isString pref then pref else builtins.toJSON pref
|
if lib.isBool pref || lib.isInt pref || lib.isString pref then pref else builtins.toJSON pref
|
||||||
);
|
);
|
||||||
|
|
||||||
|
extensionSettingsNeedForce =
|
||||||
|
extensionSettings: builtins.any (ext: ext.settings != { }) (attrValues extensionSettings);
|
||||||
|
|
||||||
mkUserJs =
|
mkUserJs =
|
||||||
prePrefs: prefs: extraPrefs: bookmarksFile: extensions:
|
prePrefs: prefs: extraPrefs: bookmarksFile: extensions:
|
||||||
let
|
let
|
||||||
|
|
@ -88,7 +91,7 @@ let
|
||||||
"browser.bookmarks.file" = toString bookmarksFile;
|
"browser.bookmarks.file" = toString bookmarksFile;
|
||||||
"browser.places.importBookmarksHTML" = true;
|
"browser.places.importBookmarksHTML" = true;
|
||||||
}
|
}
|
||||||
// lib.optionalAttrs (extensions != { }) {
|
// lib.optionalAttrs (extensionSettingsNeedForce extensions) {
|
||||||
"extensions.webextensions.ExtensionStorageIDB.enabled" = false;
|
"extensions.webextensions.ExtensionStorageIDB.enabled" = false;
|
||||||
}
|
}
|
||||||
// prefs;
|
// prefs;
|
||||||
|
|
@ -356,6 +359,12 @@ in
|
||||||
type = types.attrsOf (
|
type = types.attrsOf (
|
||||||
types.submodule (
|
types.submodule (
|
||||||
{ config, name, ... }:
|
{ config, name, ... }:
|
||||||
|
let
|
||||||
|
profilePath = modulePath ++ [
|
||||||
|
"profiles"
|
||||||
|
name
|
||||||
|
];
|
||||||
|
in
|
||||||
{
|
{
|
||||||
imports = [ (pkgs.path + "/nixos/modules/misc/assertions.nix") ];
|
imports = [ (pkgs.path + "/nixos/modules/misc/assertions.nix") ];
|
||||||
|
|
||||||
|
|
@ -721,7 +730,19 @@ in
|
||||||
options = {
|
options = {
|
||||||
settings = mkOption {
|
settings = mkOption {
|
||||||
type = types.attrsOf jsonFormat.type;
|
type = types.attrsOf jsonFormat.type;
|
||||||
description = "Json formatted options for the specified extensionID";
|
default = { };
|
||||||
|
description = "Json formatted options for this extension.";
|
||||||
|
};
|
||||||
|
permissions = mkOption {
|
||||||
|
type = types.nullOr (types.listOf types.str);
|
||||||
|
default = null;
|
||||||
|
example = [ "activeTab" ];
|
||||||
|
defaultText = "Any permissions";
|
||||||
|
description = ''
|
||||||
|
Allowed permissions for this extension. See
|
||||||
|
<https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions>
|
||||||
|
for a list of relevant permissions.
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
force = mkOption {
|
force = mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
|
|
@ -763,36 +784,54 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
assertions = [
|
assertions =
|
||||||
(mkNoDuplicateAssertion config.containers "container")
|
[
|
||||||
{
|
(mkNoDuplicateAssertion config.containers "container")
|
||||||
assertion = config.extensions.settings == { } || config.extensions.force;
|
{
|
||||||
message = ''
|
assertion = !(extensionSettingsNeedForce config.extensions.settings) || config.extensions.force;
|
||||||
Using '${
|
message = ''
|
||||||
lib.showAttrPath (
|
Using '${lib.showOption profilePath}.extensions.settings' will override all
|
||||||
modulePath
|
previous extensions settings. Enable
|
||||||
++ [
|
'${lib.showOption profilePath}.extensions.force' to acknowledge this.
|
||||||
"profiles"
|
'';
|
||||||
config.name
|
}
|
||||||
"extensions"
|
]
|
||||||
"settings"
|
++ (builtins.concatMap (
|
||||||
]
|
{ name, value }:
|
||||||
)
|
let
|
||||||
}' will override all previous extensions settings.
|
packages = builtins.filter (pkg: pkg.addonId == name) config.extensions.packages;
|
||||||
Enable '${
|
package = builtins.head packages;
|
||||||
lib.showAttrPath (
|
unauthorized = lib.subtractLists value.permissions package.meta.mozPermissions;
|
||||||
modulePath
|
in
|
||||||
++ [
|
[
|
||||||
"profiles"
|
{
|
||||||
config.name
|
assertion = value.permissions == null || length packages == 1;
|
||||||
"extensions"
|
message = ''
|
||||||
"force"
|
Must have exactly one extension with addonId '${name}'
|
||||||
]
|
in '${lib.showOption profilePath}.extensions.packages' but found ${toString (length packages)}.
|
||||||
)
|
'';
|
||||||
}' to acknowledge this.
|
}
|
||||||
'';
|
|
||||||
}
|
{
|
||||||
] ++ config.bookmarks.assertions;
|
assertion = value.permissions == null || length packages != 1 || unauthorized == [ ];
|
||||||
|
message = ''
|
||||||
|
Extension ${name} requests permissions that weren't
|
||||||
|
authorized: ${builtins.toJSON unauthorized}.
|
||||||
|
Consider adding the missing permissions to
|
||||||
|
'${
|
||||||
|
lib.showAttrPath (
|
||||||
|
profilePath
|
||||||
|
++ [
|
||||||
|
"extensions"
|
||||||
|
name
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}.permissions'.
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
]
|
||||||
|
) (lib.attrsToList config.extensions.settings))
|
||||||
|
++ config.bookmarks.assertions;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -888,82 +927,80 @@ in
|
||||||
++ lib.flip mapAttrsToList cfg.profiles (
|
++ lib.flip mapAttrsToList cfg.profiles (
|
||||||
_: profile:
|
_: profile:
|
||||||
# Merge the regular profile settings with extension settings
|
# Merge the regular profile settings with extension settings
|
||||||
mkMerge (
|
mkMerge [
|
||||||
[
|
{
|
||||||
{
|
"${cfg.profilesPath}/${profile.path}/.keep".text = "";
|
||||||
"${cfg.profilesPath}/${profile.path}/.keep".text = "";
|
|
||||||
|
|
||||||
"${cfg.profilesPath}/${profile.path}/chrome/userChrome.css" = mkIf (profile.userChrome != "") (
|
"${cfg.profilesPath}/${profile.path}/chrome/userChrome.css" = mkIf (profile.userChrome != "") (
|
||||||
let
|
let
|
||||||
key = if builtins.isString profile.userChrome then "text" else "source";
|
key = if builtins.isString profile.userChrome then "text" else "source";
|
||||||
in
|
in
|
||||||
|
{
|
||||||
|
"${key}" = profile.userChrome;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
"${cfg.profilesPath}/${profile.path}/chrome/userContent.css" = mkIf (profile.userContent != "") (
|
||||||
|
let
|
||||||
|
key = if builtins.isString profile.userContent then "text" else "source";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
"${key}" = profile.userContent;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
"${cfg.profilesPath}/${profile.path}/user.js" =
|
||||||
|
mkIf
|
||||||
|
(
|
||||||
|
profile.preConfig != ""
|
||||||
|
|| profile.settings != { }
|
||||||
|
|| profile.extraConfig != ""
|
||||||
|
|| profile.bookmarks.configFile != null
|
||||||
|
|| extensionSettingsNeedForce profile.extensions.settings
|
||||||
|
)
|
||||||
{
|
{
|
||||||
"${key}" = profile.userChrome;
|
text =
|
||||||
}
|
mkUserJs profile.preConfig profile.settings profile.extraConfig profile.bookmarks.configFile
|
||||||
);
|
profile.extensions.settings;
|
||||||
|
};
|
||||||
|
|
||||||
"${cfg.profilesPath}/${profile.path}/chrome/userContent.css" = mkIf (profile.userContent != "") (
|
"${cfg.profilesPath}/${profile.path}/containers.json" = mkIf (profile.containers != { }) {
|
||||||
|
text = mkContainersJson profile.containers;
|
||||||
|
force = profile.containersForce;
|
||||||
|
};
|
||||||
|
|
||||||
|
"${cfg.profilesPath}/${profile.path}/search.json.mozlz4" = mkIf (profile.search.enable) {
|
||||||
|
enable = profile.search.enable;
|
||||||
|
force = profile.search.force;
|
||||||
|
source = profile.search.file;
|
||||||
|
};
|
||||||
|
|
||||||
|
"${cfg.profilesPath}/${profile.path}/extensions" = mkIf (profile.extensions.packages != [ ]) {
|
||||||
|
source =
|
||||||
let
|
let
|
||||||
key = if builtins.isString profile.userContent then "text" else "source";
|
extensionsEnvPkg = pkgs.buildEnv {
|
||||||
|
name = "hm-firefox-extensions";
|
||||||
|
paths = profile.extensions.packages;
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
"${extensionsEnvPkg}/share/mozilla/${extensionPath}";
|
||||||
"${key}" = profile.userContent;
|
recursive = true;
|
||||||
}
|
force = true;
|
||||||
);
|
};
|
||||||
|
}
|
||||||
|
|
||||||
"${cfg.profilesPath}/${profile.path}/user.js" =
|
(mkMerge (
|
||||||
mkIf
|
mapAttrsToList (
|
||||||
(
|
name: settingConfig:
|
||||||
profile.preConfig != ""
|
mkIf (settingConfig.settings != { }) {
|
||||||
|| profile.settings != { }
|
"${cfg.profilesPath}/${profile.path}/browser-extension-data/${name}/storage.js" = {
|
||||||
|| profile.extraConfig != ""
|
force = settingConfig.force || profile.extensions.force;
|
||||||
|| profile.bookmarks.configFile != null
|
text = lib.generators.toJSON { } settingConfig.settings;
|
||||||
|| profile.extensions.settings != { }
|
};
|
||||||
)
|
}
|
||||||
{
|
) profile.extensions.settings
|
||||||
text =
|
))
|
||||||
mkUserJs profile.preConfig profile.settings profile.extraConfig profile.bookmarks.configFile
|
]
|
||||||
profile.extensions.settings;
|
|
||||||
};
|
|
||||||
|
|
||||||
"${cfg.profilesPath}/${profile.path}/containers.json" = mkIf (profile.containers != { }) {
|
|
||||||
text = mkContainersJson profile.containers;
|
|
||||||
force = profile.containersForce;
|
|
||||||
};
|
|
||||||
|
|
||||||
"${cfg.profilesPath}/${profile.path}/search.json.mozlz4" = mkIf (profile.search.enable) {
|
|
||||||
enable = profile.search.enable;
|
|
||||||
force = profile.search.force;
|
|
||||||
source = profile.search.file;
|
|
||||||
};
|
|
||||||
|
|
||||||
"${cfg.profilesPath}/${profile.path}/extensions" = mkIf (profile.extensions.packages != [ ]) {
|
|
||||||
source =
|
|
||||||
let
|
|
||||||
extensionsEnvPkg = pkgs.buildEnv {
|
|
||||||
name = "hm-firefox-extensions";
|
|
||||||
paths = profile.extensions.packages;
|
|
||||||
};
|
|
||||||
in
|
|
||||||
"${extensionsEnvPkg}/share/mozilla/${extensionPath}";
|
|
||||||
recursive = true;
|
|
||||||
force = true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
]
|
|
||||||
++
|
|
||||||
# Add extension settings as separate attributes
|
|
||||||
optional (profile.extensions.settings != { }) (
|
|
||||||
mkMerge (
|
|
||||||
mapAttrsToList (name: settingConfig: {
|
|
||||||
"${cfg.profilesPath}/${profile.path}/browser-extension-data/${name}/storage.js" = {
|
|
||||||
force = settingConfig.force || profile.extensions.force;
|
|
||||||
text = lib.generators.toJSON { } settingConfig.settings;
|
|
||||||
};
|
|
||||||
}) profile.extensions.settings
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ builtins.mapAttrs
|
||||||
"${name}-profiles-containers-id-out-of-range" = ./profiles/containers/id-out-of-range.nix;
|
"${name}-profiles-containers-id-out-of-range" = ./profiles/containers/id-out-of-range.nix;
|
||||||
"${name}-profiles-duplicate-ids" = ./profiles/duplicate-ids.nix;
|
"${name}-profiles-duplicate-ids" = ./profiles/duplicate-ids.nix;
|
||||||
"${name}-profiles-extensions" = ./profiles/extensions;
|
"${name}-profiles-extensions" = ./profiles/extensions;
|
||||||
|
"${name}-profiles-extensions-assertions" = ./profiles/extensions/assertions.nix;
|
||||||
"${name}-profiles-overwrite" = ./profiles/overwrite;
|
"${name}-profiles-overwrite" = ./profiles/overwrite;
|
||||||
"${name}-profiles-search" = ./profiles/search;
|
"${name}-profiles-search" = ./profiles/search;
|
||||||
"${name}-profiles-settings" = ./profiles/settings;
|
"${name}-profiles-settings" = ./profiles/settings;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
modulePath:
|
||||||
|
{ config, lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
|
||||||
|
firefoxMockOverlay = import ../../setup-firefox-mock-overlay.nix modulePath;
|
||||||
|
|
||||||
|
uBlockStubPkg = config.lib.test.mkStubPackage {
|
||||||
|
name = "ublock-origin-dummy";
|
||||||
|
extraAttrs = {
|
||||||
|
addonId = "uBlock0@raymondhill.net";
|
||||||
|
meta.mozPermissions = [
|
||||||
|
"privacy"
|
||||||
|
"storage"
|
||||||
|
"tabs"
|
||||||
|
"<all_urls>"
|
||||||
|
"http://*/*"
|
||||||
|
"https://github.com/*"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ firefoxMockOverlay ];
|
||||||
|
|
||||||
|
config = lib.mkIf config.test.enableBig (
|
||||||
|
lib.setAttrByPath modulePath {
|
||||||
|
enable = true;
|
||||||
|
profiles.extensions = {
|
||||||
|
extensions = {
|
||||||
|
packages = [ uBlockStubPkg ];
|
||||||
|
settings = {
|
||||||
|
"uBlock0@raymondhill.net" = {
|
||||||
|
settings = {
|
||||||
|
selectedFilterLists = [
|
||||||
|
"ublock-filters"
|
||||||
|
"ublock-badware"
|
||||||
|
"ublock-privacy"
|
||||||
|
"ublock-unbreak"
|
||||||
|
"ublock-quick-fixes"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
permissions = [
|
||||||
|
"alarms"
|
||||||
|
"tabs"
|
||||||
|
"https://github.com/*"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
"unknown@example.com".permissions = [ ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// {
|
||||||
|
test.asserts.assertions.expected = [
|
||||||
|
''
|
||||||
|
Using '${lib.showOption modulePath}.profiles.extensions.extensions.settings' will override all
|
||||||
|
previous extensions settings. Enable
|
||||||
|
'${lib.showOption modulePath}.profiles.extensions.extensions.force' to acknowledge this.
|
||||||
|
''
|
||||||
|
''
|
||||||
|
Extension uBlock0@raymondhill.net requests permissions that weren't
|
||||||
|
authorized: ["privacy","storage","<all_urls>","http://*/*"].
|
||||||
|
Consider adding the missing permissions to
|
||||||
|
'${lib.showOption modulePath}.profiles.extensions.extensions."uBlock0@raymondhill.net".permissions'.
|
||||||
|
''
|
||||||
|
''
|
||||||
|
Must have exactly one extension with addonId 'unknown@example.com'
|
||||||
|
in '${lib.showOption modulePath}.profiles.extensions.extensions.packages' but found 0.
|
||||||
|
''
|
||||||
|
];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue