Lines 155-195javascript
156 const matches = findEncryptionTemplates(result);
157 const pending = matches.filter(m => !hasNestedUnencryptedTemplate(m)
158 && !String(m.attrs).includes('data-cipher="aes-256-gcm-v2"')
159 && !String(m.attrs).includes('data-cipher="aes-256-gcm-v1"'));
160 if (pending.length === 0)
162 // Process in reverse order to preserve positions
163 for (let i = pending.length - 1; i >= 0; i--) {
164 const match = pending[i];
165 const attrs = String(match.attrs);
166 const passwordMatch = attrs.match(/\bdata-password\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s>]+))/);
169 const passwordHash = passwordMatch[1] || passwordMatch[2] || passwordMatch[3];
172 // Replace raw SHA-256 hash with PBKDF2 verification hash
173 const { hash: verifyHash, salt: verifySalt } = deriveVerificationHash(passwordHash);
174 const encryptedPayload = encryptPayload(match.content, passwordHash);
175 const replacedAttrs = attrs.replace(/\bdata-password\s*=\s*(?:"[^"]+"|'[^']+'|[^\s>]+)/, `data-password="${verifyHash}"`);
176 const newAttrs = `${replacedAttrs} data-verify-salt="${verifySalt.toString('base64')}" data-cipher="${AES_MARKER}"`;
177 const newBlock = `<template${newAttrs}>${encryptedPayload}</template>`;
178 result = result.slice(0, match.openStart) + newBlock + result.slice(match.closeEnd);
182 return { content: result, changed };
184function hasUnencryptedTemplate(matches) {
185 return matches.some((match) => {
186 const attrs = String(match.attrs);
187 const hasAesMarker = /\bdata-cipher\s*=\s*"aes-256-gcm-v[12]"/.test(attrs);
188 return !hasAesMarker;
192 const options = parseArgs(process.argv.slice(2));
193 const targetDir = path.isAbsolute(options.input)
195 : path.resolve(options.cwd, options.input);