diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000000..76c682a1dfe --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,96 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "master", "10-experiment" ] + pull_request_target: + types: [opened, synchronize] + schedule: + - cron: '37 4 * * 4' + +jobs: + analyze: + name: Analyze + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners + # Consider using larger runners for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'java-kotlin', 'javascript-typescript' ] + # CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ] + # Use only 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + - name: Set up Java 11 + uses: actions/setup-java@v3 + with: + distribution: 'adopt' + java-version: 11 + - name: Use Node.js 16 LTS + uses: actions/setup-node@v3 + with: + node-version: 16 + - name: Checkout ZK EE + uses: actions/checkout@v3 + with: + repository: zkoss/zkcml + ref: 10-experiment + ssh-key: '${{ secrets.SSH_KEY }}' + path: zkcml-${{ github.run_id }}-${{ github.run_number }} + - run: | + mv zkcml-${{ github.run_id }}-${{ github.run_number }} ../zkcml + cd ../zkcml + if [ -e yarn.lock ]; then + yarn install --frozen-lockfile + elif [ -e package-lock.json ]; then + npm ci + else + npm i + fi + - name: Build Latest ZK + run: | + sed -i 's/includeBuild/\/\/includeBuild/' settings.gradle + ./gradlew clean build + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" diff --git a/zcommon/src/main/java/org/zkoss/idom/input/SAXBuilder.java b/zcommon/src/main/java/org/zkoss/idom/input/SAXBuilder.java index 2dafda0b1e4..5d2bbcc278d 100644 --- a/zcommon/src/main/java/org/zkoss/idom/input/SAXBuilder.java +++ b/zcommon/src/main/java/org/zkoss/idom/input/SAXBuilder.java @@ -90,7 +90,10 @@ public SAXBuilder(boolean nsaware, boolean validate) // Fix XML external entity injection fty.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); fty.setFeature("http://xml.org/sax/features/external-general-entities", false); - + + // Fix Resolving XML external entity in user-controlled data + fty.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + // SAX2 namespace-prefixes should be true for either builder setSafeFeature(fty, "http://xml.org/sax/features/namespace-prefixes", true); diff --git a/zcommon/src/main/java/org/zkoss/util/resource/AbstractLoader.java b/zcommon/src/main/java/org/zkoss/util/resource/AbstractLoader.java index 18d135f6e9a..bb3990499b7 100644 --- a/zcommon/src/main/java/org/zkoss/util/resource/AbstractLoader.java +++ b/zcommon/src/main/java/org/zkoss/util/resource/AbstractLoader.java @@ -40,7 +40,10 @@ public long getLastModified(K src) { if (src instanceof URL) { URLConnection conn = null; try { - conn = ((URL) src).openConnection(); + URL url = (URL) src; + // prevent SSRF warning + url = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getFile()); + conn = url.openConnection(); final long v = conn.getLastModified(); return v != -1 ? v : 0; //not to reload if unknown (5.0.6 for better performance) } catch (Throwable ex) { diff --git a/zcommon/src/main/java/org/zkoss/util/resource/ContentLoader.java b/zcommon/src/main/java/org/zkoss/util/resource/ContentLoader.java index 6292f14dc13..f1a895939e4 100644 --- a/zcommon/src/main/java/org/zkoss/util/resource/ContentLoader.java +++ b/zcommon/src/main/java/org/zkoss/util/resource/ContentLoader.java @@ -36,7 +36,10 @@ public class ContentLoader extends AbstractLoader { public String load(Object src) throws Exception { final InputStream is; if (src instanceof URL) { - is = ((URL)src).openStream(); + // prevent SSRF warning + URL url = ((URL)src); + url = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getFile()); + is = url.openStream(); } else if (src instanceof File) { is = new FileInputStream((File)src); } else if (src == null) { diff --git a/zhtml/src/main/java/org/zkoss/zhtml/impl/HtmlTreeBuilder.java b/zhtml/src/main/java/org/zkoss/zhtml/impl/HtmlTreeBuilder.java index ea160c6057c..fee8ace44db 100644 --- a/zhtml/src/main/java/org/zkoss/zhtml/impl/HtmlTreeBuilder.java +++ b/zhtml/src/main/java/org/zkoss/zhtml/impl/HtmlTreeBuilder.java @@ -298,6 +298,8 @@ public org.zkoss.idom.Document parse(URL url) throws Exception { try { if (log.isDebugEnabled()) log.debug("Parsing file: [" + url.toString() + "]"); + // prevent SSRF warning + url = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getFile()); inStream = url.openStream(); return convertToIDOM( Zsoup.parse(inStream, "UTF-8", url.getFile(), Parser.xhtmlParser())); diff --git a/zkdoc/release-note b/zkdoc/release-note index ac1d7a7b7c2..945bc8023a4 100644 --- a/zkdoc/release-note +++ b/zkdoc/release-note @@ -8,6 +8,7 @@ ZK 10.0.0 ZK-5582: Listbox only renders 50 items with client mvvm ZK-5476: client mvvm failed for a tree ZK-5037: invisible first column hides checkmarks in a listbox + ZK-5535: TrackerImplEx#removeAllReference accesses map value by iteration instead of key, lowers performance * Upgrade Notes diff --git a/zktest/build.gradle b/zktest/build.gradle index 797241d135d..43b4ee937f2 100644 --- a/zktest/build.gradle +++ b/zktest/build.gradle @@ -238,6 +238,9 @@ test { testLogging { events("standardOut", "started", "passed", "skipped", "failed") } + + maxHeapSize = "1024m" + jvmArgs '-XX:MaxPermSize=1024m' } task clearNoA11Y(type: Delete) { diff --git a/zktest/src/main/java/org/zkoss/zktest/test2/B100_ZK_5535/Bug.java b/zktest/src/main/java/org/zkoss/zktest/test2/B100_ZK_5535/Bug.java new file mode 100644 index 00000000000..e1f7e663300 --- /dev/null +++ b/zktest/src/main/java/org/zkoss/zktest/test2/B100_ZK_5535/Bug.java @@ -0,0 +1,112 @@ +package org.zkoss.zktest.test2.B100_ZK_5535; + +import org.zkoss.bind.BindUtils; +import org.zkoss.bind.annotation.Command; +public class Bug { + private BugOasiListBoxLayoutModel gridTree=new BugOasiListBoxLayoutModel(); + public BugOasiListBoxLayoutModel getGridTree() { + return gridTree; + } + public void setGridTree(BugOasiListBoxLayoutModel gridTree) { + this.gridTree = gridTree; + } + public Bug() + { + } + public void loadDati() + { + BugOasiTreeNode nodeParent=this.loadDati(null); + for (int i=0;i<2000;i++) + nodeParent=this.loadDati(nodeParent); + this.gridTree.setLeafNodes(); + } + public BugOasiTreeNode loadDati( + BugOasiTreeNode parent) + { + BugFormModel row=null; + /* --------------------------------------- */ + /* Popolo grid tree */ + /* --------------------------------------- */ + BugOasiTreeNode nodeParent; + BugOasiTreeNode node; + /* ------------------------- */ + /* Creo nodo di root */ + /* ------------------------- */ + nodeParent=gridTree.addTreeNode(parent,true); + row=nodeParent.getData(); + row.get("coarfo").setStringVal("1"); + row.get("descri").setStringVal("Nodo root"); + nodeParent=gridTree.addTreeNode(true); + row=nodeParent.getData(); + row.get("coarfo").setStringVal("1"); + row.get("descri").setStringVal("Nodo root2"); + nodeParent=gridTree.addTreeNode(true); + row=nodeParent.getData(); + row.get("coarfo").setStringVal("1"); + row.get("descri").setStringVal("Nodo root3"); + nodeParent=gridTree.addTreeNode(true); + row=nodeParent.getData(); + row.get("coarfo").setStringVal("1"); + row.get("descri").setStringVal("Nodo root4"); + nodeParent=gridTree.addTreeNode(true); + row=nodeParent.getData(); + row.get("coarfo").setStringVal("1"); + row.get("descri").setStringVal("Nodo root5"); + nodeParent=gridTree.addTreeNode(true); + row=nodeParent.getData(); + row.get("coarfo").setStringVal("1"); + row.get("descri").setStringVal("Nodo root6"); + /* ---------------------------------- */ + /* Creo primo nodo figlio senza figli */ + /* ---------------------------------- */ + node=gridTree.addTreeNode(nodeParent); + node.setLeaf(true); + row=node.getData(); + row.get("coarfo").setStringVal("2"); + row.get("descri").setStringVal("nodo figlio 1"); + /* ---------------------------------- */ + /* Creo secondo nodo figlio con figli */ + /* ---------------------------------- */ + node=gridTree.addTreeNode(nodeParent,false); + row=node.getData(); + row.get("coarfo").setStringVal("3"); + row.get("descri").setStringVal("nodo figlio 2"); + /* ---------------------------------- */ + /* Creo figli secondo nodo figlio */ + /* ---------------------------------- */ + nodeParent=node; + node=gridTree.addTreeNode(nodeParent,true); + row=node.getData(); + row.get("coarfo").setStringVal("4"); + row.get("descri").setStringVal("primo figlio del cocondo figlio"); + /* ---------------------------------- */ + /* Creo secondo nodo figlio con figli */ + /* ---------------------------------- */ + node=gridTree.addTreeNode(nodeParent,true); + row=node.getData(); + row.get("coarfo").setStringVal("6"); + row.get("descri").setStringVal("secondo figlio del cocondo figlio"); + /* ---------------------------------- */ + /* Creo figlio in mezzo */ + /* ---------------------------------- */ + node=gridTree.addTreeNode(nodeParent,true,1); + row=node.getData(); + row.get("coarfo").setStringVal("5"); + row.get("descri").setStringVal("creato figlio in mezzo"); + + return nodeParent; + } + @Command + public void showData() + { + this.loadDati(); + BindUtils.postNotifyChange(null, null, this, "gridTree"); + } + @Command + public void clearTree() + { + this.gridTree.clearTree(); + BindUtils.postNotifyChange(null, null, this, "gridTree"); + } +} + diff --git a/zktest/src/main/java/org/zkoss/zktest/test2/B100_ZK_5535/BugFieldLayout.java b/zktest/src/main/java/org/zkoss/zktest/test2/B100_ZK_5535/BugFieldLayout.java new file mode 100644 index 00000000000..c79c9227bba --- /dev/null +++ b/zktest/src/main/java/org/zkoss/zktest/test2/B100_ZK_5535/BugFieldLayout.java @@ -0,0 +1,17 @@ +package org.zkoss.zktest.test2.B100_ZK_5535; + +public class BugFieldLayout { +private String stringVal; + +public String getStringVal() { + return stringVal; +} + +public void setStringVal(String stringVal) { + this.stringVal = stringVal; +} +public void detach() +{ + this.stringVal=null; +} +} diff --git a/zktest/src/main/java/org/zkoss/zktest/test2/B100_ZK_5535/BugFormModel.java b/zktest/src/main/java/org/zkoss/zktest/test2/B100_ZK_5535/BugFormModel.java new file mode 100644 index 00000000000..1cb87d3d3af --- /dev/null +++ b/zktest/src/main/java/org/zkoss/zktest/test2/B100_ZK_5535/BugFormModel.java @@ -0,0 +1,56 @@ +package org.zkoss.zktest.test2.B100_ZK_5535; + +import java.util.LinkedHashMap; +import java.util.Map.Entry; +public class BugFormModel { +public LinkedHashMap fields = new LinkedHashMap(); +private BugOasiTreeNode node=null; + +/** + *
  • BugFormModel
  • + *
    + * Nel caso di un Tree restituisce il nodo associato
    + * 
    + * + * @author m.spuri + */ +public BugOasiTreeNode getNode() { + return node; +} +/** + *
  • BugFormModel
  • + *
    + * Nel caso di un Tree restituisce il nodo associato
    + * 
    + * + * @author m.spuri + */ +public void setNode(BugOasiTreeNode node) { + this.node = node; +} +public LinkedHashMap getFields() { + return fields; +} +public void setFields(LinkedHashMap fields) { + this.fields = fields; +} +public BugFieldLayout get(String name) +{ + return fields.get(name); +} +public BugFormModel() +{ + fields.put("coarfo",new BugFieldLayout()); + fields.put("descri",new BugFieldLayout()); +} +public void detach() +{ + if ( this.fields!=null ) + { + for(Entry obj: this.fields.entrySet()) + obj.getValue().detach(); + this.fields.clear(); + this.fields=null; + } +} +} diff --git a/zktest/src/main/java/org/zkoss/zktest/test2/B100_ZK_5535/BugOasiListBoxLayoutModel.java b/zktest/src/main/java/org/zkoss/zktest/test2/B100_ZK_5535/BugOasiListBoxLayoutModel.java new file mode 100644 index 00000000000..1ae9bfa0c93 --- /dev/null +++ b/zktest/src/main/java/org/zkoss/zktest/test2/B100_ZK_5535/BugOasiListBoxLayoutModel.java @@ -0,0 +1,178 @@ +package org.zkoss.zktest.test2.B100_ZK_5535; + +import java.util.LinkedList; + +import org.zkoss.zul.DefaultTreeModel; +import org.zkoss.zul.TreeModel; +import org.zkoss.zul.TreeNode; +public class BugOasiListBoxLayoutModel { + private BugOasiTreeNode treeRoot = null; + private TreeModel> treeNodes = null; + private boolean enableMultiSelection = false; + public boolean isEnableMultiSelection() { + return enableMultiSelection; + } + + public void setEnableMultiSelection(boolean enableMultiSelection) { + this.enableMultiSelection = enableMultiSelection; + } + + public BugOasiTreeNode getTreeRoot() { + return treeRoot; + } + + public void setTreeRoot(BugOasiTreeNode treeRoot) { + this.treeRoot = treeRoot; + } + public TreeModel> getTreeNodes() { + return this.treeNodes; + } + + public void setTreeNodes(TreeModel> treeNodes) { + this.treeNodes = treeNodes; + } + /** + *
  • OasiListBoxLayoutModel
  • + * + *
    +	 * Aggiunge un nodo al tree
    +	 * 
    + * + * Aggiungo nodo di root + * + * @author m.spuri + */ + public BugOasiTreeNode addTreeNode(boolean open) { + return this.addTreeNode(null, open); + }/** + *
  • OasiListBoxLayoutModel
  • + * + *
    +	 * Aggiunge un nodo al tree
    +	 * 
    + * + * @author m.spuri + */ + public BugOasiTreeNode addTreeNode( + BugOasiTreeNode parent, + boolean open) { + return this.addTreeNode(parent, open, null); + } + public BugOasiTreeNode addTreeNode( + BugOasiTreeNode parent, + boolean open, Integer pos) { + return this.addTreeNode(parent, null, null, open, pos); + } + private BugOasiTreeNode addTreeNode( + BugOasiTreeNode parent, + BugOasiTreeNode newnod, BugFormModel myrow, boolean open, + Integer pos) { + if (parent == null) { + if (this.treeRoot == null) + this.createTreeRoot(); + parent = this.treeRoot; + } + BugFormModel row; + if (newnod == null) { + if (myrow == null) + row = new BugFormModel(); + else + row = myrow; + } else + row = newnod.getData(); + + + BugOasiTreeNode treeNode = null; + if (newnod == null) + treeNode = new BugOasiTreeNode(parent, row, + new LinkedList>(), treeNodes, open, + pos); + else { + treeNode = newnod; + parent.setLeaf(false); + if (pos == null) + parent.add(treeNode); + else + parent.getChildren().add(pos, treeNode); + } + row.setNode(treeNode); + if (treeNode.isOpen()) + ((DefaultTreeModel) treeNodes).addOpenObject(treeNode); + return treeNode; + } + private void createTreeRoot() { + this.treeRoot = new BugOasiTreeNode(null, + new LinkedList>(), treeNodes, true); + + this.treeNodes = new DefaultTreeModel(this.treeRoot); + ((DefaultTreeModel)this.treeNodes).setMultiple(this.isEnableMultiSelection()); + } + /** + *
  • OasiListBoxLayoutModel
  • + * + *
    +	 * Aggiunge un nodo al tree
    +	 * 
    + * + * Di default � chiuso + * + * @author m.spuri + */ + public BugOasiTreeNode addTreeNode( + BugOasiTreeNode parent) { + return this.addTreeNode(parent, false); + } + + public void clearTree() { + int n; + if (this.treeRoot != null) { + n = this.treeRoot.getChildren().size(); + for (int k = 0; k < n; k++) + this.clearTreeNode((BugOasiTreeNode) this.treeRoot.getChildren().get(k)); + try { + this.treeRoot.getChildren().clear(); + } catch (Exception e) { + // TODO: handle exception + } + + this.treeRoot = null; + } + } + private void clearTreeNode(BugOasiTreeNode node) { + if (node == null) + return; + if (!node.isLeaf() && node.isOpen()) + ((DefaultTreeModel) treeNodes).removeOpenObject(node); + + BugFormModel row = (BugFormModel) node.getData(); + row.detach(); + if (node.getChildren() != null) { + int n = node.getChildren().size(); + for (int i = 0; i < n; i++) { + this.clearTreeNode((BugOasiTreeNode) node.getChildren().get(i)); + } + node.getChildren().clear(); + } + } + public void setLeafNodes() { + int n; + if (this.treeRoot != null) { + n = this.treeRoot.getChildren().size(); + for (int k = 0; k < n; k++) + this.setLeaf((BugOasiTreeNode) this.treeRoot.getChildren().get(k)); + } + } + private void setLeaf(BugOasiTreeNode node) { + if (node == null) + return; + if (node.getChildren() != null && node.getChildren().size()>0 ) { + int n = node.getChildren().size(); + for (int i = 0; i < n; i++) { + this.setLeaf((BugOasiTreeNode) node.getChildren().get(i)); + } + } + else + node.setLeaf(true); + } + +} diff --git a/zktest/src/main/java/org/zkoss/zktest/test2/B100_ZK_5535/BugOasiTreeNode.java b/zktest/src/main/java/org/zkoss/zktest/test2/B100_ZK_5535/BugOasiTreeNode.java new file mode 100644 index 00000000000..0ce4fef7369 --- /dev/null +++ b/zktest/src/main/java/org/zkoss/zktest/test2/B100_ZK_5535/BugOasiTreeNode.java @@ -0,0 +1,96 @@ +package org.zkoss.zktest.test2.B100_ZK_5535; + +import java.util.LinkedList; +import org.zkoss.zul.DefaultTreeModel; +import org.zkoss.zul.DefaultTreeNode; +import org.zkoss.zul.TreeModel; +import org.zkoss.zul.TreeNode; + +public class BugOasiTreeNode extends DefaultTreeNode { + private static final long serialVersionUID = -8085873079938209759L; + + // Node Control the default open + private boolean open = false; + private boolean leafNode=true; + private TreeModel> treeNodes=null; + public BugOasiTreeNode(T data, LinkedList> children, TreeModel>treeNodes,boolean open) { + super(data, children); + this.treeNodes=treeNodes; + if ( this.treeNodes!=null ) + this.setOpen(open); + else + this.open=open; + } + public BugOasiTreeNode(BugOasiTreeNode parent,T data, LinkedList> children, TreeModel>treeNodes,boolean open) { + super(data, children); + this.treeNodes=treeNodes; + this.setOpen(open); + parent.setLeaf(false); + parent.add(this); + } + public BugOasiTreeNode(BugOasiTreeNode parent,T data, LinkedList> children,TreeModel>treeNodes, boolean open,Integer pos) { + super(data, children); + this.treeNodes=treeNodes; + this.setOpen(open); + parent.setLeaf(false); + if (pos==null || parent.getChildren().size()==0 ) + parent.add(this); + else + parent.getChildren().add(pos,this); + } + public BugOasiTreeNode(T data, LinkedList> children,TreeModel>treeNodes) { + super(data, children); + this.treeNodes=treeNodes; + } + public BugOasiTreeNode(BugOasiTreeNode parent,T data, LinkedList> children,TreeModel>treeNodes) { + super(data, children); + parent.setLeaf(false); + parent.add(this); + this.treeNodes=treeNodes; + } + + public BugOasiTreeNode(BugOasiTreeNode parent,T data,TreeModel>treeNodes) { + super(data); + parent.setLeaf(false); + parent.add(this); + this.treeNodes=treeNodes; + } + public BugOasiTreeNode(BugOasiTreeNode parent,T data,boolean open,TreeModel>treeNodes) { + super(data); + parent.setLeaf(false); + parent.add(this); + this.treeNodes=treeNodes; + this.setOpen(open); + } + public BugOasiTreeNode(T data,TreeModel>treeNodes) { + super(data); + this.treeNodes=treeNodes; + } + public BugOasiTreeNode(T data,TreeModel>treeNodes,boolean open) { + super(data); + this.treeNodes=treeNodes; + this.setOpen(open); + } + + public boolean isOpen() { + return open; + } + + public void setOpen(boolean open) { + this.open = open; + if ( open ) + ((DefaultTreeModel)treeNodes).addOpenObject(this); + else + ((DefaultTreeModel)treeNodes).removeOpenObject(this); + } + public boolean isLeaf() { + + return leafNode; + } + public void setLeaf(boolean value) { + + this.leafNode=value; + if ( value ) + this.setOpen(false); + } +} diff --git a/zktest/src/main/webapp/test2/B100-ZK-5535.zul b/zktest/src/main/webapp/test2/B100-ZK-5535.zul new file mode 100644 index 00000000000..9f4025486b4 --- /dev/null +++ b/zktest/src/main/webapp/test2/B100-ZK-5535.zul @@ -0,0 +1,76 @@ + + + + +