diff --git a/.github/workflows/snyk-security.yml b/.github/workflows/snyk-security.yml index 578dcae..f0c10c8 100644 --- a/.github/workflows/snyk-security.yml +++ b/.github/workflows/snyk-security.yml @@ -54,7 +54,7 @@ jobs: # Runs Snyk Code (SAST) analysis and uploads result into GitHub. # Use || true to not fail the pipeline - name: Snyk Code test - run: snyk code test --sarif > snyk-code.sarif # || true + run: snyk code test -d --sarif > snyk-code.sarif # || true # Runs Snyk Open Source (SCA) analysis and uploads result to Snyk. - name: Snyk Open Source monitor diff --git a/index.html b/index.html index c4979f4..5f9e7de 100644 --- a/index.html +++ b/index.html @@ -77,6 +77,7 @@ + diff --git a/js/formDataToJson.js b/js/formDataToJson.js index f517caf..157aeae 100644 --- a/js/formDataToJson.js +++ b/js/formDataToJson.js @@ -78,6 +78,10 @@ async function createCodeJson(data) { delete data.submit; const codeJson = await populateCodeJson(data); + window.gh_api_key = data['gh_api_key'] + console.log("TEST") + console.log(window.gh_api_key) + const jsonString = JSON.stringify(codeJson, null, 2); document.getElementById("json-result").value = jsonString; } @@ -91,6 +95,215 @@ async function copyToClipboard(event){ document.execCommand("copy") } +const NEW_BRANCH = 'code-json-branch' + Math.random().toString(36).substring(2, 10); + +function getOrgAndRepoArgsGitHub(url) +{ + const pattern = /https:\/\/github\.com\/([^\/]+)\/([^\/]+)/; + const match = url.match(pattern); + + if(match) + { + const owner = match[1]; + const repo = match[2]; + return {owner,repo}; + } + else + { + throw new Error('Invalid URL!'); + } +} + + +async function createBranchOnProject(projectURL, token) +{ + const {owner, repo} = getOrgAndRepoArgsGitHub(projectURL); + + const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/main`, + { + method: 'GET', + headers: { + 'Authorization': 'token '.concat(token), + }, + } + ); + + const data = await response.json(); + + if (response.ok) + { + const sha = data.object.sha; + + const createBranchApiUrl = `https://api.github.com/repos/${owner}/${repo}/git/refs`; + + // Create the new branch from the base branch + const newBranchResponse = await fetch(createBranchApiUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `token ${token}`, + }, + body: JSON.stringify({ + ref: `refs/heads/${NEW_BRANCH}`, // Name of the new branch + sha: sha, // SHA of the base branch (main) + }), + }); + + const newBranchData = await newBranchResponse.json(); + + if ( newBranchResponse.ok ) + { + console.log('New branch created successfully: ', newBranchData); + return true; + } + else + { + console.error('Error creating new branch: ', newBranchData); + alert("Failed to create branch on project! Error code: " + newBranchResponse.status + ". Please check API Key permissions and try again.") + return false; + } + } + else + { + console.error('Error fetching base branch info:', data); + alert('Error fetching base branch info:', data); + return false; + } +} + + +async function addFileToBranch(projectURL, token, codeJSONObj) +{ + const {owner, repo} = getOrgAndRepoArgsGitHub(projectURL); + const FILE_PATH = 'code.json' + const createFileApiUrl = `https://api.github.com/repos/${owner}/${repo}/contents/${FILE_PATH}`; + const encodedContent = btoa(codeJSONObj); + console.log("Content: ", encodedContent); + console.log("Branch: ", NEW_BRANCH); + + const response = await fetch(createFileApiUrl, + { + method: 'PUT', + headers: { + 'Accept': 'application/vnd.github+json', + 'Authorization': 'Bearer '.concat(token), + 'X-GitHub-Api-Version': "2022-11-28" + }, + body: JSON.stringify({ + message: "Add codejson to project", + committer: { + name: "codejson-generator form site", + email: "opensource@cms.hhs.gov" + }, + content: encodedContent, + branch: NEW_BRANCH, + }), + } + ); + + const data = await response.json() + + if ( response.ok ) + { + console.log('File added successfully: ', data); + return true; + } + else + { + console.error('Error adding file: ', data); + alert("Failed to add file on project! Error code: " + response.status + ". Please check API Key permissions and try again.") + return false; + } +} + +async function createPR(projectURL, token) +{ + const {owner, repo} = getOrgAndRepoArgsGitHub(projectURL); + const createPrApiUrl = `https://api.github.com/repos/${owner}/${repo}/pulls`; + const response = await fetch(createPrApiUrl, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'token '.concat(token), + 'X-GitHub-Api-Version': "2022-11-28" + }, + body: JSON.stringify({ + title: "Add code.json to Project", + body: "Add generated code.json file to project. Code.json was generated via codejson-generator form site.", + head: NEW_BRANCH, + base: 'main', + + }), + } + ); + + const data = await response.json(); + + if (response.ok) + { + console.log('Pull request created successfully: ', data); + return true; + } + else + { + console.error("Error creating PR!: ", data); + alert("Failed to create PR on project! Error code: " + response.status + ". Please check API Key permissions and try again.") + return false; + } +} + +// Creates PR on requested project +async function createProjectPR(event){ + event.preventDefault(); + + var textArea = document.getElementById("json-result");//Step 1 + var codeJSONObj = JSON.parse(textArea.value) + + if('gh_api_key' in window) + { + var apiKey = window.gh_api_key; + + if ('repositoryURL' in codeJSONObj) + { + var prCreated = false; + //Step 1 + const branchCreated = await createBranchOnProject(codeJSONObj.repositoryURL,apiKey); + if (branchCreated) + { + const fileAdded = await addFileToBranch(codeJSONObj.repositoryURL, apiKey, textArea.value); + + if (fileAdded) + { + prCreated = await createPR(codeJSONObj.repositoryURL, apiKey); + if(prCreated) + { + console.log("PR successfully created!"); + alert("PR has been created!"); + } + } + } + else + { + console.error("Could not create branch on requested repository with the requested API key!"); + alert("Could not create branch on requested repository with the requested API key!"); + } + } + else + { + console.error("No URL found!"); + alert("No URL given for project! Please provide project URL in repositoryURL text box"); + } + + } + else + { + console.error("No API key found!"); + alert("No API Key in submitted data! Please provide an API key"); + } + //console.log(codeJSONObj) +} + // Triggers local file download async function downloadFile(event) { event.preventDefault(); @@ -112,3 +325,4 @@ async function downloadFile(event) { window.createCodeJson = createCodeJson; window.copyToClipboard = copyToClipboard; window.downloadFile = downloadFile; +window.createProjectPR = createProjectPR; diff --git a/js/generateFormComponents.js b/js/generateFormComponents.js index dc86a66..f708615 100644 --- a/js/generateFormComponents.js +++ b/js/generateFormComponents.js @@ -302,6 +302,20 @@ async function createFormComponents() { components = createAllComponents(jsonData); + //Form text box to input GitHub API Key + components.push({ + "label": "GitHub API Key (optional)", + "disableSortingAndFiltering": false, + "tableView": true, + "key": "gh_api_key", + "type": "password", + "input": true, + "description": "Generate a Github API Key from here: https://github.com/settings/tokens/new .\n\ + The token should have these permissions: \n\ + - Contents: read & write \n- Workflows: read & write\ + - Pull requests: read & write" + }); + // Add submit button to form components.push({ type: "button", @@ -312,6 +326,8 @@ async function createFormComponents() { tableView: false, }); + + console.log(components); return components;