• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bleachbit / bleachbit / 23171475269

16 Mar 2026 11:50PM UTC coverage: 73.166% (+2.0%) from 71.186%
23171475269

push

github

az0
Gracefully handle missing urllib3

- Do not crash on startup in case of missing urllib3.
- Check for missing packages.
- Notify the user.

Affects BleachBit 5.1.0 and 5.1.1 in Flatpak

Closes https://github.com/bleachbit/bleachbit/issues/2056

20 of 25 new or added lines in 2 files covered. (80.0%)

1105 existing lines in 26 files now uncovered.

7043 of 9626 relevant lines covered (73.17%)

0.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

26.92
/bleachbit/RecognizeCleanerML.py
1
# vim: ts=4:sw=4:expandtab
2

3
# BleachBit
4
# Copyright (C) 2008-2025 Andrew Ziem
5
# https://www.bleachbit.org
6
#
7
# This program is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19

20

21
"""
22
Check local CleanerML files as a security measure
23
"""
24

25
# standard imports
26
import hashlib
1✔
27
import logging
1✔
28
import os
1✔
29
import sys
1✔
30

31
# first party imports
32
from bleachbit.Language import get_text as _, pget_text as _p
1✔
33
import bleachbit
1✔
34
from bleachbit.CleanerML import list_cleanerml_files
1✔
35
from bleachbit.Options import options
1✔
36

37

38
logger = logging.getLogger(__name__)
1✔
39

40
KNOWN = 1
1✔
41
CHANGED = 2
1✔
42
NEW = 3
1✔
43

44

45
def cleaner_change_dialog(changes, parent):
1✔
46
    """Present a dialog regarding the change of cleaner definitions"""
47

48
    def toggled(_cell, path, model):
×
49
        """Callback for clicking the checkbox"""
50
        __iter = model.get_iter_from_string(path)
×
UNCOV
51
        value = not model.get_value(__iter, 0)
×
UNCOV
52
        model.set(__iter, 0, value)
×
53

54
    # TODO: move to GuiBasic
55
    from bleachbit.GtkShim import Gtk, GObject
×
56

UNCOV
57
    dialog = Gtk.Dialog(title=_("Security warning"),
×
58
                        transient_for=parent,
59
                        modal=True, destroy_with_parent=True)
UNCOV
60
    dialog.set_default_size(600, 500)
×
61

62
    # create warning
63
    warnbox = Gtk.Box()
×
64
    image = Gtk.Image()
×
UNCOV
65
    image.set_from_icon_name("dialog-warning", Gtk.IconSize.DIALOG)
×
UNCOV
66
    warnbox.pack_start(image, False, True, 0)
×
67

68
    # TRANSLATORS: Cleaner definitions are XML data files that define
69
    # which files will be cleaned.
70
    label = Gtk.Label(
×
71
        label=_("These cleaner definitions are new or have changed. Malicious definitions can damage your system. If you do not trust these changes, delete the files or quit."))
72
    label.set_line_wrap(True)
×
UNCOV
73
    warnbox.pack_start(label, True, True, 0)
×
UNCOV
74
    dialog.vbox.pack_start(warnbox, False, True, 0)
×
75

76
    # create tree view
UNCOV
77
    liststore = Gtk.ListStore(GObject.TYPE_BOOLEAN, GObject.TYPE_STRING)
×
78
    treeview = Gtk.TreeView(model=liststore)
×
79

80
    renderer0 = Gtk.CellRendererToggle()
×
UNCOV
81
    renderer0.set_property('activatable', True)
×
UNCOV
82
    renderer0.connect('toggled', toggled, liststore)
×
83
    # TRANSLATORS: This is the column label (header) in the tree view for the
84
    # security dialog
85
    treeview.append_column(
×
86
        Gtk.TreeViewColumn(_p('column_label', 'Delete'), renderer0, active=0))
UNCOV
87
    renderer1 = Gtk.CellRendererText()
×
88
    # TRANSLATORS: This is the column label (header) in the tree view for the
89
    # security dialog
