refactor: sanitize URL usages

This commit is contained in:
EnixCoda 2022-07-16 23:54:27 +08:00
parent 6fde8c1092
commit 9435e9cd52
12 changed files with 909 additions and 745 deletions

View file

@ -11,7 +11,7 @@
"prepare": "husky install",
"dev": "VERSION=dev-v$(node scripts/get-version.js) webpack --watch",
"dev-safari": "TARGET=safari yarn run dev",
"debug-firefox": "web-ext run --source-dir=dist --firefox-profile=firefox-profile --profile-create-if-missing --keep-profile-changes --start-url github.com/EnixCoda/Gitako",
"debug-firefox": "web-ext run --source-dir=dist --firefox-profile=firefox-profile --profile-create-if-missing --keep-profile-changes --start-url https://github.com/EnixCoda/Gitako",
"analyse-bundle": "ANALYSE= NODE_ENV=production webpack",
"postinstall": "node scripts/fix-deps",
"build": "VERSION=v$(node scripts/get-version.js) NODE_ENV=production webpack",
@ -89,7 +89,7 @@
"typescript": "^4.7.2",
"uglifyjs-webpack-plugin": "^2.1.2",
"url-loader": "^1.1.2",
"web-ext": "^6.8.0",
"web-ext": "^7.1.1",
"webpack": "^4.29.6",
"webpack-bundle-analyzer": "^3.6.0",
"webpack-cli": "^3.1.2"

View file

