From a53bc0cc89ff157c2f5b8612db8dd06e43f8ccc0 Mon Sep 17 00:00:00 2001 From: Pete Freitag Date: Mon, 15 Aug 2022 15:58:08 -0400 Subject: [PATCH] Version 3.0.0 Add gitLastCommit, gitWorkingCopy --- README.md | 8 ++ box.json | 7 +- commands/fixinator.cfc | 115 ++++++++++++++++++++++++++- models/fixinator/FixinatorClient.cfc | 31 ++++++-- 4 files changed, 146 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 2228796..8b462e2 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,14 @@ Default: `true` - When true returns an exit code of `1` when issues are found, t Default: `false` - Prints out a list of scanners supported by the server in the results. Automatically set to `true` when `verbose` is `true` +### gitLastCommit + +Default: `false` - When `true` scans only files changed in the HEAD git commit, this is useful in CI to scan only the files changed in a specific commit. + +### gitWorkingCopy + +Default: `false` - When `true` scans only the files changed in the working copy (compared to the HEAD git commit). This is useful to scan only the files you have modified since your last git commit. + ## Environment Variables The following environment variables are used by fixinator: diff --git a/box.json b/box.json index 098e2d2..e4301c4 100644 --- a/box.json +++ b/box.json @@ -1,8 +1,8 @@ { "name":"fixinator", - "version":"2.0.3", + "version":"3.0.0", "author":"Foundeo Inc.", - "location":"foundeo/fixinator#v2.0.3", + "location":"foundeo/fixinator#v3.0.0", "homepage":"https://fixinator.app/", "documentation":"https://github.com/foundeo/fixinator/wiki", "repository":{ @@ -31,8 +31,7 @@ "installPaths":{}, "scripts":{ "postVersion":"package set location='foundeo/fixinator#v`package version`'", - "onRelease":"publish", - "postPublish":"!git push && git push --tags" + "onRelease":"publish" }, "ignore":[] } diff --git a/commands/fixinator.cfc b/commands/fixinator.cfc index 32dc54c..604993b 100644 --- a/commands/fixinator.cfc +++ b/commands/fixinator.cfc @@ -33,6 +33,8 @@ component extends="commandbox.system.BaseCommand" excludeFromHelp=false { * @listScanners.hint List the types of scanners that are enabled, enabled automatically when verbose=true * @ignorePaths.hint A globber paths pattern to exclude * @ignoreExtensions.hint A list of extensions to exclude + * @gitLastCommit.hint Scan only files changed in the last git commit + * @gitWorkingCopy.hint Scan only files changed since the last commit in the working copy **/ function run( string path=".", @@ -48,7 +50,9 @@ component extends="commandbox.system.BaseCommand" excludeFromHelp=false { boolean debug=false, boolean listScanners=false, string ignorePaths="", - string ignoreExtensions="" + string ignoreExtensions="", + boolean gitLastCommit=false, + boolean gitChanged=false, ) { var fileInfo = ""; var severityLevel = 1; @@ -177,8 +181,54 @@ component extends="commandbox.system.BaseCommand" excludeFromHelp=false { print.greenLine("✓ DEBUG MODE ENABLED: #fixinatorClient.isDebugModeEnabled()#"); } - - if (arguments.path contains "*" || arguments.path contains "," || len(arguments.ignorePaths)) { + + if (arguments.gitLastCommit || arguments.gitChanged) { + //we are going to use git data to build file list + if (arguments.gitLastCommit && arguments.gitChanged) { + //this might be handy to enable + error("You cannot enable both gitLastCommit and gitChanged at the same time"); + } + try { + if (arguments.gitLastCommit && arguments.verbose) { + print.yellowLine("Scanning only files changed in the last git commit."); + } else { + print.yellowLine("Scanning only files changed since the last git commit."); + } + arguments.path = fileSystemUtil.resolvePath( arguments.path ); + local.gitChanges = getGitChanges(path=arguments.path, lastCommit=arguments.gitLastCommit); + for (local.change in local.gitChanges) { + if (change.type == "DELETE") { + if (arguments.verbose) { + print.redLine(" D: #change.previousPath#"); + } + } else if (change.type == "MODIFY") { + if (arguments.verbose) { + print.greenLine(" M: #change.path#"); + } + arrayAppend(paths, getDirectoryFromPath(arguments.path) & change.path); + } else if (change.type == "ADD") { + if (arguments.verbose) { + print.greenLine(" A: #change.path#"); + } + arrayAppend(paths, getDirectoryFromPath(arguments.path) & change.path); + } else { + if (arguments.verbose) { + print.yellowLine(" #change.type#: #change.path# #change.previousPath#"); + } + arrayAppend(paths, getDirectoryFromPath(arguments.path) & change.path); + } + } + + if (arrayLen(paths) == 0) { + print.redLine("No scannable paths found."); + return; + } else if (len(arguments.ignorePaths)) { + error("Sorry ignorePaths is not currently supported with gitLastCommit or gitChanged"); + } + } catch (any err) { + error("Error checking for git files, make sure this is a git repository and path is pointing to the root of it: #err.message# - #err.detail# -- #err.stacktrace#") + } + } else if (arguments.path contains "*" || arguments.path contains "," || len(arguments.ignorePaths)) { local.newPath = arguments.path.listMap( (p) => { p = fileSystemUtil.resolvePath( p ); if ( directoryExists( p ) ) { @@ -228,6 +278,7 @@ component extends="commandbox.system.BaseCommand" excludeFromHelp=false { + @@ -336,6 +387,7 @@ component extends="commandbox.system.BaseCommand" excludeFromHelp=false { if (structKeyExists(err, "detail")) { print.whiteLine(err.detail); } + error("Fixinator Exiting Due to Error"); return; } else { rethrow; @@ -544,7 +596,13 @@ component extends="commandbox.system.BaseCommand" excludeFromHelp=false { print.line(); print.boldOrangeLine("WARNINGS"); for (local.w in local.results.warnings) { - print.grayLine(serializeJSON(local.w)); + if (local.w.keyExists("message") && local.w.keyExists("path")) { + print.grayLine(local.w.message); + print.grayLine(" " & replaceNoCase(local.w.path, getDirectoryFromPath(arguments.path), "")); + } else { + print.grayLine(serializeJSON(local.w)); + } + } } @@ -638,4 +696,53 @@ component extends="commandbox.system.BaseCommand" excludeFromHelp=false { return env.keyExists("BUILD_NUMBER") && len(env.BUILD_NUMBER) && env.keyExists("JENKINS_HOME") && len(env.JENKINS_HOME); } + private function getGitChanges(path, lastCommit=true) { + var gitDir = path & ".git/"; + var gitDirFileObject = createObject("java", "java.io.File").init(gitDir); + var gitRepo = ""; + var reader = ""; + var results = []; + var result = ""; + var disIO = createObject("java", "org.eclipse.jgit.util.io.DisabledOutputStream").INSTANCE; + if (!gitDirFileObject.exists()) { + throw(message="The path: #path# is not a git repository root path"); + } + gitRepo = createObject("java", "org.eclipse.jgit.storage.file.FileRepositoryBuilder").create(gitDirFileObject); + + reader = gitRepo.newObjectReader(); + + if (lastCommit) { + oldTreeIter = createObject("java", "org.eclipse.jgit.treewalk.CanonicalTreeParser"); + oldTree = gitRepo.resolve( "HEAD~1^{tree}" ); + oldTreeIter.reset( reader, oldTree ); + newTreeIter = createObject("java", "org.eclipse.jgit.treewalk.CanonicalTreeParser"); + + newTree = gitRepo.resolve( "HEAD^{tree}" ); + newTreeIter.reset( reader, newTree ); + } else { + oldTreeIter = createObject("java", "org.eclipse.jgit.treewalk.CanonicalTreeParser"); + oldTree = gitRepo.resolve( "HEAD^{tree}" ); + oldTreeIter.reset( reader, oldTree ); + + newTreeIter = createObject("java", "org.eclipse.jgit.treewalk.FileTreeIterator").init(gitRepo); + } + + + diffFormatter = createObject("java", "org.eclipse.jgit.diff.DiffFormatter").init( disIO ); + diffFormatter.setRepository( gitRepo ); + entries = diffFormatter.scan( oldTreeIter, newTreeIter ); + + for( entry in entries.toArray() ) { + result = {"type": entry.getChangeType().toString(), "path": "", "previousPath":""} + if (!isNull(entry.getNewPath())) { + result.path = entry.getNewPath().toString(); + } + if (!isNull(entry.getOldPath())) { + result.previousPath = entry.getOldPath().toString(); + } + arrayAppend(results, result); + } + return results; + } + } \ No newline at end of file diff --git a/models/fixinator/FixinatorClient.cfc b/models/fixinator/FixinatorClient.cfc index 35b52b1..1abc7b4 100644 --- a/models/fixinator/FixinatorClient.cfc +++ b/models/fixinator/FixinatorClient.cfc @@ -232,8 +232,10 @@ component singleton="true" { if (local.fileInfo.canRead && local.fileInfo.type == "file") { local.ext = listLast(local.f, "."); if (local.fileInfo.size > variables.maxPayloadSize && local.ext != "jar") { - element.results.warnings.append( { "message":"File was too large, #local.fileInfo.size# bytes, max: #variables.maxPayloadSize#", "path":local.f } ); + element.results.warnings.append( { "message":"Skipped File: too large, #local.fileInfo.size# bytes, max: #variables.maxPayloadSize#", "path":local.f } ); continue; + } else if ( removeBasePathFromPath(element.baseDir, local.f) contains ".." ) { + element.results.warnings.append( { "message":"Skipped File: name contains .. ", "path":local.f } ); } else { if (local.size + local.fileInfo.size > variables.maxPayloadSize || arrayLen(payload.files) > variables.maxPayloadFileCount) { @@ -303,8 +305,14 @@ component singleton="true" { local.progress = fileCounter; local.progress -= (pendingCounter/2); - local.percentValue = int( (local.progress/totalFileCount) * 100); - local.upperBound = int( (fileCounter/totalFileCount) * 100 ) - 2; + if (totalFileCount > 0) { + local.percentValue = int( (local.progress/totalFileCount) * 100); + local.upperBound = int( (fileCounter/totalFileCount) * 100 ) - 2; + } else { + local.percentValue = 100; + local.upperBound = 100; + } + if (pendingCounter > 0) { if (local.percentValue <= local.upperBound && local.lastPercentValue <= local.upperBound) { //increment counter while waiting for HTTP response @@ -366,11 +374,17 @@ component singleton="true" { } if (httpResult.statusCode contains "403") { //FORBIDDEN -- API KEY ISSUE - if (getAPIKey() == "UNDEFINED") { - throw(message="Fixinator API Key must be defined in an environment variable called FIXINATOR_API_KEY", detail="If you have already set the environment variable you may need to reopen your terminal or command prompt window. Please visit https://fixinator.app/ for more information", type="FixinatorClient"); + if (isCloudAPIURL()) { + if (getAPIKey() == "UNDEFINED") { + throw(message="Fixinator API Key must be defined in an environment variable called FIXINATOR_API_KEY", detail="If you have already set the environment variable you may need to reopen your terminal or command prompt window. Please visit https://fixinator.app/ for more information", type="FixinatorClient"); + } else { + throw(message="Fixinator API Key (#getAPIKey()#) is invalid, disabled or over the API request limit. Please contact Foundeo Inc. for assistance. Please provide your API key in correspondance. https://foundeo.com/contact/ ", detail="#httpResult.statusCode# #httpResult.fileContent#", type="FixinatorClient"); + } } else { - throw(message="Fixinator API Key (#getAPIKey()#) is invalid, disabled or over the API request limit. Please contact Foundeo Inc. for assistance. Please provide your API key in correspondance. https://foundeo.com/contact/ ", detail="#httpResult.statusCode# #httpResult.fileContent#", type="FixinatorClient"); + //is enterprise api + throw(message="Fixinator API #getAPIURL()# returned 403 Error. API Key: #getAPIKey()# If your Fixinator Enterprise Server has specified FIXINATOR_API_KEY environment variable, then your API key must match that value on the server. Please contact Foundeo Inc. for assistance. https://foundeo.com/contact/ ", detail="#httpResult.statusCode# #httpResult.fileContent#", type="FixinatorClient"); } + } else if (httpResult.statusCode contains "429") { //TOO MANY REQUESTS if (arguments.isRetry == 1) { @@ -383,7 +397,10 @@ component singleton="true" { } else if (httpResult.statusCode contains "502" || httpResult.statusCode contains "504") { //502 BAD GATEWAY or 504 Gateway Timeout - lambda timeout issue if (arguments.isRetry >= 2) { - throw(message="Fixinator API Returned #httpResult.statusCode# Status Code. Please try again shortly or contact Foundeo Inc. if the problem persists.", type="FixinatorClient"); + local.payloadPaths = arrayMap(arguments.payload.files, function(item) { + return item.path; + }); + throw(message="Fixinator API Returned #httpResult.statusCode# Status Code. Please try again shortly or contact Foundeo Inc. if the problem persists.", detail="Paths: #serializeJSON(local.payloadPaths)#", type="FixinatorClient"); } else { //retry it sleep(500);