diff --git a/popup.js b/popup.js index 2a243277..a6225d15 100644 --- a/popup.js +++ b/popup.js @@ -10,14 +10,8 @@ $('#authenticate').on('click', () => { }); /* Get URL for welcome page */ -$('#welcome_URL').attr( - 'href', - chrome.runtime.getURL('welcome.html') -); -$('#hook_URL').attr( - 'href', - chrome.runtime.getURL('welcome.html') -); +$('#welcome_URL').attr('href', chrome.runtime.getURL('welcome.html')); +$('#hook_URL').attr('href', chrome.runtime.getURL('welcome.html')); chrome.storage.local.get('leethub_token', (data) => { const token = data.leethub_token; @@ -49,9 +43,17 @@ chrome.storage.local.get('leethub_token', (data) => { } const leethubHook = data3.leethub_hook; if (leethubHook) { - $('#repo_url').html( - `${leethubHook}`, + const safeHook = leethubHook.replace( + /[^a-zA-Z0-9./_-]/g, + '', ); + const anchor = document.createElement('a'); + anchor.target = 'blank'; + anchor.style = + 'color: cadetblue !important; font-size:0.8em;'; + anchor.href = `https://github.com/${safeHook}`; + anchor.textContent = safeHook; + $('#repo_url').empty().append(anchor); } }, ); diff --git a/scripts/authorize.js b/scripts/authorize.js index 9b345370..6044a32b 100644 --- a/scripts/authorize.js +++ b/scripts/authorize.js @@ -1,3 +1,4 @@ +/* global LEETHUB_CLIENT_ID, LEETHUB_CLIENT_SECRET */ /* (needs patch) IMPLEMENTATION OF AUTHENTICATION ROUTE AFTER REDIRECT FROM GITHUB. @@ -13,9 +14,15 @@ const localAuth = { 'https://github.com/login/oauth/access_token'; this.AUTHORIZATION_URL = 'https://github.com/login/oauth/authorize'; - this.CLIENT_ID = 'beb4f0aa19ab8faf5004'; - this.CLIENT_SECRET = '843f835609c7ef02ef0f2f1645bc49514c0e65a6'; - this.REDIRECT_URL = 'https://github.com/'; // for example, https://github.com + this.CLIENT_ID = + typeof LEETHUB_CLIENT_ID !== 'undefined' + ? LEETHUB_CLIENT_ID + : 'SET_YOUR_CLIENT_ID'; + this.CLIENT_SECRET = + typeof LEETHUB_CLIENT_SECRET !== 'undefined' + ? LEETHUB_CLIENT_SECRET + : 'SET_YOUR_CLIENT_SECRET'; + this.REDIRECT_URL = 'https://github.com/'; this.SCOPES = ['repo']; }, @@ -30,8 +37,22 @@ const localAuth = { chrome.tabs.remove(tab.id, function () {}); }); } else { - // eslint-disable-next-line - this.requestToken(url.match(/\?code=([\w\/\-]+)/)[1]); + const codeMatch = url.match(/\?code=([\w/-]+)/); + const stateMatch = url.match(/[&?]state=([^&]+)/); + if (!codeMatch) return; + + const code = codeMatch[1]; + const returnedState = stateMatch ? stateMatch[1] : null; + + chrome.storage.local.get('leethub_oauth_state', (data) => { + const savedState = data.leethub_oauth_state; + if (!savedState || savedState !== returnedState) { + console.error('OAuth state mismatch — possible CSRF.'); + return; + } + chrome.storage.local.remove('leethub_oauth_state'); + this.requestToken(code); + }); } }, @@ -72,8 +93,6 @@ const localAuth = { * @param token The OAuth2 token given to the application from the provider. */ finish(token) { - /* Get username */ - // To validate user, load user object from GitHub. const AUTHENTICATION_URL = 'https://api.github.com/user'; const xhr = new XMLHttpRequest(); @@ -86,7 +105,7 @@ const localAuth = { isSuccess: true, token, username, - KEY: this.KEY, + KEY: 'leethub_token', }); } } @@ -97,7 +116,7 @@ const localAuth = { }, }; -localAuth.init(); // load params. +localAuth.init(); const link = window.location.href; /* Check for open pipe */ diff --git a/scripts/background.js b/scripts/background.js index 8f23e775..66161544 100644 --- a/scripts/background.js +++ b/scripts/background.js @@ -5,17 +5,12 @@ function handleMessage(request) { request.isSuccess === true ) { /* Set username */ - chrome.storage.local.set( - { leethub_username: request.username }, - () => { - window.localStorage.leethub_username = request.username; - }, - ); + chrome.storage.local.set({ + leethub_username: request.username, + }); /* Set token */ - chrome.storage.local.set({ leethub_token: request.token }, () => { - window.localStorage[request.KEY] = request.token; - }); + chrome.storage.local.set({ leethub_token: request.token }); /* Close pipe */ chrome.storage.local.set({ pipe_leethub: false }, () => { @@ -28,11 +23,11 @@ function handleMessage(request) { /* Go to onboarding for UX */ const urlOnboarding = chrome.runtime.getURL('welcome.html'); - chrome.tabs.create({ url: urlOnboarding, active: true }); // creates new tab + chrome.tabs.create({ url: urlOnboarding, active: true }); } else if ( request && request.closeWebPage === true && - request.isSuccess === true + request.isSuccess === false ) { alert( 'Something went wrong while trying to authenticate your profile!', diff --git a/scripts/leetcode.js b/scripts/leetcode.js index a4569f25..1edd0eb9 100644 --- a/scripts/leetcode.js +++ b/scripts/leetcode.js @@ -706,7 +706,7 @@ const loader = setInterval(() => { function startUploadCountDown() { uploadState.uploading = true; uploadState['countdown'] = setTimeout(() => { - if ((uploadState.uploading = true)) { + if (uploadState.uploading === true) { // still uploading, then it failed uploadState.uploading = false; markUploadFailed(); diff --git a/scripts/oauth2.js b/scripts/oauth2.js index 545dc4d3..cacd75c9 100644 --- a/scripts/oauth2.js +++ b/scripts/oauth2.js @@ -1,3 +1,4 @@ +/* global LEETHUB_CLIENT_ID, LEETHUB_CLIENT_SECRET */ // eslint-disable-next-line no-unused-vars const oAuth2 = { /** @@ -9,9 +10,15 @@ const oAuth2 = { 'https://github.com/login/oauth/access_token'; this.AUTHORIZATION_URL = 'https://github.com/login/oauth/authorize'; - this.CLIENT_ID = 'beb4f0aa19ab8faf5004'; - this.CLIENT_SECRET = '843f835609c7ef02ef0f2f1645bc49514c0e65a6'; - this.REDIRECT_URL = 'https://github.com/'; // for example, https://github.com + this.CLIENT_ID = + typeof LEETHUB_CLIENT_ID !== 'undefined' + ? LEETHUB_CLIENT_ID + : 'SET_YOUR_CLIENT_ID'; + this.CLIENT_SECRET = + typeof LEETHUB_CLIENT_SECRET !== 'undefined' + ? LEETHUB_CLIENT_SECRET + : 'SET_YOUR_CLIENT_SECRET'; + this.REDIRECT_URL = 'https://github.com/'; this.SCOPES = ['repo']; }, @@ -19,17 +26,21 @@ const oAuth2 = { * Begin */ begin() { - this.init(); // secure token params. + this.init(); - let url = `${this.AUTHORIZATION_URL}?client_id=${this.CLIENT_ID}&redirect_uri${this.REDIRECT_URL}&scope=`; + const state = crypto.getRandomValues(new Uint32Array(2)).join(''); + chrome.storage.local.set({ leethub_oauth_state: state }); + + let url = `${this.AUTHORIZATION_URL}?client_id=${ + this.CLIENT_ID + }&redirect_uri=${encodeURIComponent(this.REDIRECT_URL)}&scope=`; for (let i = 0; i < this.SCOPES.length; i += 1) { url += this.SCOPES[i]; } + url += `&state=${state}`; chrome.storage.local.set({ pipe_leethub: true }, () => { - // opening pipe temporarily - chrome.tabs.create({ url, active: true }, function () { window.close(); chrome.tabs.getCurrent(function (tab) { diff --git a/scripts/welcome.js b/scripts/welcome.js index 9812db3c..1c372e69 100644 --- a/scripts/welcome.js +++ b/scripts/welcome.js @@ -6,6 +6,16 @@ const repositoryName = () => { return $('#name').val().trim(); }; +function escapeHtml(str) { + const div = document.createElement('div'); + div.appendChild(document.createTextNode(str)); + return div.innerHTML; +} + +function isValidRepoName(name) { + return /^[a-zA-Z0-9._-]+$/.test(name) && name.length <= 100; +} + /* Status codes for creating of repo */ const statusCode = (res, status, name) => { @@ -55,7 +65,11 @@ const statusCode = (res, status, name) => { chrome.storage.local.set({ mode_type: 'commit' }, () => { $('#error').hide(); $('#success').html( - `Successfully created ${name}. Start LeetCoding!`, + `Successfully created ${escapeHtml( + name, + )}. Start LeetCoding!`, ); $('#success').show(); $('#unlink').show(); @@ -107,7 +121,11 @@ const linkStatusCode = (status, name) => { case 301: $('#success').hide(); $('#error').html( - `Error linking ${name} to LeetHub.
This repository has been moved permenantly. Try creating a new one.`, + `Error linking ${escapeHtml( + name, + )} to LeetHub.
This repository has been moved permenantly. Try creating a new one.`, ); $('#error').show(); break; @@ -115,7 +133,11 @@ const linkStatusCode = (status, name) => { case 403: $('#success').hide(); $('#error').html( - `Error linking ${name} to LeetHub.
Forbidden action. Please make sure you have the right access to this repository.`, + `Error linking ${escapeHtml( + name, + )} to LeetHub.
Forbidden action. Please make sure you have the right access to this repository.`, ); $('#error').show(); break; @@ -123,7 +145,11 @@ const linkStatusCode = (status, name) => { case 404: $('#success').hide(); $('#error').html( - `Error linking ${name} to LeetHub.
Resource not found. Make sure you enter the right repository name.`, + `Error linking ${escapeHtml( + name, + )} to LeetHub.
Resource not found. Make sure you enter the right repository name.`, ); $('#error').show(); break; @@ -176,7 +202,11 @@ const linkRepo = (token, name) => { () => { $('#error').hide(); $('#success').html( - `Successfully linked ${name} to LeetHub. Start LeetCoding now!`, + `Successfully linked ${escapeHtml( + name, + )} to LeetHub. Start LeetCoding now!`, ); $('#success').show(); $('#unlink').show(); @@ -253,6 +283,12 @@ $('#hook_button').on('click', () => { ); $('#name').focus(); $('#error').show(); + } else if (!isValidRepoName(repositoryName())) { + $('#error').text( + 'Invalid repository name - Use only letters, numbers, hyphens, underscores, and dots (max 100 chars).', + ); + $('#name').focus(); + $('#error').show(); } else { $('#error').hide(); $('#success').text('Attempting to create Hook... Please wait.');