diff --git a/public/index.html b/public/index.html index 8b8b89c..e415752 100644 --- a/public/index.html +++ b/public/index.html @@ -71,6 +71,8 @@ + + diff --git a/public/script/app.js b/public/script/app.js index a71f6df..062561a 100644 --- a/public/script/app.js +++ b/public/script/app.js @@ -671,6 +671,7 @@ angular $scope.terms = ""; $scope.defaultTerms = ""; $scope.branches = []; + $scope.repositories = []; $scope.source = { type: "GitHubDownload", branch: "", @@ -709,6 +710,7 @@ angular if (cb) cb(); }); } + getDefault(() => { if ($routeParams.repoId && $routeParams.repoId != "") { $scope.isUpdate = true; @@ -753,8 +755,6 @@ angular } }); - $scope.repositories = []; - $scope.getRepositories = (force) => { $http .get("/api/user/all_repositories", { @@ -798,46 +798,6 @@ angular }; $('[data-toggle="tooltip"]').tooltip(); - $scope.$watch("source.branch", async (v) => { - const selected = $scope.branches.filter( - (f) => f.name == $scope.source.branch - )[0]; - if ($scope.details && $scope.details.hasPage) { - $scope.anonymize.page.$$element[0].disabled = false; - if ($scope.details.pageSource.branch != $scope.source.branch) { - $scope.anonymize.page.$$element[0].disabled = true; - } - } - - if (selected) { - $scope.source.commit = selected.commit; - $scope.readme = selected.readme; - await getReadme(); - anonymize(); - $scope.$apply(); - } - }); - - $scope.$watch("options.mode", (v) => { - if (v == "GitHubStream") { - $scope.options.page = false; - $scope.anonymize.page.$$element[0].disabled = true; - } else { - $scope.anonymize.page.$$element[0].disabled = false; - } - }); - - function parseGithubUrl(url) { - var matches = url.match(/.*?github.com\/([\w-\._]+)\/([\w-\._]+)/); - if (matches && matches.length == 3) { - return { - owner: matches[1], - repo: matches[2], - }; - } else { - throw "Invalid url"; - } - } $scope.getBranches = async (force) => { const o = parseGithubUrl($scope.repoUrl); const branches = await $http.get( @@ -857,14 +817,7 @@ angular } $scope.$apply(); }; - function generateRandomId(length) { - const alphabet = "ABCDEF0123456789"; - let output = ""; - for (let index = 0; index < length; index++) { - output += alphabet[Math.round(Math.random() * (alphabet.length - 1))]; - } - return output; - } + async function getDetails() { const o = parseGithubUrl($scope.repoUrl); try { @@ -962,7 +915,7 @@ angular } $scope.anonymize_readme = content; - let html = marked($scope.anonymize_readme); + const html = marked($scope.anonymize_readme); $scope.html_readme = $sce.trustAsHtml(html); setTimeout(Prism.highlightAll, 150); } @@ -1026,13 +979,11 @@ angular }; } - $scope.anonymizeRepo = async (event) => { - event.target.disabled = true; + async function sendRepo(url) { resetValidity(); - const newRepo = getRepo(); try { - await $http.post("/api/repo/", newRepo, { + await $http.post(url, newRepo, { headers: { "Content-Type": "application/json" }, }); window.location.href = "/status/" + $scope.repoId; @@ -1045,34 +996,54 @@ angular } else { console.error(error); } - } finally { - event.target.disabled = false; } - $scope.$apply(); + } + + $scope.anonymizeRepo = (event) => { + event.target.disabled = true; + sendRepo("/api/repo/").finally(() => { + event.target.disabled = false; + $scope.$apply(); + }); }; $scope.updateRepo = async (event) => { event.target.disabled = true; - resetValidity(); - - const newRepo = getRepo(); - try { - await $http.post("/api/repo/" + newRepo.repoId, newRepo, { - headers: { "Content-Type": "application/json" }, - }); - window.location.href = "/status/" + $scope.repoId; - } catch (error) { - if (error.data) { - displayErrorMessage(error.data.error); - } else { - console.error(error); - } - } finally { + sendRepo("/api/repo/" + $scope.repoId).finally(() => { event.target.disabled = false; - } - $scope.$apply(); + $scope.$apply(); + }); }; + $scope.$watch("source.branch", async (v) => { + const selected = $scope.branches.filter( + (f) => f.name == $scope.source.branch + )[0]; + if ($scope.details && $scope.details.hasPage) { + $scope.anonymize.page.$$element[0].disabled = false; + if ($scope.details.pageSource.branch != $scope.source.branch) { + $scope.anonymize.page.$$element[0].disabled = true; + } + } + + if (selected) { + $scope.source.commit = selected.commit; + $scope.readme = selected.readme; + await getReadme(); + anonymize(); + $scope.$apply(); + } + }); + + $scope.$watch("source.type", (v) => { + if (v == "GitHubStream") { + $scope.options.page = false; + $scope.anonymize.page.$$element[0].disabled = true; + } else { + $scope.anonymize.page.$$element[0].disabled = false; + } + }); + $scope.$watch("terms", anonymize); $scope.$watch("options.image", anonymize); $scope.$watch("options.link", anonymize); @@ -1206,7 +1177,7 @@ angular } if ($scope.type == "md") { - const md = res.data; + const md = contentAbs2Relative(res.data); $scope.content = marked(md, { baseUrl: $location.url() }); $scope.type = "html"; } diff --git a/public/script/utils.js b/public/script/utils.js new file mode 100644 index 0000000..ab87298 --- /dev/null +++ b/public/script/utils.js @@ -0,0 +1,104 @@ +function urlRel2abs(url) { + /* Only accept commonly trusted protocols: + * Only data-image URLs are accepted, Exotic flavours (escaped slash, + * html-entitied characters) are not supported to keep the function fast */ + if ( + /^(https?|file|ftps?|mailto|javascript|data:image\/[^;]{2,9};):/i.test(url) + ) { + return url; //Url is already absolute + } + var base_url = location.href.match(/^(.+)\/?(?:#.+)?$/)[0] + "/"; + if (url.substring(0, 2) == "//") return location.protocol + url; + else if (url.charAt(0) == "/") + return location.protocol + "//" + location.host + url; + else if (url.substring(0, 2) == "./") url = "." + url; + else if (/^\s*$/.test(url)) return ""; + //Empty = Return nothing + else url = "../" + url; + + url = base_url + url; + var i = 0; + while (/\/\.\.\//.test((url = url.replace(/[^\/]+\/+\.\.\//g, "")))); + + /* Escape certain characters to prevent XSS */ + url = url + .replace(/\.$/, "") + .replace(/\/\./g, "") + .replace(/"/g, "%22") + .replace(/'/g, "%27") + .replace(//g, "%3E"); + return url; +} + +const charactersAttributes = "[^-a-z0-9:._]"; +const allTagCharacters = "(?:[^>\"']*(?:\"[^\"]*\"|'[^']*'))*?[^>]*"; + +function by(match, group1, group2, group3) { + /* Note that this function can also be used to remove links: + * return group1 + "javascript://" + group3; */ + return group1 + urlRel2abs(group2) + group3; +} + +function cr(html, selector, attribute) { + if (typeof selector == "string") selector = new RegExp(selector, "gi"); + attribute = charactersAttributes + attribute; + const marker = "\\s*=\\s*"; + const end = ")("; + var re1 = new RegExp("(" + attribute + marker + '")([^"]+)()', "gi"); + var re2 = new RegExp("(" + attribute + marker + "')([^']+)()", "gi"); + var re3 = new RegExp( + "(" + attribute + marker + ")([^\"'][^\\s>]*" + end + ")", + "gi" + ); + html = html.replace(selector, function (match) { + return match.replace(re1, by).replace(re2, by).replace(re3, by); + }); + return html; +} + +function contentAbs2Relative(content) { + if (!content) return content; + content = cr( + content, + "<" + + allTagCharacters + + charactersAttributes + + "href\\s*=" + + allTagCharacters + + ">", + "href" + ); + content = cr( + content, + "<" + + allTagCharacters + + charactersAttributes + + "src\\s*=" + + allTagCharacters + + ">", + "src" + ); + return content; +} + +function generateRandomId(length) { + const alphabet = "ABCDEF0123456789"; + let output = ""; + for (let index = 0; index < length; index++) { + output += alphabet[Math.round(Math.random() * (alphabet.length - 1))]; + } + return output; +} + +function parseGithubUrl(url) { + var matches = url.match(/.*?github.com\/([\w-\._]+)\/([\w-\._]+)/); + if (matches && matches.length == 3) { + return { + owner: matches[1], + repo: matches[2], + }; + } else { + throw "Invalid url"; + } +}