Skip to content

Commit

Permalink
Merge pull request #22 from rodolfoviolac/feat/v1.2.0
Browse files Browse the repository at this point in the history
feat: fixes for null objects and new features
  • Loading branch information
rodolfoviolac authored Feb 20, 2024
2 parents 1cf9dab + 2079a33 commit 0fc6754
Show file tree
Hide file tree
Showing 13 changed files with 3,230 additions and 2,111 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ const obf = new ob.Obfuscator().blur("Credit Card Number 4024-0071-4571-8614");
//output: Credit Card Number NOT_VISIBLE_SECURITY_REASON
```

| STRUCTURES | DESCRIPTION |
|------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `object` | Obfuscation happens on any value at which the key is considered sensitive. +2k keys are parsed by default and you can add more values to obfuscate, the default fields are based on [cabinjs sensitive list](https://github.com/cabinjs/sensitive-fields). |
| `string` | The obfuscation happens in the patterns that are considered sensitive, the match is done by regex. By default we offer some patterns to obfuscate whether the sentence is formatted or not: `Brazilian CPF`, `Brazilian CNPJ`, `Brazilian RG`, `Phone Number` and `Credit Card Number`, other values can be added. |
| STRUCTURES | DESCRIPTION |
|------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `object` | Obfuscation happens on any value at which the key is considered sensitive. +2k keys are parsed by default and you can add more values to obfuscate, the default fields are based on [cabinjs sensitive list](https://github.com/cabinjs/sensitive-fields). |
| `string` | The obfuscation happens in the patterns that are considered sensitive, the match is done by regex. By default we offer some patterns to obfuscate whether the sentence is formatted or not: `Brazilian CPF`, `Brazilian CNPJ`, `Brazilian RG`, `Phone Number`, `Object Id`, `UUID` and `Credit Card Number`, other values can be added. |

### Objects

Expand Down
4,506 changes: 2,796 additions & 1,710 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "blur-sensitive-data",
"version": "1.1.1",
"version": "1.2.0",
"description": "Remove any sensitive information instantly from your data",
"author": "Rodolfo Viola Carvalho",
"license": "MIT",
Expand All @@ -13,7 +13,7 @@
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "ts-node dist/index.js",
"start:dev": "ts-node src/index.ts",
"start:example": "ts-node examples/example.ts",
"start:example": " tsc examples/example.ts && node examples/example.js",
"lint": "eslint \"{src,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
Expand All @@ -33,7 +33,7 @@
"sensitive-fields": "^0.0.9"
},
"devDependencies": {
"@commitlint/cli": "^11.0.0",
"@commitlint/cli": "^18.6.1",
"@commitlint/config-conventional": "^11.0.0",
"@types/jest": "^26.0.15",
"@types/node": "^14.14.6",
Expand Down
121 changes: 70 additions & 51 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,76 @@
import {IBlurSettings, TTargetFieldType} from './typings/interfaces';
import {securityObjectFieldCleaner} from "./utils/objectHandler";
import {customSensitiveFields} from "./utils/sensitiveFields";
import {securityStringFieldCleaner} from "./utils/stringHandler";
import {stringPatterns} from "./utils/stringsPatterns";
import {EStringPatterns} from "./typings/enums";
import { IBlurSettings, TTargetFieldType } from './typings/interfaces';
import securityObjectFieldCleaner from './utils/objectHandler';
import { customSensitiveFields } from './utils/sensitiveFields';
import { securityStringFieldCleaner } from './utils/stringHandler';
import { stringPatterns } from './utils/stringsPatterns';
import { EStringPatterns } from './typings/enums';

const sensitiveFields = require('sensitive-fields');

export class Obfuscator {
static readonly EStringLookUpFields = EStringPatterns;
readonly EStringLookUpFields = Obfuscator.EStringLookUpFields;

blurSettings: IBlurSettings = {
replacerText: "NOT_VISIBLE_SECURITY_REASON",
stringPatterns: [EStringPatterns.CPF, EStringPatterns.CNPJ, EStringPatterns.RG, EStringPatterns.PHONE, EStringPatterns.CREDIT_CARD]
}

constructor(blurSettings?: IBlurSettings) {
Object.assign(this.blurSettings, blurSettings);
}

public blur(rawData: TTargetFieldType){
const rawDataType = typeof rawData

switch (rawDataType) {
case 'object':
return this.handleObjectData(rawData as object);
case 'string':
return this.handleStringData(rawData as string);
default:
throw new Error('Data type not supported')
}
}

private handleObjectData(rawData: object): object{
const lookUpFields = [...customSensitiveFields, ...sensitiveFields, ...this.blurSettings?.additionalObjectKeys || []]
return securityObjectFieldCleaner(rawData, lookUpFields, this.blurSettings.replacerText)
}

private handleStringData(rawData: string): string{
const lookUpFieldsPatterns = this.handleStringPatterns();
return securityStringFieldCleaner(rawData, lookUpFieldsPatterns, this.blurSettings.replacerText);
}


private handleStringPatterns(): RegExp[] {
let lookUpFields = [...this.blurSettings.additionalStringPatterns || []];
for(const pattern of this.blurSettings.stringPatterns || []){
lookUpFields.push(stringPatterns[pattern])
}
return lookUpFields;
}
static readonly EStringLookUpFields = EStringPatterns;

readonly EStringLookUpFields = Obfuscator.EStringLookUpFields;

blurSettings: IBlurSettings = {
replacerText: 'NOT_VISIBLE_SECURITY_REASON',
stringPatterns: [
EStringPatterns.CPF,
EStringPatterns.CNPJ,
EStringPatterns.RG,
EStringPatterns.PHONE,
EStringPatterns.CREDIT_CARD,
EStringPatterns.UUID,
EStringPatterns.OBJECT_ID,
],
};

constructor(blurSettings?: IBlurSettings) {
Object.assign(this.blurSettings, blurSettings);
}

public blur(rawData: TTargetFieldType) {
const rawDataType = typeof rawData;

if (!rawData) throw new Error('Blur data type null or undefined is not supported');

switch (rawDataType) {
case 'object':
return this.handleObjectData(rawData as Record<string, unknown>);
case 'string':
return this.handleStringData(rawData as string);
default:
throw new Error(`Blur data type ${rawDataType} not supported`);
}
}

private handleObjectData(rawData: Record<string, unknown>): Record<string, unknown> {
const lookUpFields = [
...customSensitiveFields,
...sensitiveFields,
...(this.blurSettings?.additionalObjectKeys || []),
];
return securityObjectFieldCleaner(rawData, lookUpFields, this.blurSettings.replacerText);
}

private handleStringData(rawData: string): string {
const lookUpFieldsPatterns = this.handleStringPatterns();
return securityStringFieldCleaner(
rawData,
lookUpFieldsPatterns,
this.blurSettings.replacerText,
);
}

private handleStringPatterns(): RegExp[] {
const lookUpFields = [...(this.blurSettings.additionalStringPatterns || [])];
for (const pattern of this.blurSettings.stringPatterns || []) {
lookUpFields.push(stringPatterns[pattern]);
}
return lookUpFields;
}
}

module.exports = {
Obfuscator
}
Obfuscator,
};
14 changes: 8 additions & 6 deletions src/typings/enums.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
export enum EStringPatterns {
CPF = 'cpf',
CNPJ = 'cnpj',
RG = 'rg',
CEP = 'cep',
PHONE = 'phone',
CREDIT_CARD ='creditCard',
CPF = 'cpf',
CNPJ = 'cnpj',
RG = 'rg',
CEP = 'cep',
PHONE = 'phone',
CREDIT_CARD = 'creditCard',
UUID = 'uuid',
OBJECT_ID = 'objectId',
}
12 changes: 6 additions & 6 deletions src/typings/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {EStringPatterns} from "./enums";
import { EStringPatterns } from './enums';

export interface IBlurSettings {
additionalObjectKeys?: string[];
additionalStringPatterns? : RegExp[]
stringPatterns? : EStringPatterns[];
replacerText?: string;
additionalObjectKeys?: string[];
additionalStringPatterns?: RegExp[];
stringPatterns?: EStringPatterns[];
replacerText?: string;
}

export type TTargetFieldType = string | object;
export type TTargetFieldType = string | Record<string, unknown>;
51 changes: 29 additions & 22 deletions src/utils/objectHandler.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,35 @@
function removeCircularDependency() {
const seen = new WeakSet();
return (key: any, value: any) => {
const valueNotSeen = !seen.has(value);
if (typeof value === 'object' && value !== null && valueNotSeen) {
seen.add(value);
}
return value;
};
const seen = new WeakSet();
return (key: any, value: any) => {
const valueNotSeen = !seen.has(value);
if (typeof value === 'object' && value !== null && valueNotSeen) {
seen.add(value);
}
return typeof value === 'undefined' ? null : value;
};
}

function objectReplacer(objectToReplace: any, lookFor: string[], stringReplacer: string): object {
for (const [key] of Object.entries(objectToReplace)) {
if(lookFor.includes(key)){
objectToReplace[key] = stringReplacer;
}
if(typeof objectToReplace[key] === 'object'){
objectReplacer(objectToReplace[key], lookFor, stringReplacer)
}
}
return objectToReplace;
function objectReplacer(
objectToReplace: any,
lookFor: string[],
stringReplacer: string,
): Record<string, unknown> {
Object.keys(objectToReplace).forEach(key => {
if (lookFor.includes(key)) {
objectToReplace[key] = stringReplacer;
}
if (typeof objectToReplace[key] === 'object' && objectToReplace[key] !== null) {
objectReplacer(objectToReplace[key], lookFor, stringReplacer);
}
});
return objectToReplace;
}


export function securityObjectFieldCleaner(objectTarget: any, fieldsToLookFor: string[], stringReplacer: string = ''): object {
const cleanObject = JSON.parse(JSON.stringify(objectTarget, removeCircularDependency()));
return objectReplacer(cleanObject, fieldsToLookFor, stringReplacer);
export default function securityObjectFieldCleaner(
objectTarget: any,
fieldsToLookFor: string[],
stringReplacer = '',
): Record<string, unknown> {
const cleanObject = JSON.parse(JSON.stringify(objectTarget, removeCircularDependency()));
return objectReplacer(cleanObject, fieldsToLookFor, stringReplacer);
}
8 changes: 1 addition & 7 deletions src/utils/sensitiveFields.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
export const customSensitiveFields = [
'token',
'authorization',
'cpf',
'CPF',
'senha',
];
export const customSensitiveFields = ['token', 'authorization', 'cpf', 'CPF', 'senha'];
16 changes: 10 additions & 6 deletions src/utils/stringHandler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
export function securityStringFieldCleaner(rawData: string, lookUpFieldsPatterns: RegExp[], stringReplacer: string = ''){
let filteredString = rawData
for(const pattern of lookUpFieldsPatterns) {
filteredString = filteredString.replace(pattern,stringReplacer);
}
return filteredString;
export function securityStringFieldCleaner(
rawData: string,
lookUpFieldsPatterns: RegExp[],
stringReplacer = '',
) {
let filteredString = rawData;
for (const pattern of lookUpFieldsPatterns) {
filteredString = filteredString.replace(pattern, stringReplacer);
}
return filteredString;
}
16 changes: 9 additions & 7 deletions src/utils/stringsPatterns.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
export const stringPatterns = {
cpf: /([0-9]{2}[\.]?[0-9]{3}[\.]?[0-9]{3}[\/]?[0-9]{4}[-]?[0-9]{2})|([0-9]{3}[\.]?[0-9]{3}[\.]?[0-9]{3}[-]?[0-9]{2})/igm,
cnpj: /\d{2}\.?\d{3}\.?\d{3}\/?\d{4}\-?\d{2}/igm,
rg: /[0-9]{2,3}\.?[0-9]{2,3}\.?[0-9]{3}\-?[A-Za-z0-9]{1}/igm,
cep: /([0-9]{5})-?([0-9]{3})/igm,
phone: /(\(?\d{2}\)?\s)?(\d{4,5}\-?\d{4})/igm,
creditCard: /\b(3[47]\d{2}([ -]?)(?!(\d)\3{5}|123456|234567|345678)\d{6}\2(?!(\d)\4{4})\d{5}|((4\d|5[1-5]|65)\d{2}|6011)([ -]?)(?!(\d)\8{3}|1234|3456|5678)\d{4}\7(?!(\d)\9{3})\d{4}\7\d{4})\b|(606282\d{10}(\d{3})?)|(3841\d{15})|((((636368)|(438935)|(504175)|(451416)|(636297))\d{0,10})|((5067)|(4576)|(4011))\d{0,12})/igm
}
cpf: /([0-9]{2}[\.]?[0-9]{3}[\.]?[0-9]{3}[\/]?[0-9]{4}[-]?[0-9]{2})|([0-9]{3}[\.]?[0-9]{3}[\.]?[0-9]{3}[-]?[0-9]{2})/gim,
cnpj: /\d{2}\.?\d{3}\.?\d{3}\/?\d{4}\-?\d{2}/gim,
rg: /[0-9]{2,3}\.?[0-9]{2,3}\.?[0-9]{3}\-?[A-Za-z0-9]{1}/gim,
cep: /([0-9]{5})-?([0-9]{3})/gim,
phone: /(\(?\d{2}\)?\s)?(\d{4,5}\-?\d{4})/gim,
creditCard: /\b(3[47]\d{2}([ -]?)(?!(\d)\3{5}|123456|234567|345678)\d{6}\2(?!(\d)\4{4})\d{5}|((4\d|5[1-5]|65)\d{2}|6011)([ -]?)(?!(\d)\8{3}|1234|3456|5678)\d{4}\7(?!(\d)\9{3})\d{4}\7\d{4})\b|(606282\d{10}(\d{3})?)|(3841\d{15})|((((636368)|(438935)|(504175)|(451416)|(636297))\d{0,10})|((5067)|(4576)|(4011))\d{0,12})/gim,
uuid: /[0-F]{8}-([0-F]{4}-){3}[0-F]{12}/gim,
objectId: /[0-9a-f]{24}/gim,
};
Loading

0 comments on commit 0fc6754

Please sign in to comment.