New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@aircall/logger

Package Overview
Dependencies
Maintainers
5
Versions
60
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@aircall/logger - npm Package Compare versions

Comparing version 2.6.0 to 2.7.0

dist/constants/log_schema.d.ts

11

CHANGELOG.md

@@ -6,2 +6,13 @@ # Change Log

# [2.7.0](https://gitlab.com/aircall/shared/front-end-modules/compare/@aircall/logger@2.6.0...@aircall/logger@2.7.0) (2022-08-26)
### Features
* **logger:** add payload validation based on Aircall logs schema [PH-7453] ([7de46c3](https://gitlab.com/aircall/shared/front-end-modules/commit/7de46c3ad8378133b58c4bf3c2105b506cb7758d))
# [2.6.0](https://gitlab.com/aircall/shared/front-end-modules/compare/@aircall/logger@2.5.6...@aircall/logger@2.6.0) (2022-08-03)

@@ -8,0 +19,0 @@

3

dist/constants.d.ts
export declare const DEBUG_MODE_LIMIT_STORED_ACTIONS = 20;
export declare const SENSITIVE_KEYS: string[];
export declare const DEFAULT_SENSITIVE_TEXT = "<sensitive>";
export declare const CUSTOM_LOG_SCHEMA_KEYWORDS: string[];
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEBUG_MODE_LIMIT_STORED_ACTIONS = void 0;
exports.CUSTOM_LOG_SCHEMA_KEYWORDS = exports.DEFAULT_SENSITIVE_TEXT = exports.SENSITIVE_KEYS = exports.DEBUG_MODE_LIMIT_STORED_ACTIONS = void 0;
// Number of stored actions for the debug mode;
exports.DEBUG_MODE_LIMIT_STORED_ACTIONS = 20;
// List of keys that we need to check against to see if it needs to be hidden.
exports.SENSITIVE_KEYS = [
'password',
'Authorization',
'confirmationPassword',
'newPassword',
'currentPassword',
'idToken',
'token'
];
exports.DEFAULT_SENSITIVE_TEXT = '<sensitive>';
// Our common log schema relies on those custom keywords
// @see https://ajv.js.org/strict-mode.html#json-schema-schemas
exports.CUSTOM_LOG_SCHEMA_KEYWORDS = [
'destination',
'bytes_read',
'bytes_written',
'connectivity',
'downlink_kbps',
'signal_strength',
'uplink_kbps',
'useragent_details'
];
//# sourceMappingURL=constants.js.map

@@ -1,2 +0,2 @@

import { StatusType } from '@datadog/browser-logs';
import { StatusType, LogsEvent } from '@datadog/browser-logs';
import { Context } from '@datadog/browser-core';

@@ -11,2 +11,6 @@ export { StatusType as LOGGER_LEVEL };

}
export declare interface User extends Context {
id?: number;
company_id?: number;
}
export declare interface UserSession extends Context {

@@ -32,8 +36,36 @@ company_id?: number;

level: StatusType;
version: string;
environment: LOGGER_ENVIRONMENT;
service: string;
user: User;
}
export declare class Logger {
level: StatusType;
initialized: boolean;
queue: Function[];
level: StatusType;
init({ level, context, token }: LoggerInitOptions): void;
logSchemaValidator: any;
compiledLogSchema: any;
init({ level, context, token, version, environment, service, user }: LoggerInitOptions): void;
/**
* Remove sensitive properties, add mandatory fields and validate the payload against the log schema.
*
* @param log
* @param version
* @param environment
* @param service
* @param user
*/
beforeSendListener(log: LogsEvent, version: string, environment: string, service: string, user: User): void;
/**
* Every logs must include mandatory fields such as the environment or the version for instance.
* This function adds all of them into the log payload.
*
* @param log
* @param version
* @param environment
* @param service
* @param user
* @returns
*/
addMandatoryFields(log: LogsEvent, version: string, environment: string, service: string, user: User): LogsEvent;
setContext(context: UserContext): void;

@@ -40,0 +72,0 @@ private logOrEnqueue;

77

dist/Logger.js

@@ -6,2 +6,6 @@ "use strict";

Object.defineProperty(exports, "LOGGER_LEVEL", { enumerable: true, get: function () { return browser_logs_1.StatusType; } });
const ajv_1 = require("ajv");
const ajv_formats_1 = require("ajv-formats");
const constants_1 = require("./constants");
const log_schema_1 = require("./constants/log_schema");
const utils_1 = require("./utils");

@@ -16,13 +20,2 @@ var LOGGER_ENVIRONMENT;

})(LOGGER_ENVIRONMENT = exports.LOGGER_ENVIRONMENT || (exports.LOGGER_ENVIRONMENT = {}));
// List of keys that we need to check against to see if it needs to be hidden.
const SENSITIVE_KEYS = [
'password',
'Authorization',
'confirmationPassword',
'newPassword',
'currentPassword',
'idToken',
'token'
];
const DEFAULT_SENSITIVE_TEXT = '<sensitive>';
class Logger {

@@ -32,7 +25,11 @@ constructor() {

this.queue = [];
this.level = browser_logs_1.StatusType.info;
}
// Init SDK with user informations, token and level
init({ level, context, token }) {
init({ level, context, token, version, environment, service, user }) {
const isDevelopment = context.user_session.environment === LOGGER_ENVIRONMENT.DEVELOPMENT;
// Instantiate and configure Ajv schema validator
this.logSchemaValidator = new ajv_1.default({ allErrors: false }); // options can be passed, e.g. {allErrors: true}
(0, ajv_formats_1.default)(this.logSchemaValidator);
this.logSchemaValidator.addVocabulary(constants_1.CUSTOM_LOG_SCHEMA_KEYWORDS);
this.compiledLogSchema = this.logSchemaValidator.compile(log_schema_1.logSchema);
browser_logs_1.datadogLogs.init({

@@ -42,3 +39,5 @@ clientToken: token,

forwardErrorsToLogs: true,
sampleRate: 100
forwardConsoleLogs: ['error', 'warn'],
sampleRate: 100,
beforeSend: log => this.beforeSendListener(log, version, environment, service, user)
});

@@ -51,2 +50,48 @@ this.setContext(context);

}
/**
* Remove sensitive properties, add mandatory fields and validate the payload against the log schema.
*
* @param log
* @param version
* @param environment
* @param service
* @param user
*/
beforeSendListener(log, version, environment, service, user) {
// TODO PH-7453: move the clean properties function here
const logPayload = this.addMandatoryFields(log, version, environment, service, user);
const valid = this.compiledLogSchema(logPayload);
if (!valid) {
console.log('An error occured while validating the log payload. For more information, please refer to https://aircall-product.atlassian.net/wiki/spaces/7SE/pages/1942061270/Distributed+Logging+and+Tracing+Best+Practices.', {
error: this.compiledLogSchema.errors,
logPayload
});
}
}
/**
* Every logs must include mandatory fields such as the environment or the version for instance.
* This function adds all of them into the log payload.
*
* @param log
* @param version
* @param environment
* @param service
* @param user
* @returns
*/
addMandatoryFields(log, version, environment, service, user) {
const currentDate = new Date(Date.now());
const datadogInternalContext = browser_logs_1.datadogLogs.getInternalContext();
const commonMandatoryFields = {
version,
env: environment,
service,
host: (datadogInternalContext === null || datadogInternalContext === void 0 ? void 0 : datadogInternalContext.session_id) || '',
timestamp: currentDate.toISOString(),
level: log.status,
message: log.message,
user
};
return Object.assign(Object.assign({}, commonMandatoryFields), log);
}
// set logger context

@@ -80,5 +125,5 @@ setContext(context) {

}
const keyContainsSensitiveData = (0, utils_1.isString)(key) && (0, utils_1.containsAValue)(key, SENSITIVE_KEYS);
const keyContainsSensitiveData = (0, utils_1.isString)(key) && (0, utils_1.containsAValue)(key, constants_1.SENSITIVE_KEYS);
if (keyContainsSensitiveData) {
return DEFAULT_SENSITIVE_TEXT;
return constants_1.DEFAULT_SENSITIVE_TEXT;
}

@@ -85,0 +130,0 @@ return value;

{
"name": "@aircall/logger",
"version": "2.6.0",
"version": "2.7.0",
"main": "dist/index.js",

@@ -14,7 +14,9 @@ "types": "dist/index.d.ts",

},
"gitHead": "2caf56dc15480449b52405ad7865d355f0d1247b",
"gitHead": "662f36791e522dadaeb14a1263d39bf8819738c4",
"dependencies": {
"@datadog/browser-logs": "4.11.2",
"@datadog/browser-logs": "4.17.1",
"ajv": "8.11.0",
"ajv-formats": "2.1.1",
"redux": "4.2.0"
}
}
// Number of stored actions for the debug mode;
export const DEBUG_MODE_LIMIT_STORED_ACTIONS = 20;
// List of keys that we need to check against to see if it needs to be hidden.
export const SENSITIVE_KEYS = [
'password',
'Authorization',
'confirmationPassword',
'newPassword',
'currentPassword',
'idToken',
'token'
];
export const DEFAULT_SENSITIVE_TEXT = '<sensitive>';
// Our common log schema relies on those custom keywords
// @see https://ajv.js.org/strict-mode.html#json-schema-schemas
export const CUSTOM_LOG_SCHEMA_KEYWORDS = [
'destination',
'bytes_read',
'bytes_written',
'connectivity',
'downlink_kbps',
'signal_strength',
'uplink_kbps',
'useragent_details'
];

@@ -9,8 +9,17 @@ jest.mock('@datadog/browser-logs');

const version = 'RELEASE';
const environment = LOGGER_ENVIRONMENT.TEST;
const service = 'phone';
const user = {
id: 0,
company_id: 1
};
const host = 'session_id';
const user_session = {
company_id: 0,
device: 'DEVICE',
environment: LOGGER_ENVIRONMENT.TEST,
release: 'RELEASE',
user_id: 0,
environment,
release: version,
user_id: user.id,
is_trial: true,

@@ -40,3 +49,3 @@ tier_level: 'a',

const context = {
service: 'phone',
service,
user_session

@@ -47,3 +56,7 @@ };

token,
level: LOGGER_LEVEL.debug
level: LOGGER_LEVEL.debug,
version,
environment,
service,
user
});

