|
| 1 | +'use strict'; |
| 2 | + |
| 3 | +const common = require('../common'); |
| 4 | +const assert = require('assert'); |
| 5 | +const path = require('path'); |
| 6 | + |
| 7 | +if (!common.isWindows) { |
| 8 | + common.skip('Windows only'); |
| 9 | +} |
| 10 | + |
| 11 | +const normalizeDeviceNameTests = [ |
| 12 | + { input: 'CON', expected: 'CON' }, |
| 13 | + { input: 'con', expected: 'con' }, |
| 14 | + { input: 'CON:', expected: '.\\CON:.' }, |
| 15 | + { input: 'con:', expected: '.\\con:.' }, |
| 16 | + { input: 'CON:.', expected: '.\\CON:.' }, |
| 17 | + { input: 'coN:', expected: '.\\coN:.' }, |
| 18 | + { input: 'LPT9.foo', expected: 'LPT9.foo' }, |
| 19 | + { input: 'COM9:', expected: '.\\COM9:.' }, |
| 20 | + { input: 'COM9.', expected: '.\\COM9.' }, |
| 21 | + { input: 'C:COM9', expected: 'C:COM9' }, |
| 22 | + { input: 'C:\\COM9', expected: 'C:\\COM9' }, |
| 23 | + { input: 'CON:./foo', expected: '.\\CON:foo' }, |
| 24 | + { input: 'CON:/foo', expected: '.\\CON:foo' }, |
| 25 | + { input: 'CON:../foo', expected: '.\\CON:..\\foo' }, |
| 26 | + { input: 'CON:/../foo', expected: '.\\CON:..\\foo' }, |
| 27 | + { input: 'CON:./././foo', expected: '.\\CON:foo' }, |
| 28 | + { input: 'CON:..', expected: '.\\CON:..' }, |
| 29 | + { input: 'CON:..\\', expected: '.\\CON:..\\' }, |
| 30 | + { input: 'CON:..\\..', expected: '.\\CON:..\\..' }, |
| 31 | + { input: 'CON:..\\..\\', expected: '.\\CON:..\\..\\' }, |
| 32 | + { input: 'CON:..\\..\\foo', expected: '.\\CON:..\\..\\foo' }, |
| 33 | + { input: 'CON:..\\..\\foo\\', expected: '.\\CON:..\\..\\foo\\' }, |
| 34 | + { input: 'CON:..\\..\\foo\\bar', expected: '.\\CON:..\\..\\foo\\bar' }, |
| 35 | + { input: 'CON:..\\..\\foo\\bar\\', expected: '.\\CON:..\\..\\foo\\bar\\' }, |
| 36 | + { input: 'COM1:a:b:c', expected: '.\\COM1:a:b:c' }, |
| 37 | + { input: 'COM1:a:b:c/', expected: '.\\COM1:a:b:c\\' }, |
| 38 | + { input: 'c:lpt1', expected: 'c:lpt1' }, |
| 39 | + { input: 'c:\\lpt1', expected: 'c:\\lpt1' }, |
| 40 | + |
| 41 | + // Reserved device names with path traversal |
| 42 | + { input: 'CON:.\\..\\..\\foo', expected: '.\\CON:..\\..\\foo' }, |
| 43 | + { input: 'PRN:.\\..\\bar', expected: '.\\PRN:..\\bar' }, |
| 44 | + { input: 'AUX:/../../baz', expected: '.\\AUX:..\\..\\baz' }, |
| 45 | + |
| 46 | + { input: 'COM1:', expected: '.\\COM1:.' }, |
| 47 | + { input: 'COM9:', expected: '.\\COM9:.' }, |
| 48 | + { input: 'COM1:.\\..\\..\\foo', expected: '.\\COM1:..\\..\\foo' }, |
| 49 | + { input: 'LPT1:', expected: '.\\LPT1:.' }, |
| 50 | + { input: 'LPT9:', expected: '.\\LPT9:.' }, |
| 51 | + { input: 'LPT1:.\\..\\..\\foo', expected: '.\\LPT1:..\\..\\foo' }, |
| 52 | + { input: 'LpT5:/another/path', expected: '.\\LpT5:another\\path' }, |
| 53 | + |
| 54 | + { input: 'C:\\foo', expected: 'C:\\foo' }, |
| 55 | + { input: 'D:bar', expected: 'D:bar' }, |
| 56 | + |
| 57 | + { input: 'CON', expected: 'CON' }, |
| 58 | + { input: 'CON.TXT', expected: 'CON.TXT' }, |
| 59 | + { input: 'COM10:', expected: '.\\COM10:' }, |
| 60 | + { input: 'LPT10:', expected: '.\\LPT10:' }, |
| 61 | + { input: 'CONNINGTOWER:', expected: '.\\CONNINGTOWER:' }, |
| 62 | + { input: 'AUXILIARYDEVICE:', expected: '.\\AUXILIARYDEVICE:' }, |
| 63 | + { input: 'NULLED:', expected: '.\\NULLED:' }, |
| 64 | + { input: 'PRNINTER:', expected: '.\\PRNINTER:' }, |
| 65 | + |
| 66 | + { input: 'CON:\\..\\..\\windows\\system32', expected: '.\\CON:..\\..\\windows\\system32' }, |
| 67 | + { input: 'PRN:.././../etc/passwd', expected: '.\\PRN:..\\..\\etc\\passwd' }, |
| 68 | + |
| 69 | + // Test with trailing slashes |
| 70 | + { input: 'CON:\\', expected: '.\\CON:.\\' }, |
| 71 | + { input: 'COM1:\\foo\\bar\\', expected: '.\\COM1:foo\\bar\\' }, |
| 72 | + |
| 73 | + // Test cases from original vulnerability reports or similar scenarios |
| 74 | + { input: 'COM1:.\\..\\..\\foo.js', expected: '.\\COM1:..\\..\\foo.js' }, |
| 75 | + { input: 'LPT1:.\\..\\..\\another.txt', expected: '.\\LPT1:..\\..\\another.txt' }, |
| 76 | + |
| 77 | + // Paths with device names not at the beginning |
| 78 | + { input: 'C:\\CON', expected: 'C:\\CON' }, |
| 79 | + { input: 'C:\\path\\to\\COM1:', expected: 'C:\\path\\to\\COM1:' }, |
| 80 | + |
| 81 | + // Device name followed by multiple colons |
| 82 | + { input: 'CON::', expected: '.\\CON::' }, |
| 83 | + { input: 'COM1:::foo', expected: '.\\COM1:::foo' }, |
| 84 | + |
| 85 | + // Device name with mixed path separators |
| 86 | + { input: 'AUX:/foo\\bar/baz', expected: '.\\AUX:foo\\bar\\baz' }, |
| 87 | +]; |
| 88 | + |
| 89 | +for (const { input, expected } of normalizeDeviceNameTests) { |
| 90 | + const actual = path.win32.normalize(input); |
| 91 | + assert.strictEqual(actual, expected, |
| 92 | + `path.win32.normalize(${JSON.stringify(input)}) === ${JSON.stringify(expected)}, but got ${JSON.stringify(actual)}`); |
| 93 | +} |
| 94 | + |
| 95 | +assert.strictEqual(path.win32.normalize('CON:foo/../bar'), '.\\CON:bar'); |
| 96 | + |
| 97 | +// This should NOT be prefixed because 'c:' is treated as a drive letter. |
| 98 | +assert.strictEqual(path.win32.normalize('c:COM1:'), 'c:COM1:'); |
0 commit comments