Hemmelig.app/cli.js
bjarneo 4323c6bb6e
feat: change the application to use url hash for the encryption key (#167)
* feat: change the application to use url hash for the encryption key

the application will still support use url params for a while, but will be removed at the 4.0 breaking release

* chore: update the hemmelig.ts file with the output

* chore: bump the version
2023-03-22 07:30:11 +01:00

181 lines
4.8 KiB
JavaScript

#!/usr/bin/env node
import meow from 'meow';
import fetch from 'node-fetch';
import YAML from 'yaml';
import { generateKey, encrypt } from './src/shared/helpers/crypto.js';
// Adding this hack to make it work for pkg
// https://github.com/vercel/pkg/issues/1291#issuecomment-1360586986
let url = import.meta.url;
// Allow rewriting `import.meta.url` to `__dirname` when bundling with esbuild
if (!url.startsWith('file://')) url = new URL(`file://${import.meta.url}`).toString();
// end hack
const cli = meow(
`
Usage
$ hemmelig <secret>
Options
--title, -t The secret title
--password, -p The password to protect the secret
--lifetime, -l The lifetime of the secret
--maxViews, -m The max views of the secret
--cidr, -c Provide the IP or CIDR range
--expire, -e Burn the secret only after the expire time
--url, -u If you have your own instance of the Hemmelig.app
--output, -o Present the result as json|yaml. Example: --output=json
Examples
$ hemmelig "my super secret" --password=1337
[*] Hemmelig.app URL: https://hemmelig.app/secret/thesecretid#encryption_key=myencryptionkey
# Pipe data to the hemmelig cli
$ cat mysecret.txt | hemmelig
[*] Hemmelig.app URL: https://hemmelig.app/secret/thesecretid2#encryption_key=myencryptionkey2
# Different output
$ hemmelig "I am secret" -o=json
{"encryptionKey":"9LiWq3iMAF0IkQs1tecOxbYKFesEnTN9","secretId":"manageable_CEsgWtxEaNNbwld6PjwyF1bQaiy4jQl9","url":"https://hemmelig.app/secret/manageable_CEsgWtxEaNNbwld6PjwyF1bQaiy4jQl9#encryption_key=9LiWq3iMAF0IkQs1tecOxbYKFesEnTN9"}
`,
{
importMeta: { url },
flags: {
title: {
type: 'string',
alias: 't',
default: '',
},
password: {
type: 'string',
alias: 'p',
default: '',
},
lifetime: {
type: 'number',
alias: 'l',
default: 3600,
},
maxViews: {
type: 'number',
alias: 'm',
default: 1,
},
cidr: {
type: 'string',
alias: 'c',
default: '',
},
expire: {
type: 'boolean',
alias: 'e',
default: false,
},
url: {
type: 'string',
alias: 'u',
default: 'https://hemmelig.app',
},
output: {
type: 'string',
alias: 'o',
default: 'text',
},
},
}
);
const createSecret = async (data = {}) => {
const options = {
method: 'POST',
cache: 'no-cache',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
},
};
try {
const data = await fetch(`${cli.flags.url}/api/secret`, options);
const json = await data.json();
return { ...json, statusCode: data.status };
} catch (error) {
console.error(error);
return { error: 'Failed to create your secret' };
}
};
const getSecretURL = (encryptionKey, secretId) =>
`${cli.flags.url}/secret/${secretId}#encryption_key=${encryptionKey}`;
const createOutput = (encryptionKey, secretId) => {
const url = getSecretURL(encryptionKey, secretId);
if (cli.flags.output === 'json') {
return JSON.stringify({
encryptionKey,
secretId,
url,
});
} else if (cli.flags.output === 'yaml') {
return YAML.stringify({
encryptionKey,
secretId,
url,
});
}
return `[*] Hemmelig.app URL: ${url}`;
};
const submit = async (secret, values) => {
if (!secret) {
console.error('No secret set');
return;
}
const userEncryptionKey = generateKey();
const body = {
text: encrypt(secret, userEncryptionKey),
files: [],
title: encrypt(values.title, userEncryptionKey),
password: values.password,
ttl: values.lifetime,
allowedIp: values.cidr,
preventBurn: values.expire,
maxViews: values.maxViews,
};
const json = await createSecret(body);
console.log(createOutput(userEncryptionKey, json.id));
};
async function getSecretText() {
const [secret] = cli.input;
if (secret) {
return secret;
}
const data = await new Promise((res) => {
process.stdin.once('data', function (data) {
res(data.toString().trim());
});
});
return data;
}
// Execute the CLI
(async function execute() {
const secret = await getSecretText();
submit(secret, cli.flags);
})();