Address comments from Copilot

This commit is contained in:
Yigit Colakoglu 2025-10-21 19:25:10 +02:00
parent 127e5c3c03
commit b4494595d6
6 changed files with 27 additions and 49 deletions

View File

@ -47,10 +47,10 @@ Plugin.Encrypt({
- `algorithm`: Encryption algorithm to use
- `"aes-256-cbc"` (default): AES-256 in CBC mode
- `"aes-256-gcm"`: AES-256 in GCM mode (authenticated encryption)
- `"aes-256-ecb"`: AES-256 in ECB mode (not recommended)
- `ttl`: Time-to-live for cached passwords in seconds (default: 7 days)
- `message`: Default message shown on encrypted pages
- `encryptedFolders`: Configure folder-level encryption
- Key length is automatically inferred from the algorithm (e.g., 256-bit = 32 bytes)
- `encryptedFolders`: Object mapping folder paths to passwords or configuration objects for folder-level encryption
- `ttl`: Time-to-live for cached passwords in seconds (default: 604800 = 7 days, set to 0 for session-only)
- `message`: Message to be displayed in the decryption page
## How Configuration Works

View File

@ -98,6 +98,7 @@ const decryptWithPassword = async (
if (showError) throw new Error("decryption-failed")
return false
} catch (decryptError) {
console.error("Decryption failed:", decryptError)
if (showError) showLoading(container, false)
if (showError) throw new Error("decryption-failed")
return false
@ -136,7 +137,6 @@ const decryptWithPassword = async (
}
function updateTitle(container: HTMLElement | null) {
console.log(container)
if (container) {
const span = container.querySelector(".article-title-icon") as HTMLElement
if (span) {

View File

@ -44,6 +44,7 @@ const fetchContentCache: Map<FullSlug, Element[]> = new Map()
const contextWindowWords = 30
const numSearchResults = 8
const numTagResults = 5
const RENDER_DELAY_MS = 100
const tokenizeTerm = (term: string) => {
const tokens = term.split(/\s+/).filter((t) => t.trim() !== "")
@ -395,7 +396,7 @@ async function setupSearch(searchElement: Element, currentSlug: FullSlug, data:
(a, b) => b.innerHTML.length - a.innerHTML.length,
)
highlights[0]?.scrollIntoView({ block: "start" })
await new Promise((resolve) => setTimeout(resolve, 100))
await new Promise((resolve) => setTimeout(resolve, RENDER_DELAY_MS))
dispatchRenderEvent(previewInner)
}

View File

@ -29,7 +29,7 @@ export const Description: QuartzTransformerPlugin<Partial<Options>> = (userOpts)
return [
() => {
return async (tree: HTMLRoot, file) => {
if (file.data?.encrypted && file.data.encryptionConfig) {
if (file.data.encryptionConfig) {
file.data.description =
file.data.encryptionConfig.message ||
i18n(ctx.cfg.configuration.locale).components.encryption.encryptedDescription

View File

@ -143,16 +143,22 @@ function getConfigurationForPath(
/**
* Validates the plugin configuration
*/
function validateConfig(config: PluginConfig): void {
function validateConfig(config: EncryptionConfig, file: VFile | null = null): void {
let suffixedPath = ""
if (file && file.data && file.data.relativePath) {
suffixedPath = `(in file: ${file.data.relativePath})`
}
if (!SUPPORTED_ALGORITHMS.includes(config.algorithm)) {
throw new Error(
`[EncryptPlugin] Unsupported encryption algorithm: ${config.algorithm}. ` +
`Supported algorithms: ${SUPPORTED_ALGORITHMS.join(", ")}`,
`Supported algorithms: ${SUPPORTED_ALGORITHMS.join(", ")} ${suffixedPath}`,
)
}
if (config.ttl <= 0) {
throw new Error(`[EncryptPlugin] TTL must be a positive number`)
if (config.ttl < 0) {
throw new Error(`[EncryptPlugin] TTL cannot be negative. ${suffixedPath}`)
}
}
@ -168,9 +174,6 @@ export const Encrypt: QuartzTransformerPlugin<PluginOptions> = (userOpts) => {
encryptedFolders: userOpts?.encryptedFolders ?? {},
}
// Validate configuration
validateConfig(pluginConfig)
// Pre-process folder configurations for efficient lookup
const folderConfigs = createFolderConfigHierarchy(pluginConfig.encryptedFolders, pluginConfig)
@ -241,6 +244,8 @@ export const Encrypt: QuartzTransformerPlugin<PluginOptions> = (userOpts) => {
if (!config) {
return
}
// Validate configuration
validateConfig(config, file)
file.data.encryptionConfig = config
file.data.hash = await hashString(config.password)

View File

@ -1,7 +1,4 @@
// =============================================================================
// TYPES AND INTERFACES
// =============================================================================
export const SUPPORTED_ALGORITHMS = ["aes-256-cbc", "aes-256-gcm", "aes-256-ecb"] as const
export const SUPPORTED_ALGORITHMS = ["aes-256-cbc", "aes-256-gcm"] as const
export type SupportedEncryptionAlgorithm = (typeof SUPPORTED_ALGORITHMS)[number]
@ -88,7 +85,7 @@ export function base64Encode(data: string): string {
return Buffer.from(data).toString("base64")
} else {
// Browser environment
return btoa(unescape(encodeURIComponent(data)))
return btoa(encodeURIComponent(data))
}
}
@ -98,16 +95,17 @@ export function base64Decode(data: string): string {
return Buffer.from(data, "base64").toString()
} else {
// Browser environment
return decodeURIComponent(escape(atob(data)))
return decodeURIComponent(atob(data))
}
}
// Utility functions for array buffer conversions
export function hexToArrayBuffer(hex: string): ArrayBuffer {
if (!hex) return new ArrayBuffer(0)
const bytes = new Uint8Array(hex.length / 2)
for (let i = 0; i < bytes.length; i++) {
bytes[i] = parseInt(hex.substr(i * 2, 2), 16)
bytes[i] = parseInt(hex.substring(i * 2, i * 2 + 2), 16)
}
return bytes.buffer
}
@ -209,8 +207,7 @@ export async function encryptContent(
}
// Generate random salt for encryption
const initializationVector =
algorithm === "aes-256-ecb" ? new Uint8Array(16) : crypto.getRandomValues(new Uint8Array(16)) // Zero IV for ECB simulation
const initializationVector = crypto.getRandomValues(new Uint8Array(16))
// Create encryption hash and derive key
const encryptionHashData = await hashString(password)
@ -251,16 +248,6 @@ export async function encryptContent(
key,
contentBuffer,
)
} else if (algorithm === "aes-256-ecb") {
// ECB simulation using CBC with zero IV
encryptedBuffer = await crypto.subtle.encrypt(
{
name: "AES-CBC",
iv: initializationVector, // Zero IV for ECB simulation
},
key,
contentBuffer,
)
} else {
throw new Error("Unsupported algorithm: " + algorithm)
}
@ -274,10 +261,7 @@ export async function encryptContent(
const result: EncryptionResult = {
encryptedContent: arrayBufferToHex(encryptedBuffer),
encryptionSalt: encryptionHashData.salt,
}
if (algorithm !== "aes-256-ecb") {
result.iv = arrayBufferToHex(initializationVector.buffer)
iv: arrayBufferToHex(initializationVector.buffer as ArrayBuffer),
}
if (authTag) {
@ -339,18 +323,6 @@ export async function decryptContent(
key,
ciphertext,
)
} else if (config.algorithm === "aes-256-ecb") {
// ECB simulation using CBC with zero IV
const zeroInitializationVector = new ArrayBuffer(16)
decryptedBuffer = await crypto.subtle.decrypt(
{
name: "AES-CBC",
iv: zeroInitializationVector,
},
key,
ciphertext,
)
} else {
throw new Error("Unsupported algorithm: " + config.algorithm)
}