Skip to content

Commit

Permalink
rework mvb normalization to prevent injection issues (#65)
Browse files Browse the repository at this point in the history
* rework mvb normalization to prevent injection issues

* reorder docs tests
  • Loading branch information
pirog authored Nov 9, 2024
1 parent 993c29a commit a1b2ff3
Show file tree
Hide file tree
Showing 22 changed files with 73 additions and 79 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/pr-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ jobs:
with:
key: lando-mvb-docs
path: docs/.vitepress/cache/@lando/mvb
save-always: true
- name: Install node ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
Expand All @@ -35,7 +34,8 @@ jobs:
run: npm run lint
- name: Run tests
run: npm run test
- name: Test build
run: npm run build
- name: Test multiversion build
run: npx mvb docs
- name: Test build
run: npm run build

4 changes: 0 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
## {{ UNRELEASED_VERSION }} - [{{ UNRELEASED_DATE }}]({{ UNRELEASED_LINK }})

* Added `getItemNormalizedLink` helper client util
* Added `normalize2base` helper client util
* Added `normalizeMVB` helper client util
* Added `normalizeRoot` helper client util
* Added `version` alias information to config
* Fixed bug preventing user specified `buildEnd` and `transformPageData` from running after theme's
* Fixed bug preventing `mvb` from correctly setting the `mvbase`
Expand Down
7 changes: 0 additions & 7 deletions client/normalize-mvblink.js

This file was deleted.

5 changes: 0 additions & 5 deletions client/normalize-rootlink.js

This file was deleted.

2 changes: 1 addition & 1 deletion components/VPLCollectionItemTags.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<script setup>
import {computed} from 'vue';
import {useData} from 'vitepress';
import encodeTag from '../client/encode-tag.js';
import encodeTag from '../utils/encode-tag.js';
import Link from './VPLLink.vue';
import Tag from './VPLCollectionTag.vue';
Expand Down
2 changes: 1 addition & 1 deletion components/VPLCollectionPageTags.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<script setup>
import {onMounted} from 'vue';
import {useRoute} from 'vitepress';
import encodeTag from '../client/encode-tag.js';
import encodeTag from '../utils/encode-tag.js';
import Tag from './VPLCollectionTag.vue';
Expand Down
10 changes: 5 additions & 5 deletions components/VPLLink.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ import {computed} from 'vue';
import {normalizeLink} from 'vitepress/dist/client/theme-default/support/utils.js';
import {default as checkIsFauxInternal} from '../utils/is-faux-internal';
import {default as normalizeMvb} from '../client/normalize-mvblink';
import {default as normalizeRoot} from '../client/normalize-rootlink';
import {default as normalizeMvb} from '../utils/normalize-mvblink';
import {default as normalizeRoot} from '../utils/normalize-rootlink';
const EXTERNAL_URL_RE = /^(?:[a-z]+:|\/\/)/i;
const {theme} = useData();
const {theme, site} = useData();
const {internalDomains} = theme.value;
const props = defineProps({
Expand Down Expand Up @@ -65,8 +65,8 @@ const isFauxInternal = computed(() => props.href && checkIsFauxInternal(props.hr
const isExternal = computed(() => !isFauxInternal.value && props.href && EXTERNAL_URL_RE.test(props.href));
const getLink = href => {
if (props.rel === 'mvb' && href) return normalizeMvb(href);
else if (props.rel === 'root' && href) return normalizeRoot(href);
if (props.rel === 'mvb' && href) return normalizeMvb(href, site.value);
else if (props.rel === 'root' && href) return normalizeRoot(href, site.value);
return href ? normalizeLink(href) : undefined;
};
Expand Down
2 changes: 1 addition & 1 deletion components/VPLMenuLink.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<script setup>
import {toRefs} from 'vue';
import {useData} from 'vitepress';
import isActive from '../client/is-active.js';
import isActive from '../utils/is-active.js';
import Link from './VPLLink.vue';
Expand Down
2 changes: 1 addition & 1 deletion components/VPLNavBarMenuGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<script setup>
import {computed, toRefs} from 'vue';
import {useData} from 'vitepress';
import isActive from '../client/is-active.js';
import isActive from '../utils/is-active.js';
import VPFlyout from 'vitepress/dist/client/theme-default/components/VPFlyout.vue';
Expand Down
10 changes: 6 additions & 4 deletions components/VPLVersionLink.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@
</template>

<script setup>
import {default as normalizeMvb} from '../client/normalize-mvblink';
import {default as normalizeMvb} from '../utils/normalize-mvblink';
import {useData} from 'vitepress';
const {site} = useData();
const props = defineProps({
dev: {
Expand Down Expand Up @@ -72,10 +75,9 @@ const props = defineProps({
},
});
const getLink = version => {
if (props.dev === true) return normalizeMvb('/dev/');
return normalizeMvb(`/${version}/`);
if (props.dev === true) return normalizeMvb('/dev/', site.value);
return normalizeMvb(`/${version}/`, site.value);
};
</script>
Expand Down
2 changes: 0 additions & 2 deletions config.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import {tabsMarkdownPlugin} from 'vitepress-plugin-tabs';
import {default as tabsMarkdownOverridePlugin} from './markdown/tabs-override-plugin.js';

// vitepress patches
import {default as patchVPHasActiveLink} from './vite/patch-vp-has-active-link.js';
import {default as patchVPMenuColumnsPlugin} from './vite/patch-vp-menu-columns-plugin.js';
import {default as patchVPUseSidebarControl} from './vite/patch-vp-use-sidebar-control.js';

Expand Down Expand Up @@ -135,7 +134,6 @@ export async function defineConfig(userConfig = {}, defaults = {}) {
vite.plugins.push(...[
addLayoutsPlugin(layouts, {debug: debug.extend('vite-plugin')}),
patchVPMenuColumnsPlugin({debug: debug.extend('vite-plugin')}),
patchVPHasActiveLink({debug: debug.extend('vite-plugin')}),
patchVPUseSidebarControl({debug: debug.extend('vite-plugin')}),
]);

Expand Down
7 changes: 4 additions & 3 deletions docs/.vitepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ const require = createRequire(import.meta.url);

// get version info
const {version} = require('../../package.json');
const semver = process?.env?.VPL_MVB_VERSION ? process.env.VPL_MVB_VERSION : `v${version}`;

// sidebar ender
const sidebarEnder = {
text: process?.env?.VPL_MVB_VERSION ? process.env.VPL_MVB_VERSION : `v${version}`,
text: semver,
collapsed: true,
items: [
{
Expand All @@ -30,10 +31,10 @@ const sidebarEnder = {
};

// if version is a stable or edge release then add in the release notes
if (!isDevRelease(version)) {
if (!isDevRelease(semver)) {
sidebarEnder.items.splice(1, 0, {
text: 'Release Notes',
link: `https://github.com/lando/vitepress-theme-default-plus/releases/tag/v${version}`,
link: `https://github.com/lando/vitepress-theme-default-plus/releases/tag/v${semver}`,
});
}

Expand Down
File renamed without changes.
6 changes: 2 additions & 4 deletions client/get-item-nl.js → utils/get-item-nl.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import {useData} from 'vitepress';
import {default as normalize} from './normalize-2base.js';

export default function getItemNormalizedLink(item) {
export default function getItemNormalizedLink(item, site) {
// if we dont have what we need just return that garbage
if (!item.link) return item.link;

// if this is not a special mvb then just return
if (item.rel !== 'mvb') return item.link;

// otherwise normalize on version base
const {site} = useData();
return normalize(item.link, site?.value?.themeConfig?.multiVersionBuild?.base ?? '/');
return normalize(item.link, site?.themeConfig?.multiVersionBuild?.base ?? '/', site);
};
File renamed without changes.
8 changes: 2 additions & 6 deletions client/normalize-2base.js → utils/normalize-2base.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import {useData} from 'vitepress';

const EXTERNAL_URL_RE = /^(?:[a-z]+:|\/\/)/i;
const KNOWN_EXTENSIONS = new Set();

Expand Down Expand Up @@ -30,23 +28,21 @@ const treatAsHtml = filename => {
return ext == null || !KNOWN_EXTENSIONS.has(ext.toLowerCase());
};

export default function normalize2Base(url, base = '/') {
export default function normalize2Base(url, base = '/', site) {
const {pathname, search, hash, protocol} = new URL(url, 'http://lando.dev');

// return external urls
if (isExternal(url) || url.startsWith('#') || !protocol.startsWith('http') || !treatAsHtml(pathname)) return url;

// otherwise do the usual normalization
const {site} = useData();

const path =
pathname.endsWith('/') || pathname.endsWith('.html')
? url
: url.replace(
/(?:(^\.+)\/)?.*$/,
`$1${pathname.replace(
/(\.md)?$/,
site.value.cleanUrls ? '' : '.html',
site.cleanUrls ? '' : '.html',
)}${search}${hash}`,
);

Expand Down
9 changes: 9 additions & 0 deletions utils/normalize-items.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {default as getItemNormalizedLink} from './get-item-nl.js';

export default function normalizeItems(items, site) {
return items.map(item => {
if (item.items && Array.isArray(item.items)) return normalizeItems(item.items, site);
else if (item.rel !== 'mvb') return item;
else return {...item, link: getItemNormalizedLink(item, site)};
});
};
5 changes: 5 additions & 0 deletions utils/normalize-mvblink.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {default as normalize} from './normalize-2base.js';

export default function normalizemvbLink(url, site) {
return normalize(url, site?.themeConfig?.multiVersionBuild?.mvbase ?? '/', site);
};
5 changes: 5 additions & 0 deletions utils/normalize-rootlink.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {default as normalize} from './normalize-2base.js';

export default function normalizerootLink(url, site) {
return normalize(url, '/', site);
};
21 changes: 0 additions & 21 deletions vite/patch-vp-has-active-link.js

This file was deleted.

19 changes: 17 additions & 2 deletions vite/patch-vp-use-sidebar-control.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,26 @@ export default function({debug = Debug('@lando/vite-plugin')}) { // eslint-disab
if (id.includes(supportfile)) {
// prepend our mvb normalizer
code = `import { getItemNormalizedLink } from '@lando/vitepress-theme-default-plus';${EOL}${code}`;
// and then use it
code = `import { normalizeItems } from '@lando/vitepress-theme-default-plus';${EOL}${code}`;

// make sure we get "site" as well
code = code.replace(
'const { page, hash } = useData()',
'const { site, page, hash } = useData()',
);

// and then use getItemNormalizedLink
code = code.replace(
'isActive(page.value.relativePath, item.value.link)',
'isActive(page.value.relativePath, getItemNormalizedLink(item.value))',
'isActive(page.value.relativePath, getItemNormalizedLink(item.value, site.value))',
);

// and also use normalizeItems
code = code.replace(
'containsActiveLink(page.value.relativePath, item.value.items)',
'containsActiveLink(page.value.relativePath, normalizeItems(item.value.items, site.value))',
);

// log
debug('patched %o to use getItemNormalizedLink', supportfile);
return code;
Expand Down
20 changes: 11 additions & 9 deletions vitepress-theme-default-plus.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,21 @@ import VPLSponsors from './components/VPLSponsors.vue';
import VPLYouTube from './components/VPLYouTube.vue';

// composables
export {default as isActive} from './client/is-active.js';
export {default as isDevRelease} from './utils/is-dev-release.js';
export {default as isFauxInternal} from './utils/is-faux-internal.js';
export {default as encodeTag} from './client/encode-tag.js';
export {default as getBaseUrl} from './utils/get-base-url.js';
export {default as getItemNormalizedLink} from './client/get-item-nl.js';
export {default as normalize2base} from './client/normalize-2base.js';
export {default as normalizeMVB} from './client/normalize-mvblink.js';
export {default as normalizeRoot} from './client/normalize-rootlink.js';
export {default as useCollection} from './client/use-collection.js';
export {default as useTags} from './client/use-tags.js';
export {default as useTeam} from './client/use-team.js';

// shared utils
export {default as encodeTag} from './utils/encode-tag.js';
export {default as getItemNormalizedLink} from './utils/get-item-nl.js';
export {default as isFauxInternal} from './utils/is-faux-internal.js';
export {default as isDevRelease} from './utils/is-dev-release.js';
export {default as isActive} from './utils/is-active.js';
export {default as normalize2base} from './utils/normalize-2base.js';
export {default as normalizeItems} from './utils/normalize-items.js';
export {default as normalizeMVB} from './utils/normalize-mvblink.js';
export {default as normalizeRoot} from './utils/normalize-rootlink.js';

// components
export {default as VPLAlert} from './components/VPLAlert.vue';
export {default as VPLLink} from './components/VPLLink.vue';
Expand Down

0 comments on commit a1b2ff3

Please sign in to comment.