Browse Source

add unwanted extensions whitelist mode (#1798)

* add unwanted extensions whitelist mode

* only call get_ext once

* remove unneeded .lower()
pull/1800/head
jcfp 4 years ago
committed by GitHub
parent
commit
24d3d064bb
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      interfaces/Config/templates/config_switches.tmpl
  2. 12
      sabnzbd/assembler.py
  3. 1
      sabnzbd/cfg.py
  4. 17
      sabnzbd/filesystem.py
  5. 1
      sabnzbd/interface.py
  6. 6
      sabnzbd/nzbstuff.py
  7. 8
      sabnzbd/skintext.py
  8. 46
      tests/test_filesystem.py

4
interfaces/Config/templates/config_switches.tmpl

@ -120,6 +120,10 @@
</div>
<div class="field-pair">
<label class="config" for="unwanted_extensions">$T('opt-unwanted_extensions')</label>
<select name="unwanted_extensions_mode" id="unwanted_extensions_mode">
<option value="0" <!--#if int($unwanted_extensions_mode) == 0 then 'selected="selected"' else ""#--> >$T('unwanted_extensions_blacklist')</option>
<option value="1" <!--#if int($unwanted_extensions_mode) == 1 then 'selected="selected"' else ""#--> >$T('unwanted_extensions_whitelist')</option>
</select>
<input type="text" name="unwanted_extensions" id="unwanted_extensions" value="$unwanted_extensions"/>
<span class="desc">$T('explain-unwanted_extensions')</span>
</div>

12
sabnzbd/assembler.py

@ -30,7 +30,15 @@ from typing import Tuple, Optional, List
import sabnzbd
from sabnzbd.misc import get_all_passwords, match_str
from sabnzbd.filesystem import set_permissions, clip_path, has_win_device, diskspace, get_filename, get_ext
from sabnzbd.filesystem import (
set_permissions,
clip_path,
has_win_device,
diskspace,
get_filename,
get_ext,
has_unwanted_extension,
)
from sabnzbd.constants import Status, GIGI, MAX_ASSEMBLER_QUEUE
import sabnzbd.cfg as cfg
from sabnzbd.nzbstuff import NzbObject, NzbFile
@ -376,7 +384,7 @@ def check_encrypted_and_unwanted_files(nzo: NzbObject, filepath: str) -> Tuple[b
if cfg.unwanted_extensions() and cfg.action_on_unwanted_extensions():
for somefile in zf.namelist():
logging.debug("File contains: %s", somefile)
if get_ext(somefile).replace(".", "").lower() in cfg.unwanted_extensions():
if has_unwanted_extension(somefile):
logging.debug("Unwanted file %s", somefile)
unwanted = somefile
zf.close()

1
sabnzbd/cfg.py

@ -196,6 +196,7 @@ sanitize_safe = OptionBool("misc", "sanitize_safe", False)
cleanup_list = OptionList("misc", "cleanup_list")
unwanted_extensions = OptionList("misc", "unwanted_extensions")
action_on_unwanted_extensions = OptionNumber("misc", "action_on_unwanted_extensions", 0)
unwanted_extensions_mode = OptionNumber("misc", "unwanted_extensions_mode", 0)
new_nzb_on_failure = OptionBool("misc", "new_nzb_on_failure", False)
history_retention = OptionStr("misc", "history_retention", "0")
enable_meta = OptionBool("misc", "enable_meta", True)

17
sabnzbd/filesystem.py

@ -58,6 +58,23 @@ def get_ext(filename: str) -> str:
return ""
def has_unwanted_extension(filename: str) -> bool:
""" Determine if a filename has an unwanted extension, given the configured mode """
extension = get_ext(filename).replace(".", "")
if extension and sabnzbd.cfg.unwanted_extensions():
return (
# Blacklisted
sabnzbd.cfg.unwanted_extensions_mode() == 0
and extension in sabnzbd.cfg.unwanted_extensions()
) or (
# Not whitelisted
sabnzbd.cfg.unwanted_extensions_mode() == 1
and extension not in sabnzbd.cfg.unwanted_extensions()
)
else:
return bool(sabnzbd.cfg.unwanted_extensions_mode())
def get_filename(path: str) -> str:
""" Return path without the file extension """
try:

1
sabnzbd/interface.py

@ -1231,6 +1231,7 @@ SWITCH_LIST = (
"new_nzb_on_failure",
"unwanted_extensions",
"action_on_unwanted_extensions",
"unwanted_extensions_mode",
"sanitize_safe",
"rating_enable",
"rating_api_key",

6
sabnzbd/nzbstuff.py

@ -78,6 +78,7 @@ from sabnzbd.filesystem import (
make_script_path,
globber,
is_valid_script,
has_unwanted_extension,
)
from sabnzbd.decorators import synchronized
import sabnzbd.config as config
@ -890,10 +891,7 @@ class NzbObject(TryList):
# Check if there is any unwanted extension in plain sight in the NZB itself
for nzf in self.files:
if (
cfg.action_on_unwanted_extensions() >= 1
and get_ext(nzf.filename).replace(".", "") in cfg.unwanted_extensions()
):
if cfg.action_on_unwanted_extensions() and has_unwanted_extension(nzf.filename):
# ... we found an unwanted extension
logging.warning(T("Unwanted Extension in file %s (%s)"), nzf.filename, self.final_name)
# Pause, or Abort:

8
sabnzbd/skintext.py

@ -495,9 +495,13 @@ SKIN_TEXT = {
"nodupes-tag": TT("Tag job"), #: Four way switch for duplicates
"abort": TT("Abort"), #: Three way switch for encrypted posts
"opt-action_on_unwanted_extensions": TT("Action when unwanted extension detected"),
"explain-action_on_unwanted_extensions": TT("Action when an unwanted extension is detected in RAR files"),
"explain-action_on_unwanted_extensions": TT("Action when an unwanted extension is detected"),
"opt-unwanted_extensions": TT("Unwanted extensions"),
"explain-unwanted_extensions": TT("List all unwanted extensions. For example: <b>exe</b> or <b>exe, com</b>"),
"unwanted_extensions_blacklist": TT("Blacklist"),
"unwanted_extensions_whitelist": TT("Whitelist"),
"explain-unwanted_extensions": TT(
"Select a mode and list all (un)wanted extensions. For example: <b>exe</b> or <b>exe, com</b>"
),
"opt-sfv_check": TT("Enable SFV-based checks"),
"explain-sfv_check": TT("Do an extra verification based on SFV files."),
"opt-script_can_fail": TT("User script can flag job as failed"),

46
tests/test_filesystem.py

@ -983,3 +983,49 @@ class TestSetPermissions(ffs.TestCase, PermissionCheckerHelper):
def test_dir1755_umask4755_setting(self):
# Sticky bit on directory, umask with setuid
self._runner("1755", "4755")
class TestUnwantedExtensions:
# Only test lowercase extensions without a leading dot: the unwanted_extensions
# setting is sanitized accordingly in interface.saveSwitches() before saving.
test_extensions = "iso, cmd, bat, sh"
# Test parameters as (filename, result) tuples, with result given for blacklist mode
test_params = [
("ubuntu.iso", True),
("par2.cmd", True),
("freedos.BAT", True),
("Debian.installer.SH", True),
("FREEBSD.ISO", True),
("par2.CmD", True),
("freedos.baT", True),
("Debian.Installer.sh", True),
("ubuntu.torrent", False),
("par2.cmd.notcmd", False),
("freedos.tab", False),
(".SH.hs", False),
("No_Extension", False),
(480, False),
(None, False),
("", False),
([], False),
]
@set_config({"unwanted_extensions_mode": 0, "unwanted_extensions": test_extensions})
def test_has_unwanted_extension_blacklist_mode(self):
for filename, result in self.test_params:
assert filesystem.has_unwanted_extension(filename) is result
@set_config({"unwanted_extensions_mode": 1, "unwanted_extensions": test_extensions})
def test_has_unwanted_extension_whitelist_mode(self):
for filename, result in self.test_params:
assert filesystem.has_unwanted_extension(filename) is not result
@set_config({"unwanted_extensions_mode": 0, "unwanted_extensions": ""})
def test_has_unwanted_extension_empty_blacklist(self):
for filename, result in self.test_params:
assert filesystem.has_unwanted_extension(filename) is False
@set_config({"unwanted_extensions_mode": 1, "unwanted_extensions": ""})
def test_has_unwanted_extension_empty_whitelist(self):
for filename, result in self.test_params:
assert filesystem.has_unwanted_extension(filename) is True

Loading…
Cancel
Save