feat(new tool): string obfuscator (#575)

This commit is contained in:
Corentin THOMASSET 2023-08-16 23:43:45 +02:00 committed by GitHub
parent f235dcd6c1
commit c58d6e3423
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 119 additions and 1 deletions

3
components.d.ts vendored
View file

@ -69,6 +69,8 @@ declare module '@vue/runtime-core' {
HtmlWysiwygEditor: typeof import('./src/tools/html-wysiwyg-editor/html-wysiwyg-editor.vue')['default']
HttpStatusCodes: typeof import('./src/tools/http-status-codes/http-status-codes.vue')['default']
'IconMdi:brushVariant': typeof import('~icons/mdi/brush-variant')['default']
'IconMdi:contentCopy': typeof import('~icons/mdi/content-copy')['default']
'IconMdi:copy': typeof import('~icons/mdi/copy')['default']
'IconMdi:kettleSteamOutline': typeof import('~icons/mdi/kettle-steam-outline')['default']
IconMdiArrowRightBottom: typeof import('~icons/mdi/arrow-right-bottom')['default']
IconMdiCamera: typeof import('~icons/mdi/camera')['default']
@ -170,6 +172,7 @@ declare module '@vue/runtime-core' {
SlugifyString: typeof import('./src/tools/slugify-string/slugify-string.vue')['default']
SpanCopyable: typeof import('./src/components/SpanCopyable.vue')['default']
SqlPrettify: typeof import('./src/tools/sql-prettify/sql-prettify.vue')['default']
StringObfuscator: typeof import('./src/tools/string-obfuscator/string-obfuscator.vue')['default']
SvgPlaceholderGenerator: typeof import('./src/tools/svg-placeholder-generator/svg-placeholder-generator.vue')['default']
TemperatureConverter: typeof import('./src/tools/temperature-converter/temperature-converter.vue')['default']
TextareaCopyable: typeof import('./src/components/TextareaCopyable.vue')['default']

View file

@ -1,6 +1,7 @@
import { tool as base64FileConverter } from './base64-file-converter';
import { tool as base64StringConverter } from './base64-string-converter';
import { tool as basicAuthGenerator } from './basic-auth-generator';
import { tool as stringObfuscator } from './string-obfuscator';
import { tool as emojiPicker } from './emoji-picker';
import { tool as passwordStrengthAnalyser } from './password-strength-analyser';
import { tool as yamlToToml } from './yaml-to-toml';
@ -145,7 +146,7 @@ export const toolsByCategory: ToolCategory[] = [
},
{
name: 'Text',
components: [loremIpsumGenerator, textStatistics, emojiPicker],
components: [loremIpsumGenerator, textStatistics, emojiPicker, stringObfuscator],
},
{
name: 'Data',

View file

@ -0,0 +1,12 @@
import { EyeOff } from '@vicons/tabler';
import { defineTool } from '../tool';
export const tool = defineTool({
name: 'String obfuscator',
path: '/string-obfuscator',
description: 'Obfuscate a string (like a secret, an IBAN, or a token) to make it shareable and identifiable without revealing its content.',
keywords: ['string', 'obfuscator', 'secret', 'token', 'hide', 'obscure', 'mask', 'masking'],
component: () => import('./string-obfuscator.vue'),
icon: EyeOff,
createdAt: new Date('2023-08-16'),
});

View file

@ -0,0 +1,20 @@
import { describe, expect, it } from 'vitest';
import { obfuscateString } from './string-obfuscator.model';
describe('string-obfuscator model', () => {
describe('obfuscateString', () => {
it('the characters in the middle of the string are replaced by the replacement character', () => {
expect(obfuscateString('1234567890')).toBe('1234******');
expect(obfuscateString('1234567890', { replacementChar: 'x' })).toBe('1234xxxxxx');
expect(obfuscateString('1234567890', { keepFirst: 5 })).toBe('12345*****');
expect(obfuscateString('1234567890', { keepFirst: 0, keepLast: 5 })).toBe('*****67890');
expect(obfuscateString('1234567890', { keepFirst: 5, keepLast: 5 })).toBe('1234567890');
expect(obfuscateString('1234567890', { keepFirst: 2, keepLast: 2, replacementChar: 'x' })).toBe('12xxxxxx90');
});
it('by default, the spaces are kept, they can be removed with the keepSpace option', () => {
expect(obfuscateString('12345 67890')).toBe('1234* *****');
expect(obfuscateString('12345 67890', { keepSpace: false })).toBe('1234*******');
});
});
});

View file

@ -0,0 +1,35 @@
import { get } from '@vueuse/core';
import { type MaybeRef, computed } from 'vue';
export { obfuscateString, useObfuscateString };
function obfuscateString(
str: string,
{ replacementChar = '*', keepFirst = 4, keepLast = 0, keepSpace = true }: { replacementChar?: string; keepFirst?: number; keepLast?: number; keepSpace?: boolean } = {}): string {
return str
.split('')
.map((char, index, array) => {
if (keepSpace && char === ' ') {
return char;
}
return (index < keepFirst || index >= array.length - keepLast) ? char : replacementChar;
})
.join('');
}
function useObfuscateString(
str: MaybeRef<string>,
config: { replacementChar?: MaybeRef<string>; keepFirst?: MaybeRef<number>; keepLast?: MaybeRef<number>; keepSpace?: MaybeRef<boolean> } = {},
) {
return computed(() => obfuscateString(
get(str),
{
replacementChar: get(config.replacementChar),
keepFirst: get(config.keepFirst),
keepLast: get(config.keepLast),
keepSpace: get(config.keepSpace),
},
));
}

View file

@ -0,0 +1,47 @@
<script setup lang="ts">
import { useObfuscateString } from './string-obfuscator.model';
import { useCopy } from '@/composable/copy';
const str = ref('Lorem ipsum dolor sit amet');
const keepFirst = ref(4);
const keepLast = ref(4);
const keepSpace = ref(true);
const obfuscatedString = useObfuscateString(str, { keepFirst, keepLast, keepSpace });
const { copy } = useCopy({ source: obfuscatedString });
</script>
<template>
<div>
<c-input-text v-model:value="str" raw-text placeholder="Enter string to obfuscate" label="String to obfuscate:" clearable multiline />
<div mt-4 flex gap-10px>
<div>
<div>Keep first:</div>
<n-input-number v-model:value="keepFirst" min="0" />
</div>
<div>
<div>Keep last:</div>
<n-input-number v-model:value="keepLast" min="0" />
</div>
<div>
<div mb-5px>
Keep&nbsp;spaces:
</div>
<n-switch v-model:value="keepSpace" />
</div>
</div>
<c-card v-if="obfuscatedString" mt-60px max-w-600px flex items-center gap-5px font-mono>
<div break-anywhere text-wrap>
{{ obfuscatedString }}
</div>
<c-button @click="copy">
<icon-mdi:content-copy />
</c-button>
</c-card>
</div>
</template>