Skip to content

Commit 261ae3a

Browse files
committed
Merge branch 'feat/sensitive-data-masker' into chore/all-my-stuffs
# Conflicts: # components.d.ts # package.json # pnpm-lock.yaml # src/tools/index.ts
2 parents bf46a8e + ec6c5b4 commit 261ae3a

File tree

7 files changed

+206
-2
lines changed

7 files changed

+206
-2
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
"curlconverter": "^4.10.1",
113113
"currency-codes-ts": "^3.0.0",
114114
"currency-exchanger-js": "^1.0.4",
115+
"data-guardian": "^1.1.3",
115116
"date-fns": "^2.29.3",
116117
"dns-query": "^0.11.2",
117118
"decomposerize": "^1.4.1",
@@ -147,6 +148,7 @@
147148
"image-in-browser": "^3.2.0",
148149
"ip-bigint": "^8.2.0",
149150
"jq-wasm": "^0.0.9",
151+
"ip-regex": "^5.0.0",
150152
"js-base64": "^3.7.6",
151153
"image-in-browser": "^3.1.0",
152154
"js-base64": "^3.7.7",

pnpm-lock.yaml

Lines changed: 17 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/tools/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import { tool as markdownToHtml } from './markdown-to-html';
7676
import { tool as nginxFormatter } from './nginx-formatter';
7777
import { tool as potrace } from './potrace';
7878
import { tool as rmbNumbers } from './rmb-numbers';
79+
import { tool as sensitiveDataMasker } from './sensitive-data-masker';
7980
import { tool as pdfSignatureChecker } from './pdf-signature-checker';
8081
import { tool as numeronymGenerator } from './numeronym-generator';
8182
import { tool as macAddressGenerator } from './mac-address-generator';
@@ -375,6 +376,7 @@ export const toolsByCategory: ToolCategory[] = [
375376
icalGenerator,
376377
icalParser,
377378
icalMerger,
379+
sensitiveDataMasker,
378380
],
379381
},
380382
{
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { ShieldLock } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'Sensitive data masker',
6+
path: '/sensitive-data-masker',
7+
description: 'Clean sensitive data from textual content (ie logs)',
8+
keywords: ['sensitive', 'data', 'masker', 'obfuscator', 'clean', 'log'],
9+
component: () => import('./sensitive-data-masker.vue'),
10+
icon: ShieldLock,
11+
createdAt: new Date('2024-06-16'),
12+
});
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { maskSensitiveData } from './sensitive-data-masker.service';
3+
4+
describe('sensitive-data-masker', () => {
5+
describe('maskSensitiveData', () => {
6+
const data = `{
7+
8+
creditCard: '1234 5678 9000 9876',
9+
id: '3f8a43fd-6489-4ec7-bd55-7a1ba172d77b',
10+
name: 'John',
11+
surname: 'Doe',
12+
phone: '+358 40 1234567',
13+
url: 'truc.google.com',
14+
ip4: '83.24.45.56',
15+
ip6: '2001:db8:0:85a3:0:0:ac1f:8001',
16+
mac: '3D:F2:C9:A6:B3:4F',
17+
token: 'eyJhbGciOiJIUzI1NiJ9.ew0KICAic3ViIjogIjEyMzQ1Njc4OTAiLA0KICAibmFtZSI6ICJBbGV4IEtvemxvdiIsDQogICJpYXQiOiAxNTE2MjM5MDIyDQp9.PNKysYFTCenU5bekHCmwIxCUXoYG41H_xc3uN3ZF_b8',
18+
}`;
19+
20+
it('should maks sensitive data', () => {
21+
expect(maskSensitiveData({
22+
value: data,
23+
})).toBe(`{
24+
email: 'jo****************om',
25+
creditCard: '12***************76',
26+
id: '3f********************************7b',
27+
name: 'John',
28+
surname: 'Doe',
29+
phone: '+3***********67',
30+
url: 'tr***********om',
31+
ip4: '83*******56',
32+
ip6: '20*************************01',
33+
mac: '3D*************4F',
34+
token: 'ey*****************************************************************************************************************************************************************b8',
35+
}`);
36+
});
37+
it('should maks sensitive data (with custom regex)', () => {
38+
expect(maskSensitiveData({
39+
value: data,
40+
customRegex: 'John\nDoe',
41+
})).toBe(`{
42+
email: 'jo****************om',
43+
creditCard: '12***************76',
44+
id: '3f********************************7b',
45+
name: '****',
46+
surname: '***',
47+
phone: '+3***********67',
48+
url: 'tr***********om',
49+
ip4: '83*******56',
50+
ip6: '20*************************01',
51+
mac: '3D*************4F',
52+
token: 'ey*****************************************************************************************************************************************************************b8',
53+
}`);
54+
});
55+
56+
it('should maks sensitive data (with excluded matchers)', () => {
57+
expect(maskSensitiveData({
58+
value: data,
59+
excludedMatchers: ['mac', 'ipv4'],
60+
})).toBe(`{
61+
email: 'jo****************om',
62+
creditCard: '12***************76',
63+
id: '3f********************************7b',
64+
name: 'John',
65+
surname: 'Doe',
66+
phone: '+3***********67',
67+
url: 'tr***********om',
68+
ip4: '83.24.45.56',
69+
ip6: '20*************************01',
70+
mac: '3D:F2:C9:A6:B3:4F',
71+
token: 'ey*****************************************************************************************************************************************************************b8',
72+
}`);
73+
});
74+
});
75+
});
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { type SensitiveContentKey, maskString } from 'data-guardian';
2+
import ipRegex from 'ip-regex';
3+
4+
const jwtRegex = /\b([a-zA-Z0-9_=]{5,})\.([a-zA-Z0-9_=]{5,})\.([a-zA-Z0-9_\-\+\/=]{5,})\b/g;
5+
const phoneRegex = /(?:(\+\d{1,4})[-.\s]?)(?:[(](\d{1,3})[)][-.\s]?)?(\d{1,4})[-.\s]?(\d{1,4})[-.\s]?(\d{1,9})\b/g;
6+
const macRegex = /\b([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})\b/g;
7+
const urlWithOrWithoutPrefixRegex = /\b(https?:\/\/)?(www\.)?[a-zA-Z0-9@:%._+~#=-]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&\/=]*)\b/g;
8+
9+
export type MatcherNames = 'uuid' | 'creditCard' | 'ssn' | 'url' | 'ipv4' | 'email' | 'passwordInUri' | 'mac' | 'ipv6' | 'urlWithOrWithoutPrefix' | 'jwt' | 'phone';
10+
11+
export function maskSensitiveData({
12+
value,
13+
customRegex = '',
14+
excludedMatchers = [],
15+
}: {
16+
value: string
17+
customRegex?: string
18+
excludedMatchers?: Array<MatcherNames>
19+
}) {
20+
excludedMatchers = excludedMatchers || [];
21+
const emptyRegex = /(?:)/g;
22+
return maskString(value, null as never, {
23+
customRegex: new RegExp((customRegex || '').split('\n').map(line => `(${line})`).join('|'), 'gi'),
24+
macRegex: excludedMatchers.includes('mac') ? emptyRegex : macRegex,
25+
ipv6Regex: excludedMatchers.includes('ipv6') ? emptyRegex : ipRegex.v6({ includeBoundaries: false }),
26+
urlWithOrWithoutPrefixRegex: excludedMatchers.includes('urlWithOrWithoutPrefix') ? emptyRegex : urlWithOrWithoutPrefixRegex,
27+
jwtRegex: excludedMatchers.includes('jwt') ? emptyRegex : jwtRegex,
28+
phoneRegex: excludedMatchers.includes('phone') ? emptyRegex : phoneRegex,
29+
}, {
30+
excludeMatchers: [...excludedMatchers, ...[
31+
'passwordMention', 'password', 'passwordSubstring',
32+
]] as SensitiveContentKey[],
33+
});
34+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<script setup lang="ts">
2+
import { type MatcherNames, maskSensitiveData } from './sensitive-data-masker.service';
3+
import { withDefaultOnError } from '@/utils/defaults';
4+
5+
const defaultValue = `{
6+
7+
creditCard: '1234 5678 9000 9876',
8+
id: '3f8a43fd-6489-4ec7-bd55-7a1ba172d77b',
9+
name: 'John',
10+
surname: 'Doe',
11+
phone: '+358 40 1234567',
12+
url: 'truc.google.com',
13+
ip4: '83.24.45.56',
14+
ip6: '2001:db8:0:85a3:0:0:ac1f:8001',
15+
mac: '3D:F2:C9:A6:B3:4F',
16+
token: 'eyJhbGciOiJIUzI1NiJ9.ew0KICAic3ViIjogIjEyMzQ1Njc4OTAiLA0KICAibmFtZSI6ICJBbGV4IEtvemxvdiIsDQogICJpYXQiOiAxNTE2MjM5MDIyDQp9.PNKysYFTCenU5bekHCmwIxCUXoYG41H_xc3uN3ZF_b8',
17+
}`;
18+
19+
const customRegex = useStorage('sensitive-data:regex', '');
20+
const excludedMatchers = useStorage('sensitive-data:exclude', [] as string[]);
21+
const allMatchers = [
22+
'uuid', 'creditCard', 'ssn', 'url', 'ipv4', 'email',
23+
'passwordInUri', 'mac', 'ipv6', 'urlWithOrWithoutPrefix',
24+
'jwt', 'phone'];
25+
26+
function transformer(value: string) {
27+
return withDefaultOnError(() => maskSensitiveData({
28+
value,
29+
customRegex: customRegex.value,
30+
excludedMatchers: excludedMatchers.value as MatcherNames[],
31+
}), '');
32+
}
33+
</script>
34+
35+
<template>
36+
<div style="max-width: 600px;">
37+
<c-input-text
38+
v-model:value="customRegex"
39+
label="Your custom cleaning regex(es) (case insensitive):"
40+
placeholder="Your custom cleaning regex(es)"
41+
raw-text
42+
multiline
43+
rows="4"
44+
mb-2
45+
/>
46+
47+
<n-select
48+
v-model:value="excludedMatchers"
49+
placeholder="No Fallback"
50+
multiple
51+
:fallback-option="false"
52+
:options="allMatchers.map(v => ({ label: v, value: v }))"
53+
mb-2
54+
/>
55+
56+
<format-transformer
57+
input-label="Your log/textual data:"
58+
:input-default="defaultValue"
59+
input-placeholder="Paste your log/textual data here..."
60+
output-label="Cleaned version:"
61+
:transformer="transformer"
62+
/>
63+
</div>
64+
</template>

0 commit comments

Comments
 (0)