From 0d6acdbee1b469790795519f02a5b3cf02482ad0 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Thu, 25 Apr 2024 16:45:10 -0400 Subject: [PATCH] Add restrictions property/pref, case sensitivity, CLI usage --- src/qz/App.java | 2 + src/qz/common/TrayManager.java | 2 +- src/qz/utils/ArgParser.java | 2 +- src/qz/utils/ArgValue.java | 4 ++ src/qz/ws/substitutions/Substitutions.java | 47 +++++++++++++------ .../resources/substitutions.json | 24 +++++++--- 6 files changed, 57 insertions(+), 24 deletions(-) diff --git a/src/qz/App.java b/src/qz/App.java index 39f31a81e..42d1159b9 100644 --- a/src/qz/App.java +++ b/src/qz/App.java @@ -21,6 +21,7 @@ import qz.utils.*; import qz.ws.PrintSocketServer; import qz.ws.SingleInstanceChecker; +import qz.ws.substitutions.Substitutions; import java.io.File; import java.security.cert.X509Certificate; @@ -43,6 +44,7 @@ public static void main(String ... args) { log.info(Constants.ABOUT_TITLE + " vendor: {}", Constants.ABOUT_COMPANY); log.info("Java version: {}", Constants.JAVA_VERSION.toString()); log.info("Java vendor: {}", Constants.JAVA_VENDOR); + Substitutions.setRestrictSubstitutions(PrefsSearch.getBoolean(ArgValue.SECURITY_SUBSTITUTIONS_RESTRICT)); CertificateManager certManager = null; try { diff --git a/src/qz/common/TrayManager.java b/src/qz/common/TrayManager.java index 316716af4..c5bb2c53c 100644 --- a/src/qz/common/TrayManager.java +++ b/src/qz/common/TrayManager.java @@ -102,7 +102,7 @@ public TrayManager(boolean isHeadless) { // Set strict certificate mode preference Certificate.setTrustBuiltIn(!getPref(TRAY_STRICTMODE)); - // Configure JSON substitutions + // Configure JSON substitutions = Substitutions.init(); // Set FileIO security diff --git a/src/qz/utils/ArgParser.java b/src/qz/utils/ArgParser.java index 1d7512dd2..dae8854b3 100644 --- a/src/qz/utils/ArgParser.java +++ b/src/qz/utils/ArgParser.java @@ -51,7 +51,7 @@ public int getCode() { private static final String USAGE_COMMAND = String.format("java -jar %s.jar", PROPS_FILE); private static final String USAGE_COMMAND_PARAMETER = String.format("java -Dfoo.bar= -jar %s.jar", PROPS_FILE); - private static final int DESCRIPTION_COLUMN = 30; + private static final int DESCRIPTION_COLUMN = 35; private static final int INDENT_SIZE = 2; private List args; diff --git a/src/qz/utils/ArgValue.java b/src/qz/utils/ArgValue.java index cfe0856d7..60f5fb74f 100644 --- a/src/qz/utils/ArgValue.java +++ b/src/qz/utils/ArgValue.java @@ -71,6 +71,10 @@ public enum ArgValue { "security.file.enabled"), SECURITY_FILE_STRICT(PREFERENCES, "Enable/disable signing requirements for File Communications features", null, true, "security.file.strict"), + + SECURITY_SUBSTITUTIONS_RESTRICT(PREFERENCES, "Enable/disable JSON substitution restrictions such as copies, data blobs", null, true, + "security.substitutions.restrict"), + SECURITY_DATA_PROTOCOLS(PREFERENCES, "URL protocols allowed for print, serial, hid, etc", null, "http,https", "security.data.protocols"), SECURITY_PRINT_TOFILE(PREFERENCES, "Enable/disable printing directly to file paths", null, false, diff --git a/src/qz/ws/substitutions/Substitutions.java b/src/qz/ws/substitutions/Substitutions.java index 17af76b0c..261bb8e5c 100644 --- a/src/qz/ws/substitutions/Substitutions.java +++ b/src/qz/ws/substitutions/Substitutions.java @@ -9,21 +9,18 @@ import qz.utils.FileUtilities; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; +import java.util.*; public class Substitutions { protected static final Logger log = LogManager.getLogger(Substitutions.class); public static final String FILE_NAME = "substitutions.json"; + private static final Path DEFAULT_SUBSTITUTIONS_PATH = FileUtilities.SHARED_DIR.resolve(FILE_NAME); // Subkeys that are restricted for writing @@ -34,6 +31,7 @@ public class Substitutions { restricted.put("data", Type.DATA); } private ArrayList matches, replaces; + private ArrayList matchCase; public Substitutions(Path path) throws IOException, JSONException { this(new FileInputStream(path.toFile())); @@ -45,6 +43,7 @@ public Substitutions(InputStream in) throws IOException, JSONException { public Substitutions(String serialized) throws JSONException { matches = new ArrayList<>(); + matchCase = new ArrayList<>(); replaces = new ArrayList<>(); JSONArray instructions = new JSONArray(serialized); @@ -59,11 +58,14 @@ public Substitutions(String serialized) throws JSONException { JSONObject match = step.optJSONObject("for"); if(match != null) { + this.matchCase.add(match.optBoolean("caseSensitive", false)); + match.remove("caseSensitive"); sanitize(match); this.matches.add(match); } } } + if(matches.size() != replaces.size()) { throw new SubstitutionException("Mismatched instructions; Each \"use\" must have a matching \"for\"."); } @@ -75,11 +77,17 @@ public JSONObject replace(InputStream in) throws IOException, JSONException { public JSONObject replace(JSONObject base) throws JSONException { for(int i = 0; i < matches.size(); i++) { - if (find(base, matches.get(i))) { - log.debug("Matched JSON substitution rule: for: {}, use: {}", matches.get(i), replaces.get(i)); + if (find(base, matches.get(i), matchCase.get(i))) { + log.debug("Matched {}JSON substitution rule: for: {}, use: {}", + matchCase.get(i) ? "case-sensitive " : "", + matches.get(i), + replaces.get(i)); replace(base, replaces.get(i)); } else { - log.debug("Unable to match substitution rule: for: {}, use: {}", matches.get(i), replaces.get(i)); + log.debug("Unable to match {}JSON substitution rule: for: {}, use: {}", + matchCase.get(i) ? "case-sensitive " : "", + matches.get(i), + replaces.get(i)); } } return base; @@ -121,7 +129,7 @@ public static void replace(JSONObject base, JSONObject replace) throws JSONExcep } } } - find(base, replace, true); + find(base, replace, false, true); } private static void removeRestrictedSubkeys(JSONObject jsonObject, Type type) { @@ -209,10 +217,10 @@ public static void sanitize(JSONObject match) throws JSONException { } } - private static boolean find(Object base, Object match) throws JSONException { - return find(base, match, false); + private static boolean find(Object base, Object match, boolean caseSensitive) throws JSONException { + return find(base, match, caseSensitive, false); } - private static boolean find(Object base, Object match, boolean replace) throws JSONException { + private static boolean find(Object base, Object match, boolean caseSensitive, boolean replace) throws JSONException { if(base instanceof JSONObject) { if(match instanceof JSONObject) { JSONObject jsonMatch = (JSONObject)match; @@ -229,7 +237,7 @@ private static boolean find(Object base, Object match, boolean replace) throws J // Overwrite value, don't recurse jsonBase.put(next.toString(), newMatch); continue; - } else if(find(newBase, newMatch, replace)) { + } else if(find(newBase, newMatch, caseSensitive, replace)) { continue; } } else if(replace) { @@ -251,7 +259,7 @@ private static boolean find(Object base, Object match, boolean replace) throws J Object newMatch = matchArray.get(i); for(int j = 0; j < baseArray.length(); j++) { Object newBase = baseArray.get(j); - if(find(newBase, newMatch, replace)) { + if(find(newBase, newMatch, caseSensitive, replace)) { continue match; } } @@ -263,7 +271,16 @@ private static boolean find(Object base, Object match, boolean replace) throws J } } else { // Treat as primitives - return match.equals(base); + if(!match.getClass().getName().equals("java.lang.String") && match.getClass().equals(base.getClass())) { + // Same type + return match.equals(base); + } else { + // Dissimilar types (e.g. "width": "8.5" versus "width": 8.5), cast both to String + if(caseSensitive) { + return match.toString().equals(base.toString()); + } + return match.toString().equalsIgnoreCase(base.toString()); + } } } diff --git a/test/qz/ws/substitutions/resources/substitutions.json b/test/qz/ws/substitutions/resources/substitutions.json index f9bd482f5..aff629d8d 100644 --- a/test/qz/ws/substitutions/resources/substitutions.json +++ b/test/qz/ws/substitutions/resources/substitutions.json @@ -3,8 +3,8 @@ "use":{ "config": { "size": { - "width": "100", - "height": "150" + "width": 100, + "height": 150 }, "units": "mm" } @@ -12,9 +12,10 @@ "for": { "config": { "size": { - "width": "4", - "height": "6" - } + "width": 4, + "height": 6 + }, + "units": "in" } } }, @@ -30,8 +31,8 @@ "use": { "data": { "options": { - "pageWidth": "8.5", - "pageHeight": "14" + "pageWidth": 8.5, + "pageHeight": 14 } } }, @@ -56,6 +57,15 @@ } } }, + { + "use": { + "printer": "PDFwriter" + }, + "for": { + "caseSensitive": true, + "printer": "xps document writer" + } + }, { "use": { "data": {