safety: enforce JWT secret min length, validate order symbol input

- Require JWT_SECRET >= 32 characters at startup (prevents weak secrets)
- Add SECURITY comment documenting auth.rs login as dev-only stub
- Add symbol input validation in OrderForm: uppercase, alphanumeric
  with dots/hyphens/underscores, max 20 chars, visual error state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-02-22 08:40:50 +01:00
parent 3d3a84e0b0
commit bee0591ea1
3 changed files with 18 additions and 5 deletions

View File

@@ -18,8 +18,11 @@ export function OrderForm() {
const submitOrder = useApiPost<unknown, OrderPayload>('/trading/orders');
const symbolValid = /^[A-Z0-9._-]{1,20}$/.test(symbol);
function handleSubmit(e: FormEvent) {
e.preventDefault();
if (!symbolValid) return;
const qty = parseFloat(quantity);
if (isNaN(qty) || qty <= 0) return;
@@ -52,8 +55,14 @@ export function OrderForm() {
<input
type="text"
value={symbol}
onChange={(e) => setSymbol(e.target.value)}
className="w-full px-2 py-1.5 text-sm rounded border border-[var(--color-border)] bg-[var(--color-bg-primary)] text-[var(--color-text-primary)] focus:border-[var(--color-accent)] outline-none"
onChange={(e) => setSymbol(e.target.value.toUpperCase())}
maxLength={20}
pattern="[A-Z0-9._-]+"
className={`w-full px-2 py-1.5 text-sm rounded border bg-[var(--color-bg-primary)] text-[var(--color-text-primary)] outline-none ${
symbolValid
? 'border-[var(--color-border)] focus:border-[var(--color-accent)]'
: 'border-[var(--color-red)]'
}`}
/>
</div>

View File

@@ -16,10 +16,13 @@ async fn main() -> Result<()> {
let config = GatewayConfig::from_env();
// Fail fast if JWT secret is not configured
// Fail fast if JWT secret is not configured or too short
if config.jwt_secret.is_empty() {
bail!("JWT_SECRET environment variable must be set (non-empty) for token validation");
}
if config.jwt_secret.len() < 32 {
bail!("JWT_SECRET must be at least 32 characters for adequate security");
}
let listen_addr = config.listen_addr.clone();

View File

@@ -21,12 +21,13 @@ pub fn router() -> Router<AppState> {
Router::new().route("/login", routing::post(login))
}
// SECURITY: This is a development-only login stub. Before production deployment,
// replace with real authentication (LDAP, OAuth2, bcrypt-verified credentials, etc.).
// Currently accepts any non-empty username/password combination.
async fn login(
State(state): State<AppState>,
Json(body): Json<LoginRequest>,
) -> Result<Json<LoginResponse>, AppError> {
// Validate credentials against the configured secret
// In production, this would check against a user database or auth service
if body.username.is_empty() || body.password.is_empty() {
return Err(AppError::BadRequest("Username and password required".into()));
}