fix: fetch password explictly (#1107)

Include the explicit select of the password field since the mongoose schema omits it by default. Includes test cases to verify the password against the mongo instance running in the test suite and an additional error check.
This commit is contained in:
Dan Hable 2025-08-25 19:38:15 -05:00 committed by GitHub
parent 261d4693a3
commit eb6f3a0199
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 143 additions and 1 deletions

View file

@ -0,0 +1,5 @@
---
"@hyperdx/api": patch
---
Fix the alert connection query to include the password field.

View file

@ -388,6 +388,15 @@ export default class CheckAlertTask implements HdxTask<CheckAlertsTaskArgs> {
alertCount: alerts.length,
});
if (!conn.password && conn.password !== '') {
const providerName = this.provider.constructor.name;
logger.info({
message: `alert provider did not fetch connection password`,
providerName,
connectionId: conn.id,
});
}
const clickhouseClient = new clickhouse.ClickhouseClient({
host: conn.host,
username: conn.username,

View file

@ -585,6 +585,130 @@ describe('DefaultAlertProvider', () => {
const result = await provider.getAlertTasks();
expect(result).toEqual([]);
});
it('should include password field in connection for saved search alerts', async () => {
const team = await createTeam({ name: 'Test Team' });
// Create connection with specific password
const connection = await Connection.create({
team: team._id,
name: 'Test Connection',
host: 'http://localhost:8123',
username: 'test',
password: 'secret-password-123',
});
// Create source
const source = await Source.create({
team: team._id,
name: 'Test Source',
kind: 'log',
from: {
databaseName: 'default',
tableName: 'logs',
},
timestampValueExpression: 'timestamp',
connection: connection._id,
});
// Create saved search
const savedSearch = await SavedSearch.create({
team: team._id,
name: 'Test Search',
select: 'message',
where: 'level: error',
whereLanguage: 'lucene',
orderBy: 'timestamp',
source: source._id,
tags: [],
});
// Create alert
await createAlert(
team._id,
{
source: AlertSource.SAVED_SEARCH,
savedSearchId: savedSearch._id.toString(),
threshold: 10,
thresholdType: AlertThresholdType.ABOVE,
interval: '5m',
channel: {
type: 'webhook',
webhookId: new mongoose.Types.ObjectId().toString(),
},
},
new mongoose.Types.ObjectId(),
);
const result = await provider.getAlertTasks();
expect(result).toHaveLength(1);
expect(result[0].conn.password).toBe('secret-password-123');
expect(result[0].conn.username).toBe('test');
expect(result[0].conn.host).toBe('http://localhost:8123');
});
it('should include password field in connection for tile alerts', async () => {
const team = await createTeam({ name: 'Test Team' });
// Create connection with specific password
const connection = await Connection.create({
team: team._id,
name: 'Test Connection',
host: 'http://localhost:8124',
username: 'tile-user',
password: 'tile-secret-password-456',
});
// Create source
const source = await Source.create({
team: team._id,
name: 'Test Source',
kind: 'log',
from: {
databaseName: 'default',
tableName: 'logs',
},
timestampValueExpression: 'timestamp',
connection: connection._id,
});
// Create tile with source
const tile = makeTile({ id: 'test-tile-123' });
tile.config.source = source._id.toString();
// Create dashboard
const dashboard = await Dashboard.create({
team: team._id,
name: 'Test Dashboard',
tiles: [tile],
});
// Create alert
await createAlert(
team._id,
{
source: AlertSource.TILE,
dashboardId: dashboard._id.toString(),
tileId: tile.id,
threshold: 10,
thresholdType: AlertThresholdType.ABOVE,
interval: '5m',
channel: {
type: 'webhook',
webhookId: new mongoose.Types.ObjectId().toString(),
},
},
new mongoose.Types.ObjectId(),
);
const result = await provider.getAlertTasks();
expect(result).toHaveLength(1);
expect(result[0].conn.password).toBe('tile-secret-password-456');
expect(result[0].conn.username).toBe('tile-user');
expect(result[0].conn.host).toBe('http://localhost:8124');
});
});
describe('buildLogSearchLink', () => {

View file

@ -43,7 +43,10 @@ async function getSavedSearchDetails(
const { source } = savedSearch;
const connId = source.connection;
const conn = await Connection.findOne({ _id: connId, team: alert.team });
const conn = await Connection.findOne({
_id: connId,
team: alert.team,
}).select('+password');
if (!conn) {
logger.error({
message: 'connection not found',
@ -101,6 +104,7 @@ async function getTileDetails(
}).populate<Omit<ISource, 'connection'> & { connection: IConnection }>({
path: 'connection',
match: { team: alert.team },
select: '+password',
});
if (!source) {
logger.error({