-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Inject secrets into templates command (#158)
Fix #153
- Loading branch information
Showing
9 changed files
with
193 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,3 +12,7 @@ bundle | |
!.yarn/releases | ||
!.yarn/sdks | ||
!.yarn/versions | ||
|
||
# Testing files | ||
template.txt | ||
output.txt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import fs from 'fs'; | ||
import { getVaultSecret, initVaultSecrets } from '../modules/database'; | ||
|
||
interface InjectOpts { | ||
in: string; | ||
out: string; | ||
} | ||
|
||
const parse = (template: string) => { | ||
let result = /{{(.*?)}}/g.exec(template); | ||
const arr = []; | ||
let firstPos; | ||
|
||
while (result) { | ||
firstPos = result.index; | ||
if (firstPos !== 0) { | ||
arr.push(template.substring(0, firstPos)); | ||
template = template.slice(firstPos); | ||
} | ||
|
||
arr.push(result[0]); | ||
template = template.slice(result[0].length); | ||
result = /{{(.*?)}}/g.exec(template); | ||
} | ||
|
||
if (template) { | ||
arr.push(template); | ||
} | ||
|
||
return arr; | ||
}; | ||
|
||
const compile = (template: string) => { | ||
const ast = parse(template); | ||
let fnStr = ''; | ||
|
||
ast.map((t) => { | ||
if (t.startsWith('{{') && t.endsWith('}}')) { | ||
const key = t.split(/{{|}}/).filter(Boolean)[0].trim(); | ||
if (key.startsWith('dl://')) { | ||
fnStr += getVaultSecret(key); | ||
return; | ||
} | ||
} | ||
fnStr += t; | ||
}); | ||
|
||
return fnStr; | ||
}; | ||
|
||
export const runInject = async (options: InjectOpts) => { | ||
const { in: inputFilePath, out: outputFilePath } = options; | ||
|
||
await initVaultSecrets(); | ||
|
||
if (inputFilePath) { | ||
const input = fs.readFileSync(inputFilePath, 'utf8'); | ||
|
||
outputContent(compile(input), outputFilePath); | ||
return; | ||
} | ||
|
||
let stdin = ''; | ||
|
||
process.stdin.on('readable', () => { | ||
const chunk = process.stdin.read() as string; | ||
if (chunk !== null) { | ||
stdin += chunk; | ||
} | ||
}); | ||
|
||
process.stdin.on('end', () => { | ||
outputContent(compile(stdin.trim()), outputFilePath); | ||
}); | ||
}; | ||
|
||
const outputContent = (output: string, outputFilePath?: string) => { | ||
if (outputFilePath) { | ||
fs.writeFileSync(outputFilePath, output); | ||
} else { | ||
console.log(output); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export * from './connect'; | ||
export * from './connectAndPrepare'; | ||
export * from './reset'; | ||
export * from './vaultSecrets'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { connectAndPrepare } from './connectAndPrepare'; | ||
import { | ||
VaultSecrets, | ||
BackupEditTransaction, | ||
AuthentifiantTransactionContent, | ||
SecureNoteTransactionContent, | ||
ParsedPath, | ||
} from '../../types'; | ||
import { beautifySecrets, parsePath } from '../../utils'; | ||
import { decryptTransactions } from '../crypto'; | ||
|
||
let vaultSecrets: VaultSecrets | undefined = undefined; | ||
|
||
export const initVaultSecrets = async () => { | ||
if (vaultSecrets) { | ||
return; | ||
} | ||
|
||
const { secrets, db } = await connectAndPrepare({}); | ||
|
||
const transactions = db | ||
.prepare( | ||
`SELECT * | ||
FROM transactions | ||
WHERE login = ? | ||
AND action = 'BACKUP_EDIT' | ||
AND (type = 'AUTHENTIFIANT' OR type = 'SECURENOTE') | ||
` | ||
) | ||
.bind(secrets.login) | ||
.all() as BackupEditTransaction[]; | ||
|
||
const credentials = transactions.filter((transaction) => transaction.type === 'AUTHENTIFIANT'); | ||
const notes = transactions.filter((transaction) => transaction.type === 'SECURENOTE'); | ||
|
||
const decryptedCredentials = await decryptTransactions<AuthentifiantTransactionContent>(credentials, secrets); | ||
const decryptedNotes = await decryptTransactions<SecureNoteTransactionContent>(notes, secrets); | ||
|
||
vaultSecrets = beautifySecrets({ credentials: decryptedCredentials, notes: decryptedNotes }); | ||
}; | ||
|
||
export const getVaultSecret = (path: string): string => { | ||
if (!vaultSecrets) { | ||
throw new Error('Vault secrets not initialized'); | ||
} | ||
|
||
const parsedPath = parsePath(path); | ||
|
||
return findVaultSecret(vaultSecrets, parsedPath); | ||
}; | ||
|
||
export const findVaultSecret = (vaultSecrets: VaultSecrets, parsedPath: ParsedPath): string => { | ||
if (parsedPath.title) { | ||
vaultSecrets.credentials = vaultSecrets.credentials.filter( | ||
(credential) => credential.title === parsedPath.title | ||
); | ||
vaultSecrets.notes = vaultSecrets.notes.filter((note) => note.title === parsedPath.title); | ||
} | ||
|
||
if (vaultSecrets.credentials.length === 0 && vaultSecrets.notes.length === 0) { | ||
throw new Error(`No matching secret found for "${parsedPath.secretId ?? parsedPath.title ?? ''}"`); | ||
} | ||
|
||
const secretToRender: Record<string, any> = | ||
vaultSecrets.credentials.length > 0 ? vaultSecrets.credentials[0] : vaultSecrets.notes[0]; | ||
|
||
if (parsedPath.field) { | ||
if (!secretToRender[parsedPath.field]) { | ||
throw new Error( | ||
`No matching field found for "${parsedPath.field}" in "${ | ||
parsedPath.secretId ?? parsedPath.title ?? '' | ||
}"` | ||
); | ||
} | ||
return String(secretToRender[parsedPath.field]); | ||
} | ||
|
||
return JSON.stringify(secretToRender); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters