Skip to content

Commit d4bc50f

Browse files
Refactor confighttp: code cleanup and const fixes
Clean up src/confighttp.cpp: remove unused <set> include, simplify shared_ptr typedefs, and apply const correctness (make 'now' const and use const auto for header/query iterators and token lookup). Use if-with-init for authorization, origin and referer header checks and for parsing Basic auth and CSRF token index. Tighten origin prefix validation to require ":" or "/" after allowed origins. Make numerous small comment/grammar tweaks and minor formatting clarifications to improve readability and robustness.
1 parent d130652 commit d4bc50f

1 file changed

Lines changed: 29 additions & 36 deletions

File tree

src/confighttp.cpp

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
#include <filesystem>
1111
#include <format>
1212
#include <fstream>
13-
#include <set>
1413

1514
// lib includes
1615
#include <boost/algorithm/string.hpp>
@@ -51,8 +50,8 @@ namespace confighttp {
5150
using https_server_t = SimpleWeb::Server<SimpleWeb::HTTPS>;
5251

5352
using args_t = SimpleWeb::CaseInsensitiveMultimap;
54-
using resp_https_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTPS>::Response>;
55-
using req_https_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTPS>::Request>;
53+
using resp_https_t = std::shared_ptr<SimpleWeb::ServerBase<SimpleWeb::HTTPS>::Response>;
54+
using req_https_t = std::shared_ptr<SimpleWeb::ServerBase<SimpleWeb::HTTPS>::Request>;
5655
using https_handler_t = std::function<void(resp_https_t, req_https_t)>;
5756

5857
enum class op_e {
@@ -244,7 +243,7 @@ namespace confighttp {
244243
}
245244

246245
/**
247-
* @brief Validate the request content type and send bad request when mismatch.
246+
* @brief Validate the request content type and send a bad request when mismatched.
248247
* @param response The HTTP response object.
249248
* @param request The HTTP request object.
250249
* @param contentType The expected content type
@@ -281,15 +280,12 @@ namespace confighttp {
281280
* @return A unique identifier based on username or IP address.
282281
*/
283282
std::string get_client_id(const req_https_t &request) {
284-
// Try to use authenticated username as client ID
283+
// Try to use the authenticated username as client ID
285284
if (!config::sunshine.username.empty()) {
286-
const auto auth = request->header.find("authorization");
287-
if (auth != request->header.end()) {
288-
const auto &rawAuth = auth->second;
289-
if (rawAuth.rfind("Basic "sv, 0) == 0) {
285+
if (const auto auth = request->header.find("authorization"); auth != request->header.end()) {
286+
if (const auto &rawAuth = auth->second; rawAuth.rfind("Basic "sv, 0) == 0) {
290287
auto authData = SimpleWeb::Crypto::Base64::decode(rawAuth.substr("Basic "sv.length()));
291-
const auto index = static_cast<int>(authData.find(':'));
292-
if (index < authData.size() - 1) {
288+
if (const auto index = static_cast<int>(authData.find(':')); index < authData.size() - 1) {
293289
return authData.substr(0, index); // Return username
294290
}
295291
}
@@ -312,7 +308,7 @@ namespace confighttp {
312308
std::lock_guard<std::mutex> lock(csrf_tokens_mutex);
313309

314310
// Clean up expired tokens first
315-
auto now = std::chrono::steady_clock::now();
311+
const auto now = std::chrono::steady_clock::now();
316312
for (auto it = csrf_tokens.begin(); it != csrf_tokens.end();) {
317313
if (it->second.expiration < now) {
318314
it = csrf_tokens.erase(it);
@@ -343,36 +339,33 @@ namespace confighttp {
343339
* @param response The HTTP response object.
344340
* @param request The HTTP request object.
345341
* @param client_id A unique identifier for the client (e.g., session ID or username).
346-
* @return True if the CSRF token is valid or request is same-origin, false otherwise.
342+
* @return True if the CSRF token is valid or the request is same-origin, false otherwise.
347343
*/
348344
bool validate_csrf_token(const resp_https_t &response, const req_https_t &request, const std::string &client_id) {
349345
// Helper function to check if a URL starts with any allowed origin
350346
auto is_allowed_origin = [](const std::string &url) -> bool {
351347
for (const auto &allowed_origin : config::sunshine.csrf_allowed_origins) {
352-
// Ensure exact prefix match (with : or / after to prevent malicious.com matching allowed.com)
353-
if (url.rfind(allowed_origin, 0) == 0) { // rfind with pos=0 checks if url starts with allowed_origin
354-
// Check that it's followed by : (port) or / (path) or is exact match
355-
size_t len = allowed_origin.length();
356-
if (url.length() == len || url[len] == ':' || url[len] == '/') {
348+
// Ensure the exact prefix match (with ":" or "/" after to prevent malicious.com matching allowed.com)
349+
if (url.rfind(allowed_origin, 0) == 0) { // rfind with pos=0 checks if the url starts with allowed_origin
350+
// Check that it's followed by ":" (port) or "/" (path) or is an exact match
351+
if (const size_t len = allowed_origin.length(); url.length() == len || url[len] == ':' || url[len] == '/') {
357352
return true;
358353
}
359354
}
360355
}
361356
return false;
362357
};
363358

364-
// Check if request is from same origin (Origin or Referer header matches configured allowed origins)
365-
auto origin_it = request->header.find("Origin");
366-
if (origin_it != request->header.end()) {
359+
// Check if the request is from the same origin (Origin or Referer header matches configured allowed origins)
360+
if (const auto origin_it = request->header.find("Origin"); origin_it != request->header.end()) {
367361
if (is_allowed_origin(origin_it->second)) {
368362
// Same origin request - allow without CSRF token
369363
return true;
370364
}
371365
}
372366

373367
// If we have a Referer header, check if it's same-origin
374-
auto referer_it = request->header.find("Referer");
375-
if (referer_it != request->header.end()) {
368+
if (const auto referer_it = request->header.find("Referer"); referer_it != request->header.end()) {
376369
if (is_allowed_origin(referer_it->second)) {
377370
// Same origin request - allow without CSRF token
378371
return true;
@@ -381,19 +374,19 @@ namespace confighttp {
381374

382375
// Not a same-origin request, require CSRF token
383376
// Extract token from X-CSRF-Token header
384-
auto header_it = request->header.find("X-CSRF-Token");
377+
const auto header_it = request->header.find("X-CSRF-Token");
385378
if (header_it == request->header.end()) {
386379
// Also check query parameters as fallback
387380
auto query_params = request->parse_query_string();
388-
auto query_it = query_params.find("csrf_token");
381+
const auto query_it = query_params.find("csrf_token");
389382
if (query_it == query_params.end()) {
390383
bad_request(response, request, "Missing CSRF token");
391384
return false;
392385
}
393386

394387
// Validate token from query parameter
395388
std::lock_guard<std::mutex> lock(csrf_tokens_mutex);
396-
auto token_it = csrf_tokens.find(client_id);
389+
const auto token_it = csrf_tokens.find(client_id);
397390

398391
if (token_it == csrf_tokens.end()) {
399392
bad_request(response, request, "Invalid CSRF token");
@@ -442,7 +435,7 @@ namespace confighttp {
442435
}
443436

444437
/**
445-
* @brief Validates the application index and sends error response if invalid.
438+
* @brief Validates the application index and sends an error response if invalid.
446439
* @param response The HTTP response object.
447440
* @param request The HTTP request object.
448441
* @param index The application index/id.
@@ -469,10 +462,10 @@ namespace confighttp {
469462
* @param request The HTTP request object.
470463
* @param html_file The HTML file to serve (relative to WEB_DIR).
471464
* @param require_auth Whether to require authentication (default: true).
472-
* @param redirect_if_username If true, redirect to "/" when username is set (for welcome page).
465+
* @param redirect_if_username If true, redirect to "/" when the username is set (for welcome page).
473466
*/
474467
void getPage(const resp_https_t &response, const req_https_t &request, const char *html_file, const bool require_auth, const bool redirect_if_username) {
475-
// Special handling for welcome page: redirect if username is already set
468+
// Special handling for welcome page: redirect if the username is already set
476469
if (redirect_if_username && !config::sunshine.username.empty()) {
477470
send_redirect(response, request, "/");
478471
return;
@@ -555,7 +548,7 @@ namespace confighttp {
555548
// .relative_path is needed to shed any leading slash that might exist in the request path
556549
auto filePath = fs::weakly_canonical(webDirPath / fs::path(request->path).relative_path());
557550

558-
// Don't do anything if file does not exist or is outside the assets directory
551+
// Don't do anything if the file does not exist or is outside the assets directory
559552
if (!isChildPath(filePath, nodeModulesPath)) {
560553
BOOST_LOG(warning) << "Someone requested a path " << filePath << " that is outside the assets folder";
561554
bad_request(response, request);
@@ -668,7 +661,7 @@ namespace confighttp {
668661
}
669662

670663
/**
671-
* @brief Save an application. To save a new application the index must be `-1`. To update an existing application, you must provide the current index of the application.
664+
* @brief Save an application. To save a new application, the index must be `-1`. To update an existing application, you must provide the current index of the application.
672665
* @param response The HTTP response object.
673666
* @param request The HTTP request object.
674667
* The body for the post request should be JSON serialized in the following format:
@@ -733,7 +726,7 @@ namespace confighttp {
733726
}
734727

735728
auto &apps_node = file_tree["apps"];
736-
int index = input_tree["index"].get<int>(); // this will intentionally cause exception if the provided value is the wrong type
729+
int index = input_tree["index"].get<int>(); // this will intentionally cause an exception if the provided value is the wrong type
737730

738731
input_tree.erase("index");
739732

@@ -870,7 +863,7 @@ namespace confighttp {
870863
* @brief Unpair a client.
871864
* @param response The HTTP response object.
872865
* @param request The HTTP request object.
873-
* The body for the post request should be JSON serialized in the following format:
866+
* The body for the POST request should be JSON serialized in the following format:
874867
* @code{.json}
875868
* {
876869
* "uuid": "<uuid>"
@@ -987,7 +980,7 @@ namespace confighttp {
987980
* @brief Save the configuration settings.
988981
* @param response The HTTP response object.
989982
* @param request The HTTP request object.
990-
* The body for the post request should be JSON serialized in the following format:
983+
* The body for the POST request should be JSON serialized in the following format:
991984
* @code{.json}
992985
* {
993986
* "key": "value"
@@ -1025,8 +1018,8 @@ namespace confighttp {
10251018
continue;
10261019
}
10271020

1028-
// v.dump() will dump valid json, which we do not want for strings in the config right now
1029-
// we should migrate the config file to straight json and get rid of all this nonsense
1021+
// v.dump() will dump valid json, which we do not want for strings in the config, right now
1022+
// we should migrate the config file to straight JSON and get rid of all this nonsense
10301023
config_stream << k << " = " << (v.is_string() ? v.get<std::string>() : v.dump()) << std::endl;
10311024
}
10321025
file_handler::write_file(config::sunshine.config_file.c_str(), config_stream.str());

0 commit comments

Comments
 (0)