|
|
@@ -20,6 +20,12 @@ export type AutoInstallOptions = {
|
|
|
serverDate?: Date;
|
|
|
};
|
|
|
|
|
|
+const SUPPORTED_LOCALES: string[] = ['en_US', 'ja_JP', 'zh_CN'];
|
|
|
+
|
|
|
+const getSafeLang = (lang: string): Lang => {
|
|
|
+ return (SUPPORTED_LOCALES.includes(lang) ? lang : 'en_US') as Lang;
|
|
|
+};
|
|
|
+
|
|
|
export class InstallerService {
|
|
|
crowi: Crowi;
|
|
|
|
|
|
@@ -45,7 +51,12 @@ export class InstallerService {
|
|
|
const { pageService } = this.crowi;
|
|
|
|
|
|
try {
|
|
|
- const markdown = fs.readFileSync(filePath);
|
|
|
+ const normalizedPath = path.resolve(filePath);
|
|
|
+ const baseDir = path.resolve(this.crowi.localeDir);
|
|
|
+ if (!normalizedPath.startsWith(baseDir)) {
|
|
|
+ throw new Error(`Path traversal detected: ${normalizedPath}`);
|
|
|
+ }
|
|
|
+ const markdown = fs.readFileSync(normalizedPath);
|
|
|
return pageService.forceCreateBySystem(pagePath, markdown.toString(), {});
|
|
|
} catch (err) {
|
|
|
logger.error(`Failed to create ${pagePath}`, err);
|
|
|
@@ -57,28 +68,34 @@ export class InstallerService {
|
|
|
initialPagesCreatedAt?: Date,
|
|
|
): Promise<any> {
|
|
|
const { localeDir } = this.crowi;
|
|
|
+
|
|
|
+ const safeLang = getSafeLang(lang);
|
|
|
+
|
|
|
// create /Sandbox/*
|
|
|
/*
|
|
|
* Keep in this order to
|
|
|
* 1. avoid creating the same pages
|
|
|
* 2. avoid difference for order in VRT
|
|
|
*/
|
|
|
- assertFileNameSafeForBaseDir(lang, localeDir);
|
|
|
- await this.createPage(path.join(localeDir, lang, 'sandbox.md'), '/Sandbox');
|
|
|
+ assertFileNameSafeForBaseDir(safeLang, localeDir);
|
|
|
+ await this.createPage(
|
|
|
+ path.join(localeDir, safeLang, 'sandbox.md'),
|
|
|
+ '/Sandbox',
|
|
|
+ );
|
|
|
await this.createPage(
|
|
|
- path.join(localeDir, lang, 'sandbox-markdown.md'),
|
|
|
+ path.join(localeDir, safeLang, 'sandbox-markdown.md'),
|
|
|
'/Sandbox/Markdown',
|
|
|
);
|
|
|
await this.createPage(
|
|
|
- path.join(localeDir, lang, 'sandbox-bootstrap5.md'),
|
|
|
+ path.join(localeDir, safeLang, 'sandbox-bootstrap5.md'),
|
|
|
'/Sandbox/Bootstrap5',
|
|
|
);
|
|
|
await this.createPage(
|
|
|
- path.join(localeDir, lang, 'sandbox-diagrams.md'),
|
|
|
+ path.join(localeDir, safeLang, 'sandbox-diagrams.md'),
|
|
|
'/Sandbox/Diagrams',
|
|
|
);
|
|
|
await this.createPage(
|
|
|
- path.join(localeDir, lang, 'sandbox-math.md'),
|
|
|
+ path.join(localeDir, safeLang, 'sandbox-math.md'),
|
|
|
'/Sandbox/Math',
|
|
|
);
|
|
|
|
|
|
@@ -125,11 +142,13 @@ export class InstallerService {
|
|
|
globalLang: Lang,
|
|
|
options?: AutoInstallOptions,
|
|
|
): Promise<void> {
|
|
|
+ const safeLang = getSafeLang(globalLang);
|
|
|
+
|
|
|
await configManager.updateConfigs(
|
|
|
{
|
|
|
'app:installed': true,
|
|
|
'app:isV5Compatible': true,
|
|
|
- 'app:globalLang': globalLang,
|
|
|
+ 'app:globalLang': safeLang,
|
|
|
},
|
|
|
{ skipPubsub: true },
|
|
|
);
|
|
|
@@ -151,14 +170,16 @@ export class InstallerService {
|
|
|
globalLang: Lang,
|
|
|
options?: AutoInstallOptions,
|
|
|
): Promise<IUser> {
|
|
|
- await this.initDB(globalLang, options);
|
|
|
- assertFileNameSafeForBaseDir(globalLang, this.crowi.localeDir);
|
|
|
+ const safeLang = getSafeLang(globalLang);
|
|
|
+
|
|
|
+ await this.initDB(safeLang, options);
|
|
|
+ assertFileNameSafeForBaseDir(safeLang, this.crowi.localeDir);
|
|
|
const User = mongoose.model<IUser, { createUser }>('User');
|
|
|
|
|
|
// create portal page for '/' before creating admin user
|
|
|
try {
|
|
|
await this.createPage(
|
|
|
- path.join(this.crowi.localeDir, globalLang, 'welcome.md'),
|
|
|
+ path.join(this.crowi.localeDir, safeLang, 'welcome.md'),
|
|
|
'/',
|
|
|
);
|
|
|
} catch (err) {
|
|
|
@@ -174,12 +195,12 @@ export class InstallerService {
|
|
|
username,
|
|
|
email,
|
|
|
password,
|
|
|
- globalLang,
|
|
|
+ safeLang,
|
|
|
);
|
|
|
await (adminUser as any).asyncGrantAdmin();
|
|
|
|
|
|
// create initial pages
|
|
|
- await this.createInitialPages(globalLang, options?.serverDate);
|
|
|
+ await this.createInitialPages(safeLang, options?.serverDate);
|
|
|
|
|
|
return adminUser;
|
|
|
} catch (err) {
|