Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rework mvb normalization to prevent injection issues #65

Merged
merged 2 commits into from
Nov 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading