Skip to content

Commit 6f816da

Browse files
author
Nitin Chaudhary
committed
Address PR review comments: Add centralized allowlists, specific exceptions, and validation improvements
1 parent 0a18b8d commit 6f816da

File tree

4 files changed

+51
-20
lines changed

4 files changed

+51
-20
lines changed

vnext/Shared/InputValidation.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ std::string URLValidator::DecodeURL(const std::string &url) {
6565
char *end;
6666
long value = strtol(hex, &end, 16);
6767
if (end == hex + 2 && value >= 0 && value <= 255) {
68-
temp += static_cast<char>(value & 0xFF);
68+
temp += static_cast<char>(static_cast<unsigned char>(value & 0xFF));
6969
i += 2;
7070
continue;
7171
}
@@ -305,7 +305,7 @@ std::string PathValidator::DecodePath(const std::string &path) {
305305
char *end;
306306
long value = strtol(hex, &end, 16);
307307
if (end == hex + 2 && value >= 0 && value <= 255) {
308-
temp += static_cast<char>(value & 0xFF);
308+
temp += static_cast<char>(static_cast<unsigned char>(value & 0xFF));
309309
i += 2;
310310
continue;
311311
}

vnext/Shared/InputValidation.h

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,54 @@
1111

1212
namespace Microsoft::ReactNative::InputValidation {
1313

14-
// Security exception for validation failures
14+
// Security exceptions for validation failures
1515
class ValidationException : public std::runtime_error {
1616
public:
1717
explicit ValidationException(const std::string &message) : std::runtime_error(message) {}
1818
};
1919

20+
// Specific validation exception types
21+
class InvalidSizeException : public std::logic_error {
22+
public:
23+
explicit InvalidSizeException(const std::string &message) : std::logic_error(message) {}
24+
};
25+
26+
class InvalidEncodingException : public std::logic_error {
27+
public:
28+
explicit InvalidEncodingException(const std::string &message) : std::logic_error(message) {}
29+
};
30+
31+
class InvalidPathException : public std::logic_error {
32+
public:
33+
explicit InvalidPathException(const std::string &message) : std::logic_error(message) {}
34+
};
35+
36+
class InvalidURLException : public std::logic_error {
37+
public:
38+
explicit InvalidURLException(const std::string &message) : std::logic_error(message) {}
39+
};
40+
41+
// Centralized allowlists for encodings
42+
namespace AllowedEncodings {
43+
static const std::vector<std::string> FILE_READER_ENCODINGS = {
44+
"UTF-8", "utf-8", "utf8",
45+
"UTF-16", "utf-16", "utf16",
46+
"ASCII", "ascii",
47+
"ISO-8859-1", "iso-8859-1",
48+
"" // Empty is allowed (defaults to UTF-8)
49+
};
50+
} // namespace AllowedEncodings
51+
52+
// Centralized URL scheme allowlists
53+
namespace AllowedSchemes {
54+
static const std::vector<std::string> HTTP_SCHEMES = {"http", "https"};
55+
static const std::vector<std::string> WEBSOCKET_SCHEMES = {"ws", "wss"};
56+
static const std::vector<std::string> FILE_SCHEMES = {"file"};
57+
static const std::vector<std::string> LINKING_SCHEMES = {"http", "https", "mailto", "tel"};
58+
static const std::vector<std::string> IMAGE_SCHEMES = {"http", "https"};
59+
static const std::vector<std::string> DEBUG_SCHEMES = {"http", "https", "file"};
60+
} // namespace AllowedSchemes
61+
2062
// Logging callback for validation failures (SDL requirement)
2163
using ValidationLogger = std::function<void(const std::string &category, const std::string &message)>;
2264
void SetValidationLogger(ValidationLogger logger);
@@ -98,6 +140,7 @@ class SizeValidator {
98140
static constexpr size_t MAX_CLOSE_REASON = 123; // WebSocket spec
99141
static constexpr size_t MAX_URL_LENGTH = 2048; // URL max
100142
static constexpr size_t MAX_HEADER_LENGTH = 8192; // Header max
143+
static constexpr size_t MAX_DATA_URI_SIZE = 10 * 1024 * 1024; // 10MB for data URIs
101144
};
102145

103146
// Encoding Validation - Protects against malformed data (SDL compliant)

vnext/Shared/Modules/FileReaderModule.cpp

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -103,24 +103,9 @@ void FileReaderTurboModule::ReadAsText(
103103

104104
// SDL Compliance: Validate encoding (P1 - CVSS 5.5)
105105
try {
106-
// Allowlist of safe encodings (static to avoid repeated allocations)
107-
static const std::vector<std::string> allowedEncodings = {
108-
"UTF-8",
109-
"utf-8",
110-
"utf8",
111-
"UTF-16",
112-
"utf-16",
113-
"utf16",
114-
"ASCII",
115-
"ascii",
116-
"ISO-8859-1",
117-
"iso-8859-1",
118-
"" // Empty is allowed (defaults to UTF-8)
119-
};
120-
121106
if (!encoding.empty()) {
122107
bool isAllowed = false;
123-
for (const auto &allowed : allowedEncodings) {
108+
for (const auto &allowed : Microsoft::ReactNative::InputValidation::AllowedEncodings::FILE_READER_ENCODINGS) {
124109
if (encoding == allowed) {
125110
isAllowed = true;
126111
break;

vnext/Shared/Modules/HttpModule.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,12 @@ void HttpTurboModule::SendRequest(
130130
// SDL Compliance: Validate headers for CRLF injection (P2 - CVSS 4.5)
131131
try {
132132
for (auto &entry : headersObj) {
133+
std::string headerName = entry.first;
133134
std::string headerValue = entry.second.AsString();
135+
// Validate both header name and value for CRLF injection
136+
Microsoft::ReactNative::InputValidation::EncodingValidator::ValidateHeaderValue(headerName);
134137
Microsoft::ReactNative::InputValidation::EncodingValidator::ValidateHeaderValue(headerValue);
135-
headers.emplace(entry.first, std::move(headerValue));
138+
headers.emplace(std::move(headerName), std::move(headerValue));
136139
}
137140
} catch (const Microsoft::ReactNative::InputValidation::ValidationException &ex) {
138141
// Call callback with requestId, then send error event

0 commit comments

Comments
 (0)