Skip to content

Commit fee0bbc

Browse files
committed
šŸ› fix(registries): guard schemaV1 manifest JSON.parse against malformed data
A malformed or missing v1Compatibility from a registry threw a raw SyntaxError/TypeError that silently dropped the container from the watch cycle. Fully optional-chain the access and wrap the parse in try/catch with a descriptive error.
1 parent 8e00dfd commit fee0bbc

2 files changed

Lines changed: 34 additions & 4 deletions

File tree

ā€Žapp/registries/Registry.test.tsā€Ž

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -745,13 +745,38 @@ describe('getImageManifestDigest', () => {
745745
);
746746
});
747747

748-
test('should handle malformed JSON in schemaVersion 1 v1Compatibility', async () => {
748+
test('should throw descriptive error for schemaVersion 1 with malformed (non-JSON) v1Compatibility', async () => {
749749
const registryMocked = createMockedRegistry();
750750
registryMocked.callRegistry = () => ({
751751
schemaVersion: 1,
752-
history: [{ v1Compatibility: 'not valid json' }],
752+
history: [{ v1Compatibility: 'not valid json {{{' }],
753753
});
754-
await expect(registryMocked.getImageManifestDigest(imageInput())).rejects.toThrow();
754+
await expect(registryMocked.getImageManifestDigest(imageInput())).rejects.toThrow(
755+
'Failed to parse schemaVersion 1 manifest v1Compatibility',
756+
);
757+
});
758+
759+
test('should throw descriptive error for schemaVersion 1 with missing history array', async () => {
760+
const registryMocked = createMockedRegistry();
761+
registryMocked.callRegistry = () => ({
762+
schemaVersion: 1,
763+
// history is absent — history?.[0]?.v1Compatibility is undefined, JSON.parse(undefined) throws
764+
});
765+
await expect(registryMocked.getImageManifestDigest(imageInput())).rejects.toThrow(
766+
'Failed to parse schemaVersion 1 manifest v1Compatibility',
767+
);
768+
});
769+
770+
test('should throw descriptive error for schemaVersion 1 with missing v1Compatibility field', async () => {
771+
const registryMocked = createMockedRegistry();
772+
registryMocked.callRegistry = () => ({
773+
schemaVersion: 1,
774+
history: [{}],
775+
// v1Compatibility is absent — JSON.parse(undefined) throws
776+
});
777+
await expect(registryMocked.getImageManifestDigest(imageInput())).rejects.toThrow(
778+
'Failed to parse schemaVersion 1 manifest v1Compatibility',
779+
);
755780
});
756781

757782
test('should gracefully handle blob fetch error for legacy manifest config', async () => {

ā€Žapp/registries/Registry.tsā€Ž

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,12 @@ function filterManifestByPlatform(
106106

107107
/** Handle schemaVersion 1 manifests (legacy). */
108108
function handleSchemaV1(response: RegistryManifestResponse): RegistryManifest {
109-
const v1Compat = JSON.parse(response.history?.[0].v1Compatibility);
109+
let v1Compat: { config?: { Image?: string }; created?: string };
110+
try {
111+
v1Compat = JSON.parse(response.history?.[0]?.v1Compatibility);
112+
} catch {
113+
throw new Error('Failed to parse schemaVersion 1 manifest v1Compatibility');
114+
}
110115
return {
111116
digest: v1Compat.config ? v1Compat.config.Image : undefined,
112117
created: v1Compat.created,

0 commit comments

Comments
Ā (0)