Lines 176-216javascript
177 const matches = findEncryptionTemplates(result);
178 const pending = matches.filter(m => !hasNestedUnencryptedTemplate(m)
179 && !String(m.attrs).includes('data-cipher="aes-256-gcm-v2"')
180 && !String(m.attrs).includes('data-cipher="aes-256-gcm-v1"'));
181 if (pending.length === 0)
183 // Process in reverse order to preserve positions
184 for (let i = pending.length - 1; i >= 0; i--) {
185 const match = pending[i];
186 const attrs = String(match.attrs);
187 const passwordMatch = attrs.match(/\bdata-password\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s>]+))/);
190 const passwordHash = passwordMatch[1] || passwordMatch[2] || passwordMatch[3];
193 // Replace raw SHA-256 hash with PBKDF2 verification hash
194 const { hash: verifyHash, salt: verifySalt } = deriveVerificationHash(passwordHash);
195 const encryptedPayload = encryptPayload(match.content, passwordHash);
196 const replacedAttrs = attrs.replace(/\bdata-password\s*=\s*(?:"[^"]+"|'[^']+'|[^\s>]+)/, `data-password="${verifyHash}"`);
197 const newAttrs = `${replacedAttrs} data-verify-salt="${verifySalt.toString('base64')}" data-cipher="${AES_MARKER}"`;
198 const newBlock = `<template${newAttrs}>${encryptedPayload}</template>`;
199 result = result.slice(0, match.openStart) + newBlock + result.slice(match.closeEnd);
203 return { content: result, changed };
205function hasUnencryptedTemplate(matches) {
206 return matches.some((match) => {
207 const attrs = String(match.attrs);
208 const hasAesMarker = /\bdata-cipher\s*=\s*"aes-256-gcm-v[12]"/.test(attrs);
209 return !hasAesMarker;
213 const options = parseArgs(process.argv.slice(2));
214 if (options.helpRequested) {