fix: code quality hardening — unwrap removal, unsafe forbid, Go/Raw tests (#72)

* fix: remove unwrap() from production code, improve error handling

- Replace unwrap() with proper error handling in npm, mirror, validation
- Add input validation to cargo registry (crate name + version)
- Improve expect() messages with descriptive context in metrics, rate_limit
- Remove unnecessary clone() in error.rs, docker.rs, npm.rs, dashboard_metrics
- Add #![deny(clippy::unwrap_used)] to prevent future unwrap in prod code
- Add let-else pattern for safer null checks in validation.rs

* docs: update SECURITY.md — add 0.3.x to supported versions

* security: forbid unsafe code at crate level

Add #![forbid(unsafe_code)] to both lib.rs and main.rs.
NORA has zero unsafe blocks — this prevents future additions
without removing the forbid attribute (stronger than deny).

* build: add rust-toolchain.toml, Dockerfile HEALTHCHECK

- Pin toolchain to stable with clippy + rustfmt components
- Add Docker HEALTHCHECK for standalone deployments (wget /health)

* test: add Go proxy and Raw registry integration tests

Go proxy tests: list, .info, .mod, @latest, path traversal, 404
Raw registry tests: upload/download, HEAD, 404, path traversal,
overwrite, delete, binary data (10KB)
This commit is contained in:
2026-03-31 21:15:59 +03:00
committed by GitHub
parent 9ec5fe526b
commit bb125db074
16 changed files with 186 additions and 26 deletions

View File

@@ -51,11 +51,11 @@ struct ErrorResponse {
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, message) = match &self {
AppError::NotFound(msg) => (StatusCode::NOT_FOUND, msg.clone()),
AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg.clone()),
AppError::Unauthorized(msg) => (StatusCode::UNAUTHORIZED, msg.clone()),
AppError::Internal(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg.clone()),
let (status, message) = match self {
AppError::NotFound(msg) => (StatusCode::NOT_FOUND, msg),
AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg),
AppError::Unauthorized(msg) => (StatusCode::UNAUTHORIZED, msg),
AppError::Internal(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg),
AppError::Storage(e) => match e {
StorageError::NotFound => (StatusCode::NOT_FOUND, "Resource not found".to_string()),
StorageError::Validation(v) => (StatusCode::BAD_REQUEST, v.to_string()),