Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export type { RandomGenerator } from './utils/random';
export { HawkUserManager } from './users/hawk-user-manager';
export type { Logger, LogType } from './logger/logger';
export { isLoggerSet, setLogger, resetLogger, log } from './logger/logger';
export { isPlainObject, validateUser, validateContext, isValidEventPayload, isValidBreadcrumb } from './utils/validation';
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { log } from '@hawk.so/core';
import type { AffectedUser, Breadcrumb, EventContext, EventData, JavaScriptAddons } from '@hawk.so/types';
import Sanitizer from '../modules/sanitizer';
import { log } from '../logger/logger';
import type { AffectedUser, Breadcrumb, EventAddons, EventContext, EventData } from '@hawk.so/types';

/**
* Validates user data - basic security checks
*
* @param user - user data to validate
*/
export function validateUser(user: AffectedUser): boolean {
if (!user || !Sanitizer.isObject(user)) {
if (!user || !isPlainObject(user)) {

Check warning on line 10 in packages/core/src/utils/validation.ts

View workflow job for this annotation

GitHub Actions / lint

'isPlainObject' was used before it was defined
log('validateUser: User must be an object', 'warn');

return false;
Expand All @@ -30,7 +29,7 @@
* @param context - context data to validate
*/
export function validateContext(context: EventContext | undefined): boolean {
if (context && !Sanitizer.isObject(context)) {
if (context && !isPlainObject(context)) {

Check warning on line 32 in packages/core/src/utils/validation.ts

View workflow job for this annotation

GitHub Actions / lint

'isPlainObject' was used before it was defined
log('validateContext: Context must be an object', 'warn');

return false;
Expand All @@ -40,22 +39,23 @@
}

/**
* Checks if value is a plain object (not array, Date, etc.)
* Checks if value is a plain object (not null, array, Date, Map, etc.)
*
* @param value - value to check
* @returns `true` if value is a plain object, otherwise `false`

Check warning on line 45 in packages/core/src/utils/validation.ts

View workflow job for this annotation

GitHub Actions / lint

Missing JSDoc @returns type
*/
function isPlainObject(value: unknown): value is Record<string, unknown> {
export function isPlainObject(value: unknown): value is Record<string, unknown> {
return Object.prototype.toString.call(value) === '[object Object]';
}

/**
* Runtime check for required EventData fields.
* Per @hawk.so/types EventData, `title` is the only non-optional field.
* Additionally validates `backtrace` shape if present (must be an array).
* Additionally, validates `backtrace` shape if present (must be an array).
*
* @param payload - value to validate
*/
export function isValidEventPayload(payload: unknown): payload is EventData<JavaScriptAddons> {
export function isValidEventPayload(payload: unknown): payload is EventData<EventAddons> {
if (!isPlainObject(payload)) {
return false;
}
Expand All @@ -64,11 +64,10 @@
return false;
}

if (payload.backtrace !== undefined && !Array.isArray(payload.backtrace)) {
return false;
}
const isBacktraceUndefined = payload.backtrace === undefined;
const isBacktraceArray = Array.isArray(payload.backtrace);

return true;
return isBacktraceUndefined || isBacktraceArray;
}

/**
Expand All @@ -86,9 +85,8 @@
return false;
}

if (breadcrumb.timestamp !== undefined && typeof breadcrumb.timestamp !== 'number') {
return false;
}
const isTimestampUndefined = breadcrumb.timestamp === undefined;
const isTimestampNumber = typeof breadcrumb.timestamp === 'number';

return true;
return isTimestampUndefined || isTimestampNumber;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { describe, it, expect, vi } from 'vitest';
import { validateUser, validateContext, isValidEventPayload, isValidBreadcrumb } from '../../src/utils/validation';
import { describe, expect, it, vi } from 'vitest';
import { isValidBreadcrumb, isValidEventPayload, validateContext, validateUser } from '../../src';

// Suppress log output produced by log() calls inside validation failures.
vi.mock('@hawk.so/core', () => ({ log: vi.fn(), isLoggerSet: vi.fn(() => true), setLogger: vi.fn() }));
vi.mock('../../src/logger/logger', () => ({ log: vi.fn(), isLoggerSet: vi.fn(() => true), setLogger: vi.fn() }));

describe('validateUser', () => {
it('should return false when user is null', () => {
Expand Down
3 changes: 1 addition & 2 deletions packages/javascript/src/addons/breadcrumbs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
import type { Breadcrumb, BreadcrumbLevel, BreadcrumbType, Json, JsonNode } from '@hawk.so/types';
import Sanitizer from '../modules/sanitizer';
import { buildElementSelector } from '../utils/selector';
import { log } from '@hawk.so/core';
import { isValidBreadcrumb } from '../utils/validation';
import { isValidBreadcrumb, log } from '@hawk.so/core';

/**
* Default maximum number of breadcrumbs to store
Expand Down
2 changes: 1 addition & 1 deletion packages/javascript/src/catcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { isErrorProcessed, markErrorAsProcessed } from './utils/event';
import { BrowserRandomGenerator } from './utils/random';
import { ConsoleCatcher } from './addons/consoleCatcher';
import { BreadcrumbManager } from './addons/breadcrumbs';
import { isValidEventPayload, validateContext, validateUser } from './utils/validation';
import { isValidEventPayload, validateContext, validateUser } from '@hawk.so/core';
import { HawkUserManager, isLoggerSet, log, setLogger } from '@hawk.so/core';
import { HawkLocalStorage } from './storages/hawk-local-storage';
import { createBrowserLogger } from './logger/logger';
Expand Down
Loading