Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
com.zachary-moore:JSONCustomLintr
Advanced tools
Library for the creation, running, and reporting of Custom Lint Rules for files that follow JSON Notation.
Library for the creation, running, and reporting of Custom Lint Rules for files that follow JSON Notation.
In order to pull down this library from maven check out the Maven Info
Head over to JSONCustomLinrExampleImplementation for full implementation example.
The primary motivation for creating the library is for creating linting rules for avro schemas in an API environment.
Introducing a tool to allow developers to lint JSON helps to:
JSONCustomLintr leverages JSON-java to generate Java objects from JSON files. This allows us to retain all the information we get from the library while also wrapping to provide more context to the object when creating linting rules.
Features of the library include:
Simple lint rule looking for any non-key String that is test
Example:
Bad
{
"name": "test"
}
Good
{
"name": "John"
}
Java Implementation in one method
class Example {
public static void setupLinter() {
// Create LintImplementation
LintImplementation<WrappedPrimitive<String>> lintImplementation = new LintImplementation<WrappedPrimitive<String>>() {
@Override
public Class<String> getClazz() {
return String.class;
}
@Override
public boolean shouldReport(WrappedPrimitive<String> string) {
return string.getValue().equals("test");
}
@Override
public String report(WrappedPrimitive<String> string) {
return string.getValue() + " found in file.";
}
};
// Use builder to create rule
LintRule rule = new LintRule.Builder()
.setLevel(LintLevel.ERROR)
.setImplementation(lintImplementation)
.setIssueId("STRING_VALUE_NAMED_TEST")
.build();
// Create register and register rule
LintRegister register = new LintRegister();
register.register(rule);
// Create LintRunner with register and path to lint
LintRunner lintRunner = new LintRunner(register, "./models");
// Create ReportRunner and report lint errors
ReportRunner reportRunner = new ReportRunner(lintRunner);
reportRunner.report("build/reports");
}
}
When creating and running lint rules there is a flow of classes to generate in order to create the rule.
The classes are:
LintImplementation<T>
- Target WrappedObject
implementing class type, determine rules for failure, configure output
↓
LintRule.Builder
→ LintRule
- Configure severity, set issue ID, explanation, description, and implementation.
↓
LintRegister
- Register all LintRule
s
↓
LintRunner
- Pass in LintRegister
and configure directories or files to be checked with registry's issues
↓
ReportRunner
- Pass in LintRunner
and generate HTML report
WrappedObject
is an interface that 3 of our core classes implement.
This interface allows us to have more context about the objects we look at when analyzing them for linting.
The interface provides 4 methods:
getOriginatingKey()
- returns the closest JSONObject
key associated with this Object. If there is no immediate key it will travel up the chain until one is found. Only the root JSONObject
will have a null
returngetParentObject()
- returns the parent WrappedObject
that created this Object. Only the root JSONObject
will have a null
returnparseAndReplaceWithWrappers()
- void method that will parse the sub objects of this Object and replace them with WrappedObject
s.isPrimitive()
- returns true
if the Object is simply a wrapper around a primitive valueIn the library we have 3 WrappedObject
implementing classes:
JSONObject
- A wrapper around the JSON-java JSONObject
that @Override
s the toMap()
to return this library's objectsJSONArray
- A wrapper around the JSON-java JSONArray
that @Override
s the toList()
and toJSONObject()
to return this library's objectsWrappedPrimitive<T>
- A wrapper around all other datatypes in java in order to provide extra context in terms of the JSON File. This class has a getValue()
method to return the original object it was generated from.LintImplementation
is the core of the library.
LintImplementation
is an abstract class with 3 abstract methods and a type generic.
LintImplementation
takes in a type generic which must be one of the 3 provided classes that implement WrappedObject
.
LintImplementation
has 4 methods and an instance variable:
private String reportMessage
- the message that will be reported when this implementation catches a lint error. This String
can be set at runtime or ignored and overwrote with report(T t)
getClazz()
- returns the target class to be analyzed. If using WrappedPrimitive<T>
must return T.class
else must return JSONArray
or JSONObject
shouldReport(T t)
- the main function of the class. This is where your LintRule will either catch an error or not. Every instance of the <T>
of your LintImlpementation
will run through this method. This is where you should apply your Lint logic and decide whether or not to reportreport(T t)
- funtion to return reportMessage
or be overwrote
and return a more static stringsetReportMessage()
- manually set the reportMessage
string in the class (usually during shouldReport()
) to provide more detail in the lint reportNote: If a reportMessage is not set when report()
is called a NoReportSetException
will be thrown.
When working with LintImplementation
and WrappedPrimitive
you must create your LintImplementation
of type WrappedPrimitive<T>
such as
new LintImplementatioin<WrappedPrimitive<Integer>>()
However when writing your getClazz()
method you must return the inner class of the WrappedPrimitive
.
For Example:
Bad
LintImplementation<WrappedPrimitive<String>> lintImplementation ...
...
Class<T> getClazz() {
return WrappedPrimitive.class;
}
...
Good
LintImplementation<WrappedPrimitive<String>> lintImplementation ...
...
Class<T> getClazz() {
return String.class;
}
When writing your shouldReport for a LintImplementation
you have access to a lot of helper methods to assist in navigating the JSON
File.
A list of existing helper methods available from BaseJSONAnalyzer
are:
protected boolean hasKeyAndValueEqualTo(JSONObject jsonObject, String key, Object toCheck);
protected boolean hasIndexAndValueEqualTo(JSONArray jsonArray, int index, Object toCheck);
protected WrappedPrimitive safeGetWrappedPrimitive(JSONObject jsonObject, String key);
protected JSONObject safeGetJSONObject(JSONObject jsonObject, String key);
protected JSONArray safeGetJSONArray(JSONObject jsonObject, String key);
protected WrappedPrimitive safeGetWrappedPrimitive(JSONArray array, int index);
protected JSONObject safeGetJSONObject(JSONArray array, int index);
protected JSONArray safeGetJSONArray(JSONArray array, int index);
protected <T> boolean isEqualTo(WrappedPrimitive<T> wrappedPrimitive, T toCheck);
protected boolean isOriginatingKeyEqualTo(WrappedObject object, String toCheck);
protected <T> boolean isType(Object object, Class<T> clazz);
protected <T> boolean isParentOfType(WrappedObject object, Class<T> clazz);
protected boolean reduceBooleans(Boolean... booleans);
There are 2 ways to set your reportMessage:
@Override
the report()
method.setReportMessage()
in the shouldReport()
and have more dynmic report messagesLintRule
is our class we use to setup what triggers a failure for a lint rule as well as what will happen when we have a failure.
LineRule
can only be created with LintRule.Builder
and can not be directly instantiated.
A LintRule
can have the following properties set through the builder:
LintLevel level
(REQUIRED) - can be IGNORE
, WARNING
, ERROR
and signals severity of Lint RuleLintImplementation implementation
(REQUIRED) - LintImplementation
conigured to determine when this lint rule should report issuesString issueId
(REQUIRED) - Name of this lint rule. Must be unique.String issueDescription
- Short description of this lint rule.String issueExplanation
- More in-depth description of lint rule.Note: If the required fields are not set when LintRule.Builder.build()
is called a LintRuleBuilderException
will be thrown.
LintRegister
is a simple class to register as many or as few LintRule
s as wanted.
Our only method is
register(LintRule ...toRegister)
which will register LintRule
s.
Our LintRegister
acts as a simple intermediate between non IO parts of the Lint stack and our IO parts of our Lint stack, the LintRunner
LintRunner
is our class that takes in a LintRegister
and String basePath
to load files from.
This class has a
public Map<LintRule, Map<JSONFile, List<String>>> lint()
method which will lint our files for us but usually is just used as an intermediate class between our linting stack and reporting stack.
When calling lint()
LintRunner
will internally store the result for later analysis in
public int analyzeLintAndGiveExitCode()
analyzeLintAndGiveExitCode()
will analyze the interal lint representation and return eithe a 0
or 1
, the latter indicating a lint failure.
This method is called at the end of ReportRunner
's report()
method.
ReportRunner
is the entrypoint to our Reporting stack and the end point of our linting library.
The class takes in a LintRunner
to connect and interact with our Linting stack.
The class also has a
public void report(String outputPath);
method which will generate an html report of all the lint errors in the given path as supplied by the LintRunner
as well as call System.exit()
based on the LintRunner
's analysis of whether we passed our lint or not.
In this repo we implemented a gradle task to be able to be tied into any build integration we want to do with our project.
All that needs to happen is a new repo needs to be created with your custom linting rules, a main needs to tie it all together, and a gradle task has to hit the main.
Since our ReportRunner
class handles exit codes automatically for us, we can simply tie this build task however we want into our pipeline and we will either fail or succeed based on our lint status.
When trying to hook up to existing repos we can take 2 approaches:
In this example we are checking if a JSONObject
:
type
field which a value of boolean
name
field with a value that is a String
and starts with has
fields
JSONArray
Example
Bad
{
"fields" : [
{
"name": "hasX",
"type": "boolean"
}]
}
class Example {
public static void setupLint() {
LintImplementation<JSONObject> lintImplementation = new LintImplementation<JSONObject>() {
@Override
public Class<JSONObject> getClazz() {
return JSONObject.class;
}
@Override
public boolean shouldReport(JSONObject jsonObject) {
boolean hasBooleanType = hasKeyAndValueEqualTo(jsonObject, "type", "boolean");
WrappedPrimitive name = safeGetWrappedPrimitive(jsonObject, "name");
boolean nameStartsWithHas = false;
if (name != null && name.getValue() instanceof String) {
nameStartsWithHas = ((String) name.getValue()).startsWith("has");
}
boolean originatingKeyIsFields = isOriginatingKeyEqualTo(jsonObject, "fields");
boolean isParentArray = isParentOfType(jsonObject, JSONArray.class);
setReportMessage("This is a bad one:\t" + jsonObject);
return reduceBooleans(hasBooleanType, nameStartsWithHas, originatingKeyIsFields, isParentArray);
}
};
LintRule rule = new LintRule.Builder()
.setLevel(LintLevel.ERROR)
.setImplementation(lintImplementation)
.setIssueId("BOOLEAN_NAME_STARTS_WITH_HAS")
.build();
LintRegister register = new LintRegister();
register.register(rule);
LintRunner lintRunner = new LintRunner(register, "./models");
ReportRunner reportRunner = new ReportRunner(lintRunner);
reportRunner.report("build/reports");
}
}
As this library progresses this report will evolve over time
1/14/19 - Bootstrap and more advanced styling added
1/14/19 - First report unstyled, minimal information
FAQs
Library for the creation, running, and reporting of Custom Lint Rules for files that follow JSON Notation.
We found that com.zachary-moore:JSONCustomLintr demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.