@typinghare/trick 1.0.7 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -23
- package/dist/app.d.ts +1 -1
- package/dist/app.js +65 -111
- package/dist/config.d.ts +71 -14
- package/dist/config.js +61 -24
- package/dist/encrypt.d.ts +68 -14
- package/dist/encrypt.js +79 -30
- package/dist/error.d.ts +30 -0
- package/dist/error.js +69 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/passphrase.d.ts +13 -0
- package/dist/passphrase.js +26 -0
- package/eslint.config.js +41 -0
- package/package.json +11 -8
- package/src/app.ts +85 -160
- package/src/config.ts +93 -29
- package/src/encrypt.ts +89 -44
- package/src/error.ts +82 -0
- package/src/index.ts +1 -2
- package/src/passphrase.ts +39 -0
- package/test/resources/really.json +2 -2
- package/test/resources/task.yml +3 -4
- package/test/trick.config.json +13 -0
- package/.wander/jameschan312.cn@gmail.com/.idea/codeStyles/Project.xml +0 -52
- package/.wander/jameschan312.cn@gmail.com/.idea/codeStyles/codeStyleConfig.xml +0 -5
- package/.wander/jameschan312.cn@gmail.com/.idea/jsLibraryMappings.xml +0 -6
- package/.wander/jameschan312.cn@gmail.com/.idea/misc.xml +0 -6
- package/.wander/jameschan312.cn@gmail.com/.idea/modules.xml +0 -8
- package/.wander/jameschan312.cn@gmail.com/.idea/prettier.xml +0 -6
- package/.wander/jameschan312.cn@gmail.com/.idea/trick.iml +0 -14
- package/.wander/jameschan312.cn@gmail.com/.idea/vcs.xml +0 -6
- package/.wander/jameschan312.cn@gmail.com/.idea/webResources.xml +0 -14
- package/dist/constant.d.ts +0 -2
- package/dist/constant.js +0 -3
- package/dist/secret.d.ts +0 -5
- package/dist/secret.js +0 -14
- package/src/constant.ts +0 -4
- package/src/secret.ts +0 -14
- package/trick.config.json +0 -20
package/dist/encrypt.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import { execa } from 'execa';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import fsExtra from 'fs-extra';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
export async function encryptFile(srcFilePath, destFilePath,
|
|
4
|
+
import { FailToDecryptFileError, FailToEncryptFileError } from './error.js';
|
|
5
|
+
/**
|
|
6
|
+
* Encrypts a file using OpenSSL with AES-256-CBC and PBKDF2 key derivation.
|
|
7
|
+
*
|
|
8
|
+
* This function checks whether the source file exists, constructs an OpenSSL
|
|
9
|
+
* command, ensures the destination directory exists, and then executes the
|
|
10
|
+
* encryption command.
|
|
11
|
+
*
|
|
12
|
+
* @param srcFilePath The path to the source file that needs to be encrypted.
|
|
13
|
+
* @param destFilePath The path where the encrypted file will be saved.
|
|
14
|
+
* @param passphrase The passphrase used for encryption.
|
|
15
|
+
* @param iteration_count The number of iterations to use for PBKDF2.
|
|
16
|
+
* @returns Resolves when the file is successfully encrypted.
|
|
17
|
+
* @throws {FailToEncryptFileError} If the source file does not exist or if
|
|
18
|
+
* OpenSSL returns an error during encryption.
|
|
19
|
+
* @throws {FailToDecryptFileError} If an unknown error occurs during
|
|
20
|
+
* encryption.
|
|
21
|
+
*/
|
|
22
|
+
export async function encryptFile(srcFilePath, destFilePath, passphrase, iteration_count) {
|
|
23
23
|
if (!(await fsExtra.pathExists(srcFilePath))) {
|
|
24
24
|
throw new FailToEncryptFileError(srcFilePath);
|
|
25
25
|
}
|
|
@@ -30,13 +30,13 @@ export async function encryptFile(srcFilePath, destFilePath, secret, iteration_c
|
|
|
30
30
|
'-salt',
|
|
31
31
|
'-pbkdf2',
|
|
32
32
|
'-iter',
|
|
33
|
-
iteration_count,
|
|
33
|
+
Number(iteration_count),
|
|
34
34
|
'-in',
|
|
35
35
|
srcFilePath,
|
|
36
36
|
'-out',
|
|
37
37
|
destFilePath,
|
|
38
38
|
'-pass',
|
|
39
|
-
`'pass:${
|
|
39
|
+
`'pass:${passphrase}'`,
|
|
40
40
|
].join(' ');
|
|
41
41
|
await fsExtra.ensureDir(path.dirname(destFilePath));
|
|
42
42
|
try {
|
|
@@ -44,14 +44,32 @@ export async function encryptFile(srcFilePath, destFilePath, secret, iteration_c
|
|
|
44
44
|
}
|
|
45
45
|
catch (err) {
|
|
46
46
|
if (typeof err == 'object' && err && Object.hasOwn(err, 'stderr')) {
|
|
47
|
-
|
|
47
|
+
const error = err;
|
|
48
|
+
throw new FailToEncryptFileError(srcFilePath, error.stderr);
|
|
48
49
|
}
|
|
49
50
|
else {
|
|
50
51
|
throw new FailToDecryptFileError(srcFilePath, 'Unknown error when encrypting the file.');
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Decrypts a file using OpenSSL with AES-256-CBC and PBKDF2 key derivation.
|
|
57
|
+
*
|
|
58
|
+
* This function checks whether the encrypted file exists, constructs an OpenSSL
|
|
59
|
+
* decryption command, ensures the destination directory exists, and then
|
|
60
|
+
* executes the decryption command.
|
|
61
|
+
*
|
|
62
|
+
* @param srcFilePath The path where the decrypted file will be saved.
|
|
63
|
+
* @param destFilePath The path to the encrypted source file.
|
|
64
|
+
* @param passphrase The passphrase used for decryption.
|
|
65
|
+
* @param iteration_count The number of iterations used for PBKDF2.
|
|
66
|
+
* @returns Resolves when the file is successfully decrypted.
|
|
67
|
+
* @throws {FailToDecryptFileError} If the encrypted file does not exist or if
|
|
68
|
+
* OpenSSL returns an error during decryption.
|
|
69
|
+
* @throws {FailToDecryptFileError} If an unknown error occurs during
|
|
70
|
+
* decryption.
|
|
71
|
+
*/
|
|
72
|
+
export async function decryptFile(srcFilePath, destFilePath, passphrase, iteration_count) {
|
|
55
73
|
if (!(await fsExtra.pathExists(destFilePath))) {
|
|
56
74
|
throw new FailToDecryptFileError(destFilePath);
|
|
57
75
|
}
|
|
@@ -63,13 +81,13 @@ export async function decryptFile(srcFilePath, destFilePath, secret, iteration_c
|
|
|
63
81
|
'-salt',
|
|
64
82
|
'-pbkdf2',
|
|
65
83
|
'-iter',
|
|
66
|
-
iteration_count,
|
|
84
|
+
Number(iteration_count),
|
|
67
85
|
'-in',
|
|
68
86
|
destFilePath,
|
|
69
87
|
'-out',
|
|
70
88
|
srcFilePath,
|
|
71
89
|
'-pass',
|
|
72
|
-
`'pass:${
|
|
90
|
+
`'pass:${passphrase}'`,
|
|
73
91
|
].join(' ');
|
|
74
92
|
await fsExtra.ensureDir(path.dirname(srcFilePath));
|
|
75
93
|
try {
|
|
@@ -77,24 +95,55 @@ export async function decryptFile(srcFilePath, destFilePath, secret, iteration_c
|
|
|
77
95
|
}
|
|
78
96
|
catch (err) {
|
|
79
97
|
if (typeof err == 'object' && err && Object.hasOwn(err, 'stderr')) {
|
|
80
|
-
|
|
98
|
+
const error = err;
|
|
99
|
+
throw new FailToDecryptFileError(srcFilePath, error.stderr);
|
|
81
100
|
}
|
|
82
101
|
else {
|
|
83
102
|
throw new FailToDecryptFileError(srcFilePath, 'Unknown error when decrypting the file.');
|
|
84
103
|
}
|
|
85
104
|
}
|
|
86
105
|
}
|
|
87
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Encrypts multiple files using OpenSSL with AES-256-CBC and PBKDF2 key
|
|
108
|
+
* derivation.
|
|
109
|
+
*
|
|
110
|
+
* For each source file path provided, this function constructs the destination
|
|
111
|
+
* file path by appending `.enc`, then calls `encryptFile` and logs the
|
|
112
|
+
* operation.
|
|
113
|
+
*
|
|
114
|
+
* @param srcFilePaths An array of file paths to be encrypted.
|
|
115
|
+
* @param destDir The directory where the encrypted files will be saved.
|
|
116
|
+
* @param passphrase The passphrase used for encryption.
|
|
117
|
+
* @param iteration_count The number of iterations to use for PBKDF2.
|
|
118
|
+
* @returns Resolves when all files are successfully encrypted.
|
|
119
|
+
* @throws {FailToEncryptFileError} If any file fails to encrypt.
|
|
120
|
+
*/
|
|
121
|
+
export async function encryptFiles(srcFilePaths, destDir, passphrase, iteration_count) {
|
|
88
122
|
for (const srcFilePath of srcFilePaths) {
|
|
89
123
|
const destFilePath = path.join(destDir, srcFilePath + '.enc');
|
|
90
|
-
await encryptFile(srcFilePath, destFilePath,
|
|
124
|
+
await encryptFile(srcFilePath, destFilePath, passphrase, iteration_count);
|
|
91
125
|
console.log(`[ENCRYPTED] ${srcFilePath} -> ${destFilePath}`);
|
|
92
126
|
}
|
|
93
127
|
}
|
|
94
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Decrypts multiple files using OpenSSL with AES-256-CBC and PBKDF2 key
|
|
130
|
+
* derivation.
|
|
131
|
+
*
|
|
132
|
+
* For each source file path provided, this function assumes the corresponding
|
|
133
|
+
* encrypted file has the `.enc` extension and calls `decryptFile`, logging the
|
|
134
|
+
* operation.
|
|
135
|
+
*
|
|
136
|
+
* @param srcFilePaths An array of original file paths that were encrypted.
|
|
137
|
+
* @param destDir The directory containing the encrypted files.
|
|
138
|
+
* @param passphrase The passphrase used for decryption.
|
|
139
|
+
* @param iteration_count The number of iterations used for PBKDF2.
|
|
140
|
+
* @returns Resolves when all files are successfully decrypted.
|
|
141
|
+
* @throws {FailToDecryptFileError} If any file fails to decrypt.
|
|
142
|
+
*/
|
|
143
|
+
export async function decryptFiles(srcFilePaths, destDir, passphrase, iteration_count) {
|
|
95
144
|
for (const srcFilePath of srcFilePaths) {
|
|
96
145
|
const destFilePath = path.join(destDir, srcFilePath + '.enc');
|
|
97
|
-
await decryptFile(srcFilePath, destFilePath,
|
|
146
|
+
await decryptFile(srcFilePath, destFilePath, passphrase, iteration_count);
|
|
98
147
|
console.log(`[DECRYPTED] ${destFilePath} -> ${srcFilePath}`);
|
|
99
148
|
}
|
|
100
149
|
}
|
package/dist/error.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export declare class WriteConfigError extends Error {
|
|
2
|
+
constructor(err: any);
|
|
3
|
+
}
|
|
4
|
+
export declare class ReadConfigError extends Error {
|
|
5
|
+
constructor(err: any);
|
|
6
|
+
}
|
|
7
|
+
export declare class TargetNotFoundError extends Error {
|
|
8
|
+
readonly targetName: string;
|
|
9
|
+
constructor(targetName: string);
|
|
10
|
+
}
|
|
11
|
+
export declare class FailToEncryptFileError extends Error {
|
|
12
|
+
readonly srcFilePath: string;
|
|
13
|
+
readonly opensslErrMessage?: string | undefined;
|
|
14
|
+
constructor(srcFilePath: string, opensslErrMessage?: string | undefined);
|
|
15
|
+
}
|
|
16
|
+
export declare class FailToDecryptFileError extends Error {
|
|
17
|
+
readonly destFilePath: string;
|
|
18
|
+
readonly opensslErrMessage?: string | undefined;
|
|
19
|
+
constructor(destFilePath: string, opensslErrMessage?: string | undefined);
|
|
20
|
+
}
|
|
21
|
+
export declare class PassphraseFileNotFoundError extends Error {
|
|
22
|
+
readonly passphraseFilePath: string;
|
|
23
|
+
constructor(passphraseFilePath: string);
|
|
24
|
+
}
|
|
25
|
+
export declare class PassphraseNotFoundError extends Error {
|
|
26
|
+
readonly passphraseFilePath: string;
|
|
27
|
+
readonly targetName: string;
|
|
28
|
+
constructor(passphraseFilePath: string, targetName: string);
|
|
29
|
+
}
|
|
30
|
+
export declare function resolve_error(err: any): void;
|
package/dist/error.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
export class WriteConfigError extends Error {
|
|
3
|
+
constructor(err) {
|
|
4
|
+
super(`Fail to write the configuration file. ${err}`);
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class ReadConfigError extends Error {
|
|
8
|
+
constructor(err) {
|
|
9
|
+
super(`Fail to read the configuration file: ${err}`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class TargetNotFoundError extends Error {
|
|
13
|
+
targetName;
|
|
14
|
+
constructor(targetName) {
|
|
15
|
+
super(`Target not found: ${targetName}`);
|
|
16
|
+
this.targetName = targetName;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class FailToEncryptFileError extends Error {
|
|
20
|
+
srcFilePath;
|
|
21
|
+
opensslErrMessage;
|
|
22
|
+
constructor(srcFilePath, opensslErrMessage) {
|
|
23
|
+
super(`Fail to encrypt source file: ${srcFilePath}`);
|
|
24
|
+
this.srcFilePath = srcFilePath;
|
|
25
|
+
this.opensslErrMessage = opensslErrMessage;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export class FailToDecryptFileError extends Error {
|
|
29
|
+
destFilePath;
|
|
30
|
+
opensslErrMessage;
|
|
31
|
+
constructor(destFilePath, opensslErrMessage) {
|
|
32
|
+
super(`Fail to decrypt destination file: ${destFilePath}`);
|
|
33
|
+
this.destFilePath = destFilePath;
|
|
34
|
+
this.opensslErrMessage = opensslErrMessage;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export class PassphraseFileNotFoundError extends Error {
|
|
38
|
+
passphraseFilePath;
|
|
39
|
+
constructor(passphraseFilePath) {
|
|
40
|
+
super(`Passphrase file not found: ${passphraseFilePath}`);
|
|
41
|
+
this.passphraseFilePath = passphraseFilePath;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export class PassphraseNotFoundError extends Error {
|
|
45
|
+
passphraseFilePath;
|
|
46
|
+
targetName;
|
|
47
|
+
constructor(passphraseFilePath, targetName) {
|
|
48
|
+
super(`Passphrase for target ${targetName} is not found in the passphrase file: ${passphraseFilePath}`);
|
|
49
|
+
this.passphraseFilePath = passphraseFilePath;
|
|
50
|
+
this.targetName = targetName;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function resolve_error(err) {
|
|
54
|
+
if (!(err instanceof Error)) {
|
|
55
|
+
console.error(`Unknown error: ${err}`);
|
|
56
|
+
process.exit(2);
|
|
57
|
+
}
|
|
58
|
+
if (err.message) {
|
|
59
|
+
console.log(chalk.red(err.message));
|
|
60
|
+
}
|
|
61
|
+
if (err instanceof FailToEncryptFileError ||
|
|
62
|
+
err instanceof FailToDecryptFileError) {
|
|
63
|
+
if (err.opensslErrMessage) {
|
|
64
|
+
console.error(chalk.red(err.opensslErrMessage));
|
|
65
|
+
}
|
|
66
|
+
console.error(chalk.yellow('Make sure the file exists and you have enough permission to access it.'));
|
|
67
|
+
}
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Config } from './config.js';
|
|
2
|
+
/**
|
|
3
|
+
* Retrieves the passphrase from the passphrase file.
|
|
4
|
+
*
|
|
5
|
+
* First, gets the passphrase object from the passphrase file path specified in
|
|
6
|
+
* the configuration object. Second, gets and returns the passphrase associated
|
|
7
|
+
* with the given target name.
|
|
8
|
+
*
|
|
9
|
+
* @param config The configuration to use.
|
|
10
|
+
* @param targetName The name of the target.
|
|
11
|
+
* @return The passphrase associated with the target name.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getPassphrase(config: Config, targetName: string): string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import fsExtra from 'fs-extra';
|
|
2
|
+
import { PassphraseFileNotFoundError, PassphraseNotFoundError, } from './error.js';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
/**
|
|
5
|
+
* Retrieves the passphrase from the passphrase file.
|
|
6
|
+
*
|
|
7
|
+
* First, gets the passphrase object from the passphrase file path specified in
|
|
8
|
+
* the configuration object. Second, gets and returns the passphrase associated
|
|
9
|
+
* with the given target name.
|
|
10
|
+
*
|
|
11
|
+
* @param config The configuration to use.
|
|
12
|
+
* @param targetName The name of the target.
|
|
13
|
+
* @return The passphrase associated with the target name.
|
|
14
|
+
*/
|
|
15
|
+
export function getPassphrase(config, targetName) {
|
|
16
|
+
const passphraseFilePath = config.passphrase_file_path.replaceAll(/~/g, os.homedir());
|
|
17
|
+
if (!fsExtra.existsSync(passphraseFilePath)) {
|
|
18
|
+
throw new PassphraseFileNotFoundError(passphraseFilePath);
|
|
19
|
+
}
|
|
20
|
+
const passphraseObject = fsExtra.readJsonSync(passphraseFilePath);
|
|
21
|
+
const passphrase = passphraseObject[targetName] || null;
|
|
22
|
+
if (passphrase === null) {
|
|
23
|
+
throw new PassphraseNotFoundError(passphraseFilePath, targetName);
|
|
24
|
+
}
|
|
25
|
+
return passphrase;
|
|
26
|
+
}
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import pluginJsonc from 'eslint-plugin-jsonc';
|
|
2
|
+
import jsoncParser from 'jsonc-eslint-parser';
|
|
3
|
+
|
|
4
|
+
export default [
|
|
5
|
+
{
|
|
6
|
+
files: ['**/*.json'],
|
|
7
|
+
languageOptions: {
|
|
8
|
+
parser: jsoncParser
|
|
9
|
+
},
|
|
10
|
+
plugins: {
|
|
11
|
+
jsonc: pluginJsonc
|
|
12
|
+
},
|
|
13
|
+
rules: {
|
|
14
|
+
'jsonc/indent': ['error', 4],
|
|
15
|
+
'jsonc/key-spacing': ['error', { beforeColon: false, afterColon: true }],
|
|
16
|
+
'jsonc/comma-dangle': ['error', 'never'],
|
|
17
|
+
'jsonc/object-curly-spacing': ['error', 'always'] },
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
files: ['**/*.ts', '**/*.tsx'],
|
|
21
|
+
languageOptions: {
|
|
22
|
+
parser: tsParser,
|
|
23
|
+
parserOptions: {
|
|
24
|
+
project: ['./tsconfig.json'],
|
|
25
|
+
sourceType: 'module',
|
|
26
|
+
ecmaVersion: 'latest'
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
plugins: {
|
|
30
|
+
'@typescript-eslint': tsPlugin
|
|
31
|
+
},
|
|
32
|
+
rules: {
|
|
33
|
+
'@typescript-eslint/no-unused-vars': ['warn'],
|
|
34
|
+
'@typescript-eslint/no-explicit-any': ['warn'],
|
|
35
|
+
'@typescript-eslint/explicit-function-return-type': ['off'],
|
|
36
|
+
semi: ['error', 'always'],
|
|
37
|
+
quotes: ['error', 'single']
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
];
|
|
41
|
+
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typinghare/trick",
|
|
3
|
-
"description": "Save credential files to remote safely.",
|
|
4
|
-
"version": "
|
|
3
|
+
"description": "Save credential files to remote safely and easily.",
|
|
4
|
+
"version": "2.0.1",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"type": "module",
|
|
@@ -15,15 +15,18 @@
|
|
|
15
15
|
"homepage": "https://github.com/typinghare/trick",
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"chalk": "^5.
|
|
19
|
-
"commander": "^
|
|
20
|
-
"execa": "^9.3
|
|
21
|
-
"fs-extra": "^11.
|
|
18
|
+
"chalk": "^5.4.1",
|
|
19
|
+
"commander": "^14.0.0",
|
|
20
|
+
"execa": "^9.5.3",
|
|
21
|
+
"fs-extra": "^11.3.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/fs-extra": "^11.0.4",
|
|
25
|
-
"
|
|
26
|
-
"
|
|
25
|
+
"eslint": "^9.27.0",
|
|
26
|
+
"eslint-plugin-jsonc": "^2.20.1",
|
|
27
|
+
"jsonc-eslint-parser": "^2.4.0",
|
|
28
|
+
"prettier": "^3.5.3",
|
|
29
|
+
"typescript": "^5.8.3"
|
|
27
30
|
},
|
|
28
31
|
"prettier": {
|
|
29
32
|
"trailingComma": "es5",
|