Skip to content

Commit

Permalink
Fixinator Client Version 6.0.0
Browse files Browse the repository at this point in the history
* Add support for running Fixinator Enterprise directly inside commandbox (requires enterprise version 6.0.0+).
* Reporting enhancements.
* Command argument validation.
* Autocomplete goals on tab
  • Loading branch information
pfreitag committed Mar 3, 2025
1 parent d06ee7c commit 9e2977b
Show file tree
Hide file tree
Showing 5 changed files with 292 additions and 40 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Default: `false` - Prints out a list of scanners supported by the server in the

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
### gitChanged

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.

Expand Down
4 changes: 2 additions & 2 deletions box.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name":"fixinator",
"version":"5.0.1",
"version":"6.0.0",
"author":"Foundeo Inc.",
"location":"foundeo/fixinator#v5.0.1",
"location":"foundeo/fixinator#v6.0.0",
"homepage":"https://fixinator.app/",
"documentation":"https://github.com/foundeo/fixinator/wiki",
"repository":{
Expand Down
137 changes: 123 additions & 14 deletions commands/fixinator.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,29 @@ component extends="commandbox.system.BaseCommand" excludeFromHelp=false {
property name="shell" inject="shell";


//used for validation to avoid typos
variables.supportedArgs = ["path",
"resultFile",
"resultFormat",
"verbose",
"listBy",
"severity",
"confidence",
"ignoreScanners",
"autofix",
"failOnIssues",
"debug",
"listScanners",
"ignorePaths",
"ignoreExtensions",
"gitLastCommit",
"gitChanged",
"engines",
"includeScanners",
"configFile",
"goals"
];

/**
* @path.hint A file or directory to scan
* @resultFile.hint A file path to write the results to - see resultFormat
Expand All @@ -26,24 +49,25 @@ component extends="commandbox.system.BaseCommand" excludeFromHelp=false {
* @severity.optionsUDF severityComplete
* @confidence.hint The minimum confidence level none, low, medium or high
* @confidence.optionsUDF confidenceComplete
* @ignoreScanners.hint A comma seperated list of scanner ids to ignore
* @ignoreScanners.hint A comma separated list of scanner ids to ignore
* @autofix.hint Use either off, prompt or automatic
* @failOnIssues.hint Determines if an exit code is set to 1 when issues are found.
* @debug.hint Enable debug mode
* @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
* @gitChanged.hint Scan only files changed since the last commit in the working copy
* @engines.hint A list of engines your code runs on, eg: lucee@5,adobe@2023 default any
* @includeScanners.hint A comma seperated list of scanner ids to scan, all others ignored
* @includeScanners.hint A comma separated list of scanner ids to scan, all others ignored
* @configFile.hint A path to a .fixinator.json file to use
* @goals.hint A list of goals for scanning [compatibility,security], default: security
* @goals.optionsUDF goalsComplete
**/
function run(
string path=".",
string resultFile,
string resultFormat="json",
string resultFormat="",
boolean verbose=true,
string listBy="type",
string severity="default",
Expand All @@ -68,10 +92,12 @@ component extends="commandbox.system.BaseCommand" excludeFromHelp=false {
var config = {};
var toFix = [];
var paths = [];
var isEnterprise = false;
var arg = "";
if (arguments.verbose) {
//arguments.listScanners = true;

print.greenLine("fixinator v#fixinatorClient.getClientVersion()# built by Foundeo Inc.").line();
print.greenLine("fixinator client v#fixinatorClient.getClientVersion()# built by Foundeo Inc.").line();
print.grayLine(" ___ _ ");
print.grayLine(" / __) | | ");
print.grayLine(" _| |__ ___ _ _ ____ __| |_____ ___ ");
Expand All @@ -82,23 +108,47 @@ component extends="commandbox.system.BaseCommand" excludeFromHelp=false {
print.line();
}

try {
var enterpriseInstance = new fixinatorapi.app.models.fixinator();
if (arguments.verbose) {
print.grayLine("Fixinator Enterprise v#enterpriseInstance.getVersion()# Detected");
}
fixinatorClient.setFixinatorEnterpriseInstance(enterpriseInstance);
if (fixinatorClient.getMaxPayloadSize() == (1024*640)) {
//increase default for enterprise version to 2mb, can be overridden in settings
fixinatorClient.setMaxPayloadSize(2*1024*1024);
}
isEnterprise = true;
} catch(any entErr) {
//not using enterprise version
}

//validate arguments
for (arg in arguments) {
if (!arrayFindNoCase(variables.supportedArgs, arg)) {
print.boldRedLine("Invalid argument: #reReplace(arg, '[^a-zA-Z0-9]', '', 'ALL')# passed to fixinator.");
print.redLine("Type help fixinator to see the supported arguments.");
setExitCode(1);
return;
}
}

if (configService.getSetting("modules.fixinator.api_key", "UNDEFINED") != "UNDEFINED") {
fixinatorClient.setAPIKey(configService.getSetting("modules.fixinator.api_key", "UNDEFINED"));
}

if (fixinatorClient.getAPIKey() == "UNDEFINED") {
if (!fixinatorClient.hasFixinatorEnterpriseInstance() && fixinatorClient.getAPIKey() == "UNDEFINED") {
print.boldOrangeLine("Missing Fixinator API Key");
print.orangeLine(" Set via commandbox: config set modules.fixinator.api_key=YOUR_API_KEY");
print.orangeLine(" Or set an environment variable FIXINATOR_API_KEY=YOUR_API_KEY");
print.line();
print.line("For details please visit: https://fixinator.app/");
print.line("An API Key is required, unless you are using the Enterprise Version of Fixinator.");
print.line(" For details please visit: https://fixinator.app/");
print.line();
if (isRunningInCI()) {
//in CI we don't want to prompt
print.line("Detected CI Envrionment. Please add the FIXINATOR_API_KEY as a secure environment variable to your CI platform.");

print.line("Detected CI Environment. Please add the FIXINATOR_API_KEY as a secure environment variable to your CI platform.");
if (isTravisCI()) {
print.line("Documentation: https://github.com/foundeo/fixinator/wiki/Running-Fixinator-on-Travis-CI");
}
Expand All @@ -119,17 +169,27 @@ component extends="commandbox.system.BaseCommand" excludeFromHelp=false {
print.line("Documentation: https://github.com/foundeo/fixinator/wiki/Running-Fixinator-on-Jenkins");
}

print.line();
print.line("Running the Enterprise Version of Fixinator?");
printEnterpriseInstallInstructions();

setExitCode(1);
return;
} else {
}


}
print.line();
print.line("To request a free trial api key, please go here: https://fixinator.app/try/");
local.answer = ask(message="Would you like to open https://fixinator.app/try/ in your browser now? [y/n]: ");
if (left(local.answer, 1) == "y") {
command("browse").params(URI="https://fixinator.app/try/").run();
} else {
print.line();
local.answer = ask(message="Do you have a Fixinator Enterprise Version License? [y/n]: ");
if (left(local.answer, 1) == "y") {
print.line("You do not need an API key to run the Fixinator Enterprise Version Locally.");
printEnterpriseInstallInstructions();
}
}
print.line();
print.line("Exiting Fixinator, please try again once you have an api key");
Expand All @@ -141,7 +201,7 @@ component extends="commandbox.system.BaseCommand" excludeFromHelp=false {
fixinatorClient.setAPIURL(configService.getSetting("modules.fixinator.api_url", "UNDEFINED"));
}

if (fixinatorClient.isCloudAPIURL() && configService.getSetting("modules.fixinator.accept_policy", "UNDEFINED") == "UNDEFINED") {
if (!fixinatorClient.hasFixinatorEnterpriseInstance() && fixinatorClient.isCloudAPIURL() && configService.getSetting("modules.fixinator.accept_policy", "UNDEFINED") == "UNDEFINED") {
print.line();
print.line("Fixinator will send source code to: " & fixinatorClient.getAPIURL());
print.line("for scanning. The code is kept in RAM during scanning and is not persisted.");
Expand All @@ -164,6 +224,22 @@ component extends="commandbox.system.BaseCommand" excludeFromHelp=false {

}

if (!fixinatorClient.hasFixinatorEnterpriseInstance() && fixinatorClient.isCloudAPIURL() && left(fixinatorClient.getAPIKey(), 1) != "f") {
var maskedKey = left(fixinatorClient.getAPIKey(), 4);
if (len(fixinatorClient.getAPIKey()) > 4) {
maskedKey &= repeatString("*", len(fixinatorClient.getAPIKey())-4);
}
if (fixinatorClient.getAPIKey() == "UNDEFINED") {
maskedKey = "UNDEFINED";
}
print.line();
print.line("Invalid License Key(#maskedKey#) for API Server: " & fixinatorClient.getAPIURL());
print.line("Running the Enterprise Version?");
printEnterpriseInstallInstructions();
setExitCode(1);
return;
}



if (configService.getSetting("modules.fixinator.max_payload_size", "UNDEFINED") != "UNDEFINED") {
Expand All @@ -182,7 +258,7 @@ component extends="commandbox.system.BaseCommand" excludeFromHelp=false {
fixinatorClient.setMaxConcurrency(configService.getSetting("modules.fixinator.max_concurrency", "8"));
}

if (arguments.verbose) {
if (arguments.verbose && !fixinatorClient.hasFixinatorEnterpriseInstance()) {
print.greenLine("Fixinator API Server: #fixinatorClient.getAPIURL()#");
}

Expand Down Expand Up @@ -438,6 +514,17 @@ component extends="commandbox.system.BaseCommand" excludeFromHelp=false {

if (len(arguments.resultFile)) {
local.resultIndex = 0;
//default resultFormat to the file extension if supported
if (len(arguments.resultFormat) == 0) {
if (listFindNoCase("pdf,html,csv,json", listLast(arguments.resultFile, "."))) {
arguments.resultFormat = lcase(listLast(arguments.resultFile, "."));
} else {
//defaults to json
arguments.resultFormat = "json";
}
}
local.results["fixinatorArguments"] = duplicate(arguments);
local.results["fixinatorArguments"]["pwd"] = shell.pwd();
//allow a list of formats and file paths
for (local.rFormat in listToArray(arguments.resultFormat)) {
local.resultIndex++;
Expand All @@ -449,8 +536,16 @@ component extends="commandbox.system.BaseCommand" excludeFromHelp=false {


if (arrayLen(local.results.results) == 0 && arrayLen(local.results.warnings) == 0) {
print.line().boldGreenLine("0 Issues Found");
print.line().boldGreenLine("0 Issues Found");
if (arguments.verbose) {
print.line();
print.greenLine(" //");
print.greenLine(" //");
print.greenLine(" //");
print.greenLine(" \\ //");
print.greenLine(" \\//");

print.line();
if (local.results.config.minSeverity != "low" || local.results.config.minConfidence != "low") {
print.line().line("Tip: For additional results try decreasing the severity or confidence level to medium or low");
print.line("For example: box fixinator confidence=low path=/some/file.cfm");
Expand Down Expand Up @@ -717,6 +812,20 @@ component extends="commandbox.system.BaseCommand" excludeFromHelp=false {
return [ 'low', 'medium', 'high', 'warn' ];
}

function goalsComplete() {
return [ 'security', 'compatibility', 'security,compatibility' ];
}

private function printEnterpriseInstallInstructions() {
print.boldLine("Perform one of these steps to setup the enterprise version:");
print.line(" * Install the fixinator enterprise command (version 6 and up): ");
print.line(" box install c:\temp\fixinator-enterprise-x.y.z.zip")
print.line(" * Or if running your own Fixinator server set the FIXINATOR_API_URL environment variable or run:");
print.line(" box config set modules.fixinator.api_url=http://127.0.0.1:48443/scan/");
print.line();
print.line("See instructions.txt located in your fixinator-enterprise.zip file for details.");
print.line("The zip file can be downloaded from your customer account on foundeo.com");
}

private boolean function isRunningInCI() {
var env = server.system.environment;
Expand Down
38 changes: 37 additions & 1 deletion models/fixinator/FixinatorClient.cfc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
component singleton="true" {
component {

variables.system = createObject("java", "java.lang.System");
variables.maxPayloadSize = 1 * 640 * 1024;//half mb+128b
Expand Down Expand Up @@ -233,10 +233,26 @@ component singleton="true" {
variables.maxPayloadSize = arguments.size;
}

public function getMaxPayloadSize() {
return variables.maxPayloadSize;
}

public function setMaxPayloadFileCount(numeric count) {
variables.maxPayloadFileCount = arguments.count;
}

public function setFixinatorEnterpriseInstance(any instance) {
variables.fixinatorEnterpriseInstance = arguments.instance;
}

public function getFixinatorEnterpriseInstance() {
return variables.fixinatorEnterpriseInstance;
}

public function hasFixinatorEnterpriseInstance() {
return variables.keyExists("fixinatorEnterpriseInstance");
}

private function processBatch(element, index) {

if (element.batchType == "progressBar") {
Expand Down Expand Up @@ -405,6 +421,12 @@ component singleton="true" {

public function sendPayload(payload, isRetry=0) {
var httpResult = "";

//if enterprise version locally installed, use that
if (hasFixinatorEnterpriseInstance()) {
return sendPayloadEnterprise(payload=arguments.payload);
}

local.payloadID = left(createUUID(),12);
if (isDebugModeEnabled()) {

Expand All @@ -414,6 +436,7 @@ component singleton="true" {
debugger("Sending Payload #local.payloadID# to #getAPIURL()# of #arrayLen(arguments.payload.files)# files. isRetry:#arguments.isRetry#");
debugger("Payload Paths #local.payloadID#: #serializeJSON(local.payloadPaths)#");
}

local.tick = getTickCount();
cfhttp(url=getAPIURL(), method="POST", result="httpResult", timeout=getAPITimeout()) {
cfhttpparam(type="header", name="Content-Type", value="application/json");
Expand Down Expand Up @@ -519,6 +542,19 @@ component singleton="true" {
return deserializeJSON(httpResult.fileContent);
}

public function sendPayloadEnterprise(payload) {
if (isDebugModeEnabled()) {
local.payloadID = left(createUUID(),12);
local.payloadPaths = arrayMap(arguments.payload.files, function(item) {
return item.path;
});
debugger("Sending Payload #local.payloadID# of #arrayLen(arguments.payload.files)# files to Fixinator Enterprise.");
debugger("Payload Paths #local.payloadID#: #serializeJSON(local.payloadPaths)#");
}
var fixinatorEnterprise = getFixinatorEnterpriseInstance();
return fixinatorEnterprise.scan(payload);
}

public function getAPIKey() {
if (structKeyExistS(variables, "fixinatorAPIKey")) {
return variables.fixinatorAPIKey;
Expand Down
Loading

0 comments on commit 9e2977b

Please sign in to comment.