diff --git a/components.d.ts b/components.d.ts index e31119b3..c5a10bee 100644 --- a/components.d.ts +++ b/components.d.ts @@ -37,6 +37,7 @@ declare module '@vue/runtime-core' { 'CFileUpload.demo': typeof import('./src/ui/c-file-upload/c-file-upload.demo.vue')['default'] ChmodCalculator: typeof import('./src/tools/chmod-calculator/chmod-calculator.vue')['default'] Chronometer: typeof import('./src/tools/chronometer/chronometer.vue')['default'] + CidrInCidr: typeof import('./src/tools/cidr-in-cidr/cidr-in-cidr.vue')['default'] CInputText: typeof import('./src/ui/c-input-text/c-input-text.vue')['default'] 'CInputText.demo': typeof import('./src/ui/c-input-text/c-input-text.demo.vue')['default'] CKeyValueList: typeof import('./src/ui/c-key-value-list/c-key-value-list.vue')['default'] @@ -102,6 +103,7 @@ declare module '@vue/runtime-core' { IconMdiTriangleDown: typeof import('~icons/mdi/triangle-down')['default'] InputCopyable: typeof import('./src/components/InputCopyable.vue')['default'] IntegerBaseConverter: typeof import('./src/tools/integer-base-converter/integer-base-converter.vue')['default'] + IpInRange: typeof import('./src/tools/cidr-in-cidr/cidr-in-cidr.vue')['default'] Ipv4AddressConverter: typeof import('./src/tools/ipv4-address-converter/ipv4-address-converter.vue')['default'] Ipv4RangeExpander: typeof import('./src/tools/ipv4-range-expander/ipv4-range-expander.vue')['default'] Ipv4SubnetCalculator: typeof import('./src/tools/ipv4-subnet-calculator/ipv4-subnet-calculator.vue')['default'] @@ -127,24 +129,17 @@ declare module '@vue/runtime-core' { MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default'] MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default'] NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default'] - NCode: typeof import('naive-ui')['NCode'] NCollapseTransition: typeof import('naive-ui')['NCollapseTransition'] NConfigProvider: typeof import('naive-ui')['NConfigProvider'] NDivider: typeof import('naive-ui')['NDivider'] NEllipsis: typeof import('naive-ui')['NEllipsis'] - NFormItem: typeof import('naive-ui')['NFormItem'] - NGi: typeof import('naive-ui')['NGi'] - NGrid: typeof import('naive-ui')['NGrid'] NH1: typeof import('naive-ui')['NH1'] NH3: typeof import('naive-ui')['NH3'] NIcon: typeof import('naive-ui')['NIcon'] - NInputNumber: typeof import('naive-ui')['NInputNumber'] - NLabel: typeof import('naive-ui')['NLabel'] NLayout: typeof import('naive-ui')['NLayout'] NLayoutSider: typeof import('naive-ui')['NLayoutSider'] NMenu: typeof import('naive-ui')['NMenu'] - NScrollbar: typeof import('naive-ui')['NScrollbar'] - NSpin: typeof import('naive-ui')['NSpin'] + NTable: typeof import('naive-ui')['NTable'] NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default'] OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default'] PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default'] @@ -159,6 +154,7 @@ declare module '@vue/runtime-core' { RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] RsaKeyPairGenerator: typeof import('./src/tools/rsa-key-pair-generator/rsa-key-pair-generator.vue')['default'] + SafelinkDecoder: typeof import('./src/tools/safelink-decoder/safelink-decoder.vue')['default'] 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'] diff --git a/src/tools/cidr-in-cidr/cidr-in-cidr.service.test.ts b/src/tools/cidr-in-cidr/cidr-in-cidr.service.test.ts new file mode 100644 index 00000000..8b74d9ac --- /dev/null +++ b/src/tools/cidr-in-cidr/cidr-in-cidr.service.test.ts @@ -0,0 +1,48 @@ +import { describe, expect, it } from 'vitest'; +import { cidrInCidr } from './cidr-in-cidr.service'; + +describe('cidr-in-cidr', () => { + describe('cidrInCidr', () => { + it('should return correct inclusion', () => { + expect(cidrInCidr({ baseRange: '192.168.0.0/16', ipOrRangeToTest: '192.168.0.1' }).isIncluded).toBe(true); // NOSONAR + expect(cidrInCidr({ baseRange: '192.168.1.0/24', ipOrRangeToTest: '192.168.0.10' }).isIncluded).toBe(false); // NOSONAR + expect(cidrInCidr({ baseRange: '192.168.1.*', ipOrRangeToTest: '192.168.0.10' }).isIncluded).toBe(false); // NOSONAR + + expect(cidrInCidr({ baseRange: '192.168.0.0/24', ipOrRangeToTest: '192.168.0.0/28' }).isIncluded).toBe(true); // NOSONAR + expect(cidrInCidr({ baseRange: '192.168.20.0/24', ipOrRangeToTest: '192.168.1.0/28' }).isIncluded).toBe(false); // NOSONAR + expect(cidrInCidr({ baseRange: '192.168.20.*', ipOrRangeToTest: '192.168.1.0/28' }).isIncluded).toBe(false); // NOSONAR + expect(cidrInCidr({ baseRange: '192.168.0.*', ipOrRangeToTest: '192.168.0.0/28' }).isIncluded).toBe(true); // NOSONAR + + expect(cidrInCidr({ baseRange: '192.168.20.1-192.168.20.15', ipOrRangeToTest: '192.168.20.12' }).isIncluded).toBe(true); // NOSONAR + expect(cidrInCidr({ baseRange: '192.168.20.1-192.168.20.15', ipOrRangeToTest: '192.168.20.20' }).isIncluded).toBe(false); // NOSONAR + + expect(cidrInCidr({ baseRange: '192.168.20.1-192.168.20.15', ipOrRangeToTest: '192.168.20.9-192.168.20.12' }).isIncluded).toBe(true); // NOSONAR + expect(cidrInCidr({ baseRange: '192.168.20.1-192.168.20.15', ipOrRangeToTest: '192.168.20.9-192.168.20.20' }).isIncluded).toBe(false); // NOSONAR + + expect(cidrInCidr({ baseRange: '192.168.20.0-192.168.20.255', ipOrRangeToTest: '192.168.20.0/28' }).isIncluded).toBe(true); // NOSONAR + expect(cidrInCidr({ baseRange: '192.168.20.0-192.168.20.255', ipOrRangeToTest: '192.168.1.0/28' }).isIncluded).toBe(false); // NOSONAR + expect(cidrInCidr({ baseRange: '192.168.0.0-192.168.1.255', ipOrRangeToTest: '192.168.1.0/28' }).isIncluded).toBe(true); // NOSONAR + }); + it('should return correct subnet', () => { + expect(cidrInCidr({ baseRange: '192.168.0.0/16', ipOrRangeToTest: '192.168.0.1' }).baseSubnets).to.deep.eq([ // NOSONAR + { + cidr: '192.168.0.0/16', // NOSONAR + end: '192.168.255.255', // NOSONAR + start: '192.168.0.0', // NOSONAR + }, + ]); + expect(cidrInCidr({ baseRange: '192.168.20.1-192.168.20.3', ipOrRangeToTest: '192.168.1.0/28' }).baseSubnets).to.deep.eq([ // NOSONAR + { + cidr: '192.168.20.1/32', // NOSONAR + end: '192.168.20.1', // NOSONAR + start: '192.168.20.1', // NOSONAR + }, + { + cidr: '192.168.20.2/31', // NOSONAR + end: '192.168.20.3', // NOSONAR + start: '192.168.20.2', // NOSONAR + }, + ]); + }); + }); +}); diff --git a/src/tools/cidr-in-cidr/cidr-in-cidr.service.ts b/src/tools/cidr-in-cidr/cidr-in-cidr.service.ts new file mode 100644 index 00000000..3850b329 --- /dev/null +++ b/src/tools/cidr-in-cidr/cidr-in-cidr.service.ts @@ -0,0 +1,28 @@ +import { getMatch } from 'ip-matching'; + +export function cidrInCidr( + { baseRange, ipOrRangeToTest }: + { + baseRange: string + ipOrRangeToTest: string + }) { + const baseMatchMasks = getMatch(baseRange)?.convertToMasks() || []; + const ipOrRangeToTestMatchMasks = getMatch(ipOrRangeToTest)?.convertToMasks() || []; + + const baseSubnets = baseMatchMasks.map((mask) => { + const subnet = mask.convertToSubnet(); + if (!subnet) { + return { cidr: '', start: '', end: '' }; + } + return { + cidr: subnet.toString(), + start: subnet.getFirst().toString(), + end: subnet.getLast().toString(), + }; + }).filter(subnet => subnet.cidr !== ''); + + return { + baseSubnets, + isIncluded: baseMatchMasks.some(baseMask => ipOrRangeToTestMatchMasks.every(ipOrRangeMask => ipOrRangeMask.isSubsetOf(baseMask))), + }; +} diff --git a/src/tools/ip-in-range/ip-in-range.vue b/src/tools/cidr-in-cidr/cidr-in-cidr.vue similarity index 53% rename from src/tools/ip-in-range/ip-in-range.vue rename to src/tools/cidr-in-cidr/cidr-in-cidr.vue index 4e023b35..84ab3614 100644 --- a/src/tools/ip-in-range/ip-in-range.vue +++ b/src/tools/cidr-in-cidr/cidr-in-cidr.vue @@ -2,33 +2,22 @@ import { useStorage } from '@vueuse/core'; import { Check as CheckIcon, LetterX as CrossIcon } from '@vicons/tabler'; import { getMatch } from 'ip-matching'; +import { cidrInCidr } from './cidr-in-cidr.service'; import { withDefaultOnError } from '@/utils/defaults'; import { isNotThrowing } from '@/utils/boolean'; import SpanCopyable from '@/components/SpanCopyable.vue'; -const range = useStorage('ip-in-range:range', '192.168.0.1/24'); // NOSONAR -const ip = useStorage('ip-in-range:ip', '192.168.0.1'); // NOSONAR +const baseRange = useStorage('cidr-in-cidr:range', '192.168.0.1/24'); // NOSONAR +const ipOrRangeToTest = useStorage('cidr-in-cidr:ip', '192.168.0.1'); // NOSONAR -const match = computed(() => withDefaultOnError(() => getMatch(range.value), undefined)); -const subnets = computed(() => { - return (match.value?.convertToMasks() || []).map((mask) => { - const subnet = mask.convertToSubnet(); - if (!subnet) { - return { cidr: '', start: '', end: '' }; - } - return { - cidr: subnet.toString(), - start: subnet.getFirst().toString(), - end: subnet.getLast().toString(), - }; - }).filter(subnet => subnet.cidr !== ''); -}); -const ipIsInMatch = computed(() => match.value?.matches(ip.value)); +const matchResult = computed(() => withDefaultOnError( + () => cidrInCidr({ baseRange: baseRange.value, ipOrRangeToTest: ipOrRangeToTest.value }), + { baseSubnets: [], isIncluded: false })); -const ipValidationRules = [ +const rangeValidationRules = [ { message: 'We cannot parse this CIDR/IP Range/Mask/Wildcard, check the format', - validator: (value: string) => isNotThrowing(() => getMatch(value)) && getMatch(range.value), + validator: (value: string) => isNotThrowing(() => getMatch(value)) && getMatch(value), }, ]; @@ -36,35 +25,35 @@ const ipValidationRules = [