Skip to content

Commit

Permalink
Enable strict null checks
Browse files Browse the repository at this point in the history
  • Loading branch information
perry-mitchell committed Apr 7, 2024
1 parent 7e6ec12 commit c6ed701
Show file tree
Hide file tree
Showing 38 changed files with 210 additions and 90 deletions.
2 changes: 1 addition & 1 deletion source/background/services/autoLogin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function getAutoLoginForTab(tabID: number): SearchResult | null {
const register = getRegister();
const key = `tab-${tabID}`;
if (register.has(key)) {
const item = register.get(key).entry;
const item = (register.get(key) as RegisteredItem).entry;
register.delete(key);
return item;
}
Expand Down
5 changes: 3 additions & 2 deletions source/background/services/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { getSyncValue, setSyncValue } from "./storage.js";
import { Configuration, InputButtonType, SyncStorageItem } from "../types.js";
import { naiveClone } from "../../shared/library/clone.js";

const DEFAULTS: Configuration = {
entryIcons: true,
Expand All @@ -9,7 +10,7 @@ const DEFAULTS: Configuration = {
useSystemTheme: true
};

let __lastConfig: Configuration = null;
let __lastConfig: Configuration | null = null;

export function getConfig(): Configuration {
if (!__lastConfig) {
Expand All @@ -24,7 +25,7 @@ export async function initialise() {

export async function updateConfigValue<T extends keyof Configuration>(key: T, value: Configuration[T]): Promise<void> {
const configRaw = await getSyncValue(SyncStorageItem.Configuration);
const config = JSON.parse(configRaw);
const config = configRaw ? JSON.parse(configRaw) : naiveClone(DEFAULTS);
config[key] = value;
__lastConfig = config;
await setSyncValue(SyncStorageItem.Configuration, JSON.stringify(config));
Expand Down
2 changes: 1 addition & 1 deletion source/background/services/desktop/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export async function getVaultsTree(): Promise<VaultsTree> {
...output,
[sourceID]: {
...tree[sourceID],
name: names[sourceID] ?? "Untitled vault"
name: names?.[sourceID] ?? "Untitled vault"
}
};
}, {});
Expand Down
21 changes: 17 additions & 4 deletions source/background/services/desktop/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { decryptPayload, encryptPayload } from "../crypto.js";
import { getLocalValue } from "../storage.js";
import { LocalStorageItem } from "../../types.js";

type OutputType = "body" | "status";
type OutputType = "body" | "status" | undefined;

interface DesktopRequestConfig<O extends OutputType> {
auth?: string | null;
Expand Down Expand Up @@ -45,12 +45,13 @@ export async function sendDesktopRequest<O extends OutputType>(
url = newURL.toString();
} else {
requestConfig.body = JSON.stringify(payload);
Object.assign(requestConfig.headers, {
Object.assign(requestConfig.headers as HeadersInit, {
"Content-Type": "application/json"
});
}
}
if (auth !== null) {
requestConfig.headers = requestConfig.headers || {};
// Request requires encryption, perform setup now
requestConfig.headers["Authorization"] = auth;
if (typeof requestConfig.body === "string") {
Expand All @@ -59,6 +60,12 @@ export async function sendDesktopRequest<O extends OutputType>(
// Encrypt
const privateKey = await getLocalValue(LocalStorageItem.APIPrivateKey);
const publicKey = await getLocalValue(LocalStorageItem.APIServerPublicKey);
if (!privateKey) {
throw new Error("Authenticated request failed: No private key available");
}
if (!publicKey) {
throw new Error("Authenticated request failed: No public key available");
}
requestConfig.body = await encryptPayload(requestConfig.body, privateKey, publicKey);
}
}
Expand All @@ -81,19 +88,25 @@ export async function sendDesktopRequest<O extends OutputType>(
}
// Handle encrypted response
if (resp.headers.get("X-Bcup-API")) {
const components = resp.headers.get("X-Bcup-API").split(",");
const components = resp.headers.get("X-Bcup-API")?.split(",") ?? [];
if (components.includes("enc")) {
const content = await resp.text();
const contentType = resp.headers.get("X-Content-Type") || resp.headers.get("Content-Type") || "text/plain";
// Decrypt
const privateKey = await getLocalValue(LocalStorageItem.APIPrivateKey);
const publicKey = await getLocalValue(LocalStorageItem.APIServerPublicKey);
if (!privateKey) {
throw new Error("Decrypting response failed: No private key available");
}
if (!publicKey) {
throw new Error("Decrypting response failed: No public key available");
}
const rawDecrypted = await decryptPayload(content, publicKey, privateKey);
return /application\/json/.test(contentType) ? JSON.parse(rawDecrypted) : rawDecrypted;
}
}
// Standard, unencrypted response
if (/application\/json/.test(resp.headers.get("Content-Type"))) {
if (/application\/json/.test(resp.headers.get("Content-Type") ?? "")) {
return resp.json();
}
return resp.text();
Expand Down
3 changes: 3 additions & 0 deletions source/background/services/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ import { formatURL } from "../../shared/library/url.js";

export async function openEntryPageInNewTab(_: SearchResult, url: string): Promise<number> {
const tab = await createNewTab(formatURL(url));
if (typeof tab.id !== "number") {
throw new Error("No tab ID for created tab");
}
return tab.id;
}
6 changes: 3 additions & 3 deletions source/background/services/loginMemory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function clearCredentials(id: string): void {
}
if (memory.has("last")) {
const last = memory.get("last");
if (last.credentials.id === id) {
if (last?.credentials.id === id) {
memory.delete("last");
}
}
Expand All @@ -35,7 +35,7 @@ export function getAllCredentials(): Array<UsedCredentials> {

export function getCredentialsForID(id: string): UsedCredentials | null {
const memory = getLoginMemory();
return memory.has(id) ? memory.get(id).credentials : null;
return memory.has(id) ? (memory.get(id) as LoginMemoryItem).credentials : null;
}

export function getLastCredentials(tabID: number): UsedCredentials | null {
Expand All @@ -55,7 +55,7 @@ function getLoginMemory(): ExpiryMap<string, LoginMemoryItem> {
export function stopPromptForID(id: string): void {
const memory = getLoginMemory();
if (memory.has(id)) {
const existing = memory.get(id);
const existing = memory.get(id) as LoginMemoryItem;
memory.set(id, {
...existing,
credentials: {
Expand Down
69 changes: 65 additions & 4 deletions source/background/services/messaging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,12 @@ async function handleMessage(
) {
switch (msg.type) {
case BackgroundMessageType.AuthenticateDesktopConnection: {
const { code } = msg;
if (!code) {
throw new Error("No auth code provided");
}
log("complete desktop authentication");
const publicKey = await authenticateBrowserAccess(msg.code);
const publicKey = await authenticateBrowserAccess(code);
await setLocalValue(LocalStorageItem.APIServerPublicKey, publicKey);
sendResponse({});
break;
Expand All @@ -78,13 +82,19 @@ async function handleMessage(
}
case BackgroundMessageType.ClearSavedCredentials: {
const { credentialsID } = msg;
if (!credentialsID) {
throw new Error("No credentials ID provided");
}
log(`clear saved credentials: ${credentialsID}`);
clearCredentials(credentialsID);
sendResponse({});
break;
}
case BackgroundMessageType.ClearSavedCredentialsPrompt: {
const { credentialsID } = msg;
if (!credentialsID) {
throw new Error("No credentials ID provided");
}
log(`clear saved credentials prompt: ${credentialsID}`);
stopPromptForID(credentialsID);
await sendTabsMessage({
Expand All @@ -95,6 +105,9 @@ async function handleMessage(
}
case BackgroundMessageType.DeleteDisabledDomains: {
const { domains } = msg;
if (!domains) {
throw new Error("No domains list provided");
}
log(`remove disabled domains: ${domains.join(", ")}`);
for (const domain of domains) {
await removeDisabledFlagForDomain(domain);
Expand All @@ -104,6 +117,9 @@ async function handleMessage(
}
case BackgroundMessageType.DisableSavePromptForCredentials: {
const { credentialsID } = msg;
if (!credentialsID) {
throw new Error("No credentials ID provided");
}
log(`disable save prompt for credentials: ${credentialsID}`);
try {
const credentials = getCredentialsForID(credentialsID);
Expand Down Expand Up @@ -208,6 +224,9 @@ async function handleMessage(
break;
}
case BackgroundMessageType.GetSavedCredentialsForID: {
if (!msg.credentialsID) {
throw new Error("No credentials ID provided");
}
const credentials = getCredentialsForID(msg.credentialsID);
sendResponse({
credentials: [credentials]
Expand All @@ -223,13 +242,19 @@ async function handleMessage(
}
case BackgroundMessageType.MarkNotificationRead: {
const { notification } = msg;
if (!notification) {
throw new Error("No notification provided");
}
log(`mark notification read: ${notification}`);
await markNotificationRead(notification);
sendResponse({});
break;
}
case BackgroundMessageType.OpenEntryPage: {
const { autoLogin, entry } = msg;
if (!entry) {
throw new Error("No entry provided");
}
const [url = null] = getEntryURLs(entry.properties, EntryURLType.Login);
if (!url) {
sendResponse({ opened: false });
Expand All @@ -253,6 +278,9 @@ async function handleMessage(
}
case BackgroundMessageType.PromptLockSource: {
const { sourceID } = msg;
if (!sourceID) {
throw new Error("No source ID provided");
}
log(`request lock source: ${sourceID}`);
const locked = await promptSourceLock(sourceID);
sendResponse({
Expand All @@ -262,6 +290,9 @@ async function handleMessage(
}
case BackgroundMessageType.PromptUnlockSource: {
const { sourceID } = msg;
if (!sourceID) {
throw new Error("No source ID provided");
}
log(`request unlock source: ${sourceID}`);
await promptSourceUnlock(sourceID);
sendResponse({});
Expand All @@ -276,6 +307,15 @@ async function handleMessage(
}
case BackgroundMessageType.SaveCredentialsToVault: {
const { sourceID, groupID, entryID = null, entryProperties, entryType = EntryType.Website } = msg;
if (!sourceID) {
throw new Error("No source ID provided");
}
if (!groupID) {
throw new Error("No group ID provided");
}
if (!entryProperties) {
throw new Error("No entry properties provided");
}
if (entryID) {
log(`save credentials to existing entry: ${entryID} (source=${sourceID})`);
await saveExistingEntry(sourceID, groupID, entryID, entryProperties);
Expand All @@ -293,31 +333,52 @@ async function handleMessage(
}
case BackgroundMessageType.SaveUsedCredentials: {
const { credentials } = msg;
if (!credentials) {
throw new Error("No source ID provided");
}
if (!sender.tab?.id) {
throw new Error("No tab ID available for background message");
}
updateUsedCredentials(credentials, sender.tab.id);
sendResponse({});
break;
}
case BackgroundMessageType.SearchEntriesByTerm: {
const searchResults = await searchEntriesByTerm(msg.searchTerm);
const { searchTerm } = msg;
if (!searchTerm) {
throw new Error("No search term provided");
}
const searchResults = await searchEntriesByTerm(searchTerm);
sendResponse({
searchResults
});
break;
}
case BackgroundMessageType.SearchEntriesByURL: {
const searchResults = await searchEntriesByURL(msg.url);
const { url } = msg;
if (!url) {
throw new Error("No URL provided");
}
const searchResults = await searchEntriesByURL(url);
sendResponse({
searchResults
});
break;
}
case BackgroundMessageType.SetConfigurationValue: {
await updateConfigValue(msg.configKey, msg.configValue);
const { configKey, configValue } = msg;
if (!configKey || typeof configValue === "undefined") {
throw new Error("Invalid configuration proivided provided");
}
await updateConfigValue(configKey, configValue);
sendResponse({});
break;
}
case BackgroundMessageType.TrackRecentEntry: {
const { entry } = msg;
if (!entry) {
throw new Error("No entry provided");
}
if (!entry.sourceID) {
throw new Error(`No source ID in entry result: ${entry.id}`);
}
Expand Down
7 changes: 5 additions & 2 deletions source/background/services/tabs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ export async function sendTabsMessage(payload: TabEvent, tabIDs: Array<number> |
await browser.tabs.query({
status: "complete"
})
).map((tab) => tab.id);
).reduce((output: Array<number>, tab) => {
if (!tab.id) return output;
return [...output, tab.id];
}, []);
await Promise.all(
targetTabIDs.map(async (tabID) => {
await browser.tabs.sendMessage(tabID, payload);
browser.tabs.sendMessage(tabID, payload);
})
);
}
3 changes: 2 additions & 1 deletion source/full/components/pages/NotificationsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export function NotificationsPage() {
])]);
setCurrentTab(newTabID);
const key = Object.keys(NOTIFICATIONS).find(nKey => NOTIFICATIONS[nKey][0] === newTabID);
if (!key) return;
updateReadNotifications(key).catch(err => {
console.error(err);
getToaster().show({
Expand All @@ -38,7 +39,7 @@ export function NotificationsPage() {
}, [currentTab, handleTabChange, notifications]);
return (
<Layout title={t("notifications.title")}>
<Tabs onChange={handleTabChange} selectedTabId={currentTab}>
<Tabs onChange={handleTabChange} selectedTabId={currentTab ?? undefined}>
{notifications.map(([nameKey, Component]) => (
<Tab
key={nameKey}
Expand Down
Loading

0 comments on commit c6ed701

Please sign in to comment.