Browse Source

fix(auto-update): harden compatible version fallback

Alvin Unreal 2 months ago
parent
commit
e4ac0e7a77

+ 48 - 0
src/hooks/auto-update-checker/checker.test.ts

@@ -246,5 +246,53 @@ describe('auto-update-checker/checker', () => {
 
 
       globalThis.fetch = originalFetch;
       globalThis.fetch = originalFetch;
     });
     });
+
+    test('treats unparseable current version as unsafe for auto-update', async () => {
+      const originalFetch = globalThis.fetch;
+      globalThis.fetch = mock(async () =>
+        Response.json({
+          latest: '2.0.0',
+        }),
+      ) as never;
+
+      const { getLatestCompatibleVersion } = await import(
+        `./checker?test=${importCounter++}`
+      );
+
+      const result = await getLatestCompatibleVersion('^1.0.0');
+
+      expect(result).toEqual({
+        latestVersion: null,
+        latestMajorVersion: '2.0.0',
+        blockedByMajor: true,
+      });
+
+      globalThis.fetch = originalFetch;
+    });
+
+    test('fallback dist-tags never return lower-major versions as compatible', async () => {
+      const originalFetch = globalThis.fetch;
+      globalThis.fetch = mock(async (url: string) => {
+        if (url.includes('/-/package/')) {
+          return Response.json({ latest: '1.5.0' });
+        }
+
+        return new Response(null, { status: 503 });
+      }) as never;
+
+      const { getLatestCompatibleVersion } = await import(
+        `./checker?test=${importCounter++}`
+      );
+
+      const result = await getLatestCompatibleVersion('2.0.0');
+
+      expect(result).toEqual({
+        latestVersion: null,
+        latestMajorVersion: null,
+        blockedByMajor: false,
+      });
+
+      globalThis.fetch = originalFetch;
+    });
   });
   });
 });
 });

+ 6 - 3
src/hooks/auto-update-checker/checker.ts

@@ -382,9 +382,9 @@ export async function getLatestCompatibleVersion(
   if (!current) {
   if (!current) {
     const latestVersion = await getLatestVersion(channel);
     const latestVersion = await getLatestVersion(channel);
     return {
     return {
-      latestVersion,
+      latestVersion: null,
       latestMajorVersion: latestVersion,
       latestMajorVersion: latestVersion,
-      blockedByMajor: false,
+      blockedByMajor: latestVersion !== null,
     };
     };
   }
   }
 
 
@@ -446,9 +446,12 @@ async function getCompatibleFromDistTags(
     distTags.latest,
     distTags.latest,
   ]);
   ]);
   const blockedByMajor = latestMajorVersion !== null;
   const blockedByMajor = latestMajorVersion !== null;
+  const parsedLatest = latestVersion ? parseVersion(latestVersion) : null;
+  const compatibleLatestVersion =
+    parsedLatest?.major === current.major ? latestVersion : null;
 
 
   return {
   return {
-    latestVersion,
+    latestVersion: compatibleLatestVersion,
     latestMajorVersion,
     latestMajorVersion,
     blockedByMajor,
     blockedByMajor,
   };
   };

+ 4 - 4
src/hooks/auto-update-checker/index.test.ts

@@ -153,7 +153,7 @@ describe('auto-update-checker/index', () => {
     checkerMocks.getCachedVersion.mockImplementation(() => '0.9.1');
     checkerMocks.getCachedVersion.mockImplementation(() => '0.9.1');
     checkerMocks.getLatestCompatibleVersion.mockImplementation(async () => ({
     checkerMocks.getLatestCompatibleVersion.mockImplementation(async () => ({
       latestVersion: '0.9.11',
       latestVersion: '0.9.11',
-      latestMajorVersion: '0.9.11',
+      latestMajorVersion: null,
       blockedByMajor: false,
       blockedByMajor: false,
     }));
     }));
 
 
@@ -201,7 +201,7 @@ describe('auto-update-checker/index', () => {
     checkerMocks.getCachedVersion.mockImplementation(() => '0.9.1');
     checkerMocks.getCachedVersion.mockImplementation(() => '0.9.1');
     checkerMocks.getLatestCompatibleVersion.mockImplementation(async () => ({
     checkerMocks.getLatestCompatibleVersion.mockImplementation(async () => ({
       latestVersion: '0.9.11',
       latestVersion: '0.9.11',
-      latestMajorVersion: '0.9.11',
+      latestMajorVersion: null,
       blockedByMajor: false,
       blockedByMajor: false,
     }));
     }));
 
 
@@ -236,7 +236,7 @@ describe('auto-update-checker/index', () => {
     checkerMocks.getCachedVersion.mockImplementation(() => '0.9.1');
     checkerMocks.getCachedVersion.mockImplementation(() => '0.9.1');
     checkerMocks.getLatestCompatibleVersion.mockImplementation(async () => ({
     checkerMocks.getLatestCompatibleVersion.mockImplementation(async () => ({
       latestVersion: '0.9.11',
       latestVersion: '0.9.11',
-      latestMajorVersion: '0.9.11',
+      latestMajorVersion: null,
       blockedByMajor: false,
       blockedByMajor: false,
     }));
     }));
     cacheMocks.preparePackageUpdate.mockImplementation(() => null);
     cacheMocks.preparePackageUpdate.mockImplementation(() => null);
@@ -270,7 +270,7 @@ describe('auto-update-checker/index', () => {
     checkerMocks.getCachedVersion.mockImplementation(() => '0.9.1');
     checkerMocks.getCachedVersion.mockImplementation(() => '0.9.1');
     checkerMocks.getLatestCompatibleVersion.mockImplementation(async () => ({
     checkerMocks.getLatestCompatibleVersion.mockImplementation(async () => ({
       latestVersion: '0.9.11',
       latestVersion: '0.9.11',
-      latestMajorVersion: '0.9.11',
+      latestMajorVersion: null,
       blockedByMajor: false,
       blockedByMajor: false,
     }));
     }));