From 9506749dabf3b04f79ee3486944285ec9b2d4fe1 Mon Sep 17 00:00:00 2001
From: Vincent Tan <vtky@users.noreply.github.com>
Date: Sun, 12 Jul 2020 03:34:44 +0800
Subject: [PATCH 1/2] jsonschema validator v1

---
 engines-schema.json | 226 ++++++++++++++++++++++++++++++++++++++++++++
 jsonschema.js       |  26 +++++
 package-lock.json   |   5 +
 package.json        |   1 +
 4 files changed, 258 insertions(+)
 create mode 100644 engines-schema.json
 create mode 100644 jsonschema.js

diff --git a/engines-schema.json b/engines-schema.json
new file mode 100644
index 0000000..c63c538
--- /dev/null
+++ b/engines-schema.json
@@ -0,0 +1,226 @@
+{
+	"$schema": "http://json-schema.org/draft-07/schema",
+	"$id": "http://example.com/example.json",
+	"type": "object",
+	"title": "GuardRails Engines root schema",
+	"description": "This jsonschema is to validate and standardize all engine outputs.",
+	"required": [
+		"engine",
+		"language",
+		"status",
+		"executionTime",
+		"issues",
+		"process",
+		"output"
+	],
+	"properties": {
+		"engine": {
+			"$id": "#/properties/engine",
+			"type": "object",
+			"title": "The engine schema",
+			"description": "An explanation about the purpose of this instance.",
+			"default": {},
+			"examples": [{
+				"name": "@guardrails/guardrails-engine-typescript-tslint",
+				"version": "1.0.0"
+			}],
+			"required": ["name", "version"],
+			"properties": {
+				"name": {
+					"$id": "#/properties/engine/properties/name",
+					"type": "string",
+					"pattern": "^@guardrails/guardrails-engine-.*$",
+					"title": "The name schema",
+					"description": "An explanation about the purpose of this instance.",
+					"examples": [
+						"@guardrails/guardrails-engine-typescript-tslint"
+					]
+				},
+				"version": {
+					"$id": "#/properties/engine/properties/version",
+					"type": "string",
+					"title": "The version schema",
+					"description": "Version number of the engine"
+				}
+			}
+		},
+		"language": {
+			"$id": "#/properties/language",
+			"type": "string",
+			"enum": ["c", "ruby", "php", "python", "solidity", "elixir", "rust", "terraform", "mobile", "go", "java", "javascript", "kubernetes", "general", "typescript"],
+			"title": "The language schema",
+			"description": "Main engine lanaguage"
+		},
+		"status": {
+			"$id": "#/properties/status",
+			"type": "string",
+			"enum": ["success", "completed", "errror"],
+			"title": "The status schema",
+			"description": "An explanation about the purpose of this instance."
+		},
+		"executionTime": {
+			"$id": "#/properties/executionTime",
+			"type": "integer",
+			"title": "The executionTime schema",
+			"description": "Execution time of engine",
+			"default": 0
+		},
+		"issues": {
+			"$id": "#/properties/issues",
+			"type": "integer",
+			"title": "The issues schema",
+			"description": "Number of issues reported",
+			"default": 0
+		},
+		"process": {
+			"$id": "#/properties/process",
+			"type": "object",
+			"title": "The process schema",
+			"description": "An explanation about the purpose of this instance.",
+			"required": ["name", "version"],
+			"properties": {
+				"name": {
+					"$id": "#/properties/process/properties/name",
+					"type": "string",
+					"title": "The name schema",
+					"description": "An explanation about the purpose of this instance."
+				},
+				"version": {
+					"$id": "#/properties/process/properties/version",
+					"type": "string",
+					"title": "The version schema",
+					"description": "An explanation about the purpose of this instance."
+				}
+			}
+		},
+		"output": {
+			"$id": "#/properties/output",
+			"type": "array",
+			"title": "The output schema",
+			"description": "An explanation about the purpose of this instance.",
+			"items": {
+				"allOf": [{
+					"$id": "#/properties/output/items/allOf/0",
+					"type": "object",
+					"title": "The first allOf schema",
+					"description": "An explanation about the purpose of this instance.",
+					"required": ["type", "ruleId", "location", "metadata"],
+					"if": {
+						"properties": {
+							"type": {
+								"const": "sca"
+							}
+						}
+					},
+					"then": {
+						"properties": {
+							"metadata": {
+								"required": ["title"]
+							}
+						}
+					},
+					"properties": {
+						"type": {
+							"$id": "#/properties/output/items/allOf/0/properties/type",
+							"type": "string",
+							"enum": ["sast", "sca"],
+							"title": "The type schema",
+							"description": "Type of engine"
+						},
+						"ruleId": {
+							"$id": "#/properties/output/items/allOf/0/properties/ruleId",
+							"type": "string",
+							"title": "The ruleId schema",
+							"description": "An explanation about the purpose of this instance."
+						},
+						"location": {
+							"$id": "#/properties/output/items/allOf/0/properties/location",
+							"type": "object",
+							"title": "The location schema",
+							"description": "Location details of the issue.",
+							"required": ["path", "positions"],
+							"properties": {
+								"path": {
+									"$id": "#/properties/output/items/allOf/0/properties/location/properties/path",
+									"type": "string",
+									"pattern": "^(?!\/opt\/mount).*$",
+									"title": "The path schema",
+									"description": "Path to the affected file. Path should not start with /opt/mount"
+								},
+								"positions": {
+									"$id": "#/properties/output/items/allOf/0/properties/location/properties/positions",
+									"type": "object",
+									"title": "The positions schema",
+									"description": "An explanation about the purpose of this instance.",
+									"required": ["begin"],
+									"properties": {
+										"begin": {
+											"$id": "#/properties/output/items/allOf/0/properties/location/properties/positions/properties/begin",
+											"type": "object",
+											"title": "The begin schema",
+											"description": "An explanation about the purpose of this instance.",
+											"required": ["line"],
+											"properties": {
+												"line": {
+													"$id": "#/properties/output/items/allOf/0/properties/location/properties/positions/properties/begin/properties/line",
+													"type": "integer",
+													"title": "The line schema",
+													"description": "An explanation about the purpose of this instance.",
+													"default": 0
+												}
+											}
+										},
+										"end": {
+											"$id": "#/properties/output/items/allOf/0/properties/location/properties/positions/properties/end",
+											"type": "object",
+											"title": "The end schema",
+											"description": "An explanation about the purpose of this instance.",
+											"required": ["line"],
+											"properties": {
+												"line": {
+													"$id": "#/properties/output/items/allOf/0/properties/location/properties/positions/properties/begin/properties/line",
+													"type": "integer",
+													"title": "The line schema",
+													"description": "An explanation about the purpose of this instance.",
+													"default": 0
+												}
+											}
+										}
+									}
+								}
+							}
+						},
+						"metadata": {
+							"$id": "#/properties/output/items/allOf/0/properties/metadata",
+							"type": "object",
+							"title": "The metadata schema",
+							"description": "An explanation about the purpose of this instance.",
+							"required": ["description"],
+							"properties": {
+								"title": {
+									"$id": "#/properties/output/items/allOf/0/properties/metadata/properties/title",
+									"type": "string",
+									"title": "The title schema",
+									"description": "An explanation about the purpose of this instance."
+								},
+								"description": {
+									"$id": "#/properties/output/items/allOf/0/properties/metadata/properties/description",
+									"type": "string",
+									"title": "The description schema",
+									"description": "An explanation about the purpose of this instance."
+								},
+								"lineContent": {
+									"$id": "#/properties/output/items/allOf/0/properties/metadata/properties/lineContent",
+									"type": "string",
+									"title": "The lineContent schema",
+									"description": "An explanation about the purpose of this instance."
+								}
+							}
+						}
+					}
+				}],
+				"$id": "#/properties/output/items"
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/jsonschema.js b/jsonschema.js
new file mode 100644
index 0000000..c757e2b
--- /dev/null
+++ b/jsonschema.js
@@ -0,0 +1,26 @@
+const { validator } = require("@exodus/schemasafe");
+const fs = require("fs");
+const grschema = require("./engines-schema.json");
+
+
+// pass the external schemas as an option
+const validate = validator(grschema, { includeErrors: true, allErrors: true });
+
+function readFromStdin() {
+    return readFromFile("/dev/stdin");
+}
+
+function readFromFile(filePath) {
+    try {
+        let data = fs.readFileSync(filePath).toString();
+        return JSON.parse(data);
+    } catch (err) {
+        console.log(err.message);
+        process.exit(1);
+    }
+}
+
+reportData = readFromStdin();
+
+validate(reportData);
+console.log(validate.errors);
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index c6cfa1f..8a3a686 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4,6 +4,11 @@
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
+    "@exodus/schemasafe": {
+      "version": "1.0.0-beta.1",
+      "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.0.0-beta.1.tgz",
+      "integrity": "sha512-3H4VeIYMfSH672iDP2uljt02V7W4VZrpJBW9At0htnZdfE2ZUXUe9tRrdaWBe6RvhyWt4BiEh0BIobMrGLmQrQ=="
+    },
     "@mrmlnc/readdir-enhanced": {
       "version": "2.2.1",
       "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
diff --git a/package.json b/package.json
index b3b2121..f9eed17 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
   "author": "",
   "license": "ISC",
   "dependencies": {
+    "@exodus/schemasafe": "^1.0.0-beta.1",
     "@semantic-release/git": "^5.0.0",
     "commander": "^2.17.1",
     "joi": "^13.6.0",

From ebd7fff3fd8cab3a88375ebffc2d739afe8dd0fe Mon Sep 17 00:00:00 2001
From: Vincent Tan <vtky@users.noreply.github.com>
Date: Mon, 20 Jul 2020 17:23:07 +0800
Subject: [PATCH 2/2] added desc for statuses

---
 engines-schema.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/engines-schema.json b/engines-schema.json
index c63c538..5fb09e6 100644
--- a/engines-schema.json
+++ b/engines-schema.json
@@ -56,7 +56,7 @@
 			"type": "string",
 			"enum": ["success", "completed", "errror"],
 			"title": "The status schema",
-			"description": "An explanation about the purpose of this instance."
+			"description": "Success: complete with results. Completed: Engine ran with no results. Error: engine errored out due to something"
 		},
 		"executionTime": {
 			"$id": "#/properties/executionTime",