Bump: 0.5.2 : label charset tightened; password/custom-id validation

This commit is contained in:
2026-08-16 15:25:06 +01:00
parent 80368b3ea5
commit 72cfb26e38
3 changed files with 22 additions and 4 deletions
Generated
+1 -1
View File
@@ -2906,7 +2906,7 @@ dependencies = [
[[package]]
name = "server"
version = "0.5.1"
version = "0.5.2"
dependencies = [
"argon2",
"async-broadcast",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "server"
version = "0.5.1"
version = "0.5.2"
edition = "2024"
[target.x86_64-unknown-linux-gnu]
+20 -2
View File
@@ -16,7 +16,13 @@ use regex::Regex;
// cannot be ported verbatim. The `(?=.*[A-Za-z])` lookahead only means
// "must contain at least one letter" — we drop it from the pattern and
// enforce that condition with a separate `.chars().any(..)` check below.
static KEY_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^[A-Za-z0-9 _-]{1,67}$").unwrap());
static KEY_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^[A-Za-z0-9'-]{1,67}$").unwrap());
// Allowed charset for stream-key passwords and custom IDs: letters, numbers,
// dashes and apostrophes only — no spaces. Mirrors the frontend
// `/^[A-Za-z0-9'-]{0,67}$/` used by the keys-page popups; empty clears the value.
static PASSWORD_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^[A-Za-z0-9'-]{0,67}$").unwrap());
use axum::{
Json, Router,
@@ -166,7 +172,7 @@ async fn edit_stream_key(
) -> Result<impl IntoResponse, HttpError> {
// Trim surrounding whitespace so labels aren't stored with leading/trailing spaces.
let new_label = payload.new.as_deref().map(str::trim);
// Length (1..=67) and allowed charset ([A-Za-z0-9 _-], space included).
// Length (1..=67) and allowed charset ([A-Za-z0-9'-], no spaces).
if let Some(label) = new_label {
if !KEY_RE.is_match(label) {
return Err(HttpError::BadRequest("invalid label".into()));
@@ -176,6 +182,18 @@ async fn edit_stream_key(
return Err(HttpError::BadRequest("label must contain a letter".into()));
}
}
// Custom IDs and passwords share a charset: letters, numbers, dashes,
// apostrophes — no spaces. Empty values are allowed (they clear the field).
if let Some(custom_id) = payload.custom_id.as_deref() {
if !custom_id.is_empty() && !PASSWORD_RE.is_match(custom_id) {
return Err(HttpError::BadRequest("invalid custom id".into()));
}
}
if let Some(pwd) = payload.password.as_deref() {
if !pwd.is_empty() && !PASSWORD_RE.is_match(pwd) {
return Err(HttpError::BadRequest("invalid password".into()));
}
}
let stream_key = stream_key::Entity::find_by_id(payload.id)
.one(&state.db)