Skip to content

Commit 1475af1

Browse files
committed
fixed the path traversal issues
1 parent 5b36226 commit 1475af1

File tree

5 files changed

+47
-46
lines changed

5 files changed

+47
-46
lines changed

packages/contentstack-variants/src/export/variant-entries.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { existsSync, mkdirSync } from 'fs';
22
import { join, resolve } from 'path';
3-
import { FsUtility } from '@contentstack/cli-utilities';
3+
import { FsUtility, sanitizePath } from '@contentstack/cli-utilities';
44

55
import { APIConfig, AdapterType, ExportConfig, LogType } from '../types';
66
import VariantAdapter, { VariantHttpClient } from '../utils/variant-api-adapter';
@@ -25,7 +25,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Exp
2525
},
2626
};
2727
super(Object.assign(config, conf));
28-
this.entriesDirPath = resolve(config.data, config.branchName || '', config.modules.entries.dirName);
28+
this.entriesDirPath = resolve(sanitizePath(config.data), config.branchName || '', sanitizePath(config.modules.entries.dirName));
2929
}
3030

3131
/**
@@ -39,7 +39,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Exp
3939

4040
for (let index = 0; index < entries.length; index++) {
4141
const entry = entries[index];
42-
const variantEntryBasePath = join(this.entriesDirPath, content_type_uid, locale, variantEntry.dirName, entry.uid);
42+
const variantEntryBasePath = join(sanitizePath(this.entriesDirPath), sanitizePath(content_type_uid), sanitizePath(locale), sanitizePath(variantEntry.dirName), sanitizePath(entry.uid));
4343
const variantEntriesFs = new FsUtility({
4444
isArray: true,
4545
keepMetadata: false,

packages/contentstack-variants/src/import/experiences.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { join, resolve } from 'path';
22
import { existsSync } from 'fs';
33
import values from 'lodash/values';
44
import cloneDeep from 'lodash/cloneDeep';
5-
5+
import { sanitizePath } from '@contentstack/cli-utilities';
66
import { PersonalizationAdapter, fsUtil, lookUpAudiences, lookUpEvents } from '../utils';
77
import { APIConfig, ImportConfig, ExperienceStruct, CreateExperienceInput, LogType } from '../types';
88

@@ -50,34 +50,34 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
5050
this.personalizationConfig.dirName,
5151
this.personalizationConfig.experiences.dirName,
5252
);
53-
this.experiencesPath = join(this.experiencesDirPath, this.personalizationConfig.experiences.fileName);
53+
this.experiencesPath = join(sanitizePath(this.experiencesDirPath), sanitizePath(this.personalizationConfig.experiences.fileName));
5454
this.experienceConfig = this.personalizationConfig.experiences;
5555
this.audienceConfig = this.personalizationConfig.audiences;
56-
this.mapperDirPath = resolve(this.config.backupDir, 'mapper', this.personalizationConfig.dirName);
57-
this.expMapperDirPath = resolve(this.mapperDirPath, this.experienceConfig.dirName);
58-
this.experiencesUidMapperPath = resolve(this.expMapperDirPath, 'uid-mapping.json');
59-
this.cmsVariantGroupPath = resolve(this.expMapperDirPath, 'cms-variant-groups.json');
60-
this.cmsVariantPath = resolve(this.expMapperDirPath, 'cms-variants.json');
61-
this.audiencesMapperPath = resolve(this.mapperDirPath, this.audienceConfig.dirName, 'uid-mapping.json');
62-
this.eventsMapperPath = resolve(this.mapperDirPath, 'events', 'uid-mapping.json');
63-
this.failedCmsExpPath = resolve(this.expMapperDirPath, 'failed-cms-experience.json');
64-
this.failedCmsExpPath = resolve(this.expMapperDirPath, 'failed-cms-experience.json');
65-
this.experienceCTsPath = resolve(this.experiencesDirPath, 'experiences-content-types.json');
56+
this.mapperDirPath = resolve(sanitizePath(this.config.backupDir), 'mapper', sanitizePath(this.personalizationConfig.dirName));
57+
this.expMapperDirPath = resolve(sanitizePath(this.mapperDirPath), sanitizePath(this.experienceConfig.dirName));
58+
this.experiencesUidMapperPath = resolve(sanitizePath(this.expMapperDirPath), 'uid-mapping.json');
59+
this.cmsVariantGroupPath = resolve(sanitizePath(this.expMapperDirPath), 'cms-variant-groups.json');
60+
this.cmsVariantPath = resolve(sanitizePath(this.expMapperDirPath), 'cms-variants.json');
61+
this.audiencesMapperPath = resolve(sanitizePath(this.mapperDirPath), sanitizePath(this.audienceConfig.dirName), 'uid-mapping.json');
62+
this.eventsMapperPath = resolve(sanitizePath(this.mapperDirPath), 'events', 'uid-mapping.json');
63+
this.failedCmsExpPath = resolve(sanitizePath(this.expMapperDirPath), 'failed-cms-experience.json');
64+
this.failedCmsExpPath = resolve(sanitizePath(this.expMapperDirPath), 'failed-cms-experience.json');
65+
this.experienceCTsPath = resolve(sanitizePath(this.experiencesDirPath), 'experiences-content-types.json');
6666
this.experienceVariantsIdsPath = resolve(
67-
this.config.data,
68-
this.personalizationConfig.dirName,
69-
this.experienceConfig.dirName,
67+
sanitizePath(this.config.data),
68+
sanitizePath(this.personalizationConfig.dirName),
69+
sanitizePath(this.experienceConfig.dirName),
7070
'experiences-variants-ids.json',
7171
);
72-
this.variantUidMapperFilePath = resolve(this.expMapperDirPath, 'variants-uid-mapping.json');
72+
this.variantUidMapperFilePath = resolve(sanitizePath(this.expMapperDirPath), 'variants-uid-mapping.json');
7373
this.experiencesUidMapper = {};
7474
this.cmsVariantGroups = {};
7575
this.cmsVariants = {};
7676
this.expThresholdTimer = this.experienceConfig?.thresholdTimer ?? 30000;
7777
this.expCheckIntervalDuration = this.experienceConfig?.checkIntervalDuration ?? 5000;
7878
this.maxValidateRetry = Math.round(this.expThresholdTimer / this.expCheckIntervalDuration);
7979
this.pendingVariantAndVariantGrpForExperience = [];
80-
this.cTsSuccessPath = resolve(this.config.backupDir, 'mapper', 'content_types', 'success.json');
80+
this.cTsSuccessPath = resolve(sanitizePath(this.config.backupDir), 'mapper', 'content_types', 'success.json');
8181
this.createdCTs = [];
8282
}
8383

packages/contentstack-variants/src/import/project.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { join, resolve as pResolve } from 'path';
22
import { existsSync, readFileSync } from 'fs';
3-
3+
import { sanitizePath } from '@contentstack/cli-utilities';
44
import { PersonalizationAdapter, askProjectName, fsUtil } from '../utils';
55
import { APIConfig, CreateProjectInput, ImportConfig, LogType, ProjectStruct } from '../types';
66

@@ -14,9 +14,9 @@ export default class Project extends PersonalizationAdapter<ImportConfig> {
1414
};
1515
super(Object.assign(config, conf));
1616
this.projectMapperFolderPath = pResolve(
17-
this.config.backupDir,
17+
sanitizePath(this.config.backupDir),
1818
'mapper',
19-
this.config.modules.personalization.dirName,
19+
sanitizePath(this.config.modules.personalization.dirName),
2020
'projects',
2121
);
2222
}
@@ -28,7 +28,7 @@ export default class Project extends PersonalizationAdapter<ImportConfig> {
2828
async import() {
2929
const personalization = this.config.modules.personalization;
3030
const { dirName, fileName } = personalization.projects;
31-
const projectPath = join(this.config.data, personalization.dirName, dirName, fileName);
31+
const projectPath = join(sanitizePath(this.config.data), sanitizePath(personalization.dirName), sanitizePath(dirName), sanitizePath(fileName));
3232

3333
if (existsSync(projectPath)) {
3434
const projects = JSON.parse(readFileSync(projectPath, 'utf8')) as CreateProjectInput[];
@@ -60,7 +60,7 @@ export default class Project extends PersonalizationAdapter<ImportConfig> {
6060
this.config.modules.personalization.importData = true;
6161

6262
await fsUtil.makeDirectory(this.projectMapperFolderPath);
63-
fsUtil.writeFile(pResolve(this.projectMapperFolderPath, 'projects.json'), projectRes);
63+
fsUtil.writeFile(pResolve(sanitizePath(this.projectMapperFolderPath), 'projects.json'), projectRes);
6464
this.log(this.config, `Project Created Successfully: ${projectRes.uid}`, 'info');
6565
}
6666
} else {

packages/contentstack-variants/src/import/variant-entries.ts

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import forEach from 'lodash/forEach';
66
import indexOf from 'lodash/indexOf';
77
import { join, resolve } from 'path';
88
import { readFileSync, existsSync } from 'fs';
9-
import { FsUtility, HttpResponse } from '@contentstack/cli-utilities';
9+
import { FsUtility, HttpResponse, sanitizePath } from '@contentstack/cli-utilities';
1010

1111
import VariantAdapter, { VariantHttpClient } from '../utils/variant-api-adapter';
1212
import {
@@ -53,12 +53,12 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
5353
organization_uid: config.org_uid,
5454
'X-Project-Uid': config.modules.personalization.project_id,
5555
},
56-
};
56+
};
5757
super(Object.assign(omit(config, ['helpers']), conf));
58-
this.entriesMapperPath = resolve(config.backupDir, config.branchName || '', 'mapper', 'entries');
58+
this.entriesMapperPath = resolve(sanitizePath(config.backupDir), config.branchName || '', 'mapper', 'entries');
5959
this.personalizationConfig = this.config.modules.personalization;
60-
this.entriesDirPath = resolve(config.backupDir, config.branchName || '', config.modules.entries.dirName);
61-
this.failedVariantPath = resolve(this.entriesMapperPath, 'failed-entry-variants.json');
60+
this.entriesDirPath = resolve(sanitizePath(config.backupDir), config.branchName || '', sanitizePath(config.modules.entries.dirName));
61+
this.failedVariantPath = resolve(sanitizePath(this.entriesMapperPath), 'failed-entry-variants.json');
6262
this.failedVariantEntries = new Map();
6363
}
6464

@@ -71,12 +71,12 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
7171
* message indicating that no entries were found and return.
7272
*/
7373
async import() {
74-
const filePath = resolve(this.entriesMapperPath, 'data-for-variant-entry.json');
74+
const filePath = resolve(sanitizePath(this.entriesMapperPath), 'data-for-variant-entry.json');
7575
const variantIdPath = resolve(
76-
this.config.backupDir,
76+
sanitizePath(this.config.backupDir),
7777
'mapper',
78-
this.personalizationConfig.dirName,
79-
this.personalizationConfig.experiences.dirName,
78+
sanitizePath(this.personalizationConfig.dirName),
79+
sanitizePath(this.personalizationConfig.experiences.dirName),
8080
'variants-uid-mapping.json',
8181
);
8282

@@ -97,18 +97,18 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
9797
return;
9898
}
9999

