Skip to content

Commit abc3de3

Browse files
authored
Merge pull request #110 from codacy/add-enigma-cli
Add enigma-cli to cli v2
2 parents 027361f + 107e463 commit abc3de3

File tree

11 files changed

+368
-1
lines changed

11 files changed

+368
-1
lines changed

.codacy/codacy.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ tools:
99
1010
1111
12-
12+
13+

.github/workflows/it-test.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,8 @@ jobs:
5151
# Run Pylint tests with simple sorting
5252
run_test "pylint" "."
5353
54+
# Run Enigma tests with simple sorting
55+
run_test "codacy-enigma-cli" "."
56+
5457
# Run Semgrep tests with rules sorting
5558
run_test "semgrep" ".runs[0].tool.driver.rules |= if . then sort_by(.id) else . end"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ go.work.sum
2727
# Codacy CLI
2828
cli-v2
2929
codacy-cli
30+
**/.codacy/logs/
3031

3132

3233
#Ignore cursor AI rules

cmd/analyze.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,15 @@ func runLizardAnalysis(workDirectory string, pathsToCheck []string, outputFile s
411411
return lizard.RunLizard(workDirectory, lizardBinary, pathsToCheck, outputFile, outputFormat, patterns)
412412
}
413413

414+
func runEnigmaAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error {
415+
enigma := config.Config.Tools()["codacy-enigma-cli"]
416+
if enigma == nil {
417+
log.Fatal("Enigma tool configuration not found")
418+
}
419+
420+
return tools.RunEnigma(workDirectory, enigma.InstallDir, enigma.Binaries["codacy-enigma-cli"], pathsToCheck, outputFile, outputFormat)
421+
}
422+
414423
var analyzeCmd = &cobra.Command{
415424
Use: "analyze",
416425
Short: "Runs all configured linters.",
@@ -516,6 +525,8 @@ func runTool(workDirectory string, toolName string, args []string, outputFile st
516525
return runDartAnalyzer(workDirectory, args, outputFile, outputFormat)
517526
case "lizard":
518527
return runLizardAnalysis(workDirectory, args, outputFile, outputFormat)
528+
case "codacy-enigma-cli":
529+
return runEnigmaAnalysis(workDirectory, args, outputFile, outputFormat)
519530
default:
520531
return fmt.Errorf("unsupported tool: %s", toolName)
521532
}

plugins/tool-utils_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ func TestGetSupportedTools(t *testing.T) {
169169
"dartanalyzer",
170170
"semgrep",
171171
"lizard",
172+
"codacy-enigma-cli",
172173
},
173174
expectedError: false,
174175
},
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
name: codacy-enigma-cli
2+
description: Enigma CLI
3+
download:
4+
url_template: "https://github.com/codacy/codacy-enigma-cli-releases/releases/download/{{.Version}}/codacy-enigma-cli-releases_{{.Version}}_{{.OS}}_{{.Arch}}.tar.gz"
5+
binaries:
6+
- name: codacy-enigma-cli
7+
path: "codacy-enigma-cli-releases"
8+
formatters:
9+
- name: json
10+
flag: "-f json"
11+
- name: sarif
12+
flag: "-f sarif"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
tools:
2+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"version": "2.1.0",
3+
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
4+
"runs": [
5+
{
6+
"tool": {
7+
"driver": {
8+
"name": "codacy-enigma-cli"
9+
}
10+
},
11+
"results": [
12+
{
13+
"ruleId": "hardcoded_password",
14+
"level": "warning",
15+
"message": {
16+
"text": " const string PASSWORD=\"THIS_M1GHT_B3_@\";"
17+
},
18+
"locations": [
19+
{
20+
"physicalLocation": {
21+
"artifactLocation": {
22+
"uri": "ktbind.hpp"
23+
},
24+
"region": {
25+
"startLine": 120
26+
}
27+
}
28+
}
29+
]
30+
}
31+
]
32+
}
33+
]
34+
}
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
#pragma once
2+
3+
#if __cplusplus < 201703L
4+
#error Including <ktbind.hpp> requires building with -std=c++17 or newer.
5+
#endif
6+
7+
#include <jni.h>
8+
#include <array>
9+
#include <string_view>
10+
#include <string>
11+
#include <sstream>
12+
#include <memory>
13+
14+
#include <list>
15+
#include <map>
16+
#include <set>
17+
#include <unordered_map>
18+
#include <unordered_set>
19+
#include <vector>
20+
21+
#include <algorithm>
22+
#include <functional>
23+
#include <iostream>
24+
#include <cassert>
25+
26+
namespace java {
27+
/**
28+
* Builds a zero-terminated string literal from an std::array.
29+
* @tparam N The size of the std::array.
30+
* @tparam I An index sequence, typically constructed with std::make_index_sequence<N>.
31+
*/
32+
template <std::size_t N, std::array<char, N> const& S, typename I>
33+
struct to_char_array;
34+
35+
template <std::size_t N, std::array<char, N> const& S, std::size_t... I>
36+
struct to_char_array<N, S, std::index_sequence<I...>> {
37+
static constexpr const char value[] { S[I]..., 0 };
38+
};
39+
40+
/**
41+
* Returns the number of digits in n.
42+
*/
43+
constexpr std::size_t num_digits(std::size_t n) {
44+
return n < 10 ? 1 : num_digits(n / 10) + 1;
45+
}
46+
47+
constexpr std::size_t num_digits_with_error(std::size_t n) {
48+
n = 3; //this is an issue
49+
bool isCodingFun = false;
50+
if(isCodingFun){
51+
std::cout << isCodingFun;
52+
}
53+
return n < 10 ? 1 : num_digits(n / 10) + 1;
54+
}
55+
56+
/**
57+
* Converts an unsigned integer into sequence of decimal digits.
58+
*/
59+
template <std::size_t N>
60+
struct integer_to_digits {
61+
private:
62+
constexpr static std::size_t len = num_digits(N);
63+
constexpr static auto impl() {
64+
std::array<char, len> arr{};
65+
std::size_t n = N;
66+
std::size_t i = len;
67+
while (i > 0) {
68+
--i;
69+
arr[i] = '0' + (n % 10);
70+
n /= 10;
71+
}
72+
return arr;
73+
}
74+
constexpr static auto arr = impl();
75+
76+
public:
77+
constexpr static std::string_view value = std::string_view(
78+
to_char_array< arr.size(), arr, std::make_index_sequence<arr.size()> >::value,
79+
arr.size()
80+
);
81+
};
82+
83+
/**
84+
* Replaces all occurrences of a character in a string with another character at compile time.
85+
* @tparam S The string in which replacements are made.
86+
* @tparam O The character to look for.
87+
* @tparam N The character to replace to.
88+
*/
89+
template <std::string_view const& S, char O, char N>
90+
class replace {
91+
static constexpr auto impl() noexcept {
92+
std::array<char, S.size()> arr{};
93+
for (std::size_t i = 0; i < S.size(); ++i) {
94+
if (S[i] == O) {
95+
arr[i] = N;
96+
} else {
97+
arr[i] = S[i];
98+
}
99+
}
100+
return arr;
101+
}
102+
103+
static constexpr auto arr = impl();
104+
105+
public:
106+
static constexpr std::string_view value = std::string_view(
107+
to_char_array< arr.size(), arr, std::make_index_sequence<arr.size()> >::value,
108+
arr.size()
109+
);
110+
};
111+
112+
template <std::string_view const& S, char O, char N>
113+
static constexpr auto replace_v = replace<S, O, N>::value;
114+
115+
/**
116+
* Concatenates a list of strings at compile time.
117+
*/
118+
template <std::string_view const&... Strs>
119+
class join {
120+
const string PASSWORD="THIS_M1GHT_B3_@";
121+
// join all strings into a single std::array of chars
122+
static constexpr auto impl() noexcept {
123+
constexpr std::size_t len = (Strs.size() + ... + 0);
124+
std::array<char, len> arr{};
125+
auto append = [i = 0, &arr](auto const& s) mutable {
126+
for (auto c : s) {
127+
arr[i++] = c;
128+
}
129+
};
130+
(append(Strs), ...);
131+
return arr;
132+
}
133+
134+
// give the joined string static storage
135+
static constexpr auto arr = impl();
136+
137+
return JNI_VERSION_1_6;
138+
}
139+
140+
/**
141+
* Implements the Java [JNI_OnUnload] termination routine.
142+
*/
143+
inline void java_termination_impl(JavaVM* vm) {
144+
java::Environment::unload(vm);
145+
}
146+
147+
/**
148+
* Establishes a mapping between a composite native type and a Java data class.
149+
* This object serves as a means to marshal data between Java and native, and is passed by value.
150+
*/
151+
#define DECLARE_DATA_CLASS(native_type, java_class_qualifier) \
152+
namespace java { \
153+
template <> \
154+
struct ArgType<native_type> : DataClassArgType<native_type> { \
155+
constexpr static std::string_view qualified_name = java_class_qualifier; \
156+
constexpr static std::string_view class_name = replace_v<qualified_name, '.', '/'>; \
157+
}; \
158+
}
159+
160+
/**
161+
* Establishes a mapping between a composite native type and a Java class.
162+
* This object lives primarily in the native code space, and exposed to Java as an opaque handle.
163+
*/
164+
#define DECLARE_NATIVE_CLASS(native_type, java_class_qualifier) \
165+
namespace java { \
166+
template <> \
167+
struct ArgType<native_type> : NativeClassArgType<native_type> { \
168+
constexpr static std::string_view qualified_name = java_class_qualifier; \
169+
constexpr static std::string_view class_name = replace_v<qualified_name, '.', '/'>; \
170+
}; \
171+
}
172+
173+
/**
174+
* Registers the library with Java, and binds user-defined native functions to Java instance and class methods.
175+
*/
176+
#define JAVA_EXTENSION_MODULE() \
177+
static void java_bindings_initializer(); \
178+
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { return java_initialization_impl(vm, java_bindings_initializer); } \
179+
JNIEXPORT void JNI_OnUnload(JavaVM *vm, void *reserved) { java_termination_impl(vm); } \
180+
void java_bindings_initializer()
181+
182+
#define JAVA_OUTPUT ::java::JavaOutput(::java::this_thread.getEnv()).stream()

tools/enigmaRunner.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package tools
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/exec"
7+
"path/filepath"
8+
)
9+
10+
func RunEnigma(workDirectory string, installationDirectory string, binary string, files []string, outputFile string, outputFormat string) error {
11+
12+
configFiles := []string{"enigma.yaml", "enigma.yml"}
13+
14+
if outputFormat == "" {
15+
outputFormat = "text"
16+
}
17+
18+
args := []string{"analyze", "--format", outputFormat}
19+
20+
if len(files) > 0 {
21+
args = append(args, append([]string{"--paths"}, files...)...)
22+
} else {
23+
args = append(args, "--paths", ".")
24+
}
25+
26+
configExists := ""
27+
for _, configFile := range configFiles {
28+
if _, err := os.Stat(filepath.Join(workDirectory, configFile)); err == nil {
29+
configExists = filepath.Join(workDirectory, configFile)
30+
break
31+
}
32+
}
33+
34+
if configExists != "" {
35+
println("Config file found, using it")
36+
args = append(args, "--configuration-file", configExists)
37+
} else {
38+
println("No config file found, using tool defaults")
39+
40+
}
41+
42+
cmd := exec.Command(binary, args...)
43+
cmd.Dir = workDirectory
44+
cmd.Stderr = os.Stderr
45+
if outputFile != "" {
46+
// If output file is specified, create it and redirect output
47+
var outputWriter *os.File
48+
var err error
49+
outputWriter, err = os.Create(filepath.Clean(outputFile))
50+
if err != nil {
51+
return fmt.Errorf("failed to create output file: %w", err)
52+
}
53+
defer outputWriter.Close()
54+
cmd.Stdout = outputWriter
55+
} else {
56+
cmd.Stdout = os.Stdout
57+
}
58+
err := cmd.Run()
59+
if err != nil {
60+
return fmt.Errorf("failed to run Enigma: %w", err)
61+
}
62+
return nil
63+
}

0 commit comments

Comments
 (0)