-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_http_utils.cpp
More file actions
134 lines (104 loc) · 3.26 KB
/
test_http_utils.cpp
File metadata and controls
134 lines (104 loc) · 3.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Copyright (c) 2021-2025, Nikita Pennie <nikitapnn1@gmail.com>
// SPDX-License-Identifier: MIT
#include <gtest/gtest.h>
#include <nprpc/impl/http_utils.hpp>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <stdexcept>
namespace {
class TempDir {
public:
TempDir()
{
const auto base = std::filesystem::temp_directory_path();
for (uint32_t attempt = 0; attempt != 128; ++attempt) {
const auto candidate =
base /
("nprpc-http-utils-" +
std::to_string(std::chrono::steady_clock::now()
.time_since_epoch()
.count()) +
"-" + std::to_string(attempt));
std::error_code ec;
if (std::filesystem::create_directories(candidate, ec)) {
path_ = candidate;
return;
}
}
throw std::runtime_error("failed to create temporary directory");
}
~TempDir()
{
std::error_code ec;
std::filesystem::remove_all(path_, ec);
}
const std::filesystem::path& path() const noexcept { return path_; }
private:
std::filesystem::path path_;
};
} // namespace
TEST(HttpUtils, ResolvesPathInsideDocRoot)
{
TempDir doc_root;
std::filesystem::create_directories(doc_root.path() / "assets");
const auto resolved = nprpc::impl::resolve_http_doc_root_path(
doc_root.path().string(), "/assets/app.js");
ASSERT_TRUE(resolved.has_value());
EXPECT_EQ(*resolved,
std::filesystem::weakly_canonical(doc_root.path() / "assets/app.js"));
}
TEST(HttpUtils, RejectsParentTraversal)
{
TempDir doc_root;
const auto resolved = nprpc::impl::resolve_http_doc_root_path(
doc_root.path().string(), "/../../etc/passwd");
EXPECT_FALSE(resolved.has_value());
}
TEST(HttpUtils, RejectsTraversalAfterNormalization)
{
TempDir doc_root;
const auto resolved = nprpc::impl::resolve_http_doc_root_path(
doc_root.path().string(), "/static/../..//secret.txt");
EXPECT_FALSE(resolved.has_value());
}
TEST(HttpUtils, ParsesValidContentLength)
{
const auto parsed = nprpc::impl::parse_http_content_length("12345");
ASSERT_TRUE(parsed.has_value());
EXPECT_EQ(*parsed, 12345u);
}
TEST(HttpUtils, RejectsEmptyContentLength)
{
EXPECT_FALSE(nprpc::impl::parse_http_content_length("").has_value());
}
TEST(HttpUtils, RejectsNonNumericContentLength)
{
EXPECT_FALSE(nprpc::impl::parse_http_content_length("abc").has_value());
EXPECT_FALSE(nprpc::impl::parse_http_content_length("12x").has_value());
}
TEST(HttpUtils, RejectsOutOfRangeContentLength)
{
EXPECT_FALSE(nprpc::impl::parse_http_content_length(
"184467440737095516160").has_value());
}
#ifndef _WIN32
TEST(HttpUtils, RejectsSymlinkEscape)
{
TempDir sandbox;
const auto doc_root = sandbox.path() / "www";
const auto outside = sandbox.path() / "outside";
std::filesystem::create_directories(doc_root);
std::filesystem::create_directories(outside);
std::ofstream(outside / "secret.txt") << "secret";
std::filesystem::create_directory_symlink(outside, doc_root / "public-link");
const auto resolved = nprpc::impl::resolve_http_doc_root_path(
doc_root.string(), "/public-link/secret.txt");
EXPECT_FALSE(resolved.has_value());
}
#endif
int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}