Adjust session history

Signed-off-by: Artem Savchenko <armisav@gmail.com>
This commit is contained in:
Artem Savchenko
2026-05-28 11:22:03 +07:00
parent 23f9866ac5
commit edfccfc341
24 changed files with 545 additions and 86 deletions
+7 -7
View File
@@ -3404,12 +3404,7 @@ function maskIpForApiResponse (ip?: string): string | undefined {
function redactSecurityLoginEventRow (row: SecurityLoginEvent): SecurityLoginEvent {
const ua = row.userAgent?.trim() ?? ''
const shortUa =
ua === ''
? undefined
: ua.length <= UA_REDACT_LEN
? ua
: `${ua.slice(0, UA_REDACT_LEN - 1)}`
const shortUa = ua === '' ? undefined : ua.length <= UA_REDACT_LEN ? ua : `${ua.slice(0, UA_REDACT_LEN - 1)}`
return {
...row,
ip: maskIpForApiResponse(row.ip),
@@ -3518,7 +3513,12 @@ export async function getWorkspaceSecurityLoginHistory (
throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {}))
}
assertSecurityLoginTelemetryRateLimit(account, 'getWorkspaceSecurityLoginHistory', 'SECURITY_LOGIN_HISTORY_READ_RPM', 120)
assertSecurityLoginTelemetryRateLimit(
account,
'getWorkspaceSecurityLoginHistory',
'SECURITY_LOGIN_HISTORY_READ_RPM',
120
)
const { since, until, success, ip } = params
let accountUuid = params.accountUuid
@@ -20,7 +20,12 @@ function parsePositiveInt (raw: string | undefined, fallback: number): number {
* In-process sliding-window rate limiter (per account + RPC name).
* Multi-instance deployments only get per-process limits unless replaced with shared storage.
*/
export function assertSecurityLoginTelemetryRateLimit (accountKey: string, rpcName: string, envVar: string, fallbackRpm: number): void {
export function assertSecurityLoginTelemetryRateLimit (
accountKey: string,
rpcName: string,
envVar: string,
fallbackRpm: number
): void {
const maxPerMinute = parsePositiveInt(process.env[envVar], fallbackRpm)
const key = `${accountKey}:${rpcName}`
const now = Date.now()
+6 -2
View File
@@ -35,7 +35,9 @@ export class NoopPolicyEngine implements SecurityPolicyEngine {
const { event, recentHistory } = input
const anomalyCodes = new Set<string>()
const sameIpFailures = recentHistory.filter((entry) => !entry.success && entry.ip != null && event.ip != null && entry.ip === event.ip)
const sameIpFailures = recentHistory.filter(
(entry) => !entry.success && entry.ip != null && event.ip != null && entry.ip === event.ip
)
if (!event.success && sameIpFailures.length >= 4) {
anomalyCodes.add('repeated_failed_attempts_from_ip')
}
@@ -129,7 +131,9 @@ export async function resolveSecurityPolicyEngine (ctx: MeasureContext): Promise
| undefined
if (typeof createEngine !== 'function') {
ctx.warn('SECURITY_POLICY_MODULE loaded but createSecurityPolicyEngine is missing, fallback to noop', { moduleName })
ctx.warn('SECURITY_POLICY_MODULE loaded but createSecurityPolicyEngine is missing, fallback to noop', {
moduleName
})
cachedPolicyEngine = new NoopPolicyEngine()
return cachedPolicyEngine
}
+1 -4
View File
@@ -1741,10 +1741,7 @@ export async function purgeExpiredSecurityLoginEvents (
if (rawLower === '0' || rawLower === 'off' || rawLower === 'false') {
return
}
const days =
rawTrim !== undefined && rawTrim !== ''
? parseInt(rawTrim, 10)
: DEFAULT_SECURITY_LOGIN_RETENTION_DAYS
const days = rawTrim !== undefined && rawTrim !== '' ? parseInt(rawTrim, 10) : DEFAULT_SECURITY_LOGIN_RETENTION_DAYS
if (!Number.isFinite(days) || days <= 0) {
return
}