Delete use cookie from sober

Close #11
This commit is contained in:
komaruworld committed 2026-08-16 03:25:01 +03:00
1 parent 0b8cbef447
commit 2a154b6f58
16 files changed
+10 -891

No files matched your search

-3
View File
@@ -410,9 +410,6 @@ target_link_libraries(mocktail_auth_runtime_composition PUBLIC
mocktail_runtime
mocktail_services
)
target_link_libraries(mocktail_auth_runtime_composition PRIVATE
Mocktail::CryptoFoundation
)
mocktail_apply_compile_options(mocktail_auth_runtime_composition)
add_library(mocktail_linker STATIC
+1 -2
View File
@@ -101,8 +101,7 @@ struct AuthRuntimeComposition {
};
// A VM requires authentication or explicit guest mode. HTTP 401/403 clears
// only a managed credential; rejected Sober credentials are suppressed
// without changing Sober's file.
// only Mocktail's managed credential.
AuthRuntimeComposition ComposeAuthRuntime(const Environment& environment,
const RuntimePaths& paths,
services::AuthService& auth_service);
-8
View File
@@ -81,12 +81,6 @@ class RuntimePaths {
return active_payload_manifest_;
}
const std::filesystem::path& cookie_file() const { return cookie_file_; }
const std::filesystem::path& sober_cookie_file() const {
return sober_cookie_file_;
}
const std::filesystem::path& default_sober_rejection_file() const {
return default_sober_rejection_file_;
}
bool use_real_sober_paths() const { return use_real_sober_paths_; }
std::filesystem::path DefaultAssetPath() const;
@@ -119,8 +113,6 @@ class RuntimePaths {
std::filesystem::path config_file_;
std::filesystem::path active_payload_manifest_;
std::filesystem::path cookie_file_;
std::filesystem::path sober_cookie_file_;
std::filesystem::path default_sober_rejection_file_;
bool use_real_sober_paths_ = false;
};
-1
View File
@@ -894,7 +894,6 @@ RunCandidateCanary() {
MOCKTAIL_SKIP_UPDATE_CHECK=1 \
MOCKTAIL_ISOLATED_CANARY=1 \
MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP=1 \
MOCKTAIL_USE_SOBER_COOKIES=0 \
MOCKTAIL_IGNORE_WINDOW_CLOSE=1 \
MOCKTAIL_DISABLE_SUPPORT_BUNDLE=1 \
MOCKTAIL_AUTO_EXIT_AFTER_PRESENT_MS=5000 \
-2
View File
@@ -115,9 +115,7 @@ if [[ "${MOCKTAIL_ISOLATED_CANARY:-0}" == 1 ]]; then
# rendering, and controlled lifecycle/audio shutdown.
unset MOCKTAIL_COOKIE_FILE MOCKTAIL_ROBLOX_COOKIES
export MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP=1
export MOCKTAIL_USE_SOBER_COOKIES=0
else
export MOCKTAIL_COOKIE_FILE="${MOCKTAIL_COOKIE_FILE:-$HOME/.var/app/org.vinegarhq.Sober/data/sober/cookies}"
fi
export MOCKTAIL_ENGINE_DETACH="${MOCKTAIL_ENGINE_DETACH:-0}"
export MOCKTAIL_CALL_REAL_APP_BRIDGE_INIT="${MOCKTAIL_CALL_REAL_APP_BRIDGE_INIT:-1}"
-12
View File
@@ -1103,14 +1103,6 @@ std::string MocktailConfigRoot() {
return HomePath("/.config/mocktail");
}
std::string SoberDataRoot() {
const char* root = std::getenv("MOCKTAIL_SOBER_DATA_ROOT");
if (root && root[0] != '\0') {
return root;
}
return HomePath("/.var/app/org.vinegarhq.Sober/data/sober");
}
void EnsureCookieStoreLoadedLocked() {
if (g_cookie_store_loaded) {
return;
@@ -1129,10 +1121,6 @@ void EnsureCookieStoreLoadedLocked() {
g_cookie_header =
NormalizeCookieHeader(ReadTextFile(MocktailConfigRoot() + "/cookie"));
}
if (g_cookie_header.empty()) {
g_cookie_header =
NormalizeCookieHeader(ReadTextFile(SoberDataRoot() + "/cookies"));
}
if (TraceEnabled()) {
std::cout << " [JNI] cookie store loaded bytes="
<< g_cookie_header.size() << '\n';
+2 -8
View File
@@ -28793,11 +28793,6 @@ std::string MocktailCookiePath() {
return MocktailConfigRoot() + "/cookie";
}
std::string SoberCookiePath() {
return GetEnvString("MOCKTAIL_SOBER_COOKIE_FILE",
(SoberDataRoot() + "/cookies").c_str());
}
bool CookieHasAttribute(const std::string& cookie, const char* attribute) {
std::string lower_cookie = cookie;
std::string lower_attribute = attribute ? attribute : "";
@@ -29583,9 +29578,8 @@ void ConfigureNativeSettings(JNIEnv* env, jclass settings_class,
std::cout << " [engine] no Roblox cookie found; proceeding without login\n"
<< std::flush;
} else {
std::cout << " [engine] WARNING: no Roblox cookie found; checked "
<< MocktailCookiePath() << " and " << SoberCookiePath()
<< '\n'
std::cout << " [engine] WARNING: no Roblox cookie found at "
<< MocktailCookiePath() << '\n'
<< std::flush;
}
std::string android_id =
-4
View File
@@ -657,10 +657,6 @@ int main(int argc, char* argv[]) {
tracker_cookie_owned = false;
} else if (mocktail::runtime::RuntimePaths::Exists(paths.cookie_file())) {
tracker_cookie_file = paths.cookie_file();
} else if (mocktail::runtime::RuntimePaths::Exists(
paths.sober_cookie_file())) {
tracker_cookie_file = paths.sober_cookie_file();
tracker_cookie_owned = false;
}
const mocktail::services::BrowserTrackerResult browser_tracker_result =
browser_tracker.EnsureInitialized(app_storage_file, tracker_cookie_file,
+2 -178
View File
@@ -35,7 +35,6 @@
#include <vector>
#include "jnivm/jnivm.h"
#include "mocktail/sha256.h"
#include "runtime/environment.h"
#include "runtime/runtime_paths.h"
#include "services/auth_service.h"
@@ -45,8 +44,6 @@ namespace runtime {
namespace {
constexpr uintmax_t kMaximumCookieFileBytes = 1024 * 1024;
constexpr std::string_view kSoberRejectionMarkerPrefix = "v1:";
constexpr char kSoberRejectionDomain[] = "mocktail-default-sober-rejection-v1";
enum class CookieLoadStatus {
kFound,
@@ -59,7 +56,6 @@ enum class CookieSource {
kEnvironment,
kExplicitFile,
kManagedFile,
kSoberFile,
};
struct CookieLoadResult {
@@ -315,34 +311,6 @@ bool WritePrivateFileAtomically(const std::filesystem::path& path,
FsyncDirectoryChain(first_created_parent, existing_ancestor);
}
std::string BuildSoberRejectionMarker(std::string_view rejected_value) {
foundation::Sha256 sha256;
sha256.Update(reinterpret_cast<const unsigned char*>(kSoberRejectionDomain),
sizeof(kSoberRejectionDomain));
sha256.Update(rejected_value);
std::string marker(kSoberRejectionMarkerPrefix);
marker += sha256.FinalHex();
marker.push_back('\n');
return marker;
}
bool IsValidSoberRejectionMarker(std::string_view marker) {
constexpr size_t kSha256HexBytes = 64;
if (marker.size() !=
kSoberRejectionMarkerPrefix.size() + kSha256HexBytes + 1 ||
marker.compare(0, kSoberRejectionMarkerPrefix.size(),
kSoberRejectionMarkerPrefix) != 0 ||
marker.back() != '\n') {
return false;
}
const std::string_view digest =
marker.substr(kSoberRejectionMarkerPrefix.size(), kSha256HexBytes);
return std::all_of(digest.begin(), digest.end(), [](char character) {
return (character >= '0' && character <= '9') ||
(character >= 'a' && character <= 'f');
});
}
bool PersistRobloxCredential(void* opaque, const char* data, size_t size) {
auto* context = static_cast<CookiePersistenceContext*>(opaque);
if (context == nullptr || context->path.empty() || data == nullptr ||
@@ -517,90 +485,6 @@ CookieLoadResult ReadCookieSource(const std::filesystem::path& path,
return result;
}
enum class SoberRejectionStatus {
kNoMatch,
kMatch,
kUnavailable,
};
struct SoberRejectionResult {
SoberRejectionStatus status = SoberRejectionStatus::kNoMatch;
std::string error;
};
SoberRejectionResult CheckSoberRejection(const RuntimePaths& paths,
std::string_view cookie_contents) {
std::string cookie_value =
services::AuthService::ExtractRoblosecurityValue(cookie_contents);
if (cookie_value.empty()) {
return {};
}
struct stat auth_root_status = {};
if (lstat(paths.auth_root().c_str(), &auth_root_status) != 0) {
ClearSensitiveString(&cookie_value);
if (errno == ENOENT) {
return {};
}
return {SoberRejectionStatus::kUnavailable,
"saved Sober rejection marker storage is unavailable"};
}
if (!S_ISDIR(auth_root_status.st_mode) || S_ISLNK(auth_root_status.st_mode) ||
auth_root_status.st_uid != geteuid() ||
(auth_root_status.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
ClearSensitiveString(&cookie_value);
return {SoberRejectionStatus::kUnavailable,
"saved Sober rejection marker storage is unsafe"};
}
const std::filesystem::path& marker_path =
paths.default_sober_rejection_file();
struct stat marker_status = {};
if (lstat(marker_path.c_str(), &marker_status) != 0) {
ClearSensitiveString(&cookie_value);
if (errno == ENOENT) {
return {};
}
return {SoberRejectionStatus::kUnavailable,
"saved Sober rejection marker is unavailable"};
}
CookieLoadResult marker = ReadCookieFile(marker_path, true);
if (marker.status != CookieLoadStatus::kFound ||
!IsValidSoberRejectionMarker(marker.value)) {
ClearSensitiveString(&cookie_value);
ClearSensitiveString(&marker.value);
return {SoberRejectionStatus::kUnavailable,
"saved Sober rejection marker is invalid"};
}
const std::string expected = BuildSoberRejectionMarker(cookie_value);
ClearSensitiveString(&cookie_value);
const bool matches = marker.value == expected;
ClearSensitiveString(&marker.value);
return {
matches ? SoberRejectionStatus::kMatch : SoberRejectionStatus::kNoMatch,
{}};
}
bool PersistSoberRejection(const RuntimePaths& paths,
std::string_view rejected_value,
std::string* error) {
if (rejected_value.empty()) {
if (error != nullptr) {
*error = "rejected Sober credential could not be identified safely";
}
return false;
}
const std::string marker = BuildSoberRejectionMarker(rejected_value);
if (!WritePrivateFileAtomically(paths.default_sober_rejection_file(),
marker)) {
if (error != nullptr) {
*error = "rejected Sober credential could not be retired safely";
}
return false;
}
return true;
}
bool ReadDescriptorContents(int descriptor, std::string* contents) {
if (contents == nullptr || lseek(descriptor, 0, SEEK_SET) < 0) {
return false;
@@ -633,8 +517,7 @@ bool ReadDescriptorContents(int descriptor, std::string* contents) {
}
bool IsAutomaticallyRecoverableSource(CookieSource source) {
return source == CookieSource::kManagedFile ||
source == CookieSource::kSoberFile;
return source == CookieSource::kManagedFile;
}
bool IsCookieFileDelimiter(char character) {
@@ -680,7 +563,6 @@ std::vector<CookieRedactionRange> FindCookieRedactionRanges(
bool ClearRejectedCookieFile(const std::filesystem::path& path,
std::string_view loaded_contents,
const std::filesystem::path& protected_cookie_path,
std::string* error) {
const std::filesystem::path parent = path.parent_path().empty()
? std::filesystem::path(".")
@@ -741,13 +623,6 @@ bool ClearRejectedCookieFile(const std::filesystem::path& path,
return fail("rejected Roblox cookie could not be identified safely");
}
struct stat protected_metadata = {};
bool protected_exists = false;
if (stat(protected_cookie_path.c_str(), &protected_metadata) == 0) {
protected_exists = true;
} else if (errno != ENOENT) {
return fail("protected Sober cookie identity could not be checked");
}
const auto source_metadata_error = [&](const struct stat& metadata) {
if (!S_ISREG(metadata.st_mode) || metadata.st_uid != geteuid() ||
(metadata.st_mode & (S_IRWXG | S_IRWXO)) != 0 || metadata.st_size < 0 ||
@@ -756,10 +631,6 @@ bool ClearRejectedCookieFile(const std::filesystem::path& path,
"Roblox cookie changed while authentication was "
"checked");
}
if (protected_exists && SameFile(protected_metadata, metadata)) {
return std::string(
"managed Roblox cookie aliases the protected Sober file");
}
if (metadata.st_nlink != 1) {
return std::string("managed Roblox cookie has unexpected hard links");
}
@@ -821,14 +692,6 @@ bool ClearRejectedCookieFile(const std::filesystem::path& path,
!SameFile(path_metadata, source_metadata)) {
return fail("Roblox cookie changed while authentication was checked");
}
struct stat current_protected_metadata = {};
if (stat(protected_cookie_path.c_str(), &current_protected_metadata) == 0) {
if (SameFile(current_protected_metadata, source_metadata)) {
return fail("managed Roblox cookie aliases the protected Sober file");
}
} else if (errno != ENOENT) {
return fail("protected Sober cookie identity could not be checked");
}
struct stat prewrite_metadata = {};
if (fstat(source.get(), &prewrite_metadata) != 0 ||
!SameFileVersion(source_metadata, prewrite_metadata) ||
@@ -901,26 +764,6 @@ CookieLoadResult LoadSavedCookie(const Environment& environment,
} else if (result.status != CookieLoadStatus::kMissing) {
return result;
}
if (Enabled(environment, "MOCKTAIL_USE_SOBER_COOKIES", true)) {
const CookieSource sober_source =
environment.HasNonEmpty("MOCKTAIL_SOBER_COOKIE_FILE")
? CookieSource::kExplicitFile
: CookieSource::kSoberFile;
result = ReadCookieSource(paths.sober_cookie_file(), false, sober_source);
if (result.status == CookieLoadStatus::kFound &&
sober_source == CookieSource::kSoberFile) {
const SoberRejectionResult rejection =
CheckSoberRejection(paths, result.value);
if (rejection.status == SoberRejectionStatus::kUnavailable) {
ClearSensitiveString(&result.value);
result.status = CookieLoadStatus::kUnavailable;
result.error = rejection.error;
} else if (rejection.status == SoberRejectionStatus::kMatch) {
ClearSensitiveString(&result.value);
result = {};
}
}
}
return result;
}
@@ -1031,29 +874,11 @@ AuthRuntimeComposition ComposeAuthRuntime(
if (session.status == services::AuthSessionStatus::kInvalid &&
(session.http_status == 401 || session.http_status == 403) &&
IsAutomaticallyRecoverableSource(cookie.source)) {
std::string rejected_value =
services::AuthService::ExtractRoblosecurityValue(cookie.value);
std::string reset_error;
const bool default_sober_fallback =
Enabled(environment, "MOCKTAIL_USE_SOBER_COOKIES", true) &&
!environment.HasNonEmpty("MOCKTAIL_SOBER_COOKIE_FILE");
if ((cookie.source == CookieSource::kSoberFile ||
(cookie.source == CookieSource::kManagedFile &&
default_sober_fallback)) &&
!PersistSoberRejection(paths, rejected_value, &reset_error)) {
ClearSensitiveString(&cookie.value);
ClearSensitiveString(&rejected_value);
credential.Clear();
composition.status = AuthRuntimeStatus::kUnavailable;
composition.http_status = session.http_status;
composition.error = std::move(reset_error);
return composition;
}
if (cookie.source == CookieSource::kManagedFile &&
!ClearRejectedCookieFile(cookie.source_path, cookie.value,
paths.sober_cookie_file(), &reset_error)) {
&reset_error)) {
ClearSensitiveString(&cookie.value);
ClearSensitiveString(&rejected_value);
credential.Clear();
composition.status = AuthRuntimeStatus::kUnavailable;
composition.http_status = session.http_status;
@@ -1061,7 +886,6 @@ AuthRuntimeComposition ComposeAuthRuntime(
return composition;
}
ClearSensitiveString(&cookie.value);
ClearSensitiveString(&rejected_value);
credential.Clear();
composition.rejected_credential_retired = true;
if (!allow_guest) {
-7
View File
@@ -358,13 +358,6 @@ RuntimePaths RuntimePaths::FromEnvironment(
paths.active_payload_manifest_ = paths.data_root_ / "current.json";
paths.cookie_file_ = paths.auth_root_ / "roblox.cookie";
paths.default_sober_rejection_file_ =
paths.auth_root_ / "default-sober-rejection";
paths.sober_cookie_file_ =
environment.HasNonEmpty("MOCKTAIL_SOBER_COOKIE_FILE")
? std::filesystem::path(
environment.GetOr("MOCKTAIL_SOBER_COOKIE_FILE", ""))
: paths.sober_data_root_ / "cookies";
paths.use_real_sober_paths_ =
LegacyEnabled(environment, "MOCKTAIL_USE_REAL_SOBER_PATHS");
return paths;
-1
View File
@@ -352,7 +352,6 @@ CanaryResult RunReadinessCanary(const CanaryOptions& options) {
"MOCKTAIL_SKIP_UPDATE_CHECK=1",
"MOCKTAIL_ISOLATED_CANARY=1",
"MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP=1",
"MOCKTAIL_USE_SOBER_COOKIES=0",
"MOCKTAIL_VALIDATE_ROBLOX_COOKIE=0",
"MOCKTAIL_IGNORE_WINDOW_CLOSE=1",
"MOCKTAIL_DISABLE_SUPPORT_BUNDLE=1",
+5 -655
View File
@@ -35,7 +35,6 @@
#include "jnivm/jnivm.h"
#include "legacy/legacy_runtime.h"
#include "mocktail/sha256.h"
#include "runtime/environment.h"
#include "runtime/runtime_paths.h"
#include "services/auth_service.h"
@@ -138,17 +137,6 @@ RuntimePaths PathsFor(const MapEnvironment& environment,
return RuntimePaths::FromEnvironment(environment, directory.path());
}
std::filesystem::path SoberRejectionPathFor(const RuntimePaths& paths) {
return paths.default_sober_rejection_file();
}
std::string SoberRejectionMarkerFor(std::string_view credential) {
constexpr char kDomain[] = "mocktail-default-sober-rejection-v1";
std::string fingerprint_input(kDomain, sizeof(kDomain));
fingerprint_input.append(credential);
return "v1:" + foundation::ComputeSha256Hex(fingerprint_input) + "\n";
}
std::string ReadJavaString(JNIEnv* env, jstring value) {
if (env == nullptr || value == nullptr) {
return {};
@@ -219,11 +207,14 @@ TEST(AuthRuntimeCompositionTest,
EXPECT_EQ(identity.display_name.find(kCredential), std::string::npos);
}
TEST(AuthRuntimeCompositionTest, MissingCookieRequiresExplicitGuestPolicy) {
TEST(AuthRuntimeCompositionTest, SoberCookieIsIgnoredAndGuestPolicyRequired) {
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_USE_SOBER_COOKIES", "0");
ASSERT_TRUE(WriteFile(
directory.path() /
".var/app/org.vinegarhq.Sober/data/sober/cookies",
std::string(".ROBLOSECURITY=") + kCredential + "\n"));
FakeHttpClient http;
services::AuthService auth_service(http);
@@ -254,7 +245,6 @@ TEST(AuthRuntimeCompositionTest,
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_USE_SOBER_COOKIES", "0");
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
FakeHttpClient http;
@@ -315,7 +305,6 @@ TEST(AuthRuntimeCompositionTest,
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_USE_SOBER_COOKIES", "0");
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
auto http = std::make_shared<FakeHttpClient>();
@@ -360,7 +349,6 @@ TEST(AuthRuntimeCompositionTest,
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_USE_SOBER_COOKIES", "0");
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
FakeHttpClient initial_http;
@@ -408,7 +396,6 @@ TEST(AuthRuntimeCompositionTest,
MapEnvironment restart_environment;
restart_environment.Set("HOME", directory.path().string());
restart_environment.Set("MOCKTAIL_USE_SOBER_COOKIES", "0");
FakeHttpClient restart_http;
restart_http.response = {
true,
@@ -457,7 +444,6 @@ TEST(AuthRuntimeCompositionTest,
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_USE_SOBER_COOKIES", "0");
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
const std::string remaining_cookies =
@@ -498,406 +484,11 @@ TEST(AuthRuntimeCompositionTest,
EXPECT_EQ(restart_http.request_count, 0);
}
TEST(AuthRuntimeCompositionTest,
Http401ClearsManagedAndSuppressesMatchingDefaultSoberCredential) {
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
const std::string rejected_cookie =
std::string(".ROBLOSECURITY=") + kCredential;
const std::string managed_other = "ManagedCookie=keep\n";
const std::string sober_other = "SoberCookie=keep\n";
ASSERT_TRUE(
WriteFile(paths.cookie_file(), rejected_cookie + "\n" + managed_other));
ASSERT_TRUE(WriteFile(paths.sober_cookie_file(),
rejected_cookie + "\n" + sober_other));
FakeHttpClient http;
http.response = {true, 401, {}, {}};
services::AuthService auth_service(http);
const AuthRuntimeComposition composition =
ComposeAuthRuntime(environment, paths, auth_service);
ASSERT_TRUE(composition) << composition.error;
EXPECT_EQ(composition.status, AuthRuntimeStatus::kGuest);
EXPECT_EQ(http.request_count, 1);
const std::string redacted_prefix(rejected_cookie.size(), ' ');
EXPECT_EQ(ReadFile(paths.cookie_file()),
redacted_prefix + "\n" + managed_other);
EXPECT_EQ(ReadFile(paths.sober_cookie_file()),
rejected_cookie + "\n" + sober_other);
const std::filesystem::path rejection_path = SoberRejectionPathFor(paths);
const std::string marker = ReadFile(rejection_path);
EXPECT_EQ(marker.size(), 68U);
EXPECT_EQ(marker.rfind("v1:", 0), 0U);
EXPECT_EQ(marker.find(kCredential), std::string::npos);
struct stat marker_metadata = {};
ASSERT_EQ(lstat(rejection_path.c_str(), &marker_metadata), 0);
EXPECT_TRUE(S_ISREG(marker_metadata.st_mode));
EXPECT_EQ(marker_metadata.st_mode & (S_IRWXG | S_IRWXO), 0U);
FakeHttpClient restart_http;
services::AuthService restart_auth_service(restart_http);
const AuthRuntimeComposition restarted =
ComposeAuthRuntime(environment, paths, restart_auth_service);
ASSERT_TRUE(restarted) << restarted.error;
EXPECT_EQ(restarted.status, AuthRuntimeStatus::kGuest);
EXPECT_EQ(restart_http.request_count, 0);
}
TEST(AuthRuntimeCompositionTest,
Http401SuppressesDefaultSoberCredentialWithoutMutatingItsFile) {
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
const std::string remaining_cookies =
"RBXEventTrackerV2=browserid=654321&browserid_insert_timestamp=1\n"
"OtherCookie=keep\n";
const std::string rejected_cookie =
std::string(".ROBLOSECURITY=") + kCredential;
ASSERT_TRUE(WriteFile(paths.sober_cookie_file(),
rejected_cookie + "\n" + remaining_cookies));
FakeHttpClient http;
http.response = {true, 401, {}, {}};
services::AuthService auth_service(http);
const AuthRuntimeComposition composition =
ComposeAuthRuntime(environment, paths, auth_service);
ASSERT_TRUE(composition) << composition.error;
EXPECT_EQ(composition.status, AuthRuntimeStatus::kGuest);
EXPECT_EQ(composition.http_status, 401);
EXPECT_EQ(http.request_count, 1);
EXPECT_EQ(ReadFile(paths.sober_cookie_file()),
rejected_cookie + "\n" + remaining_cookies);
EXPECT_FALSE(std::filesystem::exists(paths.cookie_file()));
const std::filesystem::path rejection_path = SoberRejectionPathFor(paths);
EXPECT_EQ(ReadFile(rejection_path).find(kCredential), std::string::npos);
const std::string changed_tracker_cookies =
rejected_cookie +
"\nRBXEventTrackerV2=browserid=999999&browserid_insert_timestamp=2\n";
ASSERT_TRUE(WriteFile(paths.sober_cookie_file(), changed_tracker_cookies));
FakeHttpClient restart_http;
services::AuthService restart_auth_service(restart_http);
const AuthRuntimeComposition restarted =
ComposeAuthRuntime(environment, paths, restart_auth_service);
ASSERT_TRUE(restarted) << restarted.error;
EXPECT_EQ(restarted.status, AuthRuntimeStatus::kGuest);
EXPECT_EQ(restart_http.request_count, 0);
EXPECT_EQ(ReadFile(paths.sober_cookie_file()), changed_tracker_cookies);
}
TEST(AuthRuntimeCompositionTest,
RelativeNestedAuthRootPersistsDefaultSoberRejection) {
TempDirectory directory;
std::error_code error;
const std::filesystem::path current_directory =
std::filesystem::current_path(error);
ASSERT_FALSE(error);
const std::filesystem::path relative_auth_root = std::filesystem::relative(
directory.path() / "relative/auth/root", current_directory, error);
ASSERT_FALSE(error);
ASSERT_FALSE(relative_auth_root.empty());
ASSERT_FALSE(relative_auth_root.is_absolute());
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_AUTH_ROOT", relative_auth_root.string());
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
const std::string cookies =
std::string(".ROBLOSECURITY=") + kCredential + "\n";
ASSERT_TRUE(WriteFile(paths.sober_cookie_file(), cookies));
FakeHttpClient http;
http.response = {true, 401, {}, {}};
services::AuthService auth_service(http);
const AuthRuntimeComposition composition =
ComposeAuthRuntime(environment, paths, auth_service);
ASSERT_TRUE(composition) << composition.error;
EXPECT_EQ(composition.status, AuthRuntimeStatus::kGuest);
EXPECT_EQ(http.request_count, 1);
EXPECT_EQ(ReadFile(paths.default_sober_rejection_file()),
SoberRejectionMarkerFor(kCredential));
FakeHttpClient restart_http;
services::AuthService restart_auth_service(restart_http);
const AuthRuntimeComposition restarted =
ComposeAuthRuntime(environment, paths, restart_auth_service);
ASSERT_TRUE(restarted) << restarted.error;
EXPECT_EQ(restarted.status, AuthRuntimeStatus::kGuest);
EXPECT_EQ(restart_http.request_count, 0);
}
TEST(AuthRuntimeCompositionTest,
ChangedDefaultSoberCredentialIsCheckedAndReplacesOldMarker) {
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
const std::string first_cookies =
std::string(".ROBLOSECURITY=") + kCredential + "\nOther=keep\n";
ASSERT_TRUE(WriteFile(paths.sober_cookie_file(), first_cookies));
FakeHttpClient first_http;
first_http.response = {true, 401, {}, {}};
services::AuthService first_auth_service(first_http);
ASSERT_TRUE(ComposeAuthRuntime(environment, paths, first_auth_service));
const std::filesystem::path rejection_path = SoberRejectionPathFor(paths);
const std::string first_marker = ReadFile(rejection_path);
struct stat first_metadata = {};
ASSERT_EQ(lstat(rejection_path.c_str(), &first_metadata), 0);
const std::string changed_value = "_|changed-default-sober-credential";
const std::string changed_cookies =
".ROBLOSECURITY=" + changed_value + "\nOther=still-keep\n";
ASSERT_TRUE(WriteFile(paths.sober_cookie_file(), changed_cookies));
FakeHttpClient changed_http;
changed_http.response = {true, 401, {}, {}};
services::AuthService changed_auth_service(changed_http);
const AuthRuntimeComposition changed =
ComposeAuthRuntime(environment, paths, changed_auth_service);
ASSERT_TRUE(changed) << changed.error;
EXPECT_EQ(changed.status, AuthRuntimeStatus::kGuest);
ASSERT_EQ(changed_http.request_count, 1);
ASSERT_EQ(changed_http.last_request.headers.size(), 2U);
EXPECT_EQ(changed_http.last_request.headers[1],
"Cookie: .ROBLOSECURITY=" + changed_value);
EXPECT_EQ(ReadFile(paths.sober_cookie_file()), changed_cookies);
const std::string changed_marker = ReadFile(rejection_path);
EXPECT_NE(changed_marker, first_marker);
EXPECT_EQ(changed_marker.find(changed_value), std::string::npos);
struct stat changed_metadata = {};
ASSERT_EQ(lstat(rejection_path.c_str(), &changed_metadata), 0);
EXPECT_NE(changed_metadata.st_ino, first_metadata.st_ino);
FakeHttpClient restart_http;
services::AuthService restart_auth_service(restart_http);
const AuthRuntimeComposition restarted =
ComposeAuthRuntime(environment, paths, restart_auth_service);
ASSERT_TRUE(restarted) << restarted.error;
EXPECT_EQ(restarted.status, AuthRuntimeStatus::kGuest);
EXPECT_EQ(restart_http.request_count, 0);
}
TEST(AuthRuntimeCompositionTest,
DefaultSoberRefreshDuringHttp401RecoveryIsNeverModified) {
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
ASSERT_TRUE(WriteFile(paths.sober_cookie_file(),
std::string(".ROBLOSECURITY=") + kCredential + "\n"));
const std::string refreshed_value = "_|refreshed-during-http";
const std::string refreshed_cookies =
".ROBLOSECURITY=" + refreshed_value + "\nOther=keep\n";
FakeHttpClient http;
http.response = {true, 401, {}, {}};
http.before_response = [&]() {
EXPECT_TRUE(WriteFile(paths.sober_cookie_file(), refreshed_cookies));
};
services::AuthService auth_service(http);
const AuthRuntimeComposition composition =
ComposeAuthRuntime(environment, paths, auth_service);
ASSERT_TRUE(composition) << composition.error;
EXPECT_EQ(composition.status, AuthRuntimeStatus::kGuest);
EXPECT_EQ(ReadFile(paths.sober_cookie_file()), refreshed_cookies);
FakeHttpClient restart_http;
restart_http.response = {
true,
200,
R"({"id":654,"name":"Refreshed","displayName":"Refreshed User"})",
{}};
services::AuthService restart_auth_service(restart_http);
const AuthRuntimeComposition restarted =
ComposeAuthRuntime(environment, paths, restart_auth_service);
ASSERT_TRUE(restarted) << restarted.error;
EXPECT_EQ(restarted.status, AuthRuntimeStatus::kAuthenticated);
EXPECT_EQ(restart_http.request_count, 1);
ASSERT_EQ(restart_http.last_request.headers.size(), 2U);
EXPECT_EQ(restart_http.last_request.headers[1],
"Cookie: .ROBLOSECURITY=" + refreshed_value);
EXPECT_EQ(ReadFile(paths.sober_cookie_file()), refreshed_cookies);
}
TEST(AuthRuntimeCompositionTest,
MalformedDefaultSoberRejectionMarkerFailsClosedBeforeHttp) {
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
const std::string sober_cookies =
std::string(".ROBLOSECURITY=") + kCredential + "\n";
ASSERT_TRUE(WriteFile(paths.sober_cookie_file(), sober_cookies));
ASSERT_TRUE(WriteFile(SoberRejectionPathFor(paths), "not-a-valid-marker\n"));
ASSERT_EQ(chmod(paths.auth_root().c_str(), S_IRWXU), 0);
FakeHttpClient http;
http.response = {true, 200, {}, {}};
services::AuthService auth_service(http);
const AuthRuntimeComposition composition =
ComposeAuthRuntime(environment, paths, auth_service);
EXPECT_FALSE(composition);
EXPECT_EQ(composition.status, AuthRuntimeStatus::kUnavailable);
EXPECT_EQ(composition.error, "saved Sober rejection marker is invalid");
EXPECT_EQ(http.request_count, 0);
EXPECT_EQ(ReadFile(paths.sober_cookie_file()), sober_cookies);
}
TEST(AuthRuntimeCompositionTest,
UnsafeAuthRootWithoutMarkerFailsClosedBeforeHttp) {
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
const std::string sober_cookies =
std::string(".ROBLOSECURITY=") + kCredential + "\n";
ASSERT_TRUE(WriteFile(paths.sober_cookie_file(), sober_cookies));
std::error_code error;
std::filesystem::create_directories(paths.auth_root(), error);
ASSERT_FALSE(error);
ASSERT_EQ(chmod(paths.auth_root().c_str(),
S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH),
0);
FakeHttpClient http;
http.response = {true, 200, {}, {}};
services::AuthService auth_service(http);
const AuthRuntimeComposition composition =
ComposeAuthRuntime(environment, paths, auth_service);
EXPECT_FALSE(composition);
EXPECT_EQ(composition.status, AuthRuntimeStatus::kUnavailable);
EXPECT_EQ(composition.error,
"saved Sober rejection marker storage is unsafe");
EXPECT_EQ(http.request_count, 0);
EXPECT_EQ(ReadFile(paths.sober_cookie_file()), sober_cookies);
}
TEST(AuthRuntimeCompositionTest,
SymlinkDefaultSoberRejectionMarkerFailsClosedWithoutTouchingTarget) {
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
const std::string sober_cookies =
std::string(".ROBLOSECURITY=") + kCredential + "\n";
ASSERT_TRUE(WriteFile(paths.sober_cookie_file(), sober_cookies));
const std::filesystem::path protected_target =
directory.path() / "protected-marker-target";
const std::string protected_contents = "must remain untouched\n";
ASSERT_TRUE(WriteFile(protected_target, protected_contents));
std::error_code error;
std::filesystem::create_directories(paths.auth_root(), error);
ASSERT_FALSE(error);
ASSERT_EQ(chmod(paths.auth_root().c_str(), S_IRWXU), 0);
std::filesystem::create_symlink(protected_target,
SoberRejectionPathFor(paths), error);
ASSERT_FALSE(error);
FakeHttpClient http;
http.response = {true, 200, {}, {}};
services::AuthService auth_service(http);
const AuthRuntimeComposition composition =
ComposeAuthRuntime(environment, paths, auth_service);
EXPECT_FALSE(composition);
EXPECT_EQ(composition.status, AuthRuntimeStatus::kUnavailable);
EXPECT_EQ(http.request_count, 0);
EXPECT_EQ(ReadFile(protected_target), protected_contents);
EXPECT_EQ(ReadFile(paths.sober_cookie_file()), sober_cookies);
}
TEST(AuthRuntimeCompositionTest,
FifoDefaultSoberRejectionMarkerFailsClosedWithoutBlocking) {
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
const std::string sober_cookies =
std::string(".ROBLOSECURITY=") + kCredential + "\n";
ASSERT_TRUE(WriteFile(paths.sober_cookie_file(), sober_cookies));
std::error_code error;
std::filesystem::create_directories(paths.auth_root(), error);
ASSERT_FALSE(error);
ASSERT_EQ(chmod(paths.auth_root().c_str(), S_IRWXU), 0);
ASSERT_EQ(mkfifo(SoberRejectionPathFor(paths).c_str(), S_IRUSR | S_IWUSR), 0);
FakeHttpClient http;
http.response = {true, 200, {}, {}};
services::AuthService auth_service(http);
const AuthRuntimeComposition composition =
ComposeAuthRuntime(environment, paths, auth_service);
EXPECT_FALSE(composition);
EXPECT_EQ(composition.status, AuthRuntimeStatus::kUnavailable);
EXPECT_EQ(http.request_count, 0);
EXPECT_EQ(ReadFile(paths.sober_cookie_file()), sober_cookies);
}
TEST(AuthRuntimeCompositionTest,
RejectionWriterLockHardlinkCannotChangeDefaultSoberFile) {
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
const std::string sober_cookies =
std::string(".ROBLOSECURITY=") + kCredential + "\n";
ASSERT_TRUE(WriteFile(paths.sober_cookie_file(), sober_cookies));
ASSERT_EQ(chmod(paths.sober_cookie_file().c_str(), S_IRWXU), 0);
std::error_code error;
std::filesystem::create_directories(paths.auth_root(), error);
ASSERT_FALSE(error);
ASSERT_EQ(chmod(paths.auth_root().c_str(), S_IRWXU), 0);
const std::filesystem::path lock_path =
paths.auth_root() / ".default-sober-rejection.mocktail-writer.lock";
std::filesystem::create_hard_link(paths.sober_cookie_file(), lock_path,
error);
ASSERT_FALSE(error);
FakeHttpClient http;
http.response = {true, 401, {}, {}};
services::AuthService auth_service(http);
const AuthRuntimeComposition composition =
ComposeAuthRuntime(environment, paths, auth_service);
EXPECT_FALSE(composition);
EXPECT_EQ(composition.status, AuthRuntimeStatus::kUnavailable);
EXPECT_EQ(composition.http_status, 401);
EXPECT_EQ(composition.error,
"rejected Sober credential could not be retired safely");
EXPECT_EQ(http.request_count, 1);
EXPECT_EQ(ReadFile(paths.sober_cookie_file()), sober_cookies);
struct stat sober_metadata = {};
ASSERT_EQ(lstat(paths.sober_cookie_file().c_str(), &sober_metadata), 0);
EXPECT_EQ(sober_metadata.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO), S_IRWXU);
EXPECT_FALSE(std::filesystem::exists(paths.default_sober_rejection_file()));
}
TEST(AuthRuntimeCompositionTest,
Http401ClearsPrivateReadOnlyManagedCredential) {
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_USE_SOBER_COOKIES", "0");
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
const std::string remaining_cookies = "OtherCookie=keep\n";
@@ -929,7 +520,6 @@ TEST(AuthRuntimeCompositionTest,
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_USE_SOBER_COOKIES", "0");
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
const std::string raw_cookie = kCredential;
@@ -965,7 +555,6 @@ TEST(AuthRuntimeCompositionTest, Http403ClearsManagedCookieAndStartsGuest) {
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_USE_SOBER_COOKIES", "0");
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
const std::string cookies = std::string(".ROBLOSECURITY=") + kCredential +
@@ -989,97 +578,11 @@ TEST(AuthRuntimeCompositionTest, Http403ClearsManagedCookieAndStartsGuest) {
"\nRBXEventTrackerV2=browserid=123456\n");
}
TEST(AuthRuntimeCompositionTest,
DefaultSoberHttp403SuppressesRejectedCredentialAndStartsGuest) {
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
const std::string cookies =
std::string(".ROBLOSECURITY=") + kCredential + "\nOther=keep\n";
ASSERT_TRUE(WriteFile(paths.sober_cookie_file(), cookies));
FakeHttpClient http;
http.response = {true, 403, {}, {}};
services::AuthService auth_service(http);
const AuthRuntimeComposition composition =
ComposeAuthRuntime(environment, paths, auth_service);
ASSERT_TRUE(composition) << composition.error;
EXPECT_EQ(composition.status, AuthRuntimeStatus::kGuest);
EXPECT_EQ(composition.http_status, 403);
EXPECT_TRUE(composition.rejected_credential_retired);
EXPECT_EQ(http.request_count, 1);
EXPECT_EQ(ReadFile(paths.sober_cookie_file()), cookies);
EXPECT_EQ(ReadFile(SoberRejectionPathFor(paths)),
SoberRejectionMarkerFor(kCredential));
}
TEST(AuthRuntimeCompositionTest,
DefaultSoberTransportFailureDoesNotCreateRejectionMarker) {
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
const std::string cookies =
std::string(".ROBLOSECURITY=") + kCredential + "\n";
ASSERT_TRUE(WriteFile(paths.sober_cookie_file(), cookies));
FakeHttpClient http;
http.response = {false, 0, {}, "request contained sensitive details"};
services::AuthService auth_service(http);
const AuthRuntimeComposition composition =
ComposeAuthRuntime(environment, paths, auth_service);
EXPECT_FALSE(composition);
EXPECT_EQ(composition.status, AuthRuntimeStatus::kUnavailable);
EXPECT_EQ(composition.error, "authentication service unavailable");
EXPECT_EQ(http.request_count, 1);
EXPECT_EQ(ReadFile(paths.sober_cookie_file()), cookies);
EXPECT_FALSE(std::filesystem::exists(SoberRejectionPathFor(paths)));
}
TEST(AuthRuntimeCompositionTest,
DefaultSoberHttp401PersistsRejectionWhenGuestIsDisabled) {
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
const RuntimePaths paths = PathsFor(environment, directory);
const std::string cookies =
std::string(".ROBLOSECURITY=") + kCredential + "\n";
ASSERT_TRUE(WriteFile(paths.sober_cookie_file(), cookies));
FakeHttpClient http;
http.response = {true, 401, {}, {}};
services::AuthService auth_service(http);
const AuthRuntimeComposition composition =
ComposeAuthRuntime(environment, paths, auth_service);
EXPECT_FALSE(composition);
EXPECT_EQ(composition.status, AuthRuntimeStatus::kInvalidCredentials);
EXPECT_EQ(composition.http_status, 401);
EXPECT_EQ(http.request_count, 1);
EXPECT_EQ(ReadFile(paths.sober_cookie_file()), cookies);
EXPECT_EQ(ReadFile(SoberRejectionPathFor(paths)),
SoberRejectionMarkerFor(kCredential));
FakeHttpClient restart_http;
services::AuthService restart_auth_service(restart_http);
const AuthRuntimeComposition restarted =
ComposeAuthRuntime(environment, paths, restart_auth_service);
EXPECT_FALSE(restarted);
EXPECT_EQ(restarted.status, AuthRuntimeStatus::kInvalidCredentials);
EXPECT_EQ(restart_http.request_count, 0);
}
TEST(AuthRuntimeCompositionTest,
Http401ClearsManagedCredentialButDoesNotStartGuestWhenDisabled) {
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_USE_SOBER_COOKIES", "0");
const RuntimePaths paths = PathsFor(environment, directory);
const std::string remaining_cookies = "RBXEventTrackerV2=browserid=123456\n";
const std::string rejected_cookie =
@@ -1126,42 +629,11 @@ TEST(AuthRuntimeCompositionTest,
EXPECT_EQ(ReadFile(explicit_cookie), cookies);
}
TEST(AuthRuntimeCompositionTest,
Http401DoesNotClearExplicitSoberCredentialOverride) {
TempDirectory directory;
const std::filesystem::path explicit_sober_cookie =
directory.path() / "explicit-sober-cookie";
const std::string cookies =
std::string(".ROBLOSECURITY=") + kCredential + "\n";
ASSERT_TRUE(WriteFile(explicit_sober_cookie, cookies));
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_SOBER_COOKIE_FILE", explicit_sober_cookie.string());
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
const std::string marker = SoberRejectionMarkerFor(kCredential);
ASSERT_TRUE(WriteFile(SoberRejectionPathFor(paths), marker));
ASSERT_EQ(chmod(paths.auth_root().c_str(), S_IRWXU), 0);
FakeHttpClient http;
http.response = {true, 401, {}, {}};
services::AuthService auth_service(http);
const AuthRuntimeComposition composition =
ComposeAuthRuntime(environment, paths, auth_service);
EXPECT_FALSE(composition);
EXPECT_EQ(composition.status, AuthRuntimeStatus::kInvalidCredentials);
EXPECT_EQ(composition.http_status, 401);
EXPECT_EQ(ReadFile(explicit_sober_cookie), cookies);
EXPECT_EQ(ReadFile(SoberRejectionPathFor(paths)), marker);
}
TEST(AuthRuntimeCompositionTest,
Http401FailsClosedWhileCookieWriterLockIsHeld) {
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_USE_SOBER_COOKIES", "0");
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
const std::string cookies =
@@ -1195,7 +667,6 @@ TEST(AuthRuntimeCompositionTest,
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_USE_SOBER_COOKIES", "0");
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
ASSERT_TRUE(WriteFile(paths.cookie_file(),
@@ -1232,7 +703,6 @@ TEST(AuthRuntimeCompositionTest,
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_USE_SOBER_COOKIES", "0");
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
ASSERT_TRUE(WriteFile(paths.cookie_file(),
@@ -1266,43 +736,6 @@ TEST(AuthRuntimeCompositionTest,
EXPECT_EQ(ReadFile(protected_target), protected_contents);
}
TEST(AuthRuntimeCompositionTest,
ManagedHardlinkToDefaultSoberCookieFailsClosedWithoutRedaction) {
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_USE_SOBER_COOKIES", "0");
environment.Set("MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP", "1");
const RuntimePaths paths = PathsFor(environment, directory);
const std::string cookies =
std::string(".ROBLOSECURITY=") + kCredential + "\nOther=keep\n";
ASSERT_TRUE(WriteFile(paths.sober_cookie_file(), cookies));
ASSERT_EQ(chmod(paths.sober_cookie_file().c_str(), S_IRUSR), 0);
std::error_code error;
std::filesystem::create_directories(paths.auth_root(), error);
ASSERT_FALSE(error);
std::filesystem::create_hard_link(paths.sober_cookie_file(),
paths.cookie_file(), error);
ASSERT_FALSE(error);
FakeHttpClient http;
http.response = {true, 401, {}, {}};
services::AuthService auth_service(http);
const AuthRuntimeComposition composition =
ComposeAuthRuntime(environment, paths, auth_service);
EXPECT_FALSE(composition);
EXPECT_EQ(composition.status, AuthRuntimeStatus::kUnavailable);
EXPECT_EQ(composition.http_status, 401);
EXPECT_EQ(composition.error,
"managed Roblox cookie aliases the protected Sober file");
EXPECT_EQ(ReadFile(paths.cookie_file()), cookies);
EXPECT_EQ(ReadFile(paths.sober_cookie_file()), cookies);
struct stat sober_metadata = {};
ASSERT_EQ(lstat(paths.sober_cookie_file().c_str(), &sober_metadata), 0);
EXPECT_EQ(sober_metadata.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO), S_IRUSR);
}
TEST(AuthRuntimeCompositionTest, UnavailableAuthFailsClosedAndRedactsErrors) {
TempDirectory directory;
MapEnvironment environment;
@@ -1390,89 +823,6 @@ TEST(AuthRuntimeCompositionTest, RejectsSymlinkCookieSource) {
EXPECT_EQ(http.request_count, 0);
}
TEST(AuthRuntimeCompositionTest, FallsBackToConfiguredSoberCookieFile) {
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_CONFIG_ROOT",
(directory.path() / "config").string());
environment.Set("MOCKTAIL_SOBER_COOKIE_FILE",
(directory.path() / "sober-cookies").string());
const RuntimePaths paths = PathsFor(environment, directory);
ASSERT_TRUE(WriteFile(paths.sober_cookie_file(),
std::string(".ROBLOSECURITY=") + kCredential + "\n"));
FakeHttpClient http;
http.response = {
true, 200, R"({"id":654,"name":"SoberUser","displayName":"Sober"})", {}};
services::AuthService auth_service(http);
const AuthRuntimeComposition composition =
ComposeAuthRuntime(environment, paths, auth_service);
ASSERT_TRUE(composition);
EXPECT_EQ(composition.status, AuthRuntimeStatus::kAuthenticated);
EXPECT_EQ(composition.jni_vm->GetRobloxAuthIdentitySnapshot().user_id, 654);
EXPECT_EQ(http.request_count, 1);
std::ifstream migrated(paths.cookie_file(), std::ios::binary);
ASSERT_TRUE(migrated.good());
const std::string migrated_value(
(std::istreambuf_iterator<char>(migrated)),
std::istreambuf_iterator<char>());
EXPECT_EQ(migrated_value,
std::string(".ROBLOSECURITY=") + kCredential + "\n");
struct stat migrated_status = {};
ASSERT_EQ(lstat(paths.cookie_file().c_str(), &migrated_status), 0);
EXPECT_EQ(migrated_status.st_mode & (S_IRWXG | S_IRWXO), 0U);
MapEnvironment restart_environment;
restart_environment.Set("HOME", directory.path().string());
restart_environment.Set("MOCKTAIL_USE_SOBER_COOKIES", "0");
FakeHttpClient restart_http;
restart_http.response = {
true, 200, R"({"id":654,"name":"SoberUser","displayName":"Sober"})", {}};
services::AuthService restart_auth_service(restart_http);
const AuthRuntimeComposition restarted = ComposeAuthRuntime(
restart_environment, PathsFor(restart_environment, directory),
restart_auth_service);
ASSERT_TRUE(restarted);
EXPECT_EQ(restarted.status, AuthRuntimeStatus::kAuthenticated);
ASSERT_EQ(restart_http.request_count, 1);
EXPECT_EQ(restart_http.last_request.headers[1],
std::string("Cookie: .ROBLOSECURITY=") + kCredential);
}
TEST(AuthRuntimeCompositionTest,
TrackerOnlyLocalCookieFallsBackToSoberCredential) {
TempDirectory directory;
MapEnvironment environment;
environment.Set("HOME", directory.path().string());
environment.Set("MOCKTAIL_CONFIG_ROOT",
(directory.path() / "config").string());
environment.Set("MOCKTAIL_SOBER_COOKIE_FILE",
(directory.path() / "sober-cookies").string());
const RuntimePaths paths = PathsFor(environment, directory);
ASSERT_TRUE(WriteFile(
paths.cookie_file(),
"RBXEventTrackerV2=browserid=123456&browserid_insert_timestamp=1\n"));
ASSERT_TRUE(WriteFile(paths.sober_cookie_file(),
std::string(".ROBLOSECURITY=") + kCredential + "\n"));
FakeHttpClient http;
http.response = {
true, 200, R"({"id":654,"name":"SoberUser","displayName":"Sober"})", {}};
services::AuthService auth_service(http);
const AuthRuntimeComposition composition =
ComposeAuthRuntime(environment, paths, auth_service);
ASSERT_TRUE(composition) << composition.error;
EXPECT_EQ(composition.status, AuthRuntimeStatus::kAuthenticated);
ASSERT_EQ(http.request_count, 1);
ASSERT_GE(http.last_request.headers.size(), 2U);
EXPECT_EQ(http.last_request.headers[1],
std::string("Cookie: .ROBLOSECURITY=") + kCredential);
}
TEST(AuthRuntimeCompositionTest,
FileReplacementCannotChangeCredentialOrIdentityDependency) {
constexpr char kCredentialA[] = "_|validated-credential-a";
-1
View File
@@ -285,7 +285,6 @@ fi
if [[ "${MOCKTAIL_ISOLATED_CANARY:-0}" == 1 ]]; then
[[ -z "${MOCKTAIL_COOKIE_FILE+x}" &&
"${MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP:-0}" == 1 &&
"${MOCKTAIL_USE_SOBER_COOKIES:-1}" == 0 &&
"${MOCKTAIL_IGNORE_WINDOW_CLOSE:-0}" == 1 ]] || exit 1
fi
[[ "${VK_DRIVER_FILES:-}" == /test/mocktail-vk.json &&
-1
View File
@@ -22,7 +22,6 @@ output="$(
PATH="${PATH}" \
HOME="${HOME}" \
MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP=1 \
MOCKTAIL_USE_SOBER_COOKIES=0 \
MOCKTAIL_CONFIG_ROOT="${temporary_root}/config" \
MOCKTAIL_DATA_ROOT="${temporary_root}/data" \
MOCKTAIL_CACHE_ROOT="${temporary_root}/cache" \
-1
View File
@@ -79,7 +79,6 @@ run_with_override() {
PATH="${PATH}" \
HOME="${HOME}" \
MOCKTAIL_ALLOW_NO_COOKIE_LUA_APP=1 \
MOCKTAIL_USE_SOBER_COOKIES=0 \
MOCKTAIL_CONFIG_ROOT="${temporary_root}/config" \
MOCKTAIL_DATA_ROOT="${temporary_root}/data" \
MOCKTAIL_CACHE_ROOT="${temporary_root}/cache" \
-7
View File
@@ -142,9 +142,6 @@ TEST(RuntimePathsTest, DerivesXdgDefaults) {
EXPECT_EQ(paths.auth_root(), "/home/mocktail/.local/share/mocktail/auth");
EXPECT_EQ(paths.cookie_file(),
"/home/mocktail/.local/share/mocktail/auth/roblox.cookie");
EXPECT_EQ(paths.default_sober_rejection_file(),
"/home/mocktail/.local/share/mocktail/auth/"
"default-sober-rejection");
}
TEST(RuntimePathsTest, IgnoresRelativeXdgHomes) {
@@ -410,7 +407,6 @@ TEST(RuntimePathsTest, HonorsExplicitAndXdgOverrides) {
{"MOCKTAIL_DATA_ROOT", "/data"},
{"MOCKTAIL_STATE_ROOT", "/state"},
{"MOCKTAIL_AUTH_ROOT", "/auth"},
{"MOCKTAIL_SOBER_COOKIE_FILE", "/cookies/sober"},
});
const RuntimePaths explicit_paths =
RuntimePaths::FromEnvironment(explicit_environment, "/workspace");
@@ -419,9 +415,6 @@ TEST(RuntimePathsTest, HonorsExplicitAndXdgOverrides) {
EXPECT_EQ(explicit_paths.data_root(), "/data");
EXPECT_EQ(explicit_paths.state_root(), "/state");
EXPECT_EQ(explicit_paths.auth_root(), "/auth");
EXPECT_EQ(explicit_paths.default_sober_rejection_file(),
"/auth/default-sober-rejection");
EXPECT_EQ(explicit_paths.sober_cookie_file(), "/cookies/sober");
const MapEnvironment xdg_environment({
{"HOME", "/home/mocktail"},