From 8dad96c27d4c34b6f038b0c0285045112275cb90 Mon Sep 17 00:00:00 2001 From: PageZ948 Date: Wed, 19 Aug 2026 22:39:40 +0000 Subject: [PATCH] fix: support GitPeji tags and interactive helper auth --- src/proxy-launch.js | 2 +- src/update-policy.js | 14 ++++++--- test/fixtures/gitpeji-latest-release.json | 15 ++++++++++ test/proxy-launch.test.js | 6 ++-- test/update-policy.test.js | 35 ++++++++++++++++++++++- 5 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 test/fixtures/gitpeji-latest-release.json diff --git a/src/proxy-launch.js b/src/proxy-launch.js index 2954d1b..5ddfa8f 100644 --- a/src/proxy-launch.js +++ b/src/proxy-launch.js @@ -111,7 +111,7 @@ function createProxyManager({ { shell: false, detached: true, - stdio: 'ignore', + stdio: 'inherit', windowsHide: false } ); diff --git a/src/update-policy.js b/src/update-policy.js index 8f3fb6e..4302d0f 100644 --- a/src/update-policy.js +++ b/src/update-policy.js @@ -8,7 +8,7 @@ const REQUEST_TIMEOUT_MS = 5000; const MAX_BODY_BYTES = 64 * 1024; const MAX_RELEASE_NAME_LENGTH = 200; -// SemVer 2.0.0 without loose forms such as a leading "v" or omitted fields. +// Strict SemVer 2.0.0 without loose forms such as omitted fields. const SEMVER_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/; class UpdatePolicyError extends Error { @@ -41,6 +41,12 @@ function parseSemver(version, errorCode = 'INVALID_VERSION') { }; } +function normalizeReleaseTag(tag) { + const version = tag.startsWith('v') ? tag.slice(1) : tag; + parseSemver(version, 'INVALID_RELEASE_VERSION'); + return version; +} + function compareIdentifier(left, right) { const leftNumeric = /^\d+$/.test(left); const rightNumeric = /^\d+$/.test(right); @@ -290,14 +296,14 @@ async function checkForUpdate({ } const release = parseRelease(body); - parseSemver(release.latestVersion, 'INVALID_RELEASE_VERSION'); + const latestVersion = normalizeReleaseTag(release.latestVersion); const metadata = { - status: compareSemver(release.latestVersion, currentVersion) > 0 + status: compareSemver(latestVersion, currentVersion) > 0 ? 'update-available' : 'up-to-date', ...baseMetadata, - latestVersion: release.latestVersion, + latestVersion, releaseName: release.releaseName, }; if (release.publishedAt !== undefined) { diff --git a/test/fixtures/gitpeji-latest-release.json b/test/fixtures/gitpeji-latest-release.json new file mode 100644 index 0000000..c2251d8 --- /dev/null +++ b/test/fixtures/gitpeji-latest-release.json @@ -0,0 +1,15 @@ +{ + "id": 17, + "tag_name": "v1.0.0", + "target_commitish": "main", + "name": "Alta Proxy Tool v1.0.0", + "body": "Initial GitPeji release", + "draft": false, + "prerelease": false, + "created_at": "2026-08-19T12:30:00Z", + "published_at": "2026-08-19T12:34:56Z", + "html_url": "https://git.pejicorp.com/peji/Alta-Proxy-Tool/releases/tag/v1.0.0", + "tarball_url": "https://git.pejicorp.com/peji/Alta-Proxy-Tool/archive/v1.0.0.tar.gz", + "zipball_url": "https://git.pejicorp.com/peji/Alta-Proxy-Tool/archive/v1.0.0.zip", + "assets": [] +} diff --git a/test/proxy-launch.test.js b/test/proxy-launch.test.js index b95307e..5a33273 100644 --- a/test/proxy-launch.test.js +++ b/test/proxy-launch.test.js @@ -65,7 +65,7 @@ test('exports the proxy manager module', () => { assert.doesNotThrow(() => loadModule()); }); -test('launches the approved helper directly with exact argv and shell disabled', () => { +test('launches the approved helper directly in a visible interactive console', () => { const { manager, calls } = createHarness(); const result = manager.launchProxy(validRequest()); @@ -76,7 +76,7 @@ test('launches the approved helper directly with exact argv and shell disabled', { shell: false, detached: true, - stdio: 'ignore', + stdio: 'inherit', windowsHide: false } ]]); @@ -97,6 +97,8 @@ test('passes username punctuation literally in argv without invoking a shell', ( assert.equal(calls[0][1][3], username); assert.equal(calls[0][2].shell, false); + assert.equal(calls[0][2].stdio, 'inherit'); + assert.notEqual(calls[0][2].stdio, 'ignore'); }); test('rejects the CRLF calc.exe reproducer in every structured input', () => { diff --git a/test/update-policy.test.js b/test/update-policy.test.js index 956f61c..9a24a4c 100644 --- a/test/update-policy.test.js +++ b/test/update-policy.test.js @@ -3,6 +3,8 @@ const assert = require('node:assert/strict'); const test = require('node:test'); const { EventEmitter } = require('node:events'); +const fs = require('node:fs'); +const path = require('node:path'); const { LATEST_RELEASE_URL, @@ -30,6 +32,24 @@ async function rejectsWithCode(promise, code) { }); } +test('accepts a live-shape GitPeji v-tag and normalizes it to bare semver', async () => { + const fixture = fs.readFileSync( + path.join(__dirname, 'fixtures', 'gitpeji-latest-release.json'), + 'utf8', + ); + + const result = await checkForUpdate({ + currentVersion: '0.9.0', + request: async () => response(fixture), + platform: 'win32', + arch: 'x64', + }); + + assert.equal(result.status, 'update-available'); + assert.equal(result.latestVersion, '1.0.0'); + assert.equal(result.releaseName, 'Alta Proxy Tool v1.0.0'); +}); + test('valid update returns only sanitized, check-only metadata', async () => { let requestOptions; const request = async (options) => { @@ -148,7 +168,20 @@ test('redirects and response host drift are rejected', async () => { }); test('invalid or non-strict semver fails closed', async () => { - const invalidVersions = ['v1.2.3', '1.2', '01.2.3', '1.2.3.4', 'latest']; + const invalidVersions = [ + 'V1.2.3', + 'vv1.2.3', + ' v1.2.3', + 'v1.2.3 ', + 'v1.2', + 'v01.2.3', + 'v1.2.3.4', + 'vlatest', + '1.2', + '01.2.3', + '1.2.3.4', + 'latest', + ]; for (const tag_name of invalidVersions) { await rejectsWithCode(