@himorishige/noren-plugin-network
Version:
Network PII detection plugin for Noren - IPv4, IPv6, and MAC address detection
207 lines (206 loc) • 6.43 kB
JavaScript
/**
* Network PII validation functions for false positive reduction
* Implements validation logic specifically for IPv4, IPv6, and MAC addresses
*/
import { parseIPv6 } from './ipv6-parser.js';
// Reserved and special network addresses to exclude (only very specific ones)
const RESERVED_IPV4_ADDRESSES = new Set([
// Loopback
'127.0.0.1',
// Special addresses
'0.0.0.0',
'255.255.255.255',
]);
const IPV4_PATTERNS_TO_EXCLUDE = [
// Version numbers (simple patterns like 1.2.3.4 where all parts are small)
/^[0-9]\.[0-9]\.[0-9]\.[0-9]$/,
/^[0-9]\.[0-9]\.[0-9]{1,2}\.[0-9]$/,
// Date patterns (YYYY.MM.DD, DD.MM.YYYY)
/^(19|20)\d{2}\.(0[1-9]|1[0-2])\.(0[1-9]|[12]\d|3[01])$/,
/^(0[1-9]|[12]\d|3[01])\.(0[1-9]|1[0-2])\.(19|20)\d{2}$/,
];
const RESERVED_MAC_PATTERNS = [
// Broadcast
/^ff:ff:ff:ff:ff:ff$/i,
/^ff-ff-ff-ff-ff-ff$/i,
// All zeros
/^00:00:00:00:00:00$/i,
/^00-00-00-00-00-00$/i,
];
/**
* Validate IPv4 address candidate
*/
export function validateIPv4(candidate, context) {
// Basic format validation (already done by regex, but double-check)
const octets = candidate.split('.').map(Number);
if (octets.length !== 4 || octets.some((n) => n < 0 || n > 255)) {
return {
valid: false,
confidence: 0.0,
reason: 'invalid_format',
};
}
// Check against reserved addresses (in production environments)
if (RESERVED_IPV4_ADDRESSES.has(candidate)) {
return {
valid: false,
confidence: 0.0,
reason: 'reserved_address',
};
}
// Check for obvious false positives (version numbers, dates)
for (const pattern of IPV4_PATTERNS_TO_EXCLUDE) {
if (pattern.test(candidate)) {
return {
valid: false,
confidence: 0.0,
reason: 'likely_false_positive',
};
}
}
// Context analysis for better accuracy
const surroundingLower = context.surroundingText.toLowerCase();
// Positive indicators
if (/\b(ip|address|server|host|endpoint|url|api)\b/.test(surroundingLower)) {
return {
valid: true,
confidence: 0.9,
reason: 'network_context',
metadata: { contextMatched: true },
};
}
// Negative indicators
if (/\b(version|ver|v\d|release|build|date)\b/.test(surroundingLower)) {
return {
valid: false,
confidence: 0.0,
reason: 'version_context',
};
}
// Private IP addresses (lower confidence in logs/configs)
const [a, b] = octets;
const isPrivate = a === 10 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168) || a === 127;
// Default to valid for most cases - this is a plugin, let users decide
return {
valid: true,
confidence: isPrivate ? 0.7 : 0.8,
reason: isPrivate ? 'private_ip' : 'public_ip',
metadata: { isPrivate, octets },
};
}
/**
* Validate IPv6 address candidate
*/
export function validateIPv6(candidate, context) {
const parseResult = parseIPv6(candidate);
if (!parseResult.valid) {
return {
valid: false,
confidence: 0.0,
reason: parseResult.error || 'parse_failed',
};
}
// Context analysis
const surroundingLower = context.surroundingText.toLowerCase();
// Strong network context
if (/\b(ipv6|ip6|address|interface|gateway|dns)\b/.test(surroundingLower)) {
return {
valid: true,
confidence: 0.95,
reason: 'ipv6_context',
metadata: parseResult,
};
}
// Special addresses (typically not PII)
if (parseResult.isLoopback || parseResult.isDocumentation) {
return {
valid: false,
confidence: 0.0,
reason: 'special_address',
metadata: parseResult,
};
}
// Link-local and unique local (lower confidence)
if (parseResult.isLinkLocal || parseResult.isUniqueLocal) {
return {
valid: true,
confidence: 0.6,
reason: 'local_address',
metadata: parseResult,
};
}
return {
valid: true,
confidence: 0.8,
reason: 'valid_ipv6',
metadata: parseResult,
};
}
/**
* Validate MAC address candidate
*/
export function validateMAC(candidate, context) {
const normalized = candidate.replace(/[:-]/g, '').toLowerCase();
// Check for reserved patterns
for (const pattern of RESERVED_MAC_PATTERNS) {
if (pattern.test(candidate)) {
return {
valid: false,
confidence: 0.0,
reason: 'reserved_mac',
};
}
}
// Context analysis
const surroundingLower = context.surroundingText.toLowerCase();
// Strong network context
if (/\b(mac|ethernet|wifi|interface|adapter|nic|hardware)\b/.test(surroundingLower)) {
return {
valid: true,
confidence: 0.9,
reason: 'hardware_context',
metadata: { normalized },
};
}
// Negative indicators (serial numbers, product codes)
if (/\b(serial|product|model|code|id|part|sku)\b/.test(surroundingLower)) {
return {
valid: false,
confidence: 0.0,
reason: 'product_context',
};
}
// Check for obvious patterns (all same digits, etc.)
if (/^(.)\1{11}$/.test(normalized)) {
return {
valid: false,
confidence: 0.0,
reason: 'pattern_repetition',
};
}
return {
valid: true,
confidence: 0.7,
reason: 'valid_mac',
metadata: { normalized, separator: candidate.includes(':') ? ':' : '-' },
};
}
/**
* Main validation function for network PII types
*/
export function validateNetworkCandidate(candidate, type, context) {
switch (type) {
case 'ipv4':
return validateIPv4(candidate, context);
case 'ipv6':
return validateIPv6(candidate, context);
case 'mac':
return validateMAC(candidate, context);
default:
return {
valid: false,
confidence: 0.0,
reason: 'unknown_type',
};
}
}