Skip to content

Commit b760ba4

Browse files
committed
simplified salt file path validation
Now, only an absolute path to the salt file or a string value is permitted.
1 parent 046833c commit b760ba4

8 files changed

Lines changed: 26 additions & 72 deletions

File tree

src/config/Config.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,8 @@ export namespace Config {
3030
static: string[];
3131
reference: string[];
3232
}
33-
type StringBasedSalt = { source: 'STRING'; value: string };
34-
type FileBasedSalt = {
35-
source: 'FILE';
36-
validator_regex?: string;
37-
value: {
38-
win32?: string;
39-
darwin?: string;
40-
linux?: string;
41-
};
42-
};
33+
export type StringBasedSalt = { source: 'STRING'; value: string };
34+
export type FileBasedSalt = { source: 'FILE'; value: string; validator_regex?: string };
4335
export interface CoreConfiguration {
4436
meta: { id: string }
4537
source: ColumnMap;

src/config/configStore.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ interface ConfigStorePaths {
4747
config: string;
4848
appConfig: string;
4949
backupConfig: string;
50+
salt?: string;
5051
}
5152

5253
export class ConfigStore {

src/config/loadConfig.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { validateConfigFile } from './validateConfig';
2020
import { loadSaltFile } from './loadSaltFile';
2121
import { generateConfigHash } from './utils';
2222

23-
import { getSaltFilePath, attemptToReadTOMLData } from './utils';
23+
import { attemptToReadTOMLData } from './utils';
2424
import type { Config } from './Config';
2525
import Debug from 'debug';
2626
const log = Debug('CID:loadConfig');
@@ -35,10 +35,10 @@ type LoadConfigResult =
3535

3636

3737
type LoadConfigInput = {
38-
configPath: string,
39-
algorithmId: string,
40-
usingUI?: boolean,
41-
validateConfig?: boolean
38+
configPath: string;
39+
algorithmId: string;
40+
usingUI?: boolean;
41+
validateConfig?: boolean;
4242
}
4343
// Main entry point for loading a config file.
4444
// returns:
@@ -96,6 +96,8 @@ export function loadConfig({ configPath, algorithmId, usingUI=false, validateCon
9696
configData.algorithm.columns.reference = configData.algorithm.columns.reference.sort();
9797
configData.algorithm.columns.static = configData.algorithm.columns.static.sort();
9898

99+
// TODO: check whether embedded salt file path is provided, config should override embedded.
100+
99101
// check if we need to inject the salt data into the config
100102
// if not, the config loading is finished
101103
if (configData.algorithm.salt.source === 'STRING') {
@@ -122,15 +124,15 @@ export function loadConfig({ configPath, algorithmId, usingUI=false, validateCon
122124
return {
123125
success: false,
124126
isSaltFileError: true,
125-
error: `Invalid salt file: '${getSaltFilePath(saltFilePath)}'`,
127+
error: `Invalid salt file: '${saltFilePath}'`,
126128
// send the existing config alongside so if this config is the backup one, error messages
127129
// can still be loaded
128130
config: configData,
129131
};
130132
}
131133

132134
// replace the "FILE" with "STRING" amd embed the salt data
133-
configData.algorithm.salt = configData.algorithm.salt as unknown as Config.CoreConfiguration["algorithm"]["salt"];
135+
configData.algorithm.salt = configData.algorithm.salt as unknown as Config.StringBasedSalt;
134136
configData.algorithm.salt.source = 'STRING';
135137
configData.algorithm.salt.value = saltData;
136138

src/config/loadSaltFile.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import path from 'node:path';
1818
import Debug from 'debug';
1919
const log = Debug('CID:loadSaltFile');
2020

21-
import { getSaltFilePath, attemptToReadFileData } from './utils';
21+
import { attemptToReadFileData } from './utils';
2222
import type { Config } from './Config';
2323

2424
// the encoding used for the salt file
@@ -29,23 +29,19 @@ const DEFAULT_VALIDATOR_REGEXP: RegExp =
2929
/-----BEGIN PGP PUBLIC KEY BLOCK-----[A-Za-z0-9+/=\s]+-----END PGP PUBLIC KEY BLOCK-----/;
3030

3131
interface LoadSaltFileInput {
32-
saltFilePath: Config.FileConfiguration['algorithm']['salt']['value'];
32+
saltFilePath: string;
3333
validatorRegexp?: RegExp;
3434
}
3535

3636
// Attempts to load and clean up the salt file data
3737
export function loadSaltFile({ saltFilePath, validatorRegexp = DEFAULT_VALIDATOR_REGEXP }: LoadSaltFileInput) {
38-
// resolve the salt file path from the config & platform
39-
const fullSaltFilePath = getSaltFilePath(saltFilePath);
38+
log('Attempting to load salt file from ', path.resolve(saltFilePath));
4039

41-
log('Attempting to load salt file from ', path.resolve(fullSaltFilePath));
42-
// return null;
4340
// TODO: potentially clean up line endings and whitespace here
44-
const saltData = attemptToReadFileData(fullSaltFilePath, SALT_FILE_ENCODING);
41+
const saltData = attemptToReadFileData(saltFilePath, SALT_FILE_ENCODING);
4542
if (!saltData) return null;
4643

4744
// check if the structure is correct for the file
48-
// /-----BEGIN PGP PUBLIC KEY BLOCK-----[A-Za-z0-9+/=\s]+-----END PGP PUBLIC KEY BLOCK-----/
4945
const CHECK_RX = new RegExp(validatorRegexp);
5046

5147
if (!CHECK_RX.test(saltData)) {

src/config/utils.ts

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -56,28 +56,6 @@ export function attemptToReadTOMLData<T>(filePath: string, encoding: fs.Encoding
5656
}
5757
}
5858

59-
// takes into consideration the platform and the type of value provided by the config
60-
// to return an actual, absolute salt file path
61-
export function getSaltFilePath(saltValueConfig: Config.CoreConfiguration['algorithm']['salt']['value']) {
62-
// if the value is a string always use it
63-
if (typeof saltValueConfig === 'string') return saltValueConfig;
64-
65-
// no salt path means the config does not have our platform
66-
/* istanbul ignore next */
67-
if (process.platform in saltValueConfig === false) {
68-
throw new Error(`Unsupported platform for salt file location: ${process.platform}`);
69-
}
70-
const platform = process.platform as keyof typeof saltValueConfig;
71-
const platformSaltPath = saltValueConfig[platform];
72-
73-
if (!platformSaltPath) throw new Error(`Salt path not provided for platform: ${process.platform}`);
74-
75-
// token replacement
76-
return path.resolve(
77-
platformSaltPath.replaceAll('$HOME', os.homedir()).replaceAll('$APPDATA', appDataLocation()),
78-
);
79-
}
80-
8159
// Returns the prefered Application Data storage location based on the operating system
8260
export function appDataLocation() {
8361
switch (process.platform) {

src/config/validateConfig.ts

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,6 @@ const checkAlgorithm = (algorithm: Config.CoreConfiguration['algorithm'], source
210210
isOneOf('[algorithm].salt.source', ['FILE', 'STRING'], algorithm.salt.source);
211211
if (exists) return exists;
212212

213-
let check: string | undefined;
214213
if (algorithm.salt.source === 'STRING') {
215214
return (
216215
isString('[algorithm].salt.value', algorithm.salt.value) ||
@@ -219,22 +218,12 @@ const checkAlgorithm = (algorithm: Config.CoreConfiguration['algorithm'], source
219218
}
220219

221220
if (algorithm.salt.source === 'FILE') {
222-
check =
223-
isObject('[algorithm].salt.value', algorithm.salt.value) ||
221+
return (
222+
isString('[algorithm].salt.value', algorithm.salt.value) ||
223+
isNotEmptyString('[algorithm].salt.value', algorithm.salt.value) ||
224224
isOptional('[algorithm].salt.validator_regex', algorithm.salt.validator_regex, isString) ||
225-
isOptional('[algorithm].salt.validator_regex', algorithm.salt!.validator_regex, isRegexp) ||
226-
isOptional('[algorithm].salt.value.win32', algorithm.salt.value!.win32, isString) ||
227-
isOptional('[algorithm].salt.value.darwin', algorithm.salt.value!.darwin, isString) ||
228-
isOptional('[algorithm].salt.value.linux', algorithm.salt.value!.linux, isString);
229-
230-
if (check) return check;
231-
232-
// at least one of [win32, darwin, linux] must be provided
233-
if (
234-
Object.keys(algorithm.salt.value!).filter((v) => ['win32', 'darwin', 'linux'].includes(v)).length === 0
235-
) {
236-
return '[algorithm].salt.value must specify at least one win32, darwin, or linux path value.';
237-
}
225+
isOptional('[algorithm].salt.validator_regex', algorithm.salt!.validator_regex, isRegexp)
226+
)
238227
}
239228
};
240229

src/hashing/base.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ export type makeHasherFunction = (config: Config.CoreConfiguration['algorithm'])
2424

2525
export abstract class BaseHasher {
2626
config: Config.CoreConfiguration['algorithm'];
27-
saltValue: Config.CoreConfiguration['algorithm']['salt']['value'];
27+
saltValue: Config.CoreConfiguration["algorithm"]["salt"]["value"];
2828

2929
constructor(config: Config.CoreConfiguration['algorithm']) {
3030
this.config = config;
3131

3232
// at this point the salt data should be injected into the config
33-
if (config.salt.source.toLowerCase() !== 'string') {
33+
if (config.salt.source !== "STRING") {
3434
throw new Error(
3535
'only embedded salt values supported for hashing -- import & save the config if file support is desired here',
3636
);
@@ -45,7 +45,7 @@ export abstract class BaseHasher {
4545
generateHashForValue(stringValue: string, algorithm: string = 'sha256') {
4646
let hashDigest = crypto
4747
.createHash(algorithm)
48-
.update(this.saltValue as string)
48+
.update(this.config.salt.value)
4949
.update(stringValue)
5050
.digest();
5151
return base32.encode(hashDigest);

tests/config/loadConfig.test.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,7 @@ test('loadConfig salt', () => {
4949
const TEST_FILE_PATH = join(tmpdir(), 'salt-config.json');
5050
const cfg = JSON.parse(readFileSync(join(FILES_PATH, 'test-salt-loading-config.json'), 'utf-8'));
5151

52-
cfg.algorithm.salt.value.darwin = SALT_FILE_PATH;
53-
cfg.algorithm.salt.value.win32 = SALT_FILE_PATH;
54-
cfg.algorithm.salt.value.linux = SALT_FILE_PATH;
52+
cfg.algorithm.salt.value = SALT_FILE_PATH;
5553
cfg.meta.signature = generateConfigHash(cfg);
5654

5755
writeFileSync(TEST_FILE_PATH, JSON.stringify(cfg), 'utf-8');
@@ -70,9 +68,7 @@ test('loadConfig salt error', () => {
7068
const TEST_FILE_PATH = join(tmpdir(), 'salt-config.json');
7169
const cfg = JSON.parse(readFileSync(join(FILES_PATH, 'test-salt-loading-config.json'), 'utf-8'));
7270

73-
cfg.algorithm.salt.value.darwin = SALT_FILE_PATH;
74-
cfg.algorithm.salt.value.win32 = SALT_FILE_PATH;
75-
cfg.algorithm.salt.value.linux = SALT_FILE_PATH;
71+
cfg.algorithm.salt.value = SALT_FILE_PATH;
7672
cfg.meta.signature = generateConfigHash(cfg);
7773

7874
writeFileSync(TEST_FILE_PATH, JSON.stringify(cfg), 'utf-8');

0 commit comments

Comments
 (0)