From 533599f74e56dc42bdac215e2d152f9769b6b56e Mon Sep 17 00:00:00 2001 From: "B. Watson" Date: Mon, 4 Apr 2022 14:07:14 -0400 Subject: initial commit --- sbopkglint.d/05-basic-sanity.t.sh | 152 ++++++++++++++++++++++++++++++++++++++ sbopkglint.d/10-docs.t.sh | 40 ++++++++++ sbopkglint.d/15-noarch.t.sh | 14 ++++ sbopkglint.d/20-arch.t.sh | 69 +++++++++++++++++ sbopkglint.d/25-lafiles.t.sh | 22 ++++++ sbopkglint.d/30-manpages.t.sh | 110 +++++++++++++++++++++++++++ sbopkglint.d/35-desktop.t.sh | 35 +++++++++ sbopkglint.d/40-newconfig.t.sh | 22 ++++++ sbopkglint.d/45-doinst.t.sh | 53 +++++++++++++ 9 files changed, 517 insertions(+) create mode 100644 sbopkglint.d/05-basic-sanity.t.sh create mode 100644 sbopkglint.d/10-docs.t.sh create mode 100644 sbopkglint.d/15-noarch.t.sh create mode 100644 sbopkglint.d/20-arch.t.sh create mode 100644 sbopkglint.d/25-lafiles.t.sh create mode 100644 sbopkglint.d/30-manpages.t.sh create mode 100644 sbopkglint.d/35-desktop.t.sh create mode 100644 sbopkglint.d/40-newconfig.t.sh create mode 100644 sbopkglint.d/45-doinst.t.sh (limited to 'sbopkglint.d') diff --git a/sbopkglint.d/05-basic-sanity.t.sh b/sbopkglint.d/05-basic-sanity.t.sh new file mode 100644 index 0000000..6709203 --- /dev/null +++ b/sbopkglint.d/05-basic-sanity.t.sh @@ -0,0 +1,152 @@ +#!/bin/sh + +# sbopkglint test, must be sourced by sbopkglint (not run standalone). + +# PKG, PRGNAM, VERSION, ARCH are set by sbopkglint. also the current +# directory is the root of the installed package tree. + +####################################################################### +# these directories are allowed to exist in the package, but they +# must be mode 0755 and owned by root:root. if a dir from this list +# exists but is empty, that's an error. if a top-level directory +# exists that's *not* in this list (such as /dev), that's an error. +topleveldirs="bin boot etc lib lib64 opt sbin srv usr var run" + +# these directories are *required* to exist, and must be mode 0755, root:root. +# if a dir from this list exists but is empty, that's an error. note +# that the install/ dir no longer exists by the time we run (installpkg +# deleted it already). +requireddirs="usr/doc/$PRGNAM-$VERSION" + +# these directories *must not* exist. no need to list top-level dirs here, +# the topleveldirs check already catches those. +baddirs="usr/local usr/share/doc usr/share/man usr/etc usr/share/info usr/X11 usr/X11R6" + +# these directories may exist, but must contain only files or symlinks, +# and must be mode 0755, root:root. I thought usr/share/pixmaps +# belonged here, but quite a few packages create subdirs there for +# images required at runtime that aren't the app icon. +fileonlydirs="bin usr/bin sbin usr/sbin" + +# these directories may exist, but must contain only subdirectories +# (no files, symlinks, devices, etc). "." (the top-level package dir) +# doesn't need to be included here; it's checked separately. +nofiledirs="usr usr/doc usr/share usr/man" + +# these directories may exist but must not have executable files +# anywhere under them. I would put usr/doc and etc here, but too many +# packages break that rule. usr/share/applications is listed here, +# even though Slackware's KDE packages (erroneously) install .desktop +# files +x. +noexecdirs="usr/man usr/share/pixmaps usr/share/icons usr/share/applications usr/share/appdata usr/share/mime usr/share/mime-info usr/share/glib-2.0" + +# these files must exist. +requiredfiles="usr/doc/$PRGNAM-$VERSION/$PRGNAM.SlackBuild" + +# these files must not exist. +badfiles="\ +usr/info/dir \ +usr/info/dir.gz \ +usr/lib64/perl5/perllocal.pod \ +usr/lib/perl5/perllocal.pod \ +usr/share/perl5/perllocal.pod \ +usr/share/perl5/vendor_perl/perllocal.pod \ +etc/passwd \ +etc/passwd.new \ +etc/shadow \ +etc/shadow.new \ +etc/group \ +etc/group.new \ +etc/gshadow \ +etc/gshadow.new \ +etc/ld.so.conf" + +####################################################################### + +# include 'hidden' files/dirs in * wildcard expansion. +shopt -s dotglob + +dir_ok() { + [ -d "$1" ] && \ + [ "$( stat -c '%A %U %G' "$1" )" = "drwxr-xr-x root root" ] +} + +dir_empty() { + [ "$( find "$1" -mindepth 1 -maxdepth 1 )" = "" ] +} + +warn_badperms() { + warn "bad permissions/owner (should be 0755 root:root): $1" +} + +for i in *; do + if [ ! -d "$i" ]; then + warn "package root dir contains non-directory: $i" + elif ! echo "$topleveldirs" | grep -q "\\<$i\\>"; then + warn "package root dir contains non-standard directory: $i" + elif ! dir_ok "$i"; then + warn_badperms "$i" + elif dir_empty "$i"; then + warn "package contains empty top-level directory: $i" + fi +done + +for i in $requireddirs; do + if [ ! -d "$i" ]; then + warn "missing required directory: $i" + elif ! dir_ok "$i"; then + warn_badperms "$i" + fi +done + +for i in $baddirs; do + if [ -d "$i" ]; then + warn "forbidden directory exists: $i" + elif [ -e "$i" ]; then + warn "forbidden directory exists as a non-directory: $i" + fi +done + +for i in $fileonlydirs; do + [ -d "$i" ] || continue + dir_ok "$i" || warn_badperms "$i" + badstuff="$( find -L "$i" -mindepth 1 -maxdepth 1 \! -type f )" + [ -n "$badstuff" ] && warn "$i should only contain files, not:" && ls -ld $badstuff +done + +for i in $nofiledirs; do + [ -d "$i" ] || continue + dir_ok "$i" || warn_badperms "$i" + badstuff="$( find -L "$i" -mindepth 1 -maxdepth 1 \! -type d )" + [ -n "$badstuff" ] && warn "$i should only contain directories, not:" && ls -ld $badstuff +done + +for i in $requiredfiles; do + [ -f "$i" ] || warn "missing required file: $i" +done + +for i in $noexecdirs; do + [ -d "$i" ] || continue + found="$( find "$i" -type f -a -perm /0111 )" + if [ -n "$found" ]; then + warn "$i should not contain files with executable permission:" + ls -l $found + fi +done + +for i in $badfiles; do + [ -e "$i" ] && warn "forbidden file: $i" +done + +badlinks="$( find -L . -type l )" +[ -n "$badlinks" ] && for i in $badlinks; do + target="$( readlink "$i" )" + case "$target" in + /*) abslinks+="$i " ;; + *) brokenlinks+="$i " ;; + esac +done + +[ -n "$abslinks" ] && warn "package contains absolute symlinks (should be relative):" && ls -ld $abslinks +[ -n "$brokenlinks" ] && warn "package contains broken symlinks:" && ls -ld $brokenlinks + diff --git a/sbopkglint.d/10-docs.t.sh b/sbopkglint.d/10-docs.t.sh new file mode 100644 index 0000000..e1bb8d8 --- /dev/null +++ b/sbopkglint.d/10-docs.t.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +# sbopkglint test, must be sourced by sbopkglint (not run standalone). + +# PKG, PRGNAM, VERSION, ARCH are set by sbopkglint. also the current +# directory is the root of the installed package tree. + +######################################################################## +# checks file permissions and ownership in the package doc dir. files +# should all be mode 644, directories should be 755. everything should +# be owned by root:root. also checks for empty files or (possibly) +# install instructions. + + +# ideally, we'd require all files under the doc dir to be mode 0644. +# however, too many existing packages (including core Slackware ones) +# break that rule. so check for the minimum set of desired permissions: +# a doc file should be readable by all users (at least 444). + +DOCDIR=usr/doc/$PRGNAM-$VERSION + +# existence of the doc dir was already checked by a previous test, +# so just don't do anything if it's missing. + +if [ -d "$DOCDIR" ]; then + badpermfiles="$( find $DOCDIR -mindepth 1 -type f -a \! -perm -444 )" + badpermdirs="$( find $DOCDIR -mindepth 1 -type d -a \! -perm 0755 )" + badowners="$( find $DOCDIR -mindepth 1 -user root -a -group root -o -print )" + empty="$( find $DOCDIR -mindepth 1 -empty )" + bogus="$( find $DOCDIR -mindepth 1 -maxdepth 1 -type f -a \( -name INSTALL -o -name INSTALL.\* \) )" + + [ -n "$badpermfiles" ] && warn "bad file perms (should be 644, or at least 444) in doc dir:" && ls -l $badpermfiles + [ -n "$badpermdirs" ] && warn "bad directory perms (should be 755) in doc dir:" && ls -ld $badpermdirs + [ -n "$badowners" ] && warn "bad ownership (should be root:root) in doc dir:" && ls -ld $badowners + [ -n "$empty" ] && warn "empty files/dirs in doc dir: $empty" + [ -n "$bogus" ] && [ -z "$INSTALL_DOCS_OK" ] && warn "useless-looking install instructions in doc dir: $bogus" +fi + +baddocs="$( find usr/doc -mindepth 1 -maxdepth 1 \! -name $PRGNAM-$VERSION )" +[ -n "$baddocs" ] && warn "docs outside of $DOCDIR:" && ls -ld $baddocs diff --git a/sbopkglint.d/15-noarch.t.sh b/sbopkglint.d/15-noarch.t.sh new file mode 100644 index 0000000..55c17f6 --- /dev/null +++ b/sbopkglint.d/15-noarch.t.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# sbopkglint test, must be sourced by sbopkglint (not run standalone). + +# PKG, PRGNAM, VERSION, ARCH are set by sbopkglint. also the current +# directory is the root of the installed package tree. + +######################################################################## +# makes sure "noarch" packages really are noarch. + +if [ "$ARCH" = "noarch" ]; then + elfbins="$( find * -type f -print0 | xargs -0 file -m /etc/file/magic/elf | grep ELF | cut -d: -f1 )" + [ -n "$elfbins" ] && warn "package claims to be noarch, but contains ELF binaries:" && ls -l $elfbins +fi diff --git a/sbopkglint.d/20-arch.t.sh b/sbopkglint.d/20-arch.t.sh new file mode 100644 index 0000000..81a91a4 --- /dev/null +++ b/sbopkglint.d/20-arch.t.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# sbopkglint test, must be sourced by sbopkglint (not run standalone). + +# PKG, PRGNAM, VERSION, ARCH are set by sbopkglint. also the current +# directory is the root of the installed package tree. + +######################################################################## +# for noarch packages, do nothing. +# for everything else, make sure any ELF binaries/libraries match the +# ARCH, and that libs are in the correct directory (lib vs. lib64). + +# warnings: +# if an i?86 package has any 64-bit ELF objects (libs or bins) +# if an x86_64 package has any 32-bit ELF objects (libs or bins) +# if an i?86 package has lib64 or usr/lib64 at all +# if an x86_64 package has 64-bit libs in lib or usr/lib + +# note: sometimes files in /lib/firmware are ELF, and would cause +# false "wrong directory" warnings, so we exclude that dir from the +# search. + +case "$ARCH" in + noarch) ;; # ok, do nothing. + i?86) WRONGDIR="lib64"; CPU="80386" ;; + x86_64) WRONGDIR="lib"; CPU="x86-64" ;; + *) warn "ARCH isn't noarch, i?86, or x86_64. don't know how to check binaries." ;; +esac + +INWRONGDIR="" +WRONGARCH="" +NOTSTRIPPED="" +if [ -n "$WRONGDIR" ]; then + find * -type f -print0 | \ + xargs -0 file -m /etc/file/magic/elf | \ + grep 'ELF.*\(executable\|shared object\)' > .tmp.$$ + + while read line; do + file="$( echo $line | cut -d: -f1 )" + filetype="$( echo $line | cut -d: -f2 )" + case "$file" in + lib/firmware/*) continue ;; + $WRONGDIR/*|usr/$WRONGDIR/*) + INWRONGDIR+="$file " ;; + esac + + # 64-bit packages can contain 2 types of 32-bit binaries: + # - statically linked. + # - statified. very few of these exist, and we can't make + # them on 15.0 (statifier can't handle modern kernel/glibc + # and the author hasn't updated it). + if [ "$ARCH" = "x86_64" ]; then + echo "$filetype" | grep -q 'statically linked' && continue + grep -q DL_RO_DYN_TEMP_CNT "$file" && continue + fi + + if ! echo "$filetype" | grep -q "$CPU"; then + WRONGARCH+="$file " + fi + if echo "$filetype" | grep -q "not stripped"; then + NOTSTRIPPED+="$file " + fi + done < .tmp.$$ + rm -f .tmp.$$ +fi + +[ -n "$INWRONGDIR" ] && warn "shared lib(s) in wrong dir for ARCH:" && ls -l $INWRONGDIR +[ -n "$WRONGARCH" ] && warn "ELF object(s) with wrong arch (should be $CPU):" && ls -l $WRONGARCH +[ -n "$NOTSTRIPPED" ] && warn "ELF object(s) not stripped:" && ls -l $NOTSTRIPPED diff --git a/sbopkglint.d/25-lafiles.t.sh b/sbopkglint.d/25-lafiles.t.sh new file mode 100644 index 0000000..d4c56f7 --- /dev/null +++ b/sbopkglint.d/25-lafiles.t.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# sbopkglint test, must be sourced by sbopkglint (not run standalone). + +# PKG, PRGNAM, VERSION, ARCH are set by sbopkglint. also the current +# directory is the root of the installed package tree. + +######################################################################## +# check for .la files, according to the Slackware 15.0 guidelines. +# they're not allowed directly in /lib /lib64 /usr/lib /usr/lib64, but +# they're OK in subdirectories (e.g. /usr/lib64/appname/plugins/foo.la). + +for i in lib lib64 usr/lib usr/lib64; do + [ -d "$i" ] || continue + found="$( find $i -maxdepth 1 -name '*.la' )" + [ -n "$found" ] && LAFILES+="$found " +done + +if [ -n "$LAFILES" ]; then + warn "package contains .la files:" + ls -l $LAFILES +fi diff --git a/sbopkglint.d/30-manpages.t.sh b/sbopkglint.d/30-manpages.t.sh new file mode 100644 index 0000000..e0978b3 --- /dev/null +++ b/sbopkglint.d/30-manpages.t.sh @@ -0,0 +1,110 @@ +#!/bin/sh + +# sbopkglint test, must be sourced by sbopkglint (not run standalone). + +# PKG, PRGNAM, VERSION, ARCH are set by sbopkglint. also the current +# directory is the root of the installed package tree. + +####################################################################### +# if the package contains the usr/man dir, make sure that: +# all the dirs match usr/man/man[1-9n] or usr/man//man[1-9n]. +# all the files under /usr/man are gzipped and end in
.gz. +# all the files under /usr/man have mode 0644. executable man pages +# don't make sense, and man pages that aren't world-readable are +# annoying and wrong. +# all the filenames match the sections (e.g. usr/man/man1/foo.1.gz +# matches, usr/man/man1/bar.2.gz doesn't). +# all the gzipped files look like they contain *roff. + +BADPERMS="" +BADDIRPERMS="" +BADDIRS="" +BADNAMES="" +NOTGZIPPED="" +NONTROFF="" +WRONGSECT="" + +check_gzipped_page() { + local f="$1" + local d="$( dirname $f )" + local s="$( stat -c '%a %U %G' "$f" )" + + if [ "$s" = "444 root root" ]; then + : # we could warn here someday + elif [ "$s" != "644 root root" ]; then + BADPERMS+="$f " + fi + + if [ "$( file -L -b --mime-type "$f" )" != "application/gzip" ]; then + NOTGZIPPED+="$f " + fi + + # I have ~42,000 man pages on my dev box, file(1) fails to identify + # 12 of them as troff, but adding the check for .T catches them all. + if [ "$( file -z -m /etc/file/magic/troff -L -b --mime-type "$f" )" != "text/troff" ]; then + if ! zgrep -q '^\.T' "$f"; then + NONTROFF+="$f " + fi + fi + + # checking the section is tricky because e.g. /usr/man/man1 may contain + # files with with names like .1.gz, .1x.gz, .1.pm.gz. + + # if the section in the directory name is bad, don't complain here, it + # was already reported. + dir_s="$( echo "$d" | sed -n 's,.*man\([0-9n]\)$,\1,p' )" + if [ "$dir_s" = "" ]; then + return + fi + + s="$( basename "$f" | sed -n 's,.*\.\([0-9n]\)[^.]*\.gz,\1,p' )" + if [ "$s" = "" ]; then + BADNAMES+="$f " + elif [ "$s" != "$dir_s" ]; then + WRONGSECT+="$f " + fi +} + +# called for paths like /usr/man/de. right now, it accepts names like +# xx_XX or xx.UTF-8, or actually anything whose first 2 characters match +# one of the dirs in /usr/share/locale. could use some refining. I don't +# even know if the *.UTF-8 or *.ISO8859-1 dirs get searched by man-db. +# Note that slackware's own libcdio-paranoia install man pages in +# /usr/man/jp, which is invalid. +check_locale_dir() { + l="$( echo "$1" | sed 's,^.*/\(..\).*$,\1,' )" + [ -e /usr/share/locale/"$l" ] || warn "bad locale dir in /usr/man: $l" +} + +if [ -d usr/man ]; then + find usr/man -type f > .manpages.$$ + find usr/man -mindepth 1 -type d > .mandirs.$$ + + while read d; do + case "$d" in + usr/man/man[1-9n]|usr/man/*/man[1-9n]) + [ "$( stat -c '%a %U %G' "$d" )" != "755 root root" ] && BADDIRPERMS+="$d " + ;; + usr/man/??*) check_locale_dir "$d" ;; + *) BADDIRS+="$d " ;; + esac + done < .mandirs.$$ + + while read f; do + case "$f" in + *.gz) check_gzipped_page "$f" ;; + *) BADNAMES+="$f " ;; + esac + done < .manpages.$$ + + rm -f .manpages.$$ .mandirs.$$ + + [ -n "$BADPERMS" ] && warn "bad man page owner/permissions (should be 0644, root:root)" && ls -ld $BADPERMS + [ -n "$BADDIRPERMS" ] && warn "bad man directory owner/permissions (should be 0755, root:root)" && ls -ld $BADDIRPERMS + [ -n "$BADDIRS" ] && warn "bad directory names in /usr/man:" && ls -ld $BADDIRS + [ -n "$BADNAMES" ] && warn "bad man page names (not *.gz):" && ls -ld $BADNAMES + [ -n "$NOTGZIPPED" ] && warn "non-gzip (but named *.gz) man pages:" && ls -ld $NOTGZIPPED + [ -n "$NONTROFF" ] && warn "invalid man pages (not troff):" && ls -ld $NONTROFF + [ -n "$WRONGSECT" ] && warn "man pages in wrong section:" && ls -ld $WRONGSECT +fi + diff --git a/sbopkglint.d/35-desktop.t.sh b/sbopkglint.d/35-desktop.t.sh new file mode 100644 index 0000000..9650ef4 --- /dev/null +++ b/sbopkglint.d/35-desktop.t.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +# sbopkglint test, must be sourced by sbopkglint (not run standalone). + +# PKG, PRGNAM, VERSION, ARCH are set by sbopkglint. also the current +# directory is the root of the installed package tree. + +####################################################################### +# if the package contains any files in /usr/share/applications/, they +# must be named *.desktop, must pass desktop-file-validate, and must +# be mode 644, owner root:root. + +BADPERMS="" +BADDESKTOP="" +NONDESKTOP="" +if [ -d usr/share/applications ]; then + # doinst.sh creates this, don't check here now that we have a doinst test. + #[ -e "usr/share/applications/mimeinfo.cache" ] || warn "doinst.sh is missing update-desktop-database" + + for f in usr/share/applications/*; do + [ -e "$f" ] || continue + + [ "$f" = "usr/share/applications/mimeinfo.cache" ] && continue + + [ "$( stat -Lc '%a %U %G' "$f" )" = "644 root root" ] || BADPERMS+="$f " + case "$f" in + *.desktop) desktop-file-validate "$f" || BADDESKTOP+="$f " ;; + *) NONDESKTOP+="$f " ;; + esac + done + + [ -n "$BADPERMS" ] && warn "bad permissions/owner on .desktop files (should be 0644 root:root):" && ls -ld $BADPERMS + [ -n "$BADDESKTOP" ] && warn ".desktop files fail to validate:" && ls -ld $BADDESKTOP + [ -n "$NONDESKTOP" ] && warn "unknown file (not .desktop) in desktop dir:" && ls -ld $NONDESKTOP +fi diff --git a/sbopkglint.d/40-newconfig.t.sh b/sbopkglint.d/40-newconfig.t.sh new file mode 100644 index 0000000..ee0aec5 --- /dev/null +++ b/sbopkglint.d/40-newconfig.t.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# sbopkglint test, must be sourced by sbopkglint (not run standalone). + +# PKG, PRGNAM, VERSION, ARCH are set by sbopkglint. also the current +# directory is the root of the installed package tree. + +####################################################################### +# check for .new config files. there shouldn't be any, because when +# sbopkglint installed the package, it ran the doinst.sh, which +# should have renamed them. + +NEWFILES="" +for f in "$( find -type f -name \*.new )"; do + [ -z "$f" ] && continue + case "$f" in + ./usr/doc/*) continue ;; + esac + NEWFILES+="$f " +done + +[ -n "$NEWFILES" ] && warn "doinst.sh doesn't handle .new config files:" && ls -l $NEWFILES diff --git a/sbopkglint.d/45-doinst.t.sh b/sbopkglint.d/45-doinst.t.sh new file mode 100644 index 0000000..ae47c72 --- /dev/null +++ b/sbopkglint.d/45-doinst.t.sh @@ -0,0 +1,53 @@ +#!/bin/sh + +# sbopkglint test, must be sourced by sbopkglint (not run standalone). + +# PKG, PRGNAM, VERSION, ARCH are set by sbopkglint. also the current +# directory is the root of the installed package tree. This test +# also uses the filename variable. + +####################################################################### +# check the doinst.sh. its job is to generate various files by running +# e.g. update-desktop-database, which creates a cache file. if the +# cache file exists, and doinst.sh either doesn't exist, or doesn't +# contain an update-desktop-database, that's very bad: it means +# the cache file is actually included in the package. which means, +# installing such a package would overwrite the user's local cache, +# breaking his desktop, until he fixes it (either manually or giving +# up and logging out or rebooting). + +doinst=var/lib/pkgtools/scripts/"$( echo $filename | sed 's,\.[^.]*$,,' )" + +have_doinst() { + [ -e "$doinst" ] + return $? +} + +grep_doinst() { + have_doinst && grep -q "$@" $doinst + return $? +} + +doinst_warn() { + local msg="doinst.sh is missing, package needs one, with" + have_doinst && msg="doinst.sh exists, but is missing" + warn "$msg $@" +} + +doinst_chk_command() { + local cmd="$1" + grep_doinst "$cmd" || doinst_warn "$cmd" +} + +[ "$( find -L usr/share/icons -type f 2>/dev/null )" != "" ] && \ + doinst_chk_command "gtk-update-icon-cache" + +[ "$( find -L usr/share/applications -type f 2>/dev/null )" != "" ] && \ + doinst_chk_command "update-desktop-database" + +[ "$( find -L usr/share/glib-2.0/schemas -type f 2>/dev/null )" != "" ] && \ + doinst_chk_command "glib-compile-schemas" + +[ "$( find -L usr/share/fonts -type f 2>/dev/null )" != "" ] && \ + doinst_chk_command "fc-cache" + -- cgit v1.2.3