mirror of
https://github.com/TautvydasDerzinskas/Thingport.git
synced 2026-10-21 06:09:56 -05:00
fix: add validation and improve error handling in sync operations
- Hide API keys from error messages by stripping query strings - Validate that category responses contain entries before using them - Distinguish between unreadable and malformed cookie values with proper types
This commit is contained in:
1 parent
27c42fe790
commit
7c3c7ec902
2 files changed
+17
-6
No files matched your search
@@ -47,7 +47,8 @@ async function json(
|
||||
init: { method?: "GET" | "POST"; body?: string; headers?: Record<string, string> } = {},
|
||||
) {
|
||||
const res = await browserFetch(url, { ...init, headers: { Accept: "application/json", ...init.headers } });
|
||||
if (!res.ok) throw new Error(`${url}: HTTP ${res.status}`);
|
||||
// Without the query string, which can carry an API key.
|
||||
if (!res.ok) throw new Error(`${url.split("?")[0]}: HTTP ${res.status}`);
|
||||
return res.json() as Promise<unknown>;
|
||||
}
|
||||
|
||||
@@ -178,7 +179,10 @@ const FETCHERS: Record<string, () => Promise<Entry[]>> = {
|
||||
let failed = false;
|
||||
for (const [provider, fetch] of Object.entries(FETCHERS)) {
|
||||
try {
|
||||
catalog[provider] = await fetch();
|
||||
const entries = await fetch();
|
||||
// A changed payload shape reads as no categories rather than an error.
|
||||
if (!entries.length) throw new Error("no categories in the response");
|
||||
catalog[provider] = entries;
|
||||
console.log(`${provider}: ${catalog[provider].length} categories`);
|
||||
} catch (err) {
|
||||
failed = true;
|
||||
|
||||
@@ -8,13 +8,19 @@ import { apiCall, requireConfig } from "./api";
|
||||
const SITE = "https://thangs.com";
|
||||
const JWT = /^eyJ[\w-]+\.eyJ[\w-]+\.[\w-]+$/;
|
||||
|
||||
async function cookieValue(name: string): Promise<string> {
|
||||
/** The cookie's token, "" when there is none, or undefined when it couldn't be read. */
|
||||
async function cookieValue(name: string): Promise<string | undefined> {
|
||||
let raw: string | undefined;
|
||||
try {
|
||||
const cookie = await chrome.cookies.get({ url: SITE, name });
|
||||
const value = cookie?.value ? decodeURIComponent(cookie.value) : "";
|
||||
raw = (await chrome.cookies.get({ url: SITE, name }))?.value;
|
||||
} catch {
|
||||
return undefined; // Not readable: no reason to think the user signed out.
|
||||
}
|
||||
try {
|
||||
const value = raw ? decodeURIComponent(raw) : "";
|
||||
return JWT.test(value) ? value : "";
|
||||
} catch {
|
||||
return ""; // Unreadable, or malformed percent-encoding: not a token.
|
||||
return ""; // Malformed percent-encoding: not a token.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +29,7 @@ export async function syncThangsSession(): Promise<void> {
|
||||
const config = await requireConfig().catch(() => null);
|
||||
if (!config) return;
|
||||
const [token, refreshToken] = await Promise.all([cookieValue("accessToken"), cookieValue("refreshToken")]);
|
||||
if (token === undefined || refreshToken === undefined) return;
|
||||
// Per Thingport account, so switching the extension to another one sends it there too.
|
||||
const account = `${config.instanceUrl} ${config.email}`;
|
||||
const last = config.lastSyncedThangsSession;
|
||||
|
||||
Reference in new issue
Block a user