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
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,16 @@ Long year = get(Fields.In, "year").getInteger(r);

This method also takes into account the index-based optimization mentioned above.

== Metadata Injection Support
== Blocking specific code

All fields of this transform support metadata injection.
You can use this transform with ETL Metadata Injection to pass metadata to your pipeline at runtime.
As a simple security measure you can block the execution of code containing specific strings.
This can be done by adding exclusions to the `codeExclusions.xml` file located at <Hop Installation>/plugins/transforms/janino

Example:
[source,xml]
----
<exclusions>
<exclusion>System.</exclusion>
<exclusion>HopVfs.</exclusion>
</exclusions>
----
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,17 @@ If you prefer the return values to be of value type Integer, use "new Long(1)" a
----
samplestr != null && samplestr.indexOf("abc")>-1 ? 1 : 2
----

== Blocking specific code

As a simple security measure you can block the execution of code containing specific strings.
This can be done by adding exclusions to the `codeExclusions.xml` file located at <Hop Installation>/plugins/transforms/janino

Example:
[source,xml]
----
<exclusions>
<exclusion>System.</exclusion>
<exclusion>HopVfs.</exclusion>
</exclusions>
----
5 changes: 5 additions & 0 deletions plugins/transforms/janino/src/assembly/assembly.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@
<outputDirectory>plugins/transforms/janino</outputDirectory>
<filtered>true</filtered>
</file>
<file>
<source>${project.basedir}/src/main/resources/codeExclusions.xml</source>
<outputDirectory>plugins/transforms/janino</outputDirectory>
<filtered>true</filtered>
</file>
</files>

<fileSets>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.apache.hop.pipeline.PipelineMeta;
import org.apache.hop.pipeline.transform.BaseTransform;
import org.apache.hop.pipeline.transform.TransformMeta;
import org.apache.hop.pipeline.transforms.util.JaninoCheckerUtil;
import org.codehaus.janino.ExpressionEvaluator;

/** Calculate new field values using pre-defined functions. */
Expand Down Expand Up @@ -163,6 +164,14 @@ private Object[] calcFields(IRowMeta rowMeta, Object[] r) throws HopValueExcepti
parameterTypes.toArray(new Class<?>[parameterTypes.size()]));
data.expressionEvaluators[m].setReturnType(Object.class);
data.expressionEvaluators[m].setThrownExceptions(new Class<?>[] {Exception.class});

// Validate Formula
JaninoCheckerUtil janinoCheckerUtil = new JaninoCheckerUtil();
List<String> codeCheck = janinoCheckerUtil.checkCode(fn.getFormula());
if (!codeCheck.isEmpty()) {
throw new HopException("Script contains code that is not allowed : " + codeCheck);
}

