Skip to content

Commit 2977c96

Browse files
authored
Require Classic Warden locale hardening catalogue (#482)
1 parent 402c570 commit 2977c96

8 files changed

Lines changed: 239 additions & 100 deletions

src/shared/revision_data.h.in

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@
5656

5757
#define WORLD_DB_VERSION_NR "22"
5858
#define WORLD_DB_STRUCTURE_NR "6"
59-
#define WORLD_DB_CONTENT_NR "3"
60-
#define WORLD_DB_UPDATE_DESCRIPT "Warden_Checks"
59+
#define WORLD_DB_CONTENT_NR "4"
60+
#define WORLD_DB_UPDATE_DESCRIPT "Warden_Locale_Hardening"
6161

6262
#define VER_COMPANY_NAME_STR "MaNGOS Developers"
6363
#define VER_LEGALCOPYRIGHT_STR "(c)2005-@rev_year@ MaNGOS"

src/tests/DatabaseVersionTest.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ TEST(CoreDatabaseVersion_requires_database_backed_warden_catalogue)
3939

4040
CHECK_STR(GitRevision::GetWorldDBVersion(), "22");
4141
CHECK_STR(GitRevision::GetWorldDBStructure(), "6");
42-
CHECK_STR(GitRevision::GetWorldDBContent(), "3");
43-
CHECK_STR(GitRevision::GetWorldDBUpdateDescription(), "Warden_Checks");
42+
CHECK_STR(GitRevision::GetWorldDBContent(), "4");
43+
CHECK_STR(GitRevision::GetWorldDBUpdateDescription(),
44+
"Warden_Locale_Hardening");
4445
}

src/tests/WardenCheckCatalogTest.cpp

Lines changed: 93 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,12 @@ std::vector<warden::WardenCheckRowInput> FirstProfileRows()
6767
{
6868
std::vector<warden::WardenCheckRowInput> rows =
6969
warden::test::InitialWardenRows();
70-
rows.resize(7);
70+
rows.resize(9);
7171
return rows;
7272
}
7373
}
7474