@@ -53,8 +66,3 @@

expect(logger.initialized).toEqual(true);
expect(datadogLogs.init).toHaveBeenCalledWith({
clientToken: token,
datacenter: 'us',
forwardErrorsToLogs: true,
sampleRate: 100
});
expect(datadogLogs.init).toHaveBeenCalled();
expect(logger.setContext).toHaveBeenCalledWith(context);

@@ -196,2 +204,77 @@ expect(datadogLogs.logger.setContext).toHaveBeenCalledWith(context);

});
describe('addMandatoryFields', (): void => {
it('should add mandatory fields', (): void => {
jest.spyOn(datadogLogs, 'getInternalContext').mockReturnValue({ session_id: host });
//@ts-ignore
Date.now = jest.fn(() => new Date(2022, 7, 24, 8, 32, 16));
const logPayload = {
date: 1660119130,
foo: 'BAR',
bar: 10,
message: 'message',
status: StatusType.info,
view: { url: 'https://phone.aircall-staging.com/' }
};
const expected = {
level: LOGGER_LEVEL.info,
version,
env: environment,
host,
service,
timestamp: '2022-08-24T08:32:16.000Z',
user,
...logPayload
};
expect(
logger.addMandatoryFields(logPayload, version, environment, service, user)
).toStrictEqual(expected);
});
});
describe('beforeSendListener', (): void => {
//@ts-ignore
Date.now = jest.fn(() => new Date(2022, 7, 24, 8, 32, 16));
const logPayload = {
date: 1660119130,
foo: 'BAR',
bar: 10,
message: 'message',
status: StatusType.info,
view: { url: 'https://phone.aircall-staging.com/' }
};
it('should add mandatory fields and validate schema', (): void => {
jest.spyOn(logger, 'compiledLogSchema');
jest.spyOn(console, 'log');
const expected = {
level: LOGGER_LEVEL.info,
version,
env: environment,
host,
service,
timestamp: '2022-08-24T08:32:16.000Z',
user,
...logPayload
};
logger.beforeSendListener(logPayload, version, environment, service, user);
expect(logger.compiledLogSchema).toHaveBeenCalledWith(expected);
expect(console.log).toBeCalledTimes(0);
});
it('should add mandatory fields and log an error if the schema is invalid', (): void => {
jest.spyOn(logger, 'compiledLogSchema');
jest.spyOn(console, 'log');
jest.spyOn(logger, 'addMandatoryFields').mockReturnValue(logPayload);
logger.beforeSendListener(logPayload, version, environment, service, user);
expect(logger.compiledLogSchema).toHaveBeenCalledWith(logPayload);
expect(console.log).toBeCalledTimes(1);
});
});
});