data.expressionEvaluators[m].cook(fn.getFormula());
} else {
throw new HopException(
Expand Down Expand Up @@ -194,9 +203,9 @@ private Object[] calcFields(IRowMeta rowMeta, Object[] r) throws HopValueExcepti
IValueMeta valueMeta = data.returnType[i];
if (valueMeta.getNativeDataTypeClass().isAssignableFrom(formulaResult.getClass())) {
value = formulaResult;
} else if (formulaResult instanceof Integer
} else if (formulaResult instanceof Integer integer
&& valueMeta.getType() == IValueMeta.TYPE_INTEGER) {
value = ((Integer) formulaResult).longValue();
value = integer.longValue();
} else {
throw new HopValueException(
BaseMessages.getString(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@
import org.apache.hop.i18n.BaseMessages;
import org.apache.hop.pipeline.PipelineMeta;
import org.apache.hop.pipeline.transform.TransformMeta;
import org.apache.hop.pipeline.transforms.util.JaninoCheckerUtil;
import org.apache.hop.ui.core.ConstUi;
import org.apache.hop.ui.core.PropsUi;
import org.apache.hop.ui.core.dialog.BaseDialog;
import org.apache.hop.ui.core.dialog.MessageBox;
import org.apache.hop.ui.core.widget.ColumnInfo;
import org.apache.hop.ui.core.widget.TableView;
import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
Expand Down Expand Up @@ -257,11 +259,27 @@ private void ok() {
return;
}

int nrNonEmptyFields = wFields.nrNonEmpty();

// Check if code contains content that is not allowed
JaninoCheckerUtil janinoCheckerUtil = new JaninoCheckerUtil();

for (int i = 0; i < nrNonEmptyFields; i++) {
TableItem item = wFields.getNonEmpty(i);
List<String> codeCheck = janinoCheckerUtil.checkCode(item.getText(2));
if (!codeCheck.isEmpty()) {
MessageBox mb = new MessageBox(shell, SWT.OK | SWT.ICON_ERROR);
mb.setText("Invalid Code");
mb.setMessage("Script contains code that is not allowed : " + codeCheck);
mb.open();
return;
}
}

transformName = wTransformName.getText(); // return value

currentMeta.allocate(wFields.nrNonEmpty());

int nrNonEmptyFields = wFields.nrNonEmpty();
for (int i = 0; i < nrNonEmptyFields; i++) {
TableItem item = wFields.getNonEmpty(i);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ public void check(
if (prev == null || prev.size() == 0) {
cr =
new CheckResult(
CheckResult.TYPE_RESULT_WARNING,
ICheckResult.TYPE_RESULT_WARNING,
BaseMessages.getString(PKG, "JaninoMeta.CheckResult.ExpectedInputError"),
transformMeta);
remarks.add(cr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import java.util.List;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.exception.HopValueException;
import org.apache.hop.core.row.IRowMeta;
import org.apache.hop.core.row.IValueMeta;
import org.apache.hop.core.util.Utils;
import org.apache.hop.i18n.BaseMessages;
Expand Down Expand Up @@ -115,7 +114,7 @@ public boolean processRow() throws HopException {
logRowlevel("Read row #" + getLinesRead() + " : " + getInputRowMeta().getString(r));
}

boolean keep = calcFields(getInputRowMeta(), r);
boolean keep = calcFields(r);

if (!data.chosesTargetTransforms) {
if (keep) {
Expand Down Expand Up @@ -147,16 +146,14 @@ public boolean processRow() throws HopException {
}
}

if (checkFeedback(getLinesRead())) {
if (log.isBasic()) {
logBasic(BaseMessages.getString(PKG, "JavaFilter.Log.LineNumber") + getLinesRead());
}
if (checkFeedback(getLinesRead()) && log.isBasic()) {
logBasic(BaseMessages.getString(PKG, "JavaFilter.Log.LineNumber") + getLinesRead());
}

return true;
}

private boolean calcFields(IRowMeta rowMeta, Object[] r) throws HopValueException {
private boolean calcFields(Object[] r) throws HopValueException {
try {
// Initialize evaluators etc. Only do it once.
//
Expand Down Expand Up @@ -234,8 +231,8 @@ private boolean calcFields(IRowMeta rowMeta, Object[] r) throws HopValueExceptio

Object formulaResult = data.expressionEvaluator.evaluate(data.argumentData);

if (formulaResult instanceof Boolean) {
return (Boolean) formulaResult;
if (formulaResult instanceof Boolean bool) {
return bool;
} else {
throw new HopException(
"The result of the filter expression must be a boolean and we got back : "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public InetAddress getInetAddress(Object[] dataRow) throws HopValueException {
return ((ValueMetaInternetAddress) meta).getInternetAddress(dataRow[index]);
}

public Serializable getSerializable(Object[] dataRow) throws HopValueException {
public Serializable getSerializable(Object[] dataRow) {
return (Serializable) dataRow[index];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import org.apache.hop.pipeline.transform.TransformMeta;

@SuppressWarnings("java:S1104")
public class TransformDefinition implements Cloneable {
public String tag;
public String transformName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,16 @@ public UserDefinedJavaClass(
super(transformMeta, meta, data, copyNr, pipelineMeta, pipeline);

if (copyNr == 0) {
meta.cookClasses();
try {
meta.cookClasses();
} catch (HopException e) {
throw new RuntimeException(e);
}
}

child = meta.newChildInstance(this, meta, data);

if (meta.cookErrors.size() > 0) {
if (!meta.cookErrors.isEmpty()) {
for (Exception e : meta.cookErrors) {
logErrorImpl("Error initializing UserDefinedJavaClass:", e);
}
Expand Down Expand Up @@ -659,7 +663,7 @@ public long incrementLinesWrittenImpl() {

@Override
public boolean init() {
if (meta.cookErrors.size() > 0) {
if (!meta.cookErrors.isEmpty()) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public Object clone() throws CloneNotSupportedException {
return super.clone();
}

public String getTransformedSource() throws HopTransformException {
public String getTransformedSource() {
StringBuilder sb = new StringBuilder(getSource());
appendConstructor(sb);
return sb.toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import org.apache.hop.pipeline.transforms.userdefinedjavaclass.UserDefinedJavaClassCodeSnippits.Snippit;
import org.apache.hop.pipeline.transforms.userdefinedjavaclass.UserDefinedJavaClassDef.ClassType;
import org.apache.hop.pipeline.transforms.userdefinedjavaclass.UserDefinedJavaClassMeta.FieldInfo;
import org.apache.hop.pipeline.transforms.util.JaninoCheckerUtil;
import org.apache.hop.ui.core.ConstUi;
import org.apache.hop.ui.core.PropsUi;
import org.apache.hop.ui.core.dialog.BaseDialog;
Expand Down Expand Up @@ -1186,6 +1187,20 @@ private void getInfo(UserDefinedJavaClassMeta meta) {
meta.replaceFields(newFields);

CTabItem[] cTabs = folder.getItems();
if (cTabs.length > 0) {
for (int i = 0; i < cTabs.length; i++) {
JaninoCheckerUtil janinoCheckerUtil = new JaninoCheckerUtil();
List<String> codeCheck = janinoCheckerUtil.checkCode(getStyledTextComp(cTabs[i]).getText());
if (!codeCheck.isEmpty()) {
MessageBox mb = new MessageBox(shell, SWT.OK | SWT.ICON_ERROR);
mb.setText("Invalid Code");
mb.setMessage("Script contains code that is not allowed : " + codeCheck);
mb.open();
return;
}
}
}

if (cTabs.length > 0) {
List<UserDefinedJavaClassDef> definitions = new ArrayList<>(cTabs.length);
for (int i = 0; i < cTabs.length; i++) {
Expand Down Expand Up @@ -1297,7 +1312,12 @@ private boolean test() {
try {
// First, before we get into the trial run, just see if the classes
// all compile.
udjcMeta.cookClasses();
try {
udjcMeta.cookClasses();
} catch (HopException e) {
new ErrorDialog(shell, "Error during class compilation", e.toString(), e);
}

if (udjcMeta.cookErrors.size() == 1) {
Exception e = udjcMeta.cookErrors.get(0);
new ErrorDialog(shell, "Error during class compilation", e.toString(), e);
Expand Down Expand Up @@ -1498,7 +1518,7 @@ private void buildSnippitsTree() {
}
}

public boolean TreeItemExist(TreeItem itemToCheck, String strItemName) {
public boolean treeItemExist(TreeItem itemToCheck, String strItemName) {
boolean bRC = false;
if (itemToCheck.getItemCount() > 0) {
TreeItem[] items = itemToCheck.getItems();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.hop.core.CheckResult;
import org.apache.hop.core.Const;
import org.apache.hop.core.ICheckResult;
Expand All @@ -52,6 +51,7 @@
import org.apache.hop.pipeline.transform.BaseTransformMeta;
import org.apache.hop.pipeline.transform.ITransformIOMeta;
import org.apache.hop.pipeline.transform.TransformMeta;
import org.apache.hop.pipeline.transforms.util.JaninoCheckerUtil;
import org.codehaus.commons.compiler.CompileException;
import org.codehaus.janino.ClassBodyEvaluator;
import org.codehaus.janino.Scanner;
Expand Down Expand Up @@ -240,12 +240,18 @@ Class<?> cookClass(UserDefinedJavaClassDef def, ClassLoader clsloader)
return rtn;
}

public void cookClasses() {
public void cookClasses() throws HopException {
cookErrors.clear();
ClassLoader clsloader = UserDefinedJavaClass.class.getClassLoader();
for (UserDefinedJavaClassDef def : getDefinitions()) {
if (def.isActive()) {
try {
// Validate Formula
JaninoCheckerUtil janinoCheckerUtil = new JaninoCheckerUtil();
List<String> codeCheck = janinoCheckerUtil.checkCode(def.getSource());
if (!codeCheck.isEmpty()) {
throw new HopException("Script contains code that is not allowed : " + codeCheck);
}
Class<?> cookedClass = cookClass(def, clsloader);
clsloader = cookedClass.getClassLoader();
if (def.isTransformClass()) {
Expand Down Expand Up @@ -321,13 +327,13 @@ protected List<UserDefinedJavaClassDef> orderDefinitions(
definitions.stream()
.filter(def -> def.isTransformClass() && def.isActive())
.sorted((p1, p2) -> p1.getClassName().compareTo(p2.getClassName()))
.collect(Collectors.toList());
.toList();

List<UserDefinedJavaClassDef> normalClasses =
definitions.stream()
.filter(def -> !def.isTransformClass())
.sorted((p1, p2) -> p1.getClassName().compareTo(p2.getClassName()))
.collect(Collectors.toList());
.toList();

orderedDefinitions.addAll(normalClasses);
orderedDefinitions.addAll(transactions);
Expand Down Expand Up @@ -508,7 +514,12 @@ public void setDefault() {
private boolean checkClassCookings(ILogChannel logChannel) {
boolean ok = cookedTransformClass != null && cookErrors.isEmpty();
if (changed) {
cookClasses();
try {
cookClasses();
} catch (HopException e) {
throw new RuntimeException(e);
}

if (cookedTransformClass == null) {
if (!cookErrors.isEmpty()) {
logChannel.logDebug(
Expand Down
Loading