From fa450d65b821b05b73473f872effe20860245333 Mon Sep 17 00:00:00 2001 From: shivam singh Date: Sat, 26 Jul 2025 21:54:26 +0530 Subject: [PATCH] fix: added validation method for validation correct json file and throw appropriate errors fix: check for err.message type before displaying to user --- src/entrypoints/options/options.js | 3 +- src/utils/bookmarksync.js | 2 +- src/utils/github-bookmarks-loader.js | 46 ++++++++++++++++++++++++++-- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/entrypoints/options/options.js b/src/entrypoints/options/options.js index 521a9a6..86efd17 100644 --- a/src/entrypoints/options/options.js +++ b/src/entrypoints/options/options.js @@ -74,7 +74,8 @@ async function init() { } updateButton('error'); - showConnectionMessage('error', error.message); + const errorMessage = typeof error.message === 'string' ? error.message : JSON.stringify(error); + showConnectionMessage('error', errorMessage); } finally { isChecking = false; cancelCurrentCheck = false; diff --git a/src/utils/bookmarksync.js b/src/utils/bookmarksync.js index eb4ffab..49bca4a 100644 --- a/src/utils/bookmarksync.js +++ b/src/utils/bookmarksync.js @@ -121,7 +121,7 @@ async function validateBookmarkFiles(bookmarkFiles) { const validator = await validate(BOOKMARK_SCHEMA_URI); for (const bookmarkFile of bookmarkFiles) { - const validationResult = validator(bookmarkFile); + const validationResult = validator(bookmarkFile.data); if (!validationResult.valid) { const name = bookmarkFile.name || ''; throw new BookmarksDataNotValidError(`The bookmarks file with name '${name}' is not valid`); diff --git a/src/utils/github-bookmarks-loader.js b/src/utils/github-bookmarks-loader.js index ab88631..1f3b948 100644 --- a/src/utils/github-bookmarks-loader.js +++ b/src/utils/github-bookmarks-loader.js @@ -2,7 +2,7 @@ import {Octokit} from '@octokit/rest'; import {retry} from '@octokit/plugin-retry'; import optionsStorage from '@/utils/options-storage.js'; import { - AuthenticationError, DataNotFoundError, BookmarkSourceNotConfiguredError, RepositoryNotFoundError, + AuthenticationError, DataNotFoundError, BookmarkSourceNotConfiguredError, RepositoryNotFoundError, BookmarksDataNotValidError, } from '@/utils/errors.js'; class GitHubBookmarksLoader { @@ -30,6 +30,34 @@ class GitHubBookmarksLoader { return bookmarkFiles; } + extractFileName(url) { + // Github only gives url of files under a folder, so we need to extract file name from url + // example github url: https://api.github.com/repos/this-is-shivamsingh/bookmarks/contents/test_folder_3%2Fa.json + // Then full file name is after `contents` part + const fileName = url.split('contents').pop(); + + // Test_folder_3%2Fa.json -> needs get converted to test_folder_3/a.json + return decodeURIComponent(fileName.slice(1, fileName.length)); + } + + validateFile(file) { + const fileName = this.extractFileName(file.url); + + if (!fileName.endsWith('.json') && !fileName.endsWith('.json')) { + throw new BookmarksDataNotValidError(`The bookmarks file with name ${fileName} contain invalid file extension for JSON file`); + } + + if (typeof file.data !== 'string') { + throw new BookmarksDataNotValidError(`The bookmarks file with name '${fileName}' does not contain valid JSON`); + } + + if (file.data.length === 0) { + throw new BookmarksDataNotValidError(`The bookmarks file with name '${fileName}' does not any content`); + } + + return true; + } + async loadFromSource(sourceId, {force = false, cacheEtag = true} = {}) { const options = await optionsStorage.getAll(); const repo = options[`${sourceId}_repo`]; @@ -99,7 +127,21 @@ class GitHubBookmarksLoader { bookmarkFileResponses = [response]; } - const bookmarkFiles = bookmarkFileResponses.map(file => JSON.parse(file.data)); + const bookmarkFiles = bookmarkFileResponses.map(file => { + const fileName = this.extractFileName(file.url); + // When user gives fileName, then that can be either of .txt, .png etc + // in those cases a validation is required to send a correct message + this.validateFile(file); + + try { + return { + name: decodeURIComponent(fileName), + data: JSON.parse(file.data), + }; + } catch { + throw new BookmarksDataNotValidError(`The bookmarks file with name '${fileName}' does not contain valid JSON`); + } + }); if (cacheEtag) { const etagPropertyName = `${sourceId}_etag`;