100-
const entriesUidMapperPath = join(this.entriesMapperPath, 'uid-mapping.json');
101-
const assetUidMapperPath = resolve(this.config.backupDir, 'mapper', 'assets', 'uid-mapping.json');
102-
const assetUrlMapperPath = resolve(this.config.backupDir, 'mapper', 'assets', 'url-mapping.json');
100+
const entriesUidMapperPath = join(sanitizePath(this.entriesMapperPath), 'uid-mapping.json');
101+
const assetUidMapperPath = resolve(sanitizePath(this.config.backupDir), 'mapper', 'assets', 'uid-mapping.json');
102+
const assetUrlMapperPath = resolve(sanitizePath(this.config.backupDir), 'mapper', 'assets', 'url-mapping.json');
103103
const taxonomiesPath = resolve(
104-
this.config.backupDir,
104+
sanitizePath(this.config.backupDir),
105105
'mapper',
106-
this.config.modules.taxonomies.dirName,
106+
sanitizePath(this.config.modules.taxonomies.dirName),
107107
'terms',
108108
'success.json',
109109
);
110-
const marketplaceAppMapperPath = resolve(this.config.backupDir, 'mapper', 'marketplace_apps', 'uid-mapping.json');
111-
const envPath = resolve(this.config.backupDir, 'environments', 'environments.json');
110+
const marketplaceAppMapperPath = resolve(sanitizePath(this.config.backupDir), 'mapper', 'marketplace_apps', 'uid-mapping.json');
111+
const envPath = resolve(sanitizePath(this.config.backupDir), 'environments', 'environments.json');
112112
// NOTE Read and store list of variant IDs
113113
this.variantIdList = (fsUtil.readFile(variantIdPath, true) || {}) as Record<string, unknown>;
114114
if (isEmpty(this.variantIdList)) {
@@ -141,9 +141,9 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
141141
const { content_type, locale, entry_uid } = entriesForVariant;
142142
const ctConfig = this.config.modules['content-types'];
143143
const contentType: ContentTypeStruct = JSON.parse(
144-
readFileSync(resolve(this.config.backupDir, ctConfig.dirName, `${content_type}.json`), 'utf8'),
144+
readFileSync(resolve(sanitizePath(this.config.backupDir), sanitizePath(ctConfig.dirName), `${content_type}.json`), 'utf8'),
145145
);
146-
const variantEntryBasePath = join(this.entriesDirPath, content_type, locale, variantEntry.dirName, entry_uid);
146+
const variantEntryBasePath = join(sanitizePath(this.entriesDirPath), sanitizePath(content_type), sanitizePath(locale), sanitizePath(variantEntry.dirName), sanitizePath(entry_uid));
147147
const fs = new FsUtility({ basePath: variantEntryBasePath });
148148

149149
for (const _ in fs.indexFileContent) {
@@ -327,7 +327,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
327327
},
328328
this.assetUidMapper,
329329
this.assetUrlMapper,
330-
join(this.entriesDirPath, contentType.uid),
330+
join(sanitizePath(this.entriesDirPath), sanitizePath(contentType.uid)),
331331
this.installedExtensions,
332332
);
333333
}

packages/contentstack-variants/src/utils/logger.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as winston from 'winston';
22
import * as path from 'path';
33
import mkdirp from 'mkdirp';
4+
import { sanitizePath } from '@contentstack/cli-utilities';
45
import { ExportConfig, ImportConfig } from '../types';
56

67
const slice = Array.prototype.slice;
@@ -57,15 +58,15 @@ function init(_logPath: string) {
5758
mkdirp.sync(logsDir);
5859

5960
successTransport = {
60-
filename: path.join(logsDir, 'success.log'),
61+
filename: path.join(sanitizePath(logsDir), 'success.log'),
6162
maxFiles: 20,
6263
maxsize: 1000000,
6364
tailable: true,
6465
level: 'info',
6566
};
6667

6768
errorTransport = {
68-
filename: path.join(logsDir, 'error.log'),
69+
filename: path.join(sanitizePath(logsDir), 'error.log'),
6970
maxFiles: 20,
7071
maxsize: 1000000,
7172
tailable: true,

0 commit comments

Comments
 (0)