75-
TEST(WardenCheckCatalog_decodes_and_selects_three_exact_profiles)
75+
TEST(WardenCheckCatalog_decodes_and_selects_eight_exact_profiles)
7676
{
7777
warden::WardenCheckCatalogBuilder builder;
7878
warden::WardenCheckDiagnostic diagnostic;
@@ -86,21 +86,94 @@ TEST(WardenCheckCatalog_decodes_and_selects_three_exact_profiles)
8686
warden::WardenCheckCatalog catalog;
8787
REQUIRE(builder.Build(catalog, diagnostic) ==
8888
warden::CheckCatalogValidation::Valid);
89-
CHECK_EQ(catalog.TotalRows(), uint32(21));
90-
CHECK_EQ(catalog.EnabledRows(), uint32(21));
91-
CHECK_EQ(catalog.Profiles().size(), size_t(3));
89+
CHECK_EQ(catalog.TotalRows(), uint32(72));
90+
CHECK_EQ(catalog.EnabledRows(), uint32(72));
91+
CHECK_EQ(catalog.Profiles().size(), size_t(8));
92+
93+
struct ProfileExpectation
94+
{
95+
uint32 build;
96+
char const* locale;
97+
char const* mpqSha1;
98+
char const* luaText;
99+
uint32 frameDispatchAddress;
100+
char const* frameDispatchBytes;
101+
};
102+
ProfileExpectation const profiles[] =
103+
{
104+
{5875, "enUS", "7d88154d3411811985f5d81177c5453248133443",
105+
"4f6b6179", 4784584,
106+
"5eff48006bff480078ff480095ff4800"},
107+
{5875, "koKR", "755d6d7f49bb34114433386d559261ed3aa23f00",
108+
"ed9995ec9db8", 4784584,
109+
"5eff48006bff480078ff480095ff4800"},
110+
{5875, "zhTW", "2a70e6402a40a4f9e9960ced419dba5e6deb8536",
111+
"e7a2bae5ae9a", 4784584,
112+
"5eff48006bff480078ff480095ff4800"},
113+
{5875, "frFR", "af2d81af013a9ba6bb92ce171e43fa903c9e8c09",
114+
"4f4b", 4784584,
115+
"5eff48006bff480078ff480095ff4800"},
116+
{5875, "esES", "1ecec2c6596b8411fa5fe153edfb9a6ee43360e9",
117+
"41636570746172", 4784584,
118+
"5eff48006bff480078ff480095ff4800"},
119+
{6005, "enGB", "7d88154d3411811985f5d81177c5453248133443",
120+
"4f6b6179", 4784584,
121+
"5eff48006bff480078ff480095ff4800"},
122+
{6005, "deDE", "a0b3dc2d78ad892f2436bcd937be51b4989d64c1",
123+
"4f4b", 4784584,
124+
"5eff48006bff480078ff480095ff4800"},
125+
{6141, "zhCN", "c5a1de4c1cd412eb4d2e02afab6131b737efcaf0",
126+
"e7a1aee5ae9a", 4788152,
127+
"4e0d49005b0d4900680d4900850d4900"}
128+
};
129+
uint32 const expectedIds[] =
130+
{65536, 1, 2, 1107, 827, 1566, 1135, 65537, 65538};
131+
132+
for (ProfileExpectation const& expected : profiles)
133+
{
134+
warden::WardenCheckProfile const* profile =
135+
catalog.Find(expected.build, "Win", expected.locale);
136+
REQUIRE(profile != nullptr);
137+
REQUIRE(profile->checks.size() == 9u);
138+
CHECK(profile->hasActionableChecks);
139+
CHECK_EQ(profile->totalRows, uint32(9));
140+
for (size_t index = 0; index < 9u; ++index)
141+
{
142+
CHECK_EQ(warden::GetWardenCheckId(profile->checks[index]),
143+
expectedIds[index]);
144+
}
145+
146+
CHECK(warden::GetWardenCheckType(profile->checks[0]) ==
147+
warden::WardenCheckType::Timing);
148+
CHECK(!warden::IsConfirmationEligible(profile->checks[0]));
149+
CHECK(warden::IsConfirmationEligible(profile->checks[1]));
150+
151+
warden::MpqCheckProfile const& mpq =
152+
std::get<warden::MpqCheckProfile>(profile->checks[1].payload);
153+
CHECK_HEX(mpq.expectedSha1.data(), mpq.expectedSha1.size(),
154+
expected.mpqSha1);
155+
warden::LuaCheckProfile const& lua =
156+
std::get<warden::LuaCheckProfile>(profile->checks[2].payload);
157+
CHECK_HEX(reinterpret_cast<uint8 const*>(lua.expectedText.data()),
158+
lua.expectedText.size(), expected.luaText);
159+
160+
warden::MemCheckProfile const& glue =
161+
std::get<warden::MemCheckProfile>(profile->checks[7].payload);
162+
CHECK_EQ(glue.addressOrRva, uint32(4631212));
163+
CHECK_HEX(glue.expectedBytes.data(), glue.expectedBytes.size(),
164+
"1ca9460029a9460036a946009ea94600");
165+
CHECK(profile->checks[7].evidenceClass ==
166+
warden::WardenEvidenceClass::IntegrityInvariant);
167+
168+
warden::MemCheckProfile const& frame =
169+
std::get<warden::MemCheckProfile>(profile->checks[8].payload);
170+
CHECK_EQ(frame.addressOrRva, expected.frameDispatchAddress);
171+
CHECK_HEX(frame.expectedBytes.data(), frame.expectedBytes.size(),
172+
expected.frameDispatchBytes);
173+
CHECK(profile->checks[8].evidenceClass ==
174+
warden::WardenEvidenceClass::IntegrityInvariant);
175+
}
92176

93-
warden::WardenCheckProfile const* profile =
94-
catalog.Find(6141, "Win", "zhCN");
95-
REQUIRE(profile != nullptr);
96-
REQUIRE(profile->checks.size() == 7u);
97-
CHECK(profile->hasActionableChecks);
98-
CHECK_EQ(profile->totalRows, uint32(7));
99-
CHECK_EQ(warden::GetWardenCheckId(profile->checks[0]), uint32(65536));
100-
CHECK(warden::GetWardenCheckType(profile->checks[0]) ==
101-
warden::WardenCheckType::Timing);
102-
CHECK(!warden::IsConfirmationEligible(profile->checks[0]));
103-
CHECK(warden::IsConfirmationEligible(profile->checks[1]));
104177
CHECK(warden::IsActionableEvidenceClass(
105178
warden::WardenEvidenceClass::IntegrityInvariant));
106179
CHECK(warden::IsActionableEvidenceClass(
@@ -110,12 +183,7 @@ TEST(WardenCheckCatalog_decodes_and_selects_three_exact_profiles)
110183
CHECK(!warden::IsActionableEvidenceClass(
111184
warden::WardenEvidenceClass::Corroboration));
112185

113-
warden::LuaCheckProfile const& lua =
114-
std::get<warden::LuaCheckProfile>(profile->checks[2].payload);
115-
CHECK_HEX(reinterpret_cast<uint8 const*>(lua.expectedText.data()),
116-
lua.expectedText.size(), "e7a1aee5ae9a");
117-
CHECK(catalog.Find(5875, "Win", "enUS") != nullptr);
118-
CHECK(catalog.Find(6005, "Win", "enGB") != nullptr);
186+
CHECK(catalog.Find(5875, "Win", "itIT") == nullptr);
119187
CHECK(catalog.Find(6005, "Win", "enUS") == nullptr);
120188
CHECK(catalog.Find(6141, "OSX", "zhCN") == nullptr);
121189
}
@@ -239,7 +307,7 @@ TEST(WardenCheckCatalog_enforces_timing_contract_and_cardinality)
239307
CHECK(BuildRows(rows) == warden::CheckCatalogValidation::DisabledTiming);
240308
rows = FirstProfileRows();
241309
warden::WardenCheckRowInput secondTiming = rows[0];
242-
secondTiming.checkId = 65537;
310+
secondTiming.checkId = 65539;
243311
secondTiming.sortOrder = 11;
244312
rows.push_back(secondTiming);
245313
CHECK(BuildRows(rows) == warden::CheckCatalogValidation::MultipleTiming);
@@ -523,12 +591,12 @@ TEST(WardenCheckCatalog_enforces_complete_profiles_and_atomic_build)
523591

524592
warden::WardenCheckCatalog unchanged =
525593
warden::test::BuildInitialWardenCatalog();
526-
REQUIRE(unchanged.TotalRows() == 21u);
594+
REQUIRE(unchanged.TotalRows() == 72u);
527595
rows = FirstProfileRows();
528596
rows[2].checkId = rows[1].checkId;
529597
CHECK(BuildRows(rows, unchanged) ==
530598
warden::CheckCatalogValidation::DuplicateId);
531-
CHECK_EQ(unchanged.TotalRows(), uint32(21));
599+
CHECK_EQ(unchanged.TotalRows(), uint32(72));
532600
CHECK(unchanged.Find(6141, "Win", "zhCN") != nullptr);
533601
}
534602

src/tests/WardenCheckFixtures.h

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ inline void AppendInitialProfile(std::vector<WardenCheckRowInput>& rows,
5353
std::string const& mpqExpectedHex, std::string const& luaExpectedHex,
5454
uint32 functionAddress, std::string const& functionExpectedHex,
5555
uint32 flagsAddress, uint32 luaProtectionAddress,
56-
std::string const& luaProtectionExpectedHex, uint32 wallClimbAddress)
56+
std::string const& luaProtectionExpectedHex, uint32 wallClimbAddress,
57+
uint32 frameDispatchAddress, std::string const& frameDispatchExpectedHex)
5758
{
5859
rows.push_back(MakeRow(build, localeHex, 65536,
5960
WardenCheckType::Timing, 10, WardenEvidenceClass::ProtocolHealth));
@@ -98,28 +99,77 @@ inline void AppendInitialProfile(std::vector<WardenCheckRowInput>& rows,
9899
wallClimb.length = 4;
99100
wallClimb.expectedHex = "BB8D243F";
100101
rows.push_back(wallClimb);
102+
103+
// Both tables dispatch the client interface-signature result. A patched
104+
// table can turn all outcomes into success without changing Warden code.
105+
WardenCheckRowInput glueDispatch = MakeRow(build, localeHex, 65537,
106+
WardenCheckType::Mem, 80, WardenEvidenceClass::IntegrityInvariant);
107+
glueDispatch.address = 4631212;
108+
glueDispatch.length = 16;
109+
glueDispatch.expectedHex = "1CA9460029A9460036A946009EA94600";
110+
rows.push_back(glueDispatch);
111+
112+
WardenCheckRowInput frameDispatch = MakeRow(build, localeHex, 65538,
113+
WardenCheckType::Mem, 90, WardenEvidenceClass::IntegrityInvariant);
114+
frameDispatch.address = frameDispatchAddress;
115+
frameDispatch.length = 16;
116+
frameDispatch.expectedHex = frameDispatchExpectedHex;
117+
rows.push_back(frameDispatch);
101118
}
102119

103-
/** Exact database rows intended for the first three supported profiles. */
120+
/** Exact database rows intended for all supported Classic Windows profiles. */
104121
inline std::vector<WardenCheckRowInput> InitialWardenRows()
105122
{
106123
std::vector<WardenCheckRowInput> rows;
107-
rows.reserve(21);
124+
rows.reserve(72);
108125
AppendInitialProfile(rows, 5875, "656E5553",
109126
"7D88154D3411811985F5D81177C5453248133443", "4F6B6179",
110127
6392064,
111128
"558BEC8B51408B450C81E2FF7DA075508950108B450850E824DA1A005DC20800",
112-
8151558, 4803152, "A1C0EACE00", 8445948);
129+
8151558, 4803152, "A1C0EACE00", 8445948, 4784584,
130+
"5EFF48006BFF480078FF480095FF4800");
131+
AppendInitialProfile(rows, 5875, "6B6F4B52",
132+
"755D6D7F49BB34114433386D559261ED3AA23F00", "ED9995EC9DB8",
133+
6392064,
134+
"558BEC8B51408B450C81E2FF7DA075508950108B450850E824DA1A005DC20800",
135+
8151558, 4803152, "A1C0EACE00", 8445948, 4784584,
136+
"5EFF48006BFF480078FF480095FF4800");
137+
AppendInitialProfile(rows, 5875, "7A685457",
138+
"2A70E6402A40A4F9E9960CED419DBA5E6DEB8536", "E7A2BAE5AE9A",
139+
6392064,
140+
"558BEC8B51408B450C81E2FF7DA075508950108B450850E824DA1A005DC20800",
141+
8151558, 4803152, "A1C0EACE00", 8445948, 4784584,
142+
"5EFF48006BFF480078FF480095FF4800");
143+
AppendInitialProfile(rows, 5875, "66724652",
144+
"AF2D81AF013A9BA6BB92CE171E43FA903C9E8C09", "4F4B",
145+
6392064,
146+
"558BEC8B51408B450C81E2FF7DA075508950108B450850E824DA1A005DC20800",
147+
8151558, 4803152, "A1C0EACE00", 8445948, 4784584,
148+
"5EFF48006BFF480078FF480095FF4800");
149+
AppendInitialProfile(rows, 5875, "65734553",
150+
"1ECEC2C6596B8411FA5FE153EDFB9A6EE43360E9", "41636570746172",
151+
6392064,
152+
"558BEC8B51408B450C81E2FF7DA075508950108B450850E824DA1A005DC20800",
153+
8151558, 4803152, "A1C0EACE00", 8445948, 4784584,
154+
"5EFF48006BFF480078FF480095FF4800");
113155
AppendInitialProfile(rows, 6005, "656E4742",
114156
"7D88154D3411811985F5D81177C5453248133443", "4F6B6179",
115157
6392064,
116158
"558BEC8B51408B450C81E2FF7DA075508950108B450850E864DA1A005DC20800",
117-
8151622, 4803152, "A1C0EACE00", 8445948);
159+
8151622, 4803152, "A1C0EACE00", 8445948, 4784584,
160+
"5EFF48006BFF480078FF480095FF4800");
161+
AppendInitialProfile(rows, 6005, "64654445",
162+
"A0B3DC2D78AD892F2436BCD937BE51B4989D64C1", "4F4B",
163+
6392064,
164+
"558BEC8B51408B450C81E2FF7DA075508950108B450850E864DA1A005DC20800",
165+
8151622, 4803152, "A1C0EACE00", 8445948, 4784584,
166+
"5EFF48006BFF480078FF480095FF4800");
118167
AppendInitialProfile(rows, 6141, "7A68434E",
119168
"C5A1DE4C1CD412EB4D2E02AFAB6131B737EFCAF0", "E7A1AEE5AE9A",
120169
6401184,
121170
"558BEC8B51408B450C81E2FF7DA075508950108B450850E864EB1A005DC20800",
122-
8165094, 4806720, "A1E031CF00", 8462780);
171+
8165094, 4806720, "A1E031CF00", 8462780, 4788152,
172+
"4E0D49005B0D4900680D4900850D4900");
123173
return rows;
124174
}
125175

