mirror of
https://github.com/alexbelgium/hassio-addons.git
synced 2026-08-25 06:13:32 +02:00
fix(birdnet-go): reject path traversal and create parent dir for sqlite path rewrite
Addresses review feedback on the sqlite persistence fix (PR #2812): - validate_safe_path now rejects ".." path segments so a relative output.sqlite.path can't traverse outside /config when rewritten. - The rewrite now creates the destination's parent directory, since SQLite won't create one itself for a path like "db/birdnet.db". Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KHJ4o22cdcgdNtb81tBTPJ
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
## source-20260705-3 (05-07-2026)
|
||||
- Harden the `output.sqlite.path` rewrite added for the persistence fix: reject paths containing `..` traversal segments (shared `validate_safe_path` check), and create the destination's parent directory under `/config` when the relative path includes a subdirectory (e.g. `db/birdnet.db`), since SQLite does not create missing parent directories itself.
|
||||
|
||||
## source-20260705-2 (05-07-2026)
|
||||
- Fix detections/database not persisting across restarts on a fresh install: upstream's default `config.yaml` ships `output.sqlite.path: birdnet.db` (relative) explicitly, so the missing-only (`//=`) seeding introduced previously never rewrote it to an absolute path. A relative path resolves against the app's ephemeral working directory, so the database was silently recreated empty on every restart. Any relative `output.sqlite.path` is now rewritten to live under the persistent `/config` on startup; values already set to an absolute path are left untouched. (https://github.com/tphakala/birdnet-go/discussions/3774)
|
||||
|
||||
|
||||
@@ -127,5 +127,5 @@ slug: birdnet-go-dev
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
usb: true
|
||||
version: "source-20260705-2"
|
||||
version: "source-20260705-3"
|
||||
video: true
|
||||
|
||||
@@ -17,12 +17,22 @@ normalize_path() {
|
||||
|
||||
# Reject paths containing characters that would break the SQL/YAML literals
|
||||
# we substitute into below. We deliberately allow only "safe" filename chars.
|
||||
# Also reject ".." path segments so a relative path can't traverse outside
|
||||
# the directory we prefix it with (e.g. "/config") when we rewrite it below.
|
||||
validate_safe_path() {
|
||||
local p="$1"
|
||||
if [[ ! "$p" =~ ^[A-Za-z0-9._/-]+$ ]]; then
|
||||
bashio::log.fatal "Refusing unsafe path: '$p' (only [A-Za-z0-9._/-] allowed)"
|
||||
exit 1
|
||||
fi
|
||||
local segment
|
||||
local IFS=/
|
||||
for segment in $p; do
|
||||
if [[ "$segment" == ".." ]]; then
|
||||
bashio::log.fatal "Refusing unsafe path: '$p' (path traversal '..' is not allowed)"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
if [ ! -f "$CONFIG_LOCATION" ]; then
|
||||
@@ -149,8 +159,12 @@ bashio::log.info "Seeding default configuration values (only if missing)"
|
||||
CURRENT_SQLITE_PATH="$(yq -r '.output.sqlite.path // ""' "$CONFIG_LOCATION")"
|
||||
if [[ -n "$CURRENT_SQLITE_PATH" && "$CURRENT_SQLITE_PATH" != /* ]]; then
|
||||
validate_safe_path "$CURRENT_SQLITE_PATH"
|
||||
bashio::log.warning "output.sqlite.path ('$CURRENT_SQLITE_PATH') is relative and would not persist across restarts; rewriting to /config/$CURRENT_SQLITE_PATH"
|
||||
yq -i -y ".output.sqlite.path = \"/config/${CURRENT_SQLITE_PATH}\"" "$CONFIG_LOCATION"
|
||||
NEW_SQLITE_PATH="/config/${CURRENT_SQLITE_PATH}"
|
||||
bashio::log.warning "output.sqlite.path ('$CURRENT_SQLITE_PATH') is relative and would not persist across restarts; rewriting to $NEW_SQLITE_PATH"
|
||||
# SQLite does not create missing parent directories, so ensure one exists
|
||||
# if the (validated, traversal-free) path includes a subdirectory.
|
||||
mkdir -p "$(dirname "$NEW_SQLITE_PATH")"
|
||||
yq -i -y ".output.sqlite.path = \"${NEW_SQLITE_PATH}\"" "$CONFIG_LOCATION"
|
||||
fi
|
||||
|
||||
yq -i -y '.output.sqlite.path //= "/config/birdnet.db"' "$CONFIG_LOCATION"
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## nightly-20260615-3 (05-07-2026)
|
||||
- Harden the `output.sqlite.path` rewrite added for the persistence fix: reject paths containing `..` traversal segments (shared `validate_safe_path` check), and create the destination's parent directory under `/config` when the relative path includes a subdirectory (e.g. `db/birdnet.db`), since SQLite does not create missing parent directories itself.
|
||||
|
||||
## nightly-20260615-2 (05-07-2026)
|
||||
- Minor bugs fixed
|
||||
- Fix detections/database not persisting across restarts on a fresh install: upstream's default `config.yaml` ships `output.sqlite.path: birdnet.db` (relative) explicitly, so the missing-only (`//=`) seeding introduced previously never rewrote it to an absolute path. A relative path resolves against the app's ephemeral working directory, so the database was silently recreated empty on every restart. Any relative `output.sqlite.path` is now rewritten to live under the persistent `/config` on startup; values already set to an absolute path are left untouched. (https://github.com/tphakala/birdnet-go/discussions/3774)
|
||||
|
||||
@@ -128,4 +128,4 @@ slug: birdnet-go
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons/tree/master/birdnet-go
|
||||
usb: true
|
||||
version: "nightly-20260615-2"
|
||||
version: "nightly-20260615-3"
|
||||
|
||||
@@ -17,12 +17,22 @@ normalize_path() {
|
||||
|
||||
# Reject paths containing characters that would break the SQL/YAML literals
|
||||
# we substitute into below. We deliberately allow only "safe" filename chars.
|
||||
# Also reject ".." path segments so a relative path can't traverse outside
|
||||
# the directory we prefix it with (e.g. "/config") when we rewrite it below.
|
||||
validate_safe_path() {
|
||||
local p="$1"
|
||||
if [[ ! "$p" =~ ^[A-Za-z0-9._/-]+$ ]]; then
|
||||
bashio::log.fatal "Refusing unsafe path: '$p' (only [A-Za-z0-9._/-] allowed)"
|
||||
exit 1
|
||||
fi
|
||||
local segment
|
||||
local IFS=/
|
||||
for segment in $p; do
|
||||
if [[ "$segment" == ".." ]]; then
|
||||
bashio::log.fatal "Refusing unsafe path: '$p' (path traversal '..' is not allowed)"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
if [ ! -f "$CONFIG_LOCATION" ]; then
|
||||
@@ -145,8 +155,12 @@ bashio::log.info "Seeding default configuration values (only if missing)"
|
||||
CURRENT_SQLITE_PATH="$(yq -r '.output.sqlite.path // ""' "$CONFIG_LOCATION")"
|
||||
if [[ -n "$CURRENT_SQLITE_PATH" && "$CURRENT_SQLITE_PATH" != /* ]]; then
|
||||
validate_safe_path "$CURRENT_SQLITE_PATH"
|
||||
bashio::log.warning "output.sqlite.path ('$CURRENT_SQLITE_PATH') is relative and would not persist across restarts; rewriting to /config/$CURRENT_SQLITE_PATH"
|
||||
yq -i -y ".output.sqlite.path = \"/config/${CURRENT_SQLITE_PATH}\"" "$CONFIG_LOCATION"
|
||||
NEW_SQLITE_PATH="/config/${CURRENT_SQLITE_PATH}"
|
||||
bashio::log.warning "output.sqlite.path ('$CURRENT_SQLITE_PATH') is relative and would not persist across restarts; rewriting to $NEW_SQLITE_PATH"
|
||||
# SQLite does not create missing parent directories, so ensure one exists
|
||||
# if the (validated, traversal-free) path includes a subdirectory.
|
||||
mkdir -p "$(dirname "$NEW_SQLITE_PATH")"
|
||||
yq -i -y ".output.sqlite.path = \"${NEW_SQLITE_PATH}\"" "$CONFIG_LOCATION"
|
||||
fi
|
||||
|
||||
yq -i -y '.output.sqlite.path //= "/config/birdnet.db"' "$CONFIG_LOCATION"
|
||||
|
||||
Reference in New Issue
Block a user