Skip to content

Commit

Permalink
Implement issue creation via edit button
Browse files Browse the repository at this point in the history
I suspect CSURF isn't actually doing its job and preventing CSRF attacks. Will
definitely want to address that before deploying this code.
  • Loading branch information
joshbuker committed Apr 25, 2022
1 parent 8feaf11 commit 38a995b
Show file tree
Hide file tree
Showing 9 changed files with 562 additions and 28 deletions.
1 change: 1 addition & 0 deletions gsd-web-demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"cookie-session": "^2.0.0",
"core-js": "^3.6.5",
"csurf": "^1.11.0",
"octokit": "^1.7.1",
"prismjs": "^1.25.0",
"quasar": "^2.0.0",
"vue": "^3.0.0",
Expand Down
4 changes: 3 additions & 1 deletion gsd-web-demo/quasar.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ module.exports = configure(function (ctx) {
// Quasar plugins
plugins: [
'Cookies',
'Dialog'
'Dialog',
'Notify'
]
},

Expand Down Expand Up @@ -132,6 +133,7 @@ module.exports = configure(function (ctx) {

middlewares: [
ctx.prod ? 'compression' : '',
'body-parser',
'cookie-sessions',
'csrf-protection',
'github-proxy',
Expand Down
7 changes: 7 additions & 0 deletions gsd-web-demo/src-ssr/middlewares/body-parser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import bodyParser from 'body-parser'
import { ssrMiddleware } from 'quasar/wrappers'

export default ssrMiddleware(({ app }) => {
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))
})
57 changes: 57 additions & 0 deletions gsd-web-demo/src-ssr/middlewares/github-proxy.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
import axios from 'axios'
import { ssrMiddleware } from 'quasar/wrappers'
import { Octokit } from 'octokit'

const githubClientID = process.env.GSD_GITHUB_KEY
const githubClientSecret = process.env.GSD_GITHUB_SECRET

async function createFork(octokit) {
return await octokit.rest.forks.create({
owner: 'cloudsecurityalliance',
repo: 'gsd-database'
})
}

async function createBranch(octokit) {
// TODO: Allow multiple edits without overwriting existing edits
const branchPrefix = `automated/${identifier}`
const editNumber = 1
const branchName = `${branchPrefix}/${editNumber}`
// octokit.rest.branches.
}

export default ssrMiddleware(async ({ app, resolve }) => {
app.get(resolve.urlPath('/oauth/callback/github'), async (req, res) => {
try {
Expand Down Expand Up @@ -50,4 +66,45 @@ export default ssrMiddleware(async ({ app, resolve }) => {
req.session = null
res.json({ message: 'Session destroyed' })
})

// FIXME: Seems like CSURF is doing literally nothing
app.patch(resolve.urlPath('/update-gsd'), async (req, res) => {
if(!req.session.access_token) {
res.status(403).send('Login first!')
return
}

try {
const octokit = new Octokit({ auth: req.session.access_token });

console.log(req.body)

const identifier = req.body.identifier
const fileContent = req.body.file_content

const issueTitle = `Update Request - ${identifier}`
const issueBody =
'**Automated Edit Request**\n\n' +
`For: "${identifier}"\n\n` +
`\`\`\`json\n${fileContent}\n\`\`\``

// await createFork(octokit)
// await createBranch(octokit)
// await updateFile(octokit)
// await submitPullRequest(octokit)

// FIXME: Labels don't appear to work via this method, perhaps have the bot auto add them?
const response = await octokit.rest.issues.create({
owner: 'cloudsecurityalliance',
repo: 'gsd-database',
title: issueTitle,
body: issueBody
})

res.json({ redirect_url: response.data['html_url'] })
} catch(error) {
console.log(error)
res.send('broke')
}
})
})
53 changes: 41 additions & 12 deletions gsd-web-demo/src/components/EditDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@
</template>

<script>
import { useDialogPluginComponent } from 'quasar'
import { useDialogPluginComponent, useQuasar } from 'quasar'
import { computed, ref, watch } from 'vue'
import { api } from 'boot/axios'
import { errorNotification } from '../misc/ErrorNotification'
export default {
name: 'EditDialog',
Expand All @@ -36,6 +39,10 @@ export default {
gsd_json: {
type: String,
required: true
},
identifier: {
type: String,
required: true
}
},
Expand All @@ -55,6 +62,8 @@ export default {
// example: onDialogOK({ /*.../* }) - with payload
// onDialogCancel - Function to call to settle dialog with "cancel" outcome
const $q = useQuasar()
const gsdJson = ref(props.gsd_json)
const unsavedChanges = computed(
Expand All @@ -77,18 +86,38 @@ export default {
try {
// Force reformatting of the JSON string, as well as check validity.
fileContent = JSON.stringify(JSON.parse(gsdJson.value), null, 2);
console.log('Saved!')
} catch(e) {
console.log('Failed to save: ' + e.message)
api.patch('/update-gsd', {
identifier: props.identifier,
file_content: fileContent
}).then(
(response) => {
const redirectWindow = window.open(
response.data.redirect_url,
'_blank'
)
if(!redirectWindow) {
$q.notify({
color: 'negative',
position: 'top',
message: 'Failed to open issue in a new tab',
icon: 'report_problem'
})
}
$q.notify({
color: 'positive',
position: 'top',
message: 'Changes saved!',
icon: 'published_with_changes'
})
onCancelClick()
},
(error) => {
errorNotification(error, 'Failed to update GSD')
}
)
} catch(error) {
errorNotification(error, 'Failed to save changes')
}
if(!forkExists()) {
createFork()
}
createBranch()
updateFile()
submitPullRequest()
}
return {
Expand Down
4 changes: 2 additions & 2 deletions gsd-web-demo/src/layouts/MainLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,11 @@ export default defineComponent({
}
)
const loginURL = `https://github.com/login/oauth/authorize?client_id=${process.env.GSD_GITHUB_KEY}`
const loginURL = `https://github.com/login/oauth/authorize?client_id=${process.env.GSD_GITHUB_KEY}&scope=public_repo`
function login() {
// TODO: Auto redirect back to current page after login
const currentURL = encodeURI(window.location.origin + $route.path)
// const currentURL = encodeURI(window.location.origin + $route.path)
window.open(loginURL, '_self')
}
Expand Down
16 changes: 16 additions & 0 deletions gsd-web-demo/src/misc/ErrorNotification.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Notify } from 'quasar'

export function errorNotification(error, fallbackMessage) {
let errorMessage = ''
if (typeof error.response !== 'undefined') {
errorMessage = error.response.data.error
} else {
errorMessage = `${fallbackMessage}: ${error.message}`
}
Notify.create({
color: 'negative',
position: 'top',
message: errorMessage,
icon: 'report_problem'
})
}
19 changes: 7 additions & 12 deletions gsd-web-demo/src/pages/Identifier.vue
Original file line number Diff line number Diff line change
Expand Up @@ -246,26 +246,21 @@ export default defineComponent({
// TODO: Raise error or something?
if (!isValidIdentifier(identifier.value)) { return }
const repo = 'https://github.com/cloudsecurityalliance/gsd-database'
const branch = 'main'
// const repo = 'https://github.com/cloudsecurityalliance/gsd-database'
// const branch = 'main'
const year = identifier.value.split('-')[1]
const thousands = `${identifier.value.split('-')[2].slice(0, -3)}xxx`
// const year = identifier.value.split('-')[1]
// const thousands = `${identifier.value.split('-')[2].slice(0, -3)}xxx`
const editUrl = `${repo}/edit/${branch}/${year}/${thousands}/${identifier.value}.json`
// TODO: Open a dialog and allow editing locally instead, with sanity checks. Also allow editing specific fields quickly.
// window.open(
// editUrl,
// '_blank'
// )
// const editUrl = `${repo}/edit/${branch}/${year}/${thousands}/${identifier.value}.json`
$q.dialog({
component: EditDialog,
componentProps: {
gsd_json: JSON.stringify(jsonBlob.value, null, 2),
edit_url: editUrl
identifier: identifier.value
// edit_url: editUrl
}
})
}
Expand Down
Loading

0 comments on commit 38a995b

Please sign in to comment.