src/tests/WardenCheckPlannerTest.cpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ TEST(WardenCheckPlanner_profileless_planner_remains_inert)
158158
TEST(WardenCheckPlanner_initial_plan_waits_and_preserves_catalogue_order)
159159
{
160160
std::vector<warden::WardenCheckDefinition> const checks = ExactChecks();
161-
REQUIRE(checks.size() == 7u);
161+
REQUIRE(checks.size() == 9u);
162162
warden::WardenCheckPlanner planner(warden::WardenConfiguration{}, 1000,
163163
checks);
164164

@@ -169,11 +169,11 @@ TEST(WardenCheckPlanner_initial_plan_waits_and_preserves_catalogue_order)
169169
REQUIRE(plan.has_value());
170170
CHECK_EQ(plan->requestId, uint32(1));
171171
CHECK(plan->purpose == warden::CheckPlanPurpose::Initial);
172-
REQUIRE(plan->checks.size() == 7u);
172+
REQUIRE(plan->checks.size() == 9u);
173173
for (size_t index = 1; index < plan->checks.size(); ++index)
174174
CHECK(plan->checks[index - 1].sortOrder < plan->checks[index].sortOrder);
175175
CHECK_EQ(warden::GetWardenCheckId(plan->checks.front()), uint32(65536));
176-
CHECK_EQ(warden::GetWardenCheckId(plan->checks.back()), uint32(1135));
176+
CHECK_EQ(warden::GetWardenCheckId(plan->checks.back()), uint32(65538));
177177
CHECK(!planner.Update(true, 60000).has_value());
178178
}
179179

