-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathlintrepo
executable file
·356 lines (318 loc) · 12.1 KB
/
lintrepo
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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
#!/bin/bash
# This is this repository's linter script. All it does it check for errors and potential risky
# habits. It's for pre-emerge quality control purposes. It will not guarantee that a ebuild
# will install or work without problems.
CI=${CI:=0}
VERBOSE=${VERBOSE:=0}
GREP_ARGS=-q
PORTAGE_DIR=${PORTAGE_DIR:-"/usr/portage"}
LAYMAN_DIR=${LAYMAN_DIR:-"/var/lib/layman"}
if [[ ${@} =~ (-v|--verbose) ]]; then
VERBOSE=1
GREP_ARGS=
fi
einfo() {
# echo "${1}" | fold -s -80
echo "${1}"
# echo ""
}
if grep --help | grep -q -P -e "--perl-regexp" ; then
:;
else
einfo "You need grep compiled with Perl regular expressions."
return 1
fi
if ! which shellcheck 2>/dev/null 1>/dev/null ; then
einfo "You need to install shellcheck to check ebuilds as bash scripts."
return 1
fi
if ! which xmllint 2>/dev/null 1>/dev/null ; then
einfo "You need to install xmllint to check metadata.xml."
return 1
fi
if ! which pcregrep 2>/dev/null 1>/dev/null ; then
einfo "You need to install libpcre"
fi
einfo "Checking ebuilds for security risks. (This can yield false positives.)"
for f in $(find $(pwd) -name "*.ebuild") ; do
if grep ${GREP_ARGS} -F -e "SLOT=\"0\"" "${f}" 2>/dev/null 1>/dev/null ; then
if [[ ${f} =~ ("-libs/"|"-gfx/"|dev-cpp|dev-qt|dev-db) ]] ;then
# It happens when copy pasting ebuild templates from apps to libs. Apps can use SLOT=0, but libraries should have a variable in subslot.
# USE="static-libs" PackageA:0 # insecure
# USE="static-libs" PackageB:0/${PV} # precautiously-secure for dependent because subslot changes trigger updates
# If a package uses a static binary and packages it in a squashfs used in initramfs or portable apps or iso9660 container used in live CDs and uses it, it is still vulnerable.
einfo "Security risk: ${f} does not trigger update for dependent with static binaries or static-libs security updates. SR1"
fi
fi
# It happens when = slot operator is not explicitly used. The dependent using the package as a static/static-libs will not receive a security update.
# PackageA[static-libs] -> PackageB ## insecure pattern
# PackageA:=[static-libs] -> PackageB ## secure pattern
if grep ${GREP_ARGS} -P -e '(?<!=)\[(static|static-libs)' "${f}" 2>/dev/null 1>/dev/null ; then
einfo "Security risk: ${f} ignores security update for subslot. SR2"
fi
if grep ${GREP_ARGS} -P -e '(?<!=)\[.*(static|static-libs),' "${f}" 2>/dev/null 1>/dev/null ; then
einfo "Security risk: ${f} ignores security update for subslot. SR3"
fi
if grep ${GREP_ARGS} -P -e '(?<!=)\[.*(static|static-libs)\]' "${f}" 2>/dev/null 1>/dev/null ; then
einfo "Security risk: ${f} ignores security update for subslot. SR4"
fi
if grep ${GREP_ARGS} -E -e '(fperms|chmod)' "${f}" 2>/dev/null 1>/dev/null \
&& grep ${GREP_ARGS} -E -e '[0]?77[0-7]' "${f}" 2>/dev/null 1>/dev/null ; then
einfo \
"Security risk: ${f} may use insecure permissions. Users may inject \
malicious scripts or exfiltrate data in multiuser environment when file uses \
777 or 77x. When file permission is 777, it is dangerous. For 77x it is \
only dangerous if the group is \`user\` or similar. SR5"
fi
if grep ${GREP_ARGS} -E -e 'cp.*(-a|-r|-R|--recursive|--archive)' "${f}" 2>/dev/null 1>/dev/null ; then
einfo \
"Security risk: ${f} files possibly bypass sanitation to reset ownership and \
file permissions. An upstream developer may inadvertantly elevate \
permissions or forget to reset it. Use ebuild install functions only or special \
wrapper install function. SR6"
fi
if grep ${GREP_ARGS} -E -e '(chown|fowners).*users' "${f}" ; then
einfo \
"Security risk: ${f} may use users group for fowners/chown with with go+w\n\
file/folder permissions for shell wrappers or it may be abused on\n\
files/folders to replace packages with compromised ones. SR7"
fi
done
# SC2076: Don't quote right-hand side of =~
# SC2148 -- shebang
# SC2068: Double quote array expansions to avoid re-splitting elements.
einfo "Checking ebuilds for syntax errors. (This can yield false positives.)"
for f in $(find $(pwd) -name "*.ebuild") ; do
if [[ "${CI}" == "1" ]] ; then
shellcheck -e SC2148,SC2076,SC2068 -S error "${f}" 2>/dev/null 1>/dev/null
elif [[ "${VERBOSE}" == "1" ]] ; then
shellcheck -e SC2148,SC2076,SC2068 -S error "${f}" || einfo "Syntax problems in ${f}"
else
shellcheck -e SC2148,SC2076,SC2068 -S error "${f}" 2>/dev/null 1>/dev/null || einfo "Syntax problems in ${f}"
fi
if grep ${GREP_ARGS} -F -e '$(use ' "${f}" ; then
einfo "Possible USE flag error \`use\` might need to be changed to \`usex\` in src_configure or when used as inline if-else. See ${f}"
fi
done
einfo "Checking repo for missing metadata.xml"
for f in $(find $(pwd) -name "*.ebuild") ; do
d=$(dirname ${f})
if [[ ! -f ${d}/metadata.xml ]] ; then
einfo "Missing metadata.xml in ${d}"
if [[ "${CI}" == "1" ]] ; then
return 1
fi
fi
done
einfo "Checking repo for missing/incomplete LICENSE in ebuild"
for f in $(find $(pwd) -name "*.ebuild") ; do
d=$(dirname ${f})
if ! grep ${GREP_ARGS} -F -e "LICENSE=" "${f}" ; then
einfo "Missing LICENSE= in ${f}"
if [[ "${CI}" == "1" ]] ; then
return 1
fi
fi
if grep ${GREP_ARGS} -F -e "LICENSE=\"\"" "${f}" ; then
einfo "Empty LICENSE=\"\" in ${f}"
if [[ "${CI}" == "1" ]] ; then
return 1
fi
fi
done
einfo "Checking repo for deprecated/EOL python use"
for f in $(find $(pwd) -name "*.ebuild") ; do
if grep ${GREP_ARGS} -E -e "PYTHON_COMPAT=.*(python2|python2_|python\{.*2_.*\}).*" "${f}" ; then
einfo "Please edit ${f} and remove python2_x from PYTHON_COMPAT"
if [[ "${CI}" == "1" ]] ; then
return 1
fi
fi
if grep ${GREP_ARGS} -E -e "PYTHON_COMPAT=.*(python3_4|python\{.*3_4.*\}|python3\{.*_4.*\}).*" "${f}" ; then
einfo "Please edit ${f} and remove python3_4 from PYTHON_COMPAT"
if [[ "${CI}" == "1" ]] ; then
return 1
fi
fi
if grep ${GREP_ARGS} -E -e "PYTHON_COMPAT=.*(python3_5|python\{.*3_5.*\}|python3\{.*_5.*\}).*" "${f}" ; then
einfo "Please edit ${f} and remove python3_5 from PYTHON_COMPAT"
if [[ "${CI}" == "1" ]] ; then
return 1
fi
fi
done
einfo "Checking metadata.xml for syntax errors."
for f in $(find $(pwd) -name "metadata.xml") ; do
if [[ "${CI}" == "1" ]] ; then
xmllint "${f}" 2>/dev/null 1>/dev/null
elif [[ "${VERBOSE}" == "1" ]] ; then
xmllint "${f}" || einfo "Syntax problems in ${f}"
else
xmllint "${f}" 2>/dev/null 1>/dev/null || einfo "Syntax problems in ${f}"
fi
done
if [[ "${VERBOSE}" == "1" ]] ; then
einfo "Checking repo for trash directories"
for d in $(find $(pwd) -name ".trash*" -type d) ; do
einfo "Found ${d}"
done
fi
einfo "Checking files with > 80 characters for GitHub code reviewers or for packagers"
for f in $(find $(pwd) \( -name "*.ebuild" -o -name "metadata.xml" \)) ; do
if pcregrep ${GREP_ARGS} -M '^.{80}.+\n' "${f}" ; then
einfo "Found ${f}"
fi
done
einfo "Checking repo for newicon .ico use ebuild"
for f in $(find $(pwd) -name "*.ebuild") ; do
if grep ${GREP_ARGS} -E -i -e "newicon.*\.(png|svg|xpm)" "${f}" ; then
:;
elif grep ${GREP_ARGS} -F -e "newicon" "${f}" ; then
einfo "${f} uses wrong format for icon. Not one of the official XDG icon formats for Linux desktop interoperability. Use either png, svg, or xpm only."
fi
done
einfo "Checking repo for missing pcre"
for f in $(find $(pwd) -name "*.ebuild") ; do
if grep ${GREP_ARGS} -G -e "grep.*-P" "${f}" ; then
einfo "${f} makes use of sys-devel/grep[pcre] which is not enabled by default. Check if it needs to be added to DEPENDS."
fi
done
_sanitize_package_list() {
ARG=$(</dev/stdin)
echo $(cat "${ARG}" \
| sed -r -e "/^[ \t]*(ewarn|einfo|eerror|elog|die|has_version)/d" \
| sed -r -e "/^[ \t]*#/d" \
| sed -r -e "/^[ \t]*if/d" \
| sed -e "/^\"/d" \
| sed -r -e "s|^[ \t]*[A-Z_]+=||g" \
| sed -r -e "s|[\"]||g" \
-e "s#(DEPEND|RDEPEND|CDEPEND|BDEPEND)##g" \
-e "s|[*]||g" \
| tr " " "\n" \
| sort \
| uniq \
| sed -r -e "s#(=|>=|<=|<|>|\!|\~|\|\|)##g" \
-e "s|[-a-zA-Z0-9]+\?||g" \
-e "s|[()]||g" -e "s|\[.*\]||g" \
-e "s|:.*||g" \
-e "s|-[_0-9a-z\.]+[ ]*$||g" \
-e "s|[ \t]*||g" -e "s|\$||g" \
| sed -r -e "s|[$][{].*[}]||g" \
-e "s|-$||g" \
| sort \
| uniq \
| sed -r -e "/^[ \t]*$/d")
}
# meta folders
NOT_CATEGORY="[.]|.git|.local|metadata|profiles|eclass|.travis|.github|licenses|etc"
get_all_portage_categories() {
pushd "${PORTAGE_DIR}" 2>/dev/null 1>/dev/null
find . -maxdepth 1 -type d | sed -r -e "s|\./||g" -e "/^(${NOT_CATEGORY})$/d"
popd 2>/dev/null 1>/dev/null
}
get_all_oiledmachine_overlay_categories() {
find . -maxdepth 1 -type d | sed -r -e "s|\./||g" -e "/^(${NOT_CATEGORY})$/d"
}
get_all_layman_categories() {
local skipped_first=0
pushd "${LAYMAN_DIR}" 2>/dev/null 1>/dev/null
for repo in $(find . -maxdepth 1 -type d) ; do
pushd "${repo}" 2>/dev/null 1>/dev/null
if [[ "${skipped_first}" == "0" ]] ; then
skipped_first=1
continue
fi
find . -maxdepth 1 -type d | sed -r -e "s|\./||g" -e "/^(${NOT_CATEGORY})$/d" | tr " " "\n"
popd 2>/dev/null 1>/dev/null
done
popd 2>/dev/null 1>/dev/null
}
# It's implied that the main program that is multilib capable as in compiled as
# 32-bit on 64-bit or a library that is multilib have all library
# dependendencies multilib.
categories=$(echo -e "$(get_all_portage_categories)$(get_all_oiledmachine_overlay_categories)$(get_all_layman_categories)" | sort | uniq | tr "\n" "|")
einfo "Checking repo for inconsistent multilib in (R)DEPENDs"
for f in $(find $(pwd) -name "*.ebuild") ; do
if grep ${GREP_ARGS} -F -e "multilib" "${f}" ; then
results_file=$(mktemp)
if grep -P -e '('${categories}')/(?![^\]]+MULTILIB_USEDEP[^\]]+)' \
"${f}" > ${results_file} ; then
#einfo "---start results---"
#cat "${results_file}"
#einfo "---end results---"
#einfo "---start sanitation--"
#echo "${results_file}" | _sanitize_package_list | tr " " "\n"
#einfo "---end sanitiation--"
showed_header=0
for p in $(echo "${results_file}" | _sanitize_package_list | tr " " "\n") ; do
reported=0
if ls ./${p}*/*.ebuild 2>/dev/null 1>/dev/null ; then
if grep -q -F -e "multilib_" ./${p}*/*.ebuild ; then
if [[ "${showed_header}" == "0" ]] ; then
einfo "${f} may need change to (R)DEPENDs with:"
showed_header=1
fi
echo "${p}[\$MULTILIB_USEDEP]"
reported=1
fi
fi
if ls -f "${PORTAGE_DIR}"/${p}*/*.ebuild 2>/dev/null 1>/dev/null && [[ "${reported}" == "0" ]] ; then
if grep -q -F -e "multilib_" "${PORTAGE_DIR}"/${p}*/*.ebuild ; then
if [[ "${showed_header}" == "0" ]] ; then
einfo "${f} may need change to (R)DEPENDs with:"
showed_header=1
fi
echo "${p}[\$MULTILIB_USEDEP]"
reported=1
fi
fi
if ls "${LAYMAN_DIR}"/*/${p}*/*.ebuild 2>/dev/null 1>/dev/null && [[ "${reported}" == "0" ]] ; then
if grep -q -F -e "multilib_" "${LAYMAN_DIR}"/*/${p}*/*.ebuild ; then
if [[ "${showed_header}" == "0" ]] ; then
einfo "${f} may need change to (R)DEPENDs with:"
showed_header=1
fi
echo "${p}[\$MULTILIB_USEDEP]"
reported=1
fi
fi
done
if [[ "${showed_header}" == "1" ]] ; then
echo
fi
fi
rm ${results_file}
fi
done
# TODO
#einfo "Checking for incomplete python (R/B)DEPENDs"
#for f in $(find $(pwd) -name "*.ebuild") ; do
# if grep -E -e "(distutils-r[0-9]+|python-any-r[0-9]+|python-r[0-9]+|python-single-r[0-9]+|python-utils-r[0-9]+)" ; then
# results_file=$(mktemp)
# if grep -P -e '('${categories}')/(?![^\]]+MULTILIB_USEDEP[^\]]+)' \
# "${f}" > ${results_file} ; then
# fi
# fi
#done
einfo "Checking for missing pkg_setup for python eclasses"
for f in $(grep -l -r --exclude-dir=.git "python-any-r1" ./) ; do
if grep -q -e "pkg_setup" "${f}" ; then
if ! grep -q -e "python-any-r1_pkg_setup" "${f}" ; then
echo "Missing python-any-r1_pkg_setup in ${f} for pkg_setup"
fi
fi
done
for f in $(grep -l -r --exclude-dir=.git "python-single-r1" ./) ; do
if grep -q -e "pkg_setup" "${f}" ; then
if ! grep -q -e "python-single-r1_pkg_setup" "${f}" ; then
echo "Missing python-single-r1_pkg_setup in ${f} for pkg_setup"
fi
fi
done
for f in $(grep -l -r --exclude-dir=.git "python-r1" ./) ; do
if grep -q -e "pkg_setup" "${f}" ; then
if ! grep -q -e "python_setup" "${f}" ; then
echo "Missing python_setup in ${f} for pkg_setup"
fi
fi
done