-
Notifications
You must be signed in to change notification settings - Fork 566
/
jail_prober.py
executable file
·206 lines (164 loc) · 6.34 KB
/
jail_prober.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
#!/usr/bin/env python3
# This file is part of Firejail project
# Copyright (C) 2014-2022 Firejail Authors
# License GPL v2
"""
Figure out which profile options may be causing a particular program to break
when run in firejail.
Instead of having to comment out each line in a profile by hand, and then
enable each line individually until the bad line or lines are found, this
largely automates the process. Users only have to provide the path to the
profile, program name, and answer 'y' for yes or 'n' for no when prompted.
After completion, you'll be provided with some information to copy and then
paste into a GitHub issue in the Firejail project repository:
https://github.com/netblue30/firejail/issues
Paths to the profile should be absolute. If the program is in your path, then
you only have to type the profile name. Else, you'll need to provide the
absolute path to the profile.
Examples:
python jail_prober.py /etc/firejail/spotify.profile spotify
python jail_prober.py /usr/local/etc/firejail/firefox.profile /usr/bin/firefox
"""
import sys
import os
import subprocess
def check_params(profile_path):
"""
Ensure the path to the profile is valid and that an actual profile has been
passed (as opposed to a config or .local file).
Args:
profile_path: The absolute path to the problematic profile
Raises:
FileNotFoundError: If the provided path isn't real
ValueError: If the provided path is real but doesn't point to
a Firejail profile
"""
if not os.path.isfile(profile_path):
raise FileNotFoundError('The path %s is not a valid system path.' %
profile_path)
if not profile_path.endswith('.profile'):
raise ValueError('%s is not a valid Firejail profile.' % profile_path)
def get_args(profile_path):
"""
Read the profile, stripping out comments and newlines
Args:
profile_path: The absolute path to the problematic profile.
Returns:
A list containing all active profile arguments
"""
with open(profile_path, 'r') as f:
profile = f.readlines()
profile = [
arg.strip() for arg in profile
if not arg.startswith('#') and arg.strip() != ''
]
return profile
def absolute_include(word):
home = os.environ['HOME']
path = home + '/.config/firejail/'
option, filename = word.split('=')
absolute_filename = path + filename
if not os.path.isfile(absolute_filename):
absolute_filename = '${CFG}/' + filename
return option + '=' + absolute_filename
def arg_converter(arg_list, style):
"""
Convert between firejail command-line arguments (--example=something) and
profile arguments (example something)
Args:
arg_list: A list of firejail arguments
style: String, one of {'to_profile', 'to_commandline'}. Whether to
convert arguments to command-line form or profile form
"""
if style == 'to_profile':
old_sep = '='
new_sep = ' '
prefix = ''
elif style == 'to_commandline':
old_sep = ' '
new_sep = '='
prefix = '--'
new_args = [prefix + word.replace(old_sep, new_sep) for word in arg_list]
# Additional strip of '--' if converting to profile form
if style == 'to_profile':
new_args = [word[2:] for word in new_args]
elif style == 'to_commandline':
new_args = [
absolute_include(word) if word.startswith('--include')
else word
for word in new_args
]
return new_args
def run_firejail(program, all_args):
"""
Attempt to run the program in firejail, incrementally adding to the number
of firejail arguments. Initial run has no additional params besides
noprofile.
Args:
program: String, the program name. If it doesn't exist in $PATH then
the full path to the program should be provided
all_args: List, all Firejail arguments to try, in command-line format
(i.e. prefixed by '--')
Returns:
good_args: List, all Firejail arguments that the user has reported to
not adversely affect the program
bad_args: List, all Firejail arguments that the user has reported to
break the program
"""
good_args = ['firejail', '--noprofile', program]
bad_args = []
all_args.insert(0, "")
print('Attempting to run %s in Firejail' % program)
for arg in all_args:
if arg:
print('Running with', arg)
else:
print('Running without profile')
#We are adding the argument in a copy of the actual list to avoid modify it now.
myargs = good_args.copy()
if arg:
myargs.insert(-1, arg)
subprocess.call(myargs)
ans = input('Did %s run correctly? [y]/n ' % program)
if ans in ['n', 'N']:
bad_args.append(arg)
elif arg:
good_args.insert(-1, arg)
print('\n')
# Don't include 'firejail', '--noprofile', or program name in arguments
good_args = good_args[2:-1]
return good_args, bad_args
def main():
try:
profile_path = sys.argv[1]
program = sys.argv[2]
except IndexError:
print('USAGE: jail_prober.py <PROFILE-PATH> <PROGRAM>')
sys.exit()
# Quick error check and extract arguments
check_params(profile_path)
profile = get_args(profile_path)
all_args = arg_converter(profile, 'to_commandline')
# Find out which profile options break the program when running in firejail
good_args, bad_args = run_firejail(program, all_args)
good_args = arg_converter(good_args, 'to_profile')
bad_args = arg_converter(bad_args, 'to_profile')
print('\n###########################')
print('Debugging completed.')
print(
'Please copy the following and report it to the Firejail development',
'team on GitHub at %s \n\n' %
'https://github.com/netblue30/firejail/issues')
subprocess.call(['firejail', '--version'])
print('These profile options break the program.')
print('```')
for item in bad_args:
print(item)
print('```\n\n\n')
print('This is a minimal working profile:')
print('```')
for item in good_args:
print(item)
print('```')
if __name__ == '__main__':
main()