UNCOV
90
    treeview.append_column(
×
91
        Gtk.TreeViewColumn(_p('column_label', 'Filename'), renderer1, text=1))
92

93
    # populate tree view
UNCOV
94
    for change in changes:
×
UNCOV
95
        liststore.append([False, change[0]])
×
96

97
    # populate dialog with widgets
98
    scrolled_window = Gtk.ScrolledWindow()
×
UNCOV
99
    scrolled_window.add(treeview)
×
100
    dialog.vbox.pack_start(scrolled_window, True, True, 0)
×
101

UNCOV
102
    dialog.add_button(Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT)
×
UNCOV
103
    dialog.add_button(Gtk.STOCK_QUIT, Gtk.ResponseType.CLOSE)
×
104

105
    # run dialog
106
    dialog.show_all()
×
107
    while True:
108
        if Gtk.ResponseType.ACCEPT != dialog.run():
×
109
            sys.exit(0)
×
110
        delete = []
×
111
        for row in liststore:
×
112
            b = row[0]
×
113
            path = row[1]
×
114
            if b:
×
UNCOV
115
                delete.append(path)
×
116
        if 0 == len(delete):
×
117
            # no files selected to delete
118
            break
×
UNCOV
119
        from . import GuiBasic
×
120
        if not GuiBasic.delete_confirmation_dialog(parent, mention_preview=False):
×
121
            # confirmation not accepted, so do not delete files
122
            continue
×
123
        for path in delete:
×
124
            logger.info("deleting unrecognized CleanerML '%s'", path)
×
125
            os.remove(path)
×
UNCOV
126
        break
×
UNCOV
127
    dialog.destroy()
×
128

129

130
def hashdigest(string):
1✔
131
    """Return hex digest of hash for a string"""
132

133
    # hashlib requires Python 2.5
134
    if isinstance(string, str):
1✔
135
        string = string.encode()
1✔
136
    return hashlib.sha512(string).hexdigest()
1✔
137

138

139
class RecognizeCleanerML:
1✔
140

141
    """Check local CleanerML files as a security measure"""
142

143
    def __init__(self, parent_window=None):
1✔
144
        self.parent_window = parent_window
1✔
145
        try:
1✔
146
            self.salt = options.get('hashsalt')
1✔
147
        except bleachbit.NoOptionError:
×
UNCOV
148
            self.salt = hashdigest(os.urandom(512))
×
UNCOV
149
            options.set('hashsalt', self.salt)
×
150
        self.__scan()
1✔
151

152
    def __recognized(self, pathname):
1✔
153
        """Is pathname recognized?"""
154
        try:
×
155
            with open(pathname, 'rb') as f:
×
156
                body = f.read()
×
157
        except OSError:  # The file is locked, what to do next?
×
158
            return NEW, hashdigest(b"")
×
159
        new_hash = hashdigest(str.encode(self.salt, encoding='utf-8') + body)
×
160
        try:
×
161
            known_hash = options.get_hashpath(pathname)
×
162
        except bleachbit.NoOptionError:
×
163
            return NEW, new_hash
×
164
        if new_hash == known_hash:
×
UNCOV
165
            return KNOWN, new_hash
×
UNCOV
166
        return CHANGED, new_hash
×
167

168
    def __scan(self):
1✔
169
        """Look for files and act accordingly"""
170
        changes = []
1✔
171
        for pathname in sorted(list_cleanerml_files(local_only=True)):
1✔
172
            pathname = os.path.abspath(pathname)
×
173
            (status, myhash) = self.__recognized(pathname)
×
UNCOV
174
            if NEW == status or CHANGED == status:
×
175
                changes.append([pathname, status, myhash])
×
176
        if changes:
1✔
177
            cleaner_change_dialog(changes, self.parent_window)
×
178
            for change in changes:
×
179
                pathname = change[0]
×
180
                myhash = change[2]
×
181
                logger.info("remembering CleanerML file '%s'", pathname)
×
UNCOV
182
                if os.path.exists(pathname):
×
UNCOV
183
                    options.set_hashpath(pathname, myhash)
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc