Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added
Binary file not shown.
36 changes: 0 additions & 36 deletions rules/001_project_settings/001_0007_hash_algorithm_test.rego

This file was deleted.

25 changes: 25 additions & 0 deletions rules/001_project_settings/001_0007_hash_algorithm_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
TestCases:
- name: allow BCrypt hash algorithm
allow: true
input:
Settings:
$Type: Settings$ModelSettings
HashAlgorithm: BCrypt
- name: allow SSHA256 hash algorithm
allow: true
input:
Settings:
$Type: Settings$ModelSettings
HashAlgorithm: SSHA256
- name: do not allow SHA256 hash algorithm
allow: false
input:
Settings:
$Type: Settings$ModelSettings
HashAlgorithm: SHA256
- name: do not allow MD5 hash algorithm
allow: false
input:
Settings:
$Type: Settings$ModelSettings
HashAlgorithm: MD5

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
TestCases:
- name: allow user role that is checked for security
allow: true
input:
UserRoles:
- Name: Administrator
CheckSecurity: true
- name: do not allow user role that is not checked for security
allow: false
input:
UserRoles:
- Name: Administrator
CheckSecurity: false
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
# The rule reads Security$ProjectSecurity.yaml from the working directory.
# During tests the working directory is this rule's folder, which contains a
# fixture Security$ProjectSecurity.yaml with StrictMode disabled. Cases that
# enable the optimized (React) client therefore expect a violation.
TestCases:
- name: allow when UseOptimizedClient is false
- name: allow when optimized client is disabled
allow: true
input:
UseOptimizedClient: false
files:
"Security$ProjectSecurity.yaml":
StrictMode: false
- name: allow when both UseOptimizedClient and StrictMode are true
$Type: Settings$ProjectSettings
Settings:
- $Type: Forms$WebUIProjectSettingsPart
UseOptimizedClient: "No"
- name: allow when optimized client setting is absent
allow: true
input:
UseOptimizedClient: true
files:
"Security$ProjectSecurity.yaml":
StrictMode: true
- name: do not allow UseOptimizedClient true but StrictMode false
$Type: Settings$ProjectSettings
Settings:
- $Type: Settings$ModelSettings
HashAlgorithm: BCrypt
- name: do not allow optimized client enabled while strict mode is disabled
allow: false
input:
UseOptimizedClient: true
files:
"Security$ProjectSecurity.yaml":
StrictMode: false
- name: do not allow UseOptimizedClient true but StrictMode missing
allow: false
input:
UseOptimizedClient: true
files:
"Security$ProjectSecurity.yaml": {}
$Type: Settings$ProjectSettings
Settings:
- $Type: Forms$WebUIProjectSettingsPart
UseOptimizedClient: "Yes"
3 changes: 3 additions & 0 deletions rules/001_project_settings/Security$ProjectSecurity.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Fixture for 001_0009_strict_mode_with_react_client_test.yaml (ignored by rule scanner).
$Type: Security$ProjectSecurity
StrictMode: false
112 changes: 112 additions & 0 deletions rules/005_microflows/005_0002_commit_actions_with_a_loop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
const metadata = {
scope: "package",
title: "Commit actions with a loop",
description: "Commiting objects within a loop will fire a SQL Update query for each iteration. This applies whether the commit sits directly in the loop or is reached via a called microflow.",
authors: [
"Viktor Berlov <viktor@cinaq.com>",
"Jurre Tanja <jurre.tanja@mendix.com>"
],
custom: {
category: "Microflows",
rulename: "AvoidCommitInLoop",
severity: "MEDIUM",
rulenumber: "005_0002",
remediation: "Consider committing objects outside the loop. Within the loop, add them to a list.",
input: ".*\\$Microflow\\.yaml"
}
};

function rule(input = {}) {
const errors = [];

function collect(node, predicate, acc) {
acc = acc || [];
if (!node || typeof node !== "object") return acc;
if (predicate(node)) acc.push(node);
for (const key of Object.keys(node)) {
const v = node[key];
if (Array.isArray(v)) {
for (const item of v) collect(item, predicate, acc);
} else if (v && typeof v === "object") {
collect(v, predicate, acc);
}
}
return acc;
}

const isLoop = n => n["$Type"] === "Microflows$LoopedActivity";

const isCall = n =>
n["$Type"] === "Microflows$ActionActivity" &&
n.Action &&
n.Action["$Type"] === "Microflows$MicroflowCallAction" &&
n.Action.MicroflowCall &&
n.Action.MicroflowCall.Microflow;

const isCommit = n =>
n["$Type"] === "Microflows$ActionActivity" &&
n.Action && (
n.Action["$Type"] === "Microflows$CommitAction" ||
(n.Action["$Type"] === "Microflows$ChangeAction" && n.Action.Commit === "Yes") ||
(n.Action["$Type"] === "Microflows$CreateChangeAction" && n.Action.Commit === "Yes")
);

function refToPath(ref) {
const parts = ref.split(".");
if (parts.length !== 2) return null;
return parts[0] + "/" + parts[1] + ".Microflows$Microflow.yaml";
}

const pathPrefixes = ["", "rules/005_microflows/", "./rules/005_microflows/"];

function tryReadYaml(relPath) {
for (const prefix of pathPrefixes) {
try {
const doc = mxlint.io.readYaml(prefix + relPath);
if (doc) return doc;
} catch (e) {
// try next prefix
}
}
return null;
}

function commitsTransitively(ref, visited) {
if (visited.has(ref)) return false;
visited.add(ref);
const path = refToPath(ref);
if (!path) return false;
const doc = tryReadYaml(path);
if (!doc) return false;
if (collect(doc, isCommit).length > 0) return true;
for (const call of collect(doc, isCall)) {
if (commitsTransitively(call.Action.MicroflowCall.Microflow, visited)) return true;
}
return false;
}

const prefix = "[" + metadata.custom.severity + ", " + metadata.custom.category + ", " + metadata.custom.rulenumber + "] ";
const flowName = input.Name || "unknown";

const loops = collect(input, isLoop);
const reportedCommitNodes = new Set();
const reportedIndirectRefs = new Set();

for (const loop of loops) {
for (const c of collect(loop, isCommit)) {
if (reportedCommitNodes.has(c)) continue;
reportedCommitNodes.add(c);
errors.push(prefix + c.Action["$Type"] + " inside " + flowName + " loop");
}
for (const call of collect(loop, isCall)) {
const ref = call.Action.MicroflowCall.Microflow;
if (reportedIndirectRefs.has(ref)) continue;
if (commitsTransitively(ref, new Set())) {
reportedIndirectRefs.add(ref);
errors.push(prefix + "Microflow " + ref + " called from " + flowName + " loop commits (directly or transitively)");
}
}
}

return { allow: errors.length === 0, errors };
}
62 changes: 0 additions & 62 deletions rules/005_microflows/005_0002_commit_actions_with_a_loop.rego

This file was deleted.

Loading
Loading