@ -1,6 +1,7 @@
import { errors } from 'platforms'
import { isEnterprise } from '.'
import { is } from '../../utils/is'
import { gitakoServiceHost } from '../../utils/networkService'
import { continuousLoadPages, getDOM, resolveHeaderLink } from './utils'
function isAPIRateLimitExceeded(content: JSONValue) {
@ -71,14 +72,14 @@ async function request<T>(
throw new Error(`Unknown message content "${message}"`)
}
const API_ENDPOINT = isEnterprise() ? `${window.location.host}/api/v3` : 'api.github.com'
const API_ENDPOINT = isEnterprise() ? `${window.location.origin}/api/v3` : 'https://api.github.com'
export async function getRepoMeta(
userName: string,
repoName: string,
accessToken?: string,
): Promise<GitHubAPI.MetaData> {
const url = `https://${API_ENDPOINT}/repos/${userName}/${repoName}`
const url = `${API_ENDPOINT}/repos/${userName}/${repoName}`
return await request(url, { accessToken })
}
@ -91,8 +92,7 @@ export async function getTreeData(
): Promise<GitHubAPI.TreeData> {
const search = new URLSearchParams()
if (recursive) search.set('recursive', '1')
const url =
`https://${API_ENDPOINT}/repos/${userName}/${repoName}/git/trees/${branchName}?` + search
const url = `${API_ENDPOINT}/repos/${userName}/${repoName}/git/trees/${branchName}?${search}`
return await request(url, { accessToken })
}
@ -102,7 +102,7 @@ export async function getPullRequest(
pullId: string,
accessToken?: string,
): Promise<GitHubAPI.PullData> {
const url = `https://${API_ENDPOINT}/repos/${userName}/${repoName}/pulls/${pullId}`
const url = `${API_ENDPOINT}/repos/${userName}/${repoName}/pulls/${pullId}`
return await request(url, { accessToken })
}
@ -115,7 +115,7 @@ export async function requestPullTreeData(
accessToken?: string,
) {
const search = new URLSearchParams({ page: page.toString(), per_page: `${pageSize}` })
const url = `https://${API_ENDPOINT}/repos/${userName}/${repoName}/pulls/${pullId}/files?${search}`
const url = `${API_ENDPOINT}/repos/${userName}/${repoName}/pulls/${pullId}/files?${search}`
return await request(url, { accessToken }, responseBodyResolvers.asIs)
}
@ -136,7 +136,7 @@ export async function getPullComments(
pullId: string,
accessToken?: string,
): Promise<GitHubAPI.PullComments> {
const url = `https://${API_ENDPOINT}/repos/${userName}/${repoName}/pulls/${pullId}/comments`
const url = `${API_ENDPOINT}/repos/${userName}/${repoName}/pulls/${pullId}/comments`
return await request(url, { accessToken })
}
@ -150,7 +150,7 @@ export async function getPullPageDocuments(
return continuousLoadPages(
document ||
(await getDOM(
`https://${window.location.host}/${userName}/${repoName}/pull/${pullId}/files?_pjax=%23js-repo-pjax-container`,
`${window.location.origin}/${userName}/${repoName}/pull/${pullId}/files?_pjax=%23js-repo-pjax-container`,
)),
)
}
@ -169,14 +169,14 @@ export async function getBlobData(
sha: string,
accessToken?: string,
): Promise<GitHubAPI.BlobData> {
const url = `https://${API_ENDPOINT}/repos/${userName}/${repoName}/git/blobs/${sha}`
const url = `${API_ENDPOINT}/repos/${userName}/${repoName}/git/blobs/${sha}`
return await request(url, { accessToken })
}
export async function OAuth(code: string): Promise<string | null> {
try {
const endpoint = `https://gitako.enix.one/oauth/github?`
const res = await fetch(endpoint + new URLSearchParams({ code }).toString(), {
const endpoint = `https://${gitakoServiceHost}/oauth/github?${new URLSearchParams({ code })}`
const res = await fetch(endpoint, {
method: 'post',
})
if (res.ok) {
@ -201,7 +201,7 @@ export async function requestCommitTreeData(
per_page: '100',
page: `${page}`,
})
const url = `https://${API_ENDPOINT}/repos/${userName}/${repoName}/commits/${sha}?` + search
const url = `${API_ENDPOINT}/repos/${userName}/${repoName}/commits/${sha}?${search}`
return await request(url, { accessToken }, responseBodyResolvers.asIs)
}

View file

@ -1,4 +1,3 @@
import { formatHash } from 'utils/general'
import * as API from './API'
import { getPRDiffTotalStat, getPullRequestFilesCount, isInPullFilesPage } from './DOMHelper'
import { processTree } from './index'
@ -51,23 +50,27 @@ export async function getPullRequestTreeData(
}
}
const urlMainPart = `https://${window.location.host}/${userName}/${repoName}/pull/${pullId}/files${window.location.search}`
const url = new URL(window.location.href)
url.pathname = `/${userName}/${repoName}/pull/${pullId}/files`
const commentsMap = getCommentsMap(commentData)
const nodes: TreeNode[] = treeData.map(
({ filename, sha, additions, deletions, changes, status }) => ({
path: filename || '',
type: 'blob',
name: filename?.split('/').pop() || '',
url: `${urlMainPart}${formatHash(map.get(filename))}`,
sha: sha,
comments: commentsMap.get(filename),
diff: {
status,
additions,
deletions,
changes,
},
}),
({ filename, sha, additions, deletions, changes, status }) => {
url.hash = map.get(filename) || ''
return {
path: filename || '',
type: 'blob',
name: filename?.split('/').pop() || '',
url: `${url}`,
sha,
comments: commentsMap.get(filename),
diff: {
status,
additions,
deletions,
changes,
},
}
},
)
const root = processTree(nodes)

View file

@ -76,7 +76,7 @@ function getUrlForRedirect(
// Modern browsers have great support for handling unsafe URL,
// It may be possible to sanitize path with
// `path => path.includes('#') ? path.replace(/#/g, '%23') : '...'
return `https://${window.location.host}/${userName}/${repoName}/${type}/${branchName}/${path
return `${window.location.origin}/${userName}/${repoName}/${type}/${branchName}/${path
.split('/')
.map(encodeURIComponent)
.join('/')}`
@ -144,8 +144,8 @@ export const GitHub: Platform = {
return (await API.getRepoMeta(userName, repoName, accessToken)).default_branch
},
resolveUrlFromMetaData({ userName, repoName, branchName }) {
const repoUrl = `https://${window.location.host}/${userName}/${repoName}`
const userUrl = `https://${window.location.host}/${userName}`
const repoUrl = `${window.location.origin}/${userName}/${repoName}`
const userUrl = `${window.location.origin}/${userName}`
const pullId = URLHelper.isInPullPage()
const commitId = URLHelper.isInCommitPage()
const branchUrl = pullId
@ -200,7 +200,7 @@ export const GitHub: Platform = {
scope: 'repo',
redirect_uri: window.location.href,
})
return `https://github.com/login/oauth/authorize?` + params.toString()
return `https://github.com/login/oauth/authorize?${params}`
},
usePlatformHooks() {
const { copyFileButton, copySnippetButton, codeFolding } = useConfigs().value

View file

@ -1,6 +1,7 @@
import { raiseError } from 'analytics'
import { errors } from 'platforms'
import { is } from 'utils/is'
import { gitakoServiceHost } from 'utils/networkService'
function isEmptyProject(content: JSONValue) {
return is.JSON.object(content) && content?.['message'] === 'Git Repository is empty.'
@ -58,7 +59,7 @@ async function request(
}
}
export const API_ENDPOINT = `${window.location.protocol}//${window.location.host}/api/v1`
export const API_ENDPOINT = `${window.location.origin}/api/v1`
export async function getRepoMeta(
userName: string,
@ -78,7 +79,7 @@ export async function getTreeData(
): Promise<GiteaAPI.TreeData> {
const search = new URLSearchParams()
if (recursive) search.set('recursive', '1')
const url = `${API_ENDPOINT}/repos/${userName}/${repoName}/git/trees/${branchName}?` + search
const url = `${API_ENDPOINT}/repos/${userName}/${repoName}/git/trees/${branchName}?${search}`
return await request(url, { accessToken })
}
@ -93,8 +94,8 @@ export async function getBlobData(
}
export async function OAuth(code: string): Promise<string | null> {
const endpoint = `https://gitako.enix.one/oauth/gitea?`
const res = await fetch(endpoint + new URLSearchParams({ code }).toString(), {
const endpoint = `https://${gitakoServiceHost}/oauth/gitea?${new URLSearchParams({ code })}`
const res = await fetch(endpoint, {
method: 'post',
})

View file

@ -67,9 +67,7 @@ function getUrlForRedirect(
// Modern browsers have great support for handling unsafe URL,
// It may be possible to sanitize path with
// `path => path.includes('#') ? path.replace(/#/g, '%23') : '...'
return `${window.location.protocol}//${
window.location.host
}/${userName}/${repoName}/src/branch/${branchName}/${path
return `${window.location.origin}/${userName}/${repoName}/src/branch/${branchName}/${path
.split('/')
.map(encodeURIComponent)
.join('/')}`
@ -108,8 +106,8 @@ export const Gitea: Platform = {
return data.default_branch
},
resolveUrlFromMetaData({ userName, repoName, branchName }) {
const repoUrl = `${window.location.protocol}//${window.location.host}/${userName}/${repoName}`
const userUrl = `${window.location.protocol}//${window.location.host}/${userName}`
const repoUrl = `${window.location.origin}/${userName}/${repoName}`
const userUrl = `${window.location.origin}/${userName}`
const branchUrl = `${repoUrl}/src/branch/${branchName}`
return {
repoUrl,
@ -169,7 +167,7 @@ export const Gitea: Platform = {
return API.OAuth(code)
},
getOAuthLink() {
return `${window.location.protocol}//${window.location.host}/api/v1/user/applications/oauth2`
return `${window.location.origin}/api/v1/user/applications/oauth2`
},
usePlatformHooks() {
useProgressBar()

View file

@ -1,6 +1,7 @@
import { raiseError } from 'analytics'
import { errors } from 'platforms'
import { is } from 'utils/is'
import { gitakoServiceHost } from 'utils/networkService'
function isEmptyProject(content: JSONValue) {
return is.JSON.object(content) && content?.['message'] === 'Git Repository is empty.'
@ -90,8 +91,8 @@ export async function getBlobData(
}
export async function OAuth(code: string): Promise<string | null> {
const endpoint = 'https://gitako.enix.one/oauth/gitee?'
const res = await fetch(endpoint + new URLSearchParams({ code }).toString(), {
const endpoint = `https://${gitakoServiceHost}/oauth/gitee?${new URLSearchParams({ code })}`
const res = await fetch(endpoint, {
method: 'post',
})

View file

@ -5,6 +5,7 @@ import * as React from 'react'
import { resolveGitModules } from 'utils/gitSubmodule'
import { useOnPJAXDone } from 'utils/hooks/usePJAX'
import { useProgressBar } from 'utils/hooks/useProgressBar'
import { gitakoServiceHost } from 'utils/networkService'
import { sortFoldersToFront } from 'utils/treeParser'
import * as API from './API'
import * as DOMHelper from './DOMHelper'
@ -68,7 +69,7 @@ function getUrlForRedirect(
type = 'blob',
path = '',
) {
return `https://gitee.com/${userName}/${repoName}/${type}/${branchName}/${path}`
return `${window.location.origin}/${userName}/${repoName}/${type}/${branchName}/${path}`
}
export const Gitee: Platform = {
@ -105,8 +106,8 @@ export const Gitee: Platform = {
return data.default_branch
},
resolveUrlFromMetaData({ userName, repoName, branchName }) {
const repoUrl = `https://${window.location.host}/${userName}/${repoName}`
const userUrl = `https://${window.location.host}/${userName}`
const repoUrl = `${window.location.origin}/${userName}/${repoName}`
const userUrl = `${window.location.origin}/${userName}`
const branchUrl = `${repoUrl}/tree/${branchName}`
return {
repoUrl,
@ -167,11 +168,11 @@ export const Gitee: Platform = {
client_id: GITEE_OAUTH.clientId,
scope: 'projects',
response_type: 'code',
redirect_uri:
'https://gitako.enix.one/redirect/?' +
new URLSearchParams({ redirect: window.location.href }).toString(),
redirect_uri: `https://${gitakoServiceHost}/redirect/?${new URLSearchParams({
redirect: window.location.href,
})}`,
})
return `https://gitee.com/oauth/authorize?` + params.toString()
return `https://gitee.com/oauth/authorize?${params}`
},
setOAuth(code) {
return API.OAuth(code)

View file

@ -202,11 +202,6 @@ export function resolveDiffGraphMeta(additions: number, deletions: number, chang
return { g, r, w }
}
export function formatHash(hash?: string) {
if (hash) return '#' + hash
return ''
}
export function forOf<T, R>(target: T, callback: <K extends keyof T>(key: K, value: T[K]) => R) {
for (const key of Object.keys(target)) {
const $key = key as keyof typeof target

View file

@ -11,7 +11,7 @@ function transformModuleGitURL(node: TreeNode, URL: string) {
const matched = URL.match(subModuleURLRegex.git)
if (!matched) return
const [, userName, repoName] = matched
return appendCommitPath(`https://${window.location.host}/${userName}/${repoName}`, node)
return appendCommitPath(`${window.location.origin}/${userName}/${repoName}`, node)
}
function cutDotGit(URL: string) {

View file

@ -0,0 +1 @@
export const gitakoServiceHost = 'gitako.enix.one'

1536
yarn.lock

File diff suppressed because it is too large Load diff