V9.14.022.2026.06.11: document and test audit safeguards
This commit is contained in:
Executable
+191
@@ -0,0 +1,191 @@
|
||||
#!/bin/bash
|
||||
# SPDX-Version: 3.0
|
||||
# SPDX-CreationInfo: 2026-06-11; WEIDNER, Marc S.; <msw@coresecret.dev>
|
||||
# SPDX-ExternalRef: GIT https://git.coresecret.dev/msw/CISS.debian.live.builder.git
|
||||
# SPDX-FileContributor: WEIDNER, Marc S.; Centurion Intelligence Consulting Agency
|
||||
# SPDX-FileCopyrightText: 2024-2026; WEIDNER, Marc S.; <msw@coresecret.dev>
|
||||
# SPDX-FileType: SOURCE
|
||||
# SPDX-License-Identifier: LicenseRef-CNCL-1.1 OR LicenseRef-CCLA-1.1
|
||||
# SPDX-LicenseComment: This file is part of the CISS.debian.installer.secure framework.
|
||||
# SPDX-PackageName: CISS.debian.live.builder
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
set -Ceuo pipefail
|
||||
# shellcheck disable=SC1091,SC2034
|
||||
|
||||
declare TEST_ROOT=""
|
||||
declare TEST_TMP=""
|
||||
TEST_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
TEST_TMP="$(realpath "$(mktemp -d)")"
|
||||
readonly TEST_ROOT TEST_TMP
|
||||
declare -r ERR_BUILD_PATH=217
|
||||
declare -r ERR_GUARD_SRCE=131
|
||||
declare -r ERR_SANITIZING=133
|
||||
declare -r ERR_SECRET_PATH=216
|
||||
declare -r VAR_WORKDIR="${TEST_ROOT}"
|
||||
declare VAR_TMP_SECRET="${TEST_TMP}/secret-root"
|
||||
declare LOG_DEBUG=""
|
||||
declare LOG_ERROR=""
|
||||
declare LOG_VAR=""
|
||||
declare VAR_EARLY_DEBUG="false"
|
||||
declare ERRTRAP="true"
|
||||
|
||||
cleanup_test() {
|
||||
chmod -R u+rwX "${TEST_TMP}" 2>/dev/null || true
|
||||
rm -rf "${TEST_TMP}"
|
||||
}
|
||||
trap cleanup_test EXIT
|
||||
|
||||
guard_sourcing() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# shellcheck source=../lib/lib_secret_validation.sh
|
||||
. "${TEST_ROOT}/lib/lib_secret_validation.sh"
|
||||
# shellcheck source=../lib/lib_build_directory.sh
|
||||
. "${TEST_ROOT}/lib/lib_build_directory.sh"
|
||||
# shellcheck source=../lib/lib_debug_sanitizer.sh
|
||||
. "${TEST_ROOT}/lib/lib_debug_sanitizer.sh"
|
||||
# shellcheck source=../lib/lib_trap_on_exit.sh
|
||||
. "${TEST_ROOT}/lib/lib_trap_on_exit.sh"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
expect_failure() {
|
||||
declare description="$1"
|
||||
shift
|
||||
if "$@" >/dev/null 2>&1; then
|
||||
fail "${description}"
|
||||
fi
|
||||
}
|
||||
|
||||
mkdir -m 0700 "${VAR_TMP_SECRET}"
|
||||
printf 'safe-private-value\n' > "${VAR_TMP_SECRET}/safe.txt"
|
||||
chmod 0400 "${VAR_TMP_SECRET}/safe.txt"
|
||||
|
||||
validate_secret_directory "${VAR_TMP_SECRET}" "test secret root" "false" || fail "safe secret root rejected"
|
||||
validate_secret_filename "safe.txt" "test secret filename" || fail "safe secret filename rejected"
|
||||
validate_secret_file "${VAR_TMP_SECRET}/safe.txt" "test secret file" || fail "safe secret file rejected"
|
||||
validate_secret_absolute_file "${VAR_TMP_SECRET}/safe.txt" "test absolute secret file" \
|
||||
|| fail "safe absolute secret file rejected"
|
||||
expect_failure "relative external secret path accepted" \
|
||||
validate_secret_absolute_file "secret-root/safe.txt" "test absolute secret file"
|
||||
mkdir -m 0700 "${TEST_TMP}/external-secret-dir"
|
||||
printf 'external-private-value\n' > "${TEST_TMP}/external-secret-dir/external.txt"
|
||||
chmod 0400 "${TEST_TMP}/external-secret-dir/external.txt"
|
||||
ln -s "${TEST_TMP}/external-secret-dir" "${TEST_TMP}/external-secret-dir-link"
|
||||
expect_failure "external secret file through a symlinked parent accepted" \
|
||||
validate_secret_absolute_file "${TEST_TMP}/external-secret-dir-link/external.txt" "test absolute secret file"
|
||||
expect_failure "external secret directory through a symlinked parent accepted" \
|
||||
validate_secret_absolute_directory "${TEST_TMP}/external-secret-dir-link" "test absolute secret directory"
|
||||
declare secret_root_fs=""
|
||||
secret_root_fs="$(secure_stat -f -c '%T' "${VAR_TMP_SECRET}")"
|
||||
if [[ "${secret_root_fs}" != "tmpfs" && "${secret_root_fs}" != "ramfs" ]]; then
|
||||
expect_failure "persistent secret staging area accepted" validate_secret_staging_area
|
||||
fi
|
||||
expect_failure "secret filename traversal accepted" validate_secret_filename "../safe.txt" "test secret filename"
|
||||
expect_failure "absolute filename-only secret accepted" validate_secret_filename "/tmp/safe.txt" "test secret filename"
|
||||
expect_failure "slash in filename-only secret accepted" validate_secret_filename "subdir/safe.txt" "test secret filename"
|
||||
|
||||
ln -s "${VAR_TMP_SECRET}" "${TEST_TMP}/secret-root-link"
|
||||
expect_failure "secret-root symlink accepted" \
|
||||
validate_secret_directory "${TEST_TMP}/secret-root-link" "test secret root" "false"
|
||||
mkdir -m 0700 "${TEST_TMP}/unsafe-secret-root-mode"
|
||||
chmod 0755 "${TEST_TMP}/unsafe-secret-root-mode"
|
||||
expect_failure "broad secret-root permissions accepted" \
|
||||
validate_secret_directory "${TEST_TMP}/unsafe-secret-root-mode" "test secret root" "false"
|
||||
ln -s "${VAR_TMP_SECRET}/safe.txt" "${VAR_TMP_SECRET}/unsafe-link"
|
||||
expect_failure "secret-file symlink accepted" validate_secret_file "${VAR_TMP_SECRET}/unsafe-link" "test secret file"
|
||||
rm "${VAR_TMP_SECRET}/unsafe-link"
|
||||
ln "${VAR_TMP_SECRET}/safe.txt" "${VAR_TMP_SECRET}/unsafe-hardlink"
|
||||
expect_failure "hardlinked secret file accepted" validate_secret_file "${VAR_TMP_SECRET}/safe.txt" "test secret file"
|
||||
rm "${VAR_TMP_SECRET}/unsafe-hardlink"
|
||||
|
||||
declare fake_secret='CISS-CANARY-[exact]-value'
|
||||
declare expected_redaction=""
|
||||
declare sanitisation_status=0
|
||||
printf -v expected_redaction '%*s' "${#fake_secret}" ''
|
||||
expected_redaction="${expected_redaction// /*}"
|
||||
register_secret_value "${fake_secret}"
|
||||
|
||||
LOG_DEBUG="${TEST_TMP}/debug.log"
|
||||
LOG_VAR="${TEST_TMP}/var.log"
|
||||
LOG_ERROR="${TEST_TMP}/error.log"
|
||||
printf 'before %s after\nunrelated line\n' "${fake_secret}" > "${LOG_DEBUG}"
|
||||
printf 'unrelated vars\n' > "${LOG_VAR}"
|
||||
printf 'unrelated error\n' > "${LOG_ERROR}"
|
||||
chmod 0600 "${LOG_DEBUG}" "${LOG_VAR}" "${LOG_ERROR}"
|
||||
|
||||
sanitize_debug_logs || fail "debug-log sanitisation failed"
|
||||
grep -Fq "${fake_secret}" "${LOG_DEBUG}" && fail "debug-log canary remained"
|
||||
grep -Fq "${expected_redaction}" "${LOG_DEBUG}" || fail "expected exact-value redaction missing"
|
||||
grep -Fq 'unrelated line' "${LOG_DEBUG}" || fail "unrelated debug content changed"
|
||||
|
||||
ln -s "${LOG_DEBUG}" "${TEST_TMP}/unsafe-debug-link"
|
||||
(
|
||||
LOG_DEBUG="${TEST_TMP}/unsafe-debug-link"
|
||||
trap_on_exit 73 "test" 1 "test" "false"
|
||||
) 2>/dev/null || sanitisation_status=$?
|
||||
[[ ${sanitisation_status} -eq 73 ]] || fail "sanitisation failure masked the original exit status"
|
||||
|
||||
expect_failure "empty build-directory path accepted" validate_build_directory_path ""
|
||||
expect_failure "root build-directory path accepted" validate_build_directory_path "/"
|
||||
expect_failure "broad parent build-directory path accepted" validate_build_directory_path "/tmp"
|
||||
expect_failure "secret root accepted as build directory" validate_build_directory_path "${VAR_TMP_SECRET}"
|
||||
mkdir -m 0700 "${VAR_TMP_SECRET}/unsafe-build-child"
|
||||
expect_failure "secret-root descendant accepted as build directory" \
|
||||
validate_build_directory_path "${VAR_TMP_SECRET}/unsafe-build-child"
|
||||
|
||||
mkdir -m 0700 "${TEST_TMP}/unmarked"
|
||||
expect_failure "build directory without marker accepted" validate_build_directory_marker "${TEST_TMP}/unmarked"
|
||||
printf 'do-not-adopt\n' > "${TEST_TMP}/unmarked/content"
|
||||
expect_failure "non-empty unmarked build directory adopted" initialize_build_directory "${TEST_TMP}/unmarked" unsafe_result
|
||||
|
||||
mkdir -m 0700 "${TEST_TMP}/unsafe-build-mode"
|
||||
chmod 0777 "${TEST_TMP}/unsafe-build-mode"
|
||||
expect_failure "unsafe build-directory permissions accepted" \
|
||||
initialize_build_directory "${TEST_TMP}/unsafe-build-mode" unsafe_result
|
||||
|
||||
mkdir -m 0700 "${TEST_TMP}/marker-link-dir"
|
||||
printf '%s\n' "${TEST_TMP}/marker-link-dir" > "${TEST_TMP}/marker-target"
|
||||
chmod 0400 "${TEST_TMP}/marker-target"
|
||||
ln -s "${TEST_TMP}/marker-target" "${TEST_TMP}/marker-link-dir/.ciss-live-builder-owned"
|
||||
expect_failure "symlinked builder-owned marker accepted" validate_build_directory_marker "${TEST_TMP}/marker-link-dir"
|
||||
|
||||
mkdir -m 0700 "${TEST_TMP}/marker-hardlink-dir"
|
||||
printf '%s\n' "${TEST_TMP}/marker-hardlink-dir" > "${TEST_TMP}/marker-hardlink-target"
|
||||
chmod 0400 "${TEST_TMP}/marker-hardlink-target"
|
||||
ln "${TEST_TMP}/marker-hardlink-target" "${TEST_TMP}/marker-hardlink-dir/.ciss-live-builder-owned"
|
||||
expect_failure "hardlinked builder-owned marker accepted" validate_build_directory_marker "${TEST_TMP}/marker-hardlink-dir"
|
||||
|
||||
mkdir -m 0700 "${TEST_TMP}/marker-extra-content-dir"
|
||||
printf '%s\nunexpected\n' "${TEST_TMP}/marker-extra-content-dir" \
|
||||
> "${TEST_TMP}/marker-extra-content-dir/.ciss-live-builder-owned"
|
||||
chmod 0400 "${TEST_TMP}/marker-extra-content-dir/.ciss-live-builder-owned"
|
||||
expect_failure "builder-owned marker with extra content accepted" \
|
||||
validate_build_directory_marker "${TEST_TMP}/marker-extra-content-dir"
|
||||
|
||||
ln -s "${TEST_TMP}/unmarked" "${TEST_TMP}/build-link"
|
||||
expect_failure "build-directory symlink accepted" validate_build_directory_path "${TEST_TMP}/build-link"
|
||||
|
||||
declare validated_build_dir=""
|
||||
initialize_build_directory "${TEST_TMP}/owned-build" validated_build_dir || fail "safe builder-owned directory rejected"
|
||||
validate_build_directory_marker "${validated_build_dir}" || fail "builder marker rejected"
|
||||
mkdir "${validated_build_dir}/subdir"
|
||||
printf 'remove me\n' > "${validated_build_dir}/artifact"
|
||||
printf 'remove me too\n' > "${validated_build_dir}/.hidden-artifact"
|
||||
printf 'nested\n' > "${validated_build_dir}/subdir/nested"
|
||||
clean_build_directory_contents "${validated_build_dir}" || fail "safe builder-owned cleanup failed"
|
||||
validate_build_directory_marker "${validated_build_dir}" || fail "builder marker removed by cleanup"
|
||||
[[ -z "$(find "${validated_build_dir}" -mindepth 1 ! -name '.ciss-live-builder-owned' -print -quit)" ]] \
|
||||
|| fail "builder-owned cleanup left unexpected content"
|
||||
|
||||
mkdir -m 0700 "${TEST_TMP}/outside-build"
|
||||
mkdir "${TEST_TMP}/outside-build/includes.chroot"
|
||||
ln -s "${TEST_TMP}/outside-build" "${validated_build_dir}/config"
|
||||
expect_failure "cleanup subpath through a symlinked parent accepted" \
|
||||
validate_build_directory_subpath "${validated_build_dir}" "config/includes.chroot" unsafe_subpath
|
||||
|
||||
printf 'PASS: secret validation, debug sanitisation, and build cleanup guards\n'
|
||||
Reference in New Issue
Block a user