fix(v2): reject routeBasePath: '' (#3377)

* routeBasePath: '' should be forbidden

* routeBasePath: '' should be forbidden

* commit

* try to trigger cla bot
This commit is contained in:
Sébastien Lorber 2020-08-31 19:59:27 +02:00 committed by GitHub
parent d8cfabb66a
commit 1eb6e13fb7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 33 additions and 24 deletions

View file

@ -15,6 +15,7 @@
"@docusaurus/core": "^2.0.0-alpha.62", "@docusaurus/core": "^2.0.0-alpha.62",
"@docusaurus/types": "^2.0.0-alpha.62", "@docusaurus/types": "^2.0.0-alpha.62",
"@docusaurus/utils": "^2.0.0-alpha.62", "@docusaurus/utils": "^2.0.0-alpha.62",
"@docusaurus/utils-validation": "^2.0.0-alpha.62",
"@hapi/joi": "^17.1.1", "@hapi/joi": "^17.1.1",
"@types/hapi__joi": "^17.1.2", "@types/hapi__joi": "^17.1.2",
"chalk": "^3.0.0", "chalk": "^3.0.0",

View file

@ -7,7 +7,7 @@
import {PluginOptions, RedirectOption, UserPluginOptions} from './types'; import {PluginOptions, RedirectOption, UserPluginOptions} from './types';
import * as Joi from '@hapi/joi'; import * as Joi from '@hapi/joi';
import {PathnameValidator} from './redirectValidation'; import {PathnameSchema} from '@docusaurus/utils-validation';
import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants';
export const DefaultPluginOptions: PluginOptions = { export const DefaultPluginOptions: PluginOptions = {
@ -18,10 +18,10 @@ export const DefaultPluginOptions: PluginOptions = {
}; };
const RedirectPluginOptionValidation = Joi.object<RedirectOption>({ const RedirectPluginOptionValidation = Joi.object<RedirectOption>({
to: PathnameValidator.required(), to: PathnameSchema.required(),
from: Joi.alternatives().try( from: Joi.alternatives().try(
PathnameValidator.required(), PathnameSchema.required(),
Joi.array().items(PathnameValidator.required()), Joi.array().items(PathnameSchema.required()),
), ),
}); });

View file

@ -6,24 +6,12 @@
*/ */
import * as Joi from '@hapi/joi'; import * as Joi from '@hapi/joi';
import {isValidPathname} from '@docusaurus/utils';
import {RedirectMetadata} from './types'; import {RedirectMetadata} from './types';
import {PathnameSchema} from '@docusaurus/utils-validation';
export const PathnameValidator = Joi.string()
.custom((val) => {
if (!isValidPathname(val)) {
throw new Error();
} else {
return val;
}
})
.message(
'{{#label}} is not a valid pathname. Pathname should start with / and not contain any domain or query string',
);
const RedirectSchema = Joi.object<RedirectMetadata>({ const RedirectSchema = Joi.object<RedirectMetadata>({
from: PathnameValidator.required(), from: PathnameSchema.required(),
to: PathnameValidator.required(), to: PathnameSchema.required(),
}); });
export function validateRedirect(redirect: RedirectMetadata): void { export function validateRedirect(redirect: RedirectMetadata): void {

View file

@ -22,7 +22,7 @@ test('should accept correctly defined user options', () => {
...DEFAULT_OPTIONS, ...DEFAULT_OPTIONS,
feedOptions: {type: 'rss', title: 'myTitle'}, feedOptions: {type: 'rss', title: 'myTitle'},
path: 'not_blog', path: 'not_blog',
routeBasePath: '', routeBasePath: 'myBlog',
postsPerPage: 5, postsPerPage: 5,
include: ['api/*', 'docs/*'], include: ['api/*', 'docs/*'],
}; };
@ -37,7 +37,7 @@ test('should accept correctly defined user options', () => {
test('should accept valid user options', async () => { test('should accept valid user options', async () => {
const userOptions = { const userOptions = {
...DEFAULT_OPTIONS, ...DEFAULT_OPTIONS,
routeBasePath: '', routeBasePath: 'myBlog',
beforeDefaultRemarkPlugins: [], beforeDefaultRemarkPlugins: [],
beforeDefaultRehypePlugins: [markdownPluginsFunctionStub], beforeDefaultRehypePlugins: [markdownPluginsFunctionStub],
remarkPlugins: [[markdownPluginsFunctionStub, {option1: '42'}]], remarkPlugins: [[markdownPluginsFunctionStub, {option1: '42'}]],

View file

@ -35,7 +35,10 @@ export const DEFAULT_OPTIONS = {
export const PluginOptionSchema = Joi.object({ export const PluginOptionSchema = Joi.object({
path: Joi.string().default(DEFAULT_OPTIONS.path), path: Joi.string().default(DEFAULT_OPTIONS.path),
routeBasePath: Joi.string().allow('').default(DEFAULT_OPTIONS.routeBasePath), routeBasePath: Joi.string()
// '' not allowed, see https://github.com/facebook/docusaurus/issues/3374
// .allow('')
.default(DEFAULT_OPTIONS.routeBasePath),
include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include), include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include),
postsPerPage: Joi.number() postsPerPage: Joi.number()
.integer() .integer()

View file

@ -49,7 +49,10 @@ const VersionsOptionsSchema = Joi.object()
export const OptionsSchema = Joi.object({ export const OptionsSchema = Joi.object({
path: Joi.string().default(DEFAULT_OPTIONS.path), path: Joi.string().default(DEFAULT_OPTIONS.path),
editUrl: URISchema, editUrl: URISchema,
routeBasePath: Joi.string().allow('').default(DEFAULT_OPTIONS.routeBasePath), routeBasePath: Joi.string()
// '' not allowed, see https://github.com/facebook/docusaurus/issues/3374
// .allow('') ""
.default(DEFAULT_OPTIONS.routeBasePath),
homePageId: Joi.string().optional(), homePageId: Joi.string().optional(),
include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include), include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include),
sidebarPath: Joi.string().allow('').default(DEFAULT_OPTIONS.sidebarPath), sidebarPath: Joi.string().allow('').default(DEFAULT_OPTIONS.sidebarPath),

View file

@ -14,7 +14,7 @@ import {
export const DEFAULT_OPTIONS: PluginOptions = { export const DEFAULT_OPTIONS: PluginOptions = {
path: 'src/pages', // Path to data on filesystem, relative to site dir. path: 'src/pages', // Path to data on filesystem, relative to site dir.
routeBasePath: '', // URL Route. routeBasePath: '/', // URL Route.
include: ['**/*.{js,jsx,ts,tsx,md,mdx}'], // Extensions to include. include: ['**/*.{js,jsx,ts,tsx,md,mdx}'], // Extensions to include.
mdxPageComponent: '@theme/MDXPage', mdxPageComponent: '@theme/MDXPage',
remarkPlugins: [], remarkPlugins: [],

View file

@ -16,6 +16,7 @@
"@types/hapi__joi": "^17.1.2" "@types/hapi__joi": "^17.1.2"
}, },
"dependencies": { "dependencies": {
"@docusaurus/utils": "^2.0.0-alpha.62",
"@hapi/joi": "17.1.1", "@hapi/joi": "17.1.1",
"chalk": "^3.0.0" "chalk": "^3.0.0"
}, },

View file

@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import * as Joi from '@hapi/joi'; import * as Joi from '@hapi/joi';
import {isValidPathname} from '@docusaurus/utils';
export const PluginIdSchema = Joi.string() export const PluginIdSchema = Joi.string()
.regex(/^[a-zA-Z_\-]+$/) .regex(/^[a-zA-Z_\-]+$/)
@ -39,3 +40,15 @@ export const URISchema = Joi.alternatives(
} }
}), }),
); );
export const PathnameSchema = Joi.string()
.custom((val) => {
if (!isValidPathname(val)) {
throw new Error();
} else {
return val;
}
})
.message(
'{{#label}} is not a valid pathname. Pathname should start with / and not contain any domain or query string',
);