@@ -1,4 +0,8 @@

import { datadogLogs as sdk, StatusType, HandlerType } from '@datadog/browser-logs';
import { datadogLogs as sdk, StatusType, HandlerType, LogsEvent } from '@datadog/browser-logs';
import { Context } from '@datadog/browser-core';
import Ajv from 'ajv';
import addFormats from 'ajv-formats';
import { CUSTOM_LOG_SCHEMA_KEYWORDS, SENSITIVE_KEYS, DEFAULT_SENSITIVE_TEXT } from './constants';
import { logSchema } from './constants/log_schema';
import { containsAValue, isString, deepMap, isObject, isArray } from './utils';

@@ -16,2 +20,27 @@

// Corresponds to the required properties for all logs across Aircall services and apps
interface MandatoryFields {
version: string;
env: string;
service: string;
host: string;
timestamp: string; // date-time format
level: StatusType;
message: string;
user?: User;
}
export declare interface User extends Context {
id?: number;
company_id?: number;
// cti_name?: string;
// device: string;
// electron_version?: string;
// environment: LOGGER_ENVIRONMENT;
// release: string;
// is_trial?: boolean;
// tier_level?: string;
// provider?: string;
}
export declare interface UserSession extends Context {

@@ -39,26 +68,34 @@ company_id?: number;

level: StatusType;
version: string;
environment: LOGGER_ENVIRONMENT;
service: string;
user: User;
}
// List of keys that we need to check against to see if it needs to be hidden.
const SENSITIVE_KEYS = [
'password',
'Authorization',
'confirmationPassword',
'newPassword',
'currentPassword',
'idToken',
'token'
];
const DEFAULT_SENSITIVE_TEXT = '<sensitive>';
export class Logger {
public level: StatusType;
public initialized = false;
public queue: Function[] = [];
public level: StatusType = StatusType.info;
public logSchemaValidator: any;
public compiledLogSchema: any;
// Init SDK with user informations, token and level
public init({ level, context, token }: LoggerInitOptions): void {
public init({
level,
context,
token,
version,
environment,
service,
user
}: LoggerInitOptions): void {
const isDevelopment: boolean =
context.user_session.environment === LOGGER_ENVIRONMENT.DEVELOPMENT;
// Instantiate and configure Ajv schema validator
this.logSchemaValidator = new Ajv({ allErrors: false }); // options can be passed, e.g. {allErrors: true}
addFormats(this.logSchemaValidator);
this.logSchemaValidator.addVocabulary(CUSTOM_LOG_SCHEMA_KEYWORDS);
this.compiledLogSchema = this.logSchemaValidator.compile(logSchema);
sdk.init({

@@ -68,3 +105,5 @@ clientToken: token,

forwardErrorsToLogs: true,
sampleRate: 100
forwardConsoleLogs: ['error', 'warn'], // TODO PH-7453: do we want to forward warn logs as well?
sampleRate: 100,
beforeSend: log => this.beforeSendListener(log, version, environment, service, user)
});

@@ -80,2 +119,68 @@

/**
* Remove sensitive properties, add mandatory fields and validate the payload against the log schema.
*
* @param log
* @param version
* @param environment
* @param service
* @param user
*/
public beforeSendListener(
log: LogsEvent,
version: string,
environment: string,
service: string,
user: User
): void {
// TODO PH-7453: move the clean properties function here
const logPayload = this.addMandatoryFields(log, version, environment, service, user);
const valid = this.compiledLogSchema(logPayload);
if (!valid) {
console.log(
'An error occured while validating the log payload. For more information, please refer to https://aircall-product.atlassian.net/wiki/spaces/7SE/pages/1942061270/Distributed+Logging+and+Tracing+Best+Practices.',
{
error: this.compiledLogSchema.errors,
logPayload
}
);
}
}
/**
* Every logs must include mandatory fields such as the environment or the version for instance.
* This function adds all of them into the log payload.
*
* @param log
* @param version
* @param environment
* @param service
* @param user
* @returns
*/
public addMandatoryFields(
log: LogsEvent,
version: string,
environment: string,
service: string,
user: User
): LogsEvent {
const currentDate = new Date(Date.now());
const datadogInternalContext = sdk.getInternalContext();
const commonMandatoryFields: MandatoryFields = {
version,
env: environment,
service,
host: datadogInternalContext?.session_id || '',
timestamp: currentDate.toISOString(),
level: log.status,
message: log.message,
user
};
return { ...commonMandatoryFields, ...log };
}
// set logger context

@@ -82,0 +187,0 @@ public setContext(context: UserContext) {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc