Merge a9bf3fe6c7 into 07eea0f484
				
					
				
			This commit is contained in:
		
						commit
						1f859dc98b
					
				
							
								
								
									
										1
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -103,6 +103,7 @@ declare module '@vue/runtime-core' { | |||||||
|     IconMdiTriangleDown: typeof import('~icons/mdi/triangle-down')['default'] |     IconMdiTriangleDown: typeof import('~icons/mdi/triangle-down')['default'] | ||||||
|     InputCopyable: typeof import('./src/components/InputCopyable.vue')['default'] |     InputCopyable: typeof import('./src/components/InputCopyable.vue')['default'] | ||||||
|     IntegerBaseConverter: typeof import('./src/tools/integer-base-converter/integer-base-converter.vue')['default'] |     IntegerBaseConverter: typeof import('./src/tools/integer-base-converter/integer-base-converter.vue')['default'] | ||||||
|  |     IpIncludeExclude: typeof import('./src/tools/ip-include-exclude/ip-include-exclude.vue')['default'] | ||||||
|     Ipv4AddressConverter: typeof import('./src/tools/ipv4-address-converter/ipv4-address-converter.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'] |     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'] |     Ipv4SubnetCalculator: typeof import('./src/tools/ipv4-subnet-calculator/ipv4-subnet-calculator.vue')['default'] | ||||||
|  | |||||||
| @ -54,6 +54,7 @@ | |||||||
|     "@vueuse/router": "^10.0.0", |     "@vueuse/router": "^10.0.0", | ||||||
|     "bcryptjs": "^2.4.3", |     "bcryptjs": "^2.4.3", | ||||||
|     "change-case": "^4.1.2", |     "change-case": "^4.1.2", | ||||||
|  |     "cidr-tools": "^11.0.2", | ||||||
|     "colord": "^2.9.3", |     "colord": "^2.9.3", | ||||||
|     "composerize-ts": "^0.6.2", |     "composerize-ts": "^0.6.2", | ||||||
|     "country-code-lookup": "^0.1.0", |     "country-code-lookup": "^0.1.0", | ||||||
| @ -70,6 +71,7 @@ | |||||||
|     "highlight.js": "^11.7.0", |     "highlight.js": "^11.7.0", | ||||||
|     "iarna-toml-esm": "^3.0.5", |     "iarna-toml-esm": "^3.0.5", | ||||||
|     "ibantools": "^4.3.3", |     "ibantools": "^4.3.3", | ||||||
|  |     "ip-matching": "^2.1.2", | ||||||
|     "js-base64": "^3.7.6", |     "js-base64": "^3.7.6", | ||||||
|     "json5": "^2.2.3", |     "json5": "^2.2.3", | ||||||
|     "jwt-decode": "^3.1.2", |     "jwt-decode": "^3.1.2", | ||||||
|  | |||||||
							
								
								
									
										14924
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										14924
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,6 +1,7 @@ | |||||||
| import { tool as base64FileConverter } from './base64-file-converter'; | import { tool as base64FileConverter } from './base64-file-converter'; | ||||||
| import { tool as base64StringConverter } from './base64-string-converter'; | import { tool as base64StringConverter } from './base64-string-converter'; | ||||||
| import { tool as basicAuthGenerator } from './basic-auth-generator'; | import { tool as basicAuthGenerator } from './basic-auth-generator'; | ||||||
|  | import { tool as ipIncludeExclude } from './ip-include-exclude'; | ||||||
| import { tool as emailNormalizer } from './email-normalizer'; | import { tool as emailNormalizer } from './email-normalizer'; | ||||||
| 
 | 
 | ||||||
| import { tool as asciiTextDrawer } from './ascii-text-drawer'; | import { tool as asciiTextDrawer } from './ascii-text-drawer'; | ||||||
| @ -164,7 +165,15 @@ export const toolsByCategory: ToolCategory[] = [ | |||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     name: 'Network', |     name: 'Network', | ||||||
|     components: [ipv4SubnetCalculator, ipv4AddressConverter, ipv4RangeExpander, macAddressLookup, macAddressGenerator, ipv6UlaGenerator], |     components: [ | ||||||
|  |       ipv4SubnetCalculator, | ||||||
|  |       ipv4AddressConverter, | ||||||
|  |       ipv4RangeExpander, | ||||||
|  |       ipIncludeExclude, | ||||||
|  |       macAddressLookup, | ||||||
|  |       macAddressGenerator, | ||||||
|  |       ipv6UlaGenerator, | ||||||
|  |     ], | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     name: 'Math', |     name: 'Math', | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								src/tools/ip-include-exclude/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/tools/ip-include-exclude/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | import { UnfoldMoreOutlined } from '@vicons/material'; | ||||||
|  | import { defineTool } from '../tool'; | ||||||
|  | 
 | ||||||
|  | export const tool = defineTool({ | ||||||
|  |   name: 'IP Subnets Exclude Calculator', | ||||||
|  |   path: '/ip-include-exclude', | ||||||
|  |   description: 'Substract a disallowed IP Ranges/Mask/CIDR list from an allowed IP Ranges/Mask/CIDR list', | ||||||
|  |   keywords: ['ip', 'allowed', 'disallowed', 'include', 'exclude', 'subnet', 'cidr'], | ||||||
|  |   component: () => import('./ip-include-exclude.vue'), | ||||||
|  |   icon: UnfoldMoreOutlined, | ||||||
|  |   createdAt: new Date('2024-08-15'), | ||||||
|  | }); | ||||||
| @ -0,0 +1,63 @@ | |||||||
|  | import { describe, expect, it } from 'vitest'; | ||||||
|  | import { substractCIDRs } from './ip-include-exclude.service'; | ||||||
|  | 
 | ||||||
|  | describe('ip-include-exclude', () => { | ||||||
|  |   describe('substractCIDRs', () => { | ||||||
|  |     it('should return error on invalid values', () => { | ||||||
|  |       expect(substractCIDRs({ allowedRanges: '192.168.0.', disallowedRanges: '' })).to.deep.eq({ // NOSONAR
 | ||||||
|  |         allowedCIDRs: [], | ||||||
|  |         allowedSubnets: [], | ||||||
|  |         disallowedSubnets: [], | ||||||
|  |         error: 'Error: Invalid IP (range/subnetwork)', | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |     it('should return correct substractions and subnets', () => { | ||||||
|  |       expect(substractCIDRs({ allowedRanges: '192.168.3.0/24', disallowedRanges: '' })).to.deep.eq({ // NOSONAR
 | ||||||
|  |         allowedCIDRs: ['192.168.3.0/24'], // NOSONAR
 | ||||||
|  |         allowedSubnets: [{ cidr: '192.168.3.0/24', end: '192.168.3.255', start: '192.168.3.0' }], // NOSONAR
 | ||||||
|  |         disallowedSubnets: [], | ||||||
|  |         error: '', | ||||||
|  |       }); | ||||||
|  |       expect(substractCIDRs({ allowedRanges: '192.168.2.0/24', disallowedRanges: '192.168.2.1' })).to.deep.eq({ // NOSONAR
 | ||||||
|  |         allowedCIDRs: ['192.168.2.0/32', // NOSONAR
 | ||||||
|  |           '192.168.2.2/31', // NOSONAR
 | ||||||
|  |           '192.168.2.4/30', // NOSONAR
 | ||||||
|  |           '192.168.2.8/29', // NOSONAR
 | ||||||
|  |           '192.168.2.16/28', // NOSONAR
 | ||||||
|  |           '192.168.2.32/27', // NOSONAR
 | ||||||
|  |           '192.168.2.64/26', // NOSONAR
 | ||||||
|  |           '192.168.2.128/25'], // NOSONAR
 | ||||||
|  |         allowedSubnets: [ | ||||||
|  |           { cidr: '192.168.2.0/24', end: '192.168.2.255', start: '192.168.2.0' }, // NOSONAR
 | ||||||
|  |         ], | ||||||
|  |         disallowedSubnets: [ | ||||||
|  |           { cidr: '192.168.2.1/32', end: '192.168.2.1', start: '192.168.2.1' }, // NOSONAR
 | ||||||
|  |         ], | ||||||
|  |         error: '', | ||||||
|  |       }); | ||||||
|  |       expect(substractCIDRs({ allowedRanges: '192.168.12.0/24', disallowedRanges: '192.168.12.1-192.168.12.10, 192.168.12.34' })).to.deep.eq({ // NOSONAR
 | ||||||
|  |         allowedCIDRs: ['192.168.12.0/32', // NOSONAR
 | ||||||
|  |           '192.168.12.11/32', // NOSONAR
 | ||||||
|  |           '192.168.12.12/30', // NOSONAR
 | ||||||
|  |           '192.168.12.16/28', // NOSONAR
 | ||||||
|  |           '192.168.12.32/31', // NOSONAR
 | ||||||
|  |           '192.168.12.35/32', // NOSONAR
 | ||||||
|  |           '192.168.12.36/30', // NOSONAR
 | ||||||
|  |           '192.168.12.40/29', // NOSONAR
 | ||||||
|  |           '192.168.12.48/28', // NOSONAR
 | ||||||
|  |           '192.168.12.64/26', // NOSONAR
 | ||||||
|  |           '192.168.12.128/25'], // NOSONAR
 | ||||||
|  |         allowedSubnets: [{ cidr: '192.168.12.0/24', end: '192.168.12.255', start: '192.168.12.0' }], // NOSONAR
 | ||||||
|  |         disallowedSubnets: [ | ||||||
|  |           { cidr: '192.168.12.1/32', end: '192.168.12.1', start: '192.168.12.1' }, // NOSONAR
 | ||||||
|  |           { cidr: '192.168.12.2/31', end: '192.168.12.3', start: '192.168.12.2' }, // NOSONAR
 | ||||||
|  |           { cidr: '192.168.12.4/30', end: '192.168.12.7', start: '192.168.12.4' }, // NOSONAR
 | ||||||
|  |           { cidr: '192.168.12.8/31', end: '192.168.12.9', start: '192.168.12.8' }, // NOSONAR
 | ||||||
|  |           { cidr: '192.168.12.10/32', end: '192.168.12.10', start: '192.168.12.10' }, // NOSONAR
 | ||||||
|  |           { cidr: '192.168.12.34/32', end: '192.168.12.34', start: '192.168.12.34' }, // NOSONAR
 | ||||||
|  |         ], | ||||||
|  |         error: '', | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										49
									
								
								src/tools/ip-include-exclude/ip-include-exclude.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/tools/ip-include-exclude/ip-include-exclude.service.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | |||||||
|  | import type { IPMask } from 'ip-matching'; | ||||||
|  | import { getMatch } from 'ip-matching'; | ||||||
|  | import { excludeCidr } from 'cidr-tools'; | ||||||
|  | 
 | ||||||
|  | function convertToCIDR(mask: IPMask) { | ||||||
|  |   const subnet = mask.convertToSubnet(); | ||||||
|  |   if (!subnet) { | ||||||
|  |     return { cidr: mask.toString(), start: '', end: '' }; | ||||||
|  |   } | ||||||
|  |   return { | ||||||
|  |     cidr: subnet.toString(), | ||||||
|  |     start: subnet.getFirst().toString(), | ||||||
|  |     end: subnet.getLast().toString(), | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function substractCIDRs( | ||||||
|  |   { allowedRanges, disallowedRanges }: | ||||||
|  |   { | ||||||
|  |     allowedRanges: string | ||||||
|  |     disallowedRanges: string | ||||||
|  |   }) { | ||||||
|  |   try { | ||||||
|  |     const allowedRangesMatchMasks = allowedRanges.split(/\s*[,;|]+\s*/g) // NOSONAR
 | ||||||
|  |       .filter(range => range) | ||||||
|  |       .flatMap(range => getMatch(range)?.convertToMasks() || []); | ||||||
|  |     const disallowedRangesMatchMasks = disallowedRanges.split(/\s*[,;|]+\s*/g) // NOSONAR
 | ||||||
|  |       .filter(range => range) | ||||||
|  |       .flatMap(range => getMatch(range)?.convertToMasks() || []); | ||||||
|  | 
 | ||||||
|  |     const allowedSubnets = allowedRangesMatchMasks.map(convertToCIDR); | ||||||
|  |     const disallowedSubnets = disallowedRangesMatchMasks.map(convertToCIDR); | ||||||
|  | 
 | ||||||
|  |     return { | ||||||
|  |       error: '', | ||||||
|  |       allowedSubnets, | ||||||
|  |       disallowedSubnets, | ||||||
|  |       allowedCIDRs: excludeCidr(allowedSubnets.map(net => net.cidr), disallowedSubnets.map(net => net.cidr)), | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  |   catch (e: any) { | ||||||
|  |     return { | ||||||
|  |       error: e.toString(), | ||||||
|  |       allowedSubnets: [], | ||||||
|  |       disallowedSubnets: [], | ||||||
|  |       allowedCIDRs: [], | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										80
									
								
								src/tools/ip-include-exclude/ip-include-exclude.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								src/tools/ip-include-exclude/ip-include-exclude.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,80 @@ | |||||||
|  | <script setup lang="ts"> | ||||||
|  | import { useStorage } from '@vueuse/core'; | ||||||
|  | import { substractCIDRs } from './ip-include-exclude.service'; | ||||||
|  | import SpanCopyable from '@/components/SpanCopyable.vue'; | ||||||
|  | import TextareaCopyable from '@/components/TextareaCopyable.vue'; | ||||||
|  | 
 | ||||||
|  | const allowedRanges = useStorage('ip-inc-exc:allow', '192.168.0.1/24'); // NOSONAR | ||||||
|  | const disallowedRanges = useStorage('ip-inc-exc:disallow', '192.168.0.6'); // NOSONAR | ||||||
|  | 
 | ||||||
|  | const result = computed(() => substractCIDRs({ | ||||||
|  |   allowedRanges: allowedRanges.value, disallowedRanges: disallowedRanges.value, | ||||||
|  | })); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <c-input-text | ||||||
|  |       v-model:value="allowedRanges" | ||||||
|  |       label="AllowedIPs (IPv4/6 CIDR/Range/Mask/Wildcard)" | ||||||
|  |       placeholder="An IPv4/6 CIDR/Range/Mask/Wildcard..." | ||||||
|  |       mb-4 | ||||||
|  |     /> | ||||||
|  | 
 | ||||||
|  |     <c-input-text | ||||||
|  |       v-model:value="disallowedRanges" | ||||||
|  |       label="DisallowedIPs (IPv4/6 CIDR/Range/Mask/Wildcard)" | ||||||
|  |       placeholder="An IPv4/6 CIDR/Range/Mask/Wildcard..." | ||||||
|  |       mb-4 | ||||||
|  |     /> | ||||||
|  | 
 | ||||||
|  |     <n-divider /> | ||||||
|  | 
 | ||||||
|  |     <c-alert v-if="result.error"> | ||||||
|  |       {{ result.error }} | ||||||
|  |     </c-alert> | ||||||
|  | 
 | ||||||
|  |     <div v-if="!result.error"> | ||||||
|  |       <n-form-item label="Final AllowedIPs:"> | ||||||
|  |         <TextareaCopyable :value="result.allowedCIDRs.join(', ')" /> | ||||||
|  |       </n-form-item> | ||||||
|  | 
 | ||||||
|  |       <n-divider /> | ||||||
|  | 
 | ||||||
|  |       <c-card title="Allowed Subnets"> | ||||||
|  |         <n-table> | ||||||
|  |           <tbody> | ||||||
|  |             <tr v-for="{ cidr, start, end } in result.allowedSubnets" :key="cidr"> | ||||||
|  |               <td font-bold> | ||||||
|  |                 <SpanCopyable :value="cidr" /> | ||||||
|  |               </td> | ||||||
|  |               <td> | ||||||
|  |                 <SpanCopyable :value="start" /> | ||||||
|  |               </td> | ||||||
|  |               <td> | ||||||
|  |                 <SpanCopyable :value="end" /> | ||||||
|  |               </td> | ||||||
|  |             </tr> | ||||||
|  |           </tbody> | ||||||
|  |         </n-table> | ||||||
|  |       </c-card> | ||||||
|  |       <c-card title="Disallowed Subnets"> | ||||||
|  |         <n-table> | ||||||
|  |           <tbody> | ||||||
|  |             <tr v-for="{ cidr, start, end } in result.disallowedSubnets" :key="cidr"> | ||||||
|  |               <td font-bold> | ||||||
|  |                 <SpanCopyable :value="cidr" /> | ||||||
|  |               </td> | ||||||
|  |               <td> | ||||||
|  |                 <SpanCopyable :value="start" /> | ||||||
|  |               </td> | ||||||
|  |               <td> | ||||||
|  |                 <SpanCopyable :value="end" /> | ||||||
|  |               </td> | ||||||
|  |             </tr> | ||||||
|  |           </tbody> | ||||||
|  |         </n-table> | ||||||
|  |       </c-card> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user