Skip to content

Conversation

@drazisil
Copy link
Collaborator

@drazisil drazisil commented Sep 3, 2025

No description provided.

@deepsource-io
Copy link

deepsource-io bot commented Sep 3, 2025

Here's the code health analysis summary for commits c4ddd48..6180cfa. View details on DeepSource ↗.

Analysis Summary

AnalyzerStatusSummaryLink
DeepSource JavaScript LogoJavaScript❌ Failure
❗ 30 occurences introduced
View Check ↗

💡 If you’re a repository administrator, you can configure the quality gates from the settings.

@drazisil
Copy link
Collaborator Author

drazisil commented Sep 3, 2025

@sentry review

@seer-by-sentry

This comment has been minimized.

@codecov
Copy link

codecov bot commented Sep 3, 2025

Codecov Report

❌ Patch coverage is 1.51515% with 455 lines in your changes missing coverage. Please review.
✅ Project coverage is 44.65%. Comparing base (c4ddd48) to head (6180cfa).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
app/authserver/db.ts 0.60% 165 Missing ⚠️
app/authserver/handleShardList.ts 0.82% 120 Missing ⚠️
app/authserver/handleAuthLogin.ts 1.36% 72 Missing ⚠️
app/authserver/AuthServer.ts 0.00% 52 Missing ⚠️
app/authserver/server.ts 4.54% 21 Missing ⚠️
app/authserver/databaseConstrants.ts 7.69% 12 Missing ⚠️
app/authserver/instrument.cjs 12.50% 7 Missing ⚠️
app/authserver/config.ts 14.28% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##              dev    #2784      +/-   ##
==========================================
- Coverage   46.39%   44.65%   -1.75%     
==========================================
  Files         205      213       +8     
  Lines       11452    11914     +462     
  Branches      722      724       +2     
==========================================
+ Hits         5313     5320       +7     
- Misses       6138     6593     +455     
  Partials        1        1              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@drazisil
Copy link
Collaborator Author

drazisil commented Sep 3, 2025

@sentry review

@drazisil
Copy link
Collaborator Author

drazisil commented Sep 3, 2025

@sentry generate-test