@@ -189,15 +189,15 @@ TEST(WardenCheckPlanner_resets_partial_initial_delay_when_ineligible)
189189

190190
TEST(WardenCheckPlanner_normal_rotation_covers_every_nonhealth_check_once)
191191
{
192-
ScriptedRandom random{{30, 30, 30, 30}, {}, {}};
192+
ScriptedRandom random{{30, 30, 30, 30, 30}, {}, {}};
193193
warden::WardenCheckPlanner planner(warden::WardenConfiguration{}, 1000,
194194
ExactChecks(), random.Callback());
195195
std::optional<warden::CheckPlan> initial = planner.Update(true, 1000);
196196
REQUIRE(initial.has_value());
197197
planner.Complete(*initial);
198198

199199
std::map<uint32, uint32> counts;
200-
for (uint32 batch = 0; batch < 3; ++batch)
200+
for (uint32 batch = 0; batch < 4; ++batch)
201201
{
202202
std::optional<warden::CheckPlan> recurring =
203203
planner.Update(true, 30000);
@@ -216,9 +216,9 @@ TEST(WardenCheckPlanner_normal_rotation_covers_every_nonhealth_check_once)
216216
planner.Complete(*recurring);
217217
}
218218

219-
CHECK_EQ(counts.size(), size_t(6));
219+
CHECK_EQ(counts.size(), size_t(8));
220220
for (uint32 id : {uint32(1), uint32(2), uint32(1107), uint32(827),
221-
uint32(1566), uint32(1135)})
221+
uint32(1566), uint32(1135), uint32(65537), uint32(65538)})
222222
CHECK_EQ(counts[id], uint32(1));
223223
}
224224

