Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .changeset/port-tests-to-typescript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
---

Port internal-helpers tests from JavaScript to TypeScript
2 changes: 1 addition & 1 deletion knip.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// @ts-check
const testEntry = 'test/**/*.test.js';
const testEntry = 'test/**/*.test.{js,ts}';

/** @type {import('knip').KnipConfig} */
export default {
Expand Down
3 changes: 2 additions & 1 deletion packages/internal-helpers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@
"build": "astro-scripts build \"src/**/*.ts\" && tsc -p tsconfig.json",
"build:ci": "astro-scripts build \"src/**/*.ts\"",
"dev": "astro-scripts dev \"src/**/*.ts\"",
"test": "astro-scripts test \"test/**/*.test.js\""
"test": "astro-scripts test \"test/**/*.test.ts\" --strip-types",
"typecheck:tests": "tsc --project tsconfig.test.json"
},
"dependencies": {
"picomatch": "^4.0.3"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import assert from 'node:assert/strict';
import * as assert from 'node:assert/strict';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed

import { describe, it } from 'node:test';
import { createFilter } from '../dist/create-filter.js';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import assert from 'node:assert/strict';
import * as assert from 'node:assert/strict';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed

import { describe, it } from 'node:test';
import { isParentDirectory, isRemotePath, normalizePathname } from '../dist/path.js';

describe('isRemotePath', () => {
const remotePaths = [
const remotePaths: string[] = [
// Standard remote protocols
'https://example.com/foo/bar.js',
'http://example.com/foo/bar.js',
Expand Down Expand Up @@ -156,14 +156,14 @@ describe('isRemotePath', () => {
'//root:22@server.com', // SSH port as password

// Unicode in credentials
'http://üser:pāss@example.com', // Unicode username/password
'http://用户:密码@example.com', // Chinese characters
'http://админ:пароль@example.com', // Cyrillic
'http://\u00FCser:p\u0101ss@example.com', // Unicode username/password
'http://\u7528\u6237:\u5BC6\u7801@example.com', // Chinese characters
'http://\u0430\u0434\u043C\u0438\u043D:\u043F\u0430\u0440\u043E\u043B\u044C@example.com', // Cyrillic
Comment on lines +159 to +161

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert the internal changes of this array

'http://\u0075ser:\u0070ass@example.com', // Unicode escapes

// Homograph attacks in credentials
'http://аdmin:pаssword@example.com', // Cyrillic 'а' looks like Latin 'a'
'http://adⅿin:password@example.com', // Unicode small m lookalike
'http://\u0430dmin:p\u0430ssword@example.com', // Cyrillic 'a' looks like Latin 'a'
'http://ad\u217Fin:password@example.com', // Unicode small m lookalike

// Large credentials trying to overflow
'http://' + 'a'.repeat(1000) + ':' + 'b'.repeat(1000) + '@example.com',
Expand Down Expand Up @@ -253,8 +253,8 @@ describe('isRemotePath', () => {
// Punycode and Unicode domains
'http://xn--e1afmkfd.xn--p1ai/test',
'\\\\xn--e1afmkfd.xn--p1ai/test',
'http://例え.jp/test',
'\\\\例え.jp/test',
'http://\u4F8B\u3048.jp/test',
'\\\\\u4F8B\u3048.jp/test',
Comment on lines +256 to +257

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May I ask why you changed these Japanese characters to \u unicode code?


// Case variations
'HtTp://example.com/test',
Expand Down Expand Up @@ -369,7 +369,7 @@ describe('isRemotePath', () => {
'http:/\\example.com', // Mixed slash backslash (this is actually http:/\example.com)
];

const localPaths = [
const localPaths: string[] = [
// Standard Unix/Linux absolute paths
'/local/path/file.js',
'/usr/local/bin/node',
Expand Down Expand Up @@ -513,12 +513,12 @@ describe('isRemotePath', () => {
'C:\\Users\\user!\\file%.txt',

// Paths with Unicode characters
'/用户/文档/文件.js',
'/путь/к/файлу.js',
'/مسار/إلى/ملف.js',
'/パス/ファイル.js',
'/경로/파일.js',
'C:\\文档\\项目\\app.js',
'/\u7528\u6237/\u6587\u6863/\u6587\u4EF6.js',
'/\u043F\u0443\u0442\u044C/\u043A/\u0444\u0430\u0439\u043B\u0443.js',
'/\u0645\u0633\u0627\u0631/\u0625\u0644\u0649/\u0645\u0644\u0641.js',
'/\u30D1\u30B9/\u30D5\u30A1\u30A4\u30EB.js',
'/\uACBD\uB85C/\uD30C\uC77C.js',
'C:\\\u6587\u6863\\\u9879\u76EE\\app.js',

// Query parameters on local paths (common in dev servers)
'/src/main.js?v=12345',
Expand Down Expand Up @@ -599,7 +599,7 @@ describe('isRemotePath', () => {

describe('isParentDirectory', () => {
it('should correctly identify parent-child relationships', () => {
const validCases = [
const validCases: [string, string][] = [
// Unix absolute paths
['/home', '/home/user'],
['/home', '/home/user/documents'],
Expand Down Expand Up @@ -652,7 +652,7 @@ describe('isParentDirectory', () => {
});

it('should correctly reject non-parent relationships', () => {
const invalidCases = [
const invalidCases: [string | null | undefined, string | null | undefined][] = [
// Different directories
['/home', '/usr'],
['/home/user', '/home/otheruser'],
Expand Down Expand Up @@ -704,15 +704,15 @@ describe('isParentDirectory', () => {

invalidCases.forEach(([parent, child]) => {
assert.equal(
isParentDirectory(parent, child),
isParentDirectory(parent!, child!),
false,
`Expected "${parent}" NOT to be parent of "${child}"`,
);
});
});

it('should handle adversarial inputs safely', () => {
const adversarialCases = [
const adversarialCases: [string, string][] = [
// Path traversal attacks
['/safe', '/safe/../../../etc/passwd'],
['/app', '/app/../../../../root/.ssh'],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import assert from 'node:assert/strict';
import * as assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import {
getClientIpAddress,
Expand Down Expand Up @@ -43,7 +43,7 @@ describe('getFirstForwardedValue', () => {
});

describe('isValidIpAddress', () => {
const validAddresses = [
const validAddresses: string[] = [
// IPv4
'127.0.0.1',
'0.0.0.0',
Expand All @@ -62,7 +62,7 @@ describe('isValidIpAddress', () => {
'fd12:3456:789a::1',
];

const invalidAddresses = [
const invalidAddresses: string[] = [
// Injection payloads
'<script>alert(1)</script>',
"'; DROP TABLE users; --",
Expand Down Expand Up @@ -137,7 +137,7 @@ describe('getClientIpAddress', () => {
/**
* Helper to create a minimal Request with given headers.
*/
function makeRequest(headers = {}) {
function makeRequest(headers: Record<string, string> = {}): Request {
return new Request('https://example.com', { headers });
}

Expand Down
11 changes: 11 additions & 0 deletions packages/internal-helpers/tsconfig.test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.base.json",
"include": ["test/**/*.ts"],
"compilerOptions": {
"noEmit": true,
"allowJs": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"rewriteRelativeImportExtensions": true
}
}
Loading