Comment on lines +184 to +188
return {
customerId: user.customerId,
profileId: user.profileId,
contextId: user.contextId,
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential bug: The findUser function accesses properties (profileId, contextId) from a database query result that doesn't contain them, causing it to return undefined for these fields.
  • Description: The findUser function queries the user table, which only contains username, password, and customerId columns. However, the function's return statement at app/authserver/db.ts:184~188 attempts to access user.profileId and user.contextId from the query result. These properties do not exist on the result object, so their values will be undefined. This causes the function to return a UserRecordMini object where profileId and contextId are undefined, violating the type contract and leading to runtime errors or logical failures in any downstream code that uses these values.

  • Suggested fix: The findUser function should be modified to only return data available from the user table. Alternatively, if profileId and contextId are required, the SQL query must be updated to join with the appropriate table to retrieve these values before returning them.
    severity: 0.82, confidence: 0.95

Did we get this right? 👍 / 👎 to inform future reviews.

Comment on lines 36 to 37
if (!authDB.isDatabaseConnected) {
coreLogger.fatal("Database connection failed. Exiting.");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential bug: The database connection check accesses isDatabaseConnected as a property instead of calling it as a function, causing the critical startup check to be completely bypassed.
  • Description: The startup check for database connectivity is written as if (!authDB.isDatabaseConnected). However, isDatabaseConnected is a function, not a boolean property. In JavaScript, a function object is "truthy", so !authDB.isDatabaseConnected will always evaluate to false. Consequently, the check is completely bypassed, and the failure logic, which logs a fatal error and exits, will never be executed. This allows the server to start even with a failed database connection, leading to unexpected crashes later when a database operation is attempted.

  • Suggested fix: The check should be changed to call the function: if (!authDB.isDatabaseConnected()). This will correctly execute the database connection check and ensure the server fails fast if the database is unavailable.
    severity: 0.85, confidence: 0.98

Did we get this right? 👍 / 👎 to inform future reviews.

Comment on lines +171 to +181
): UserRecordMini | null {
const logger = getServerLogger("database");
const query = database.prepare(SQL.FIND_USER);
const hashedPassword = this.generatePasswordHash(password);
const user = query.get(username) as UserRecordMini | null;
if (user == null) {
logger.warn(`User ${username} not found`);
return null
}
if (compareSync(password, (user as any).password) === false) {
logger.warn(`Invalid password for user ${username}`);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical security vulnerability: The findUser method is hashing the provided password and comparing it with the stored hash, but this is incorrect. The stored value should already be a hash, and compareSync should be used to compare the plain password with the stored hash. Currently, this will never authenticate users correctly.

Suggested change
): UserRecordMini | null {
const logger = getServerLogger("database");
const query = database.prepare(SQL.FIND_USER);
const hashedPassword = this.generatePasswordHash(password);
const user = query.get(username) as UserRecordMini | null;
if (user == null) {
logger.warn(`User ${username} not found`);
return null
}
if (compareSync(password, (user as any).password) === false) {
logger.warn(`Invalid password for user ${username}`);
findUser(
database: DatabaseSync,
username: string,
password: string,
): UserRecordMini | null {
const logger = getServerLogger("database");
const query = database.prepare(SQL.FIND_USER);
const user = query.get(username) as any;
if (user == null) {
logger.warn(`User ${username} not found`);
return null;
}
if (!compareSync(password, user.password)) {
logger.warn(`Invalid password for user ${username}`);
return null;
}
return {
customerId: user.customerId,
profileId: user.profileId || 0,
contextId: user.contextId || '',
};
},

Did we get this right? 👍 / 👎 to inform future reviews.

Comment on lines +58 to +87
function generateTicket(customerId: string): string {
console.log(`Generating ticket for customerId: ${customerId}`);
const ticket = randomUUID();
console.log(`Generated ticket: ${ticket}`);
return ticket;
}


/**
* Retrieves a user account based on the provided username and password.
*
* @param username - The username of the account to retrieve.
* @param password - The password of the account to retrieve.
* @returns An object containing the username, ticket, and customerId if the account is found, or null if not.
*/
function retrieveUserAccount(
username: string,
password: string,
): { username: string; ticket: string; customerId: string } | null {
const customer = authDB.findUser(username, password);
if (customer == null) {
console.log(`No user found for username: ${username}`);
return null;
}
console.log(`User found: ${username} with customerId: ${customer.customerId}`);
return {
username,
ticket: generateTicket(customer.customerId.toString()),
customerId: customer.customerId.toString(),
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security issue: Passwords are being logged in debug statements and passed around in function calls. This could expose sensitive information in logs. Remove password logging and ensure passwords are not stored in variables longer than necessary.

Suggested change
function generateTicket(customerId: string): string {
console.log(`Generating ticket for customerId: ${customerId}`);
const ticket = randomUUID();
console.log(`Generated ticket: ${ticket}`);
return ticket;
}
/**
* Retrieves a user account based on the provided username and password.
*
* @param username - The username of the account to retrieve.
* @param password - The password of the account to retrieve.
* @returns An object containing the username, ticket, and customerId if the account is found, or null if not.
*/
function retrieveUserAccount(
username: string,
password: string,
): { username: string; ticket: string; customerId: string } | null {
const customer = authDB.findUser(username, password);
if (customer == null) {
console.log(`No user found for username: ${username}`);
return null;
}
console.log(`User found: ${username} with customerId: ${customer.customerId}`);
return {
username,
ticket: generateTicket(customer.customerId.toString()),
customerId: customer.customerId.toString(),
};
function generateTicket(customerId: string): string {
console.log(`Generating ticket for customerId: ${customerId}`);
const ticket = randomUUID();
console.log(`Generated ticket: ${ticket}`);
return ticket;
}
function retrieveUserAccount(
username: string,
password: string,
): { username: string; ticket: string; customerId: string } | null {
const customer = authDB.findUser(username, password);
if (customer == null) {
console.log(`Authentication failed for username: ${username}`);
return null;
}
console.log(`User authenticated: ${username} with customerId: ${customer.customerId}`);
return {
username,
ticket: generateTicket(customer.customerId.toString()),
customerId: customer.customerId.toString(),
};
}

Did we get this right? 👍 / 👎 to inform future reviews.

Comment on lines +17 to +26
export interface AuthServerConfig {
dbConnectionUrl: string;
logLevel: string;
}

export function getConfig() : AuthServerConfig {
return {
dbConnectionUrl: process.env.AUTH_DB_CONNECTION_URL || "sqlite://:memory:",
logLevel: process.env.LOG_LEVEL || "debug",
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing port configuration: The AuthServer tries to read a 'port' property from the config but it's not defined in the AuthServerConfig interface or getConfig function. This will cause the server to always use the default port 3000.

Suggested change
export interface AuthServerConfig {
dbConnectionUrl: string;
logLevel: string;
}
export function getConfig() : AuthServerConfig {
return {
dbConnectionUrl: process.env.AUTH_DB_CONNECTION_URL || "sqlite://:memory:",
logLevel: process.env.LOG_LEVEL || "debug",
};
export interface AuthServerConfig {
dbConnectionUrl: string;
logLevel: string;
port: string;
}
export function getConfig() : AuthServerConfig {
return {
dbConnectionUrl: process.env.AUTH_DB_CONNECTION_URL || "sqlite://:memory:",
logLevel: process.env.LOG_LEVEL || "debug",
port: process.env.PORT || "3000",
};
}

Did we get this right? 👍 / 👎 to inform future reviews.

Comment on lines +244 to +247
// Database Instance Management
let databaseInstance: DatabaseSync | null = null;

/**
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security concern: The database connection string from environment variables could allow arbitrary file system access with SQLite. Consider validating the connection string format and restricting file paths to a specific directory.

Suggested change
// Database Instance Management
let databaseInstance: DatabaseSync | null = null;
/**
function initializeDatabaseService(): AuthDatabaseService {
if (databaseInstance === null) {
const config = getConfig();
// Validate SQLite connection string to prevent arbitrary file access
if (config.dbConnectionUrl.startsWith('sqlite://') &&
!config.dbConnectionUrl.includes('..') &&
(config.dbConnectionUrl === 'sqlite://:memory:' ||
config.dbConnectionUrl.startsWith('sqlite://./') ||
config.dbConnectionUrl.startsWith('sqlite:///tmp/'))) {
databaseInstance = new DatabaseSync(config.dbConnectionUrl);
} else {
throw new Error('Invalid database connection URL');
}
// ... rest of initialization
}
return DatabaseImpl.createDatabaseService(databaseInstance);
}

Did we get this right? 👍 / 👎 to inform future reviews.

Comment on lines +90 to +118

export function handleAuthLogin(request: IncomingMessage, response: ServerResponse) {
this.log.info("Handling AuthLogin request");
// Implement authentication logic here
const url = new URL(
`http://${process.env["HOST"] ?? "localhost"}${request.url}`,
);
const username = url.searchParams.get("username") ?? "";
const password = url.searchParams.get("password") ?? "";


response.setHeader("Content-Type", "text/plain");
let authResponse = "Invalid Request";
const user = retrieveUserAccount(username, password);

if (user !== null) {
const ticket = generateTicket(user.customerId);
if (ticket !== "") {
authResponse = AuthLoginResponse.createValid(ticket);
}
} else {

authResponse = AuthLoginResponse.createInvalid(
"INV-100",
"Opps!",
"https://winehq.com",
);
}
response.end(authResponse);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Input validation missing: The username and password parameters from the URL are not validated for length, content, or presence. This could lead to injection attacks or denial of service.

Suggested change
export function handleAuthLogin(request: IncomingMessage, response: ServerResponse) {
this.log.info("Handling AuthLogin request");
// Implement authentication logic here
const url = new URL(
`http://${process.env["HOST"] ?? "localhost"}${request.url}`,
);
const username = url.searchParams.get("username") ?? "";
const password = url.searchParams.get("password") ?? "";
response.setHeader("Content-Type", "text/plain");
let authResponse = "Invalid Request";
const user = retrieveUserAccount(username, password);
if (user !== null) {
const ticket = generateTicket(user.customerId);
if (ticket !== "") {
authResponse = AuthLoginResponse.createValid(ticket);
}
} else {
authResponse = AuthLoginResponse.createInvalid(
"INV-100",
"Opps!",
"https://winehq.com",
);
}
response.end(authResponse);
export function handleAuthLogin(request: IncomingMessage, response: ServerResponse) {
this.log.info("Handling AuthLogin request");
const url = new URL(
`http://${process.env["HOST"] ?? "localhost"}${request.url}`,
);
const username = url.searchParams.get("username") ?? "";
const password = url.searchParams.get("password") ?? "";
// Input validation
if (!username || !password) {
response.setHeader("Content-Type", "text/plain");
response.end(AuthLoginResponse.createInvalid("INV-101", "Missing credentials", "https://winehq.com"));
return;
}
if (username.length > 50 || password.length > 100) {
response.setHeader("Content-Type", "text/plain");
response.end(AuthLoginResponse.createInvalid("INV-102", "Invalid input length", "https://winehq.com"));
return;
}
response.setHeader("Content-Type", "text/plain");
let authResponse = "Invalid Request";
const user = retrieveUserAccount(username, password);
if (user !== null) {
const ticket = generateTicket(user.customerId);
if (ticket !== "") {
authResponse = AuthLoginResponse.createValid(ticket);
}
} else {
authResponse = AuthLoginResponse.createInvalid(
"INV-100",
"Authentication failed",
"https://winehq.com",
);
}
response.end(authResponse);
}

Did we get this right? 👍 / 👎 to inform future reviews.

Comment on lines +77 to +92
}

// Database Service Interface
export interface AuthDatabaseService {
isDatabaseConnected: () => boolean;
registerUser: (
username: string,
password: string,
customerId: number,
) => void;
findUser: (username: string, password: string) => UserRecordMini;
updateSession: (
customerId: number,
contextId: string,
userId: number,
) => void;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance issue: Database operations are synchronous and could block the event loop. Consider using asynchronous database operations for better performance, especially under load.

Suggested change
}
// Database Service Interface
export interface AuthDatabaseService {
isDatabaseConnected: () => boolean;
registerUser: (
username: string,
password: string,
customerId: number,
) => void;
findUser: (username: string, password: string) => UserRecordMini;
updateSession: (
customerId: number,
contextId: string,
userId: number,
) => void;
// Consider implementing async versions of database operations:
// async findUser(...): Promise<UserRecordMini | null>
// async registerUser(...): Promise<void>
// async updateSession(...): Promise<void>

Did we get this right? 👍 / 👎 to inform future reviews.

Comment on lines 34 to 43
coreLogger.info("Starting Auth Server...");
try {
if (!authDB.isDatabaseConnected) {
coreLogger.fatal("Database connection failed. Exiting.");
process.exit(1);
}
} catch (err) {
coreLogger.fatal(`Error in core server: ${String(err)}`);
process.exitCode = 1;
return;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error handling: The database connection check uses a property 'isDatabaseConnected' that returns a boolean, but the actual database initialization in db.ts could throw exceptions that aren't caught here.

Suggested change
coreLogger.info("Starting Auth Server...");
try {
if (!authDB.isDatabaseConnected) {
coreLogger.fatal("Database connection failed. Exiting.");
process.exit(1);
}
} catch (err) {
coreLogger.fatal(`Error in core server: ${String(err)}`);
process.exitCode = 1;
return;
try {
if (!authDB.isDatabaseConnected()) {
coreLogger.fatal("Database connection failed. Exiting.");
process.exit(1);
}
// Test database connection with a simple query
authDB.getAllUsers();
} catch (err) {
coreLogger.fatal(`Database connection error: ${String(err)}`);
process.exit(1);
}

Did we get this right? 👍 / 👎 to inform future reviews.

Co-authored-by: seer-by-sentry[bot] <157164994+seer-by-sentry[bot]@users.noreply.github.com>
},
"keywords": [],
"author": "",
"license": "ISC",
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sentry you should explain why you removed the license key

@seer-by-sentry
Copy link

seer-by-sentry bot commented Sep 3, 2025

Sentry has determined that unit tests are not necessary for this PR.

@drazisil drazisil enabled auto-merge September 3, 2025 11:16
@drazisil drazisil disabled auto-merge September 3, 2025 11:16
@drazisil drazisil enabled auto-merge September 3, 2025 11:20
@sonarqubecloud
Copy link

sonarqubecloud bot commented Sep 3, 2025

Quality Gate Failed Quality Gate failed

Failed conditions
32.4% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants