Bump: 0.5.2 : label charset tightened; password/custom-id validation
This commit is contained in:
Generated
+1
-1
@@ -2906,7 +2906,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "server"
|
name = "server"
|
||||||
version = "0.5.1"
|
version = "0.5.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argon2",
|
"argon2",
|
||||||
"async-broadcast",
|
"async-broadcast",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "server"
|
name = "server"
|
||||||
version = "0.5.1"
|
version = "0.5.2"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[target.x86_64-unknown-linux-gnu]
|
[target.x86_64-unknown-linux-gnu]
|
||||||
|
|||||||
@@ -16,7 +16,13 @@ use regex::Regex;
|
|||||||
// cannot be ported verbatim. The `(?=.*[A-Za-z])` lookahead only means
|
// cannot be ported verbatim. The `(?=.*[A-Za-z])` lookahead only means
|
||||||
// "must contain at least one letter" — we drop it from the pattern and
|
// "must contain at least one letter" — we drop it from the pattern and
|
||||||
// enforce that condition with a separate `.chars().any(..)` check below.
|
// 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::{
|
use axum::{
|
||||||
Json, Router,
|
Json, Router,
|
||||||
@@ -166,7 +172,7 @@ async fn edit_stream_key(
|
|||||||
) -> Result<impl IntoResponse, HttpError> {
|
) -> Result<impl IntoResponse, HttpError> {
|
||||||
// Trim surrounding whitespace so labels aren't stored with leading/trailing spaces.
|
// Trim surrounding whitespace so labels aren't stored with leading/trailing spaces.
|
||||||
let new_label = payload.new.as_deref().map(str::trim);
|
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 let Some(label) = new_label {
|
||||||
if !KEY_RE.is_match(label) {
|
if !KEY_RE.is_match(label) {
|
||||||
return Err(HttpError::BadRequest("invalid label".into()));
|
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()));
|
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)
|
let stream_key = stream_key::Entity::find_by_id(payload.id)
|
||||||
.one(&state.db)
|
.one(&state.db)
|
||||||
|
|||||||
Reference in New Issue
Block a user