@@ -275,7 +275,7 @@ TEST(WardenCheckPlanner_aggressive_plans_include_only_actionable_checks)
275275
CHECK(immediate->purpose ==
276276
warden::CheckPlanPurpose::AggressiveImmediate);
277277
CHECK(CheckIds(*immediate) ==
278-
std::vector<uint32>({1107, 827, 1566}));
278+
std::vector<uint32>({1107, 827, 1566, 65537, 65538}));
279279
planner.Complete(*immediate);
280280

281281
std::optional<warden::CheckPlan> initial = planner.Update(true, 1000);
@@ -287,7 +287,7 @@ TEST(WardenCheckPlanner_aggressive_plans_include_only_actionable_checks)
287287
CHECK(recurring->purpose ==
288288
warden::CheckPlanPurpose::AggressiveRecurring);
289289
CHECK(CheckIds(*recurring) ==
290-
std::vector<uint32>({1107, 827, 1566}));
290+
std::vector<uint32>({1107, 827, 1566, 65537, 65538}));
291291
}
292292

293293
TEST(WardenCheckPlanner_observation_only_aggressive_mode_uses_normal_checks)
@@ -354,10 +354,10 @@ TEST(WardenCheckPlanner_preflight_is_linear_and_uses_exact_purposes)
354354
REQUIRE(exact != nullptr);
355355
std::vector<warden::CheckPlan> plans =
356356
warden::BuildWardenPreflightPlans(*exact);
357-
REQUIRE(plans.size() == 7u);
357+
REQUIRE(plans.size() == 9u);
358358
CHECK(plans[0].purpose == warden::CheckPlanPurpose::Initial);
359359
CHECK_EQ(plans[0].requestId, uint32(1));
360-
CHECK_EQ(plans[0].checks.size(), size_t(7));
360+
CHECK_EQ(plans[0].checks.size(), size_t(9));
361361
for (size_t index = 1; index < plans.size(); ++index)
362362
{
363363
CHECK(plans[index].purpose ==

src/tests/WardenModuleCatalogTest.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ TEST(WardenCatalog_coverage_requires_profiles_and_modules_both_directions)
9191

9292
std::vector<warden::WardenCheckRowInput> rows =
9393
warden::test::InitialWardenRows();
94-
rows.resize(7);
94+
rows.resize(9);
9595
warden::WardenCheckCatalog missingModule = BuildCheckCatalog(rows);
9696
CHECK(warden::ValidateWardenCatalogCoverage(missingModule, modules) ==
9797
warden::WardenCheckCatalogLoadFailure::ModuleWithoutProfile);

0 commit comments

Comments
 (0)