Skip to content

Commit

Permalink
feat: decrypt aes-256 encrypted urls in canarist config
Browse files Browse the repository at this point in the history
  • Loading branch information
ZauberNerd committed Dec 10, 2020
1 parent 4bf6911 commit 0a3ed2a
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 7 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,4 +207,3 @@ Examples:
- [ ] allow to unpin dependencies which would otherwise be installed multiple times (for example: packages of both repositories have "webpack" as a dependency, one has it pinned to "4.40.0" and the other has a semver range "^4.30.0". If webpack@latest is at 4.40.0 we have no issues, but if webpack has a new release, say 4.41.0, we now have two versions installed).
- [ ] allow to configure which dotfiles should be copied / merged into the root (currently only ".npmrc" will be merged)
- [ ] implement a `--no-install` or `--no-commands` flag, as a simple way to "link" two repositories and debug inside them?
- [ ] encrypted urls in project config to prevent leakage of internal URLs?
24 changes: 24 additions & 0 deletions src/__tests__/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,30 @@ describe('normalize config', () => {
]);
});

it('should decrypt encrypted repository url', () => {
process.env.CANARIST_ENCRYPTION_KEY =
'C7DA20FEF7B7E363043C75F7D580A86E3997F0A58A15F9977814F310835DC2FB';
// $ canarist -r enc:G6VSEW8lxBM3OYn7E3k4yGH61ExqKxx/rsUtKS/h8GU=
const config = normalizeConfig(
{
_: [],
repository: 'enc:G6VSEW8lxBM3OYn7E3k4yGH61ExqKxx/rsUtKS/h8GU=',
},
null
);

expect(config.repositories).toEqual([
{
url: 'https://github.com/a/repo.git',
branch: 'master',
directory: 'repo',
commands: ['yarn test'],
},
]);

process.env.CANARIST_ENCRYPTION_KEY = undefined;
});

it('should normalize arguments for multiple repositories', () => {
// $ canarist -r a-repo -r b-repo
const config = normalizeConfig(
Expand Down
46 changes: 40 additions & 6 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createDecipheriv } from 'crypto';
import { mkdtempSync } from 'fs';
import { tmpdir } from 'os';
import { basename, join, resolve } from 'path';
Expand Down Expand Up @@ -72,25 +73,58 @@ function tryParse(input: string): Record<string, unknown> {
}
}

/**
* Decrypts a URL that has been encrypted using:
* `echo -n "https://url" | openssl enc -a -pbkdf2 -nosalt -iv 0 -e -aes-256-cbc -K $CANARIST_ENCRYPTION_KEY`
* The URL must start with `enc:$base64value` and the env var CANARIST_ENCRYPTION_KEY
* must be set to the encryption key.
* Note: We're using -nosalt and an iv value of 0 here, because we don't care if
* two identical URLs create the same output.
*/
function decryptUrl(input: string): string {
const prefix = 'enc:';
if (!input.startsWith(prefix)) {
return input;
}

const keyString = `${process.env.CANARIST_ENCRYPTION_KEY}`;
if (!/^[0-9a-fA-F]{64}$/.test(keyString)) {
throw new Error(`CANARIST_ENCRYPTION_KEY is not 64 hex characters`);
}

const key = Buffer.from(keyString, 'hex');
delete process.env.CANARIST_ENCRYPTION_KEY;

const decipher = createDecipheriv('aes-256-cbc', key, Buffer.alloc(16, 0));

return (
decipher.update(input.slice(prefix.length), 'base64', 'utf8') +
decipher.final('utf8')
);
}

function normalizeRepository(
input: string | RepositoryArguments | Partial<RepositoryConfig>
): RepositoryConfig {
const defaultBranch = 'master';
const defaultCommands = ['yarn test'];

if (typeof input === 'string') {
const { name } = gitUrlParse(input);
const url = decryptUrl(input);
const { name } = gitUrlParse(url);
return {
url: input,
url,
branch: name === '.' ? '' : defaultBranch,
directory: name === '.' ? basename(resolve(name)) : name,
commands: defaultCommands,
};
}

const url = Array.isArray((input as RepositoryArguments)._)
? (input as RepositoryArguments)._[0]
: input.url;
const url = decryptUrl(
Array.isArray((input as RepositoryArguments)._)
? (input as RepositoryArguments)._[0]
: input.url
);
const { name } = gitUrlParse(url);
const directory =
typeof input.directory === 'string'
Expand All @@ -115,7 +149,7 @@ function normalizeRepository(
: defaultCommands;

return {
url: url,
url,
branch,
directory,
commands,
Expand Down

0 comments on commit 0a3ed2a

Please sign in to comment.