VYPR
Medium severity4.9NVD Advisory· Published May 26, 2026

CVE-2026-41917

CVE-2026-41917

Description

OpenKM 6.3.12 contains a local file inclusion vulnerability in the administrative scripting interface at /admin/Scripting that allows authenticated administrators to read arbitrary files by supplying an attacker-controlled filesystem path through the fsPath parameter with action=Load. Attackers can exploit this to access sensitive files including /etc/passwd, configuration files containing database credentials, and JVM keystores accessible to the OpenKM process.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

OpenKM 6.3.12 contains a local file inclusion vulnerability in the admin scripting interface allowing authenticated admins to read arbitrary files via the fsPath parameter.

Vulnerability

OpenKM Community Edition 6.3.12 and OpenKM Pro Edition 7.1.47 and previous versions contain a local file inclusion vulnerability in the administrative scripting interface at /admin/Scripting [1][3]. The flaw allows an authenticated administrator to read arbitrary files by supplying a filesystem path through the fsPath parameter with action=Load [1]. No special configuration beyond administrative access is required to reach the vulnerable code path.

Exploitation

An attacker needs valid administrative credentials and network access to the OpenKM web interface [1][3]. The exploit sends a crafted HTTP request to /admin/Scripting with action=Load and a fsPath parameter pointing to a target file (e.g., /etc/passwd) [1]. The attacker can also read configuration files containing database credentials and JVM keystores accessible to the OpenKM process [1]. No user interaction beyond the authenticated session is required.

Impact

Successful exploitation leads to unauthorized information disclosure of sensitive files readable by the OpenKM process, including system files like /etc/passwd, application configuration files with database credentials, and JVM keystores [1][3]. The attacker gains the ability to extract secrets that can be used to further compromise the server, backend database, and stored documents [3]. This vulnerability can be chained with other flaws (e.g., RCE via administrative scripting) for full system compromise [3].

Mitigation

At the time of publication, no official vendor patch was available [2][3]. Organizations should implement compensating controls such as restricting access to the /admin/Scripting interface to trusted IPs, enforcing strong authentication, and monitoring administrative activities [2][3]. If possible, disable or restrict the scripting interface for non-essential users. The vendor page does not mention a fix [4]. This vulnerability is not currently listed in CISA's Known Exploited Vulnerabilities catalog.

AI Insight generated on May 26, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2

Patches

1
f2aa3c322d2e

Remove Scripting y DatabaseQuery features

https://github.com/openkm/document-management-systemPaco AvilaFeb 3, 2026Fixed in 6.3.13via llm-release-walk
17 files changed · +83 1570
  • build.sh+1 1 modified
    @@ -2,7 +2,7 @@
     YELLOW='\e[1;33m'
     RESET='\e[0m'
     
    -# export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
    +export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
     mvn -Dmaven.test.skip=true clean gwt:compile install $*
     
     echo -e "${YELLOW}Community version compiled${RESET}"
    
  • javadoc.sh+1 0 modified
    @@ -1,3 +1,4 @@
     #!/bin/bash
     
    +export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
     mvn javadoc:javadoc -Dmaven.javadoc.failOnError=false
    
  • src/main/java/com/openkm/bean/DbQueryGlobalResult.java+0 97 removed
    @@ -1,97 +0,0 @@
    -/**
    - * OpenKM, Open Document Management System (http://www.openkm.com)
    - * Copyright (c) 2006-2017 Paco Avila & Josep Llort
    - * <p>
    - * No bytes were intentionally harmed during the development of this application.
    - * <p>
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - * <p>
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - * <p>
    - * You should have received a copy of the GNU General Public License along
    - * with this program; if not, write to the Free Software Foundation, Inc.,
    - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    - */
    -
    -package com.openkm.bean;
    -
    -import java.io.Serializable;
    -import java.util.ArrayList;
    -import java.util.HashMap;
    -import java.util.List;
    -
    -/**
    - * Container helper class
    - */
    -public class DbQueryGlobalResult implements Serializable {
    -	private static final long serialVersionUID = 1L;
    -	private List<HashMap<String, String>> errors = new ArrayList<>();
    -	private List<List<String>> results = new ArrayList<>();
    -	private List<String> columns = new ArrayList<>();
    -	private String extra = "";
    -	private Integer rows = 0;
    -	private String sql = "";
    -	private Long time = 0L;
    -
    -	public List<String> getColumns() {
    -		return columns;
    -	}
    -
    -	public void setColumns(List<String> columns) {
    -		this.columns = columns;
    -	}
    -
    -	public List<List<String>> getResults() {
    -		return results;
    -	}
    -
    -	public void setResults(List<List<String>> results) {
    -		this.results = results;
    -	}
    -
    -	public Integer getRows() {
    -		return rows;
    -	}
    -
    -	public void setRows(Integer rows) {
    -		this.rows = rows;
    -	}
    -
    -	public String getSql() {
    -		return sql;
    -	}
    -
    -	public void setSql(String sql) {
    -		this.sql = sql;
    -	}
    -
    -	public String getExtra() {
    -		return extra;
    -	}
    -
    -	public void setExtra(String extra) {
    -		this.extra = extra;
    -	}
    -
    -	public Long getTime() {
    -		return time;
    -	}
    -
    -	public void setTime(Long time) {
    -		this.time = time;
    -	}
    -
    -	public List<HashMap<String, String>> getErrors() {
    -		return errors;
    -	}
    -
    -	public void setErrors(List<HashMap<String, String>> errors) {
    -		this.errors = errors;
    -	}
    -}
    
  • src/main/java/com/openkm/bean/Scripting.java+0 37 removed
    @@ -1,37 +0,0 @@
    -/**
    - * OpenKM, Open Document Management System (http://www.openkm.com)
    - * Copyright (c) Paco Avila & Josep Llort
    - * <p>
    - * No bytes were intentionally harmed during the development of this application.
    - * <p>
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - * <p>
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    - * GNU General Public License for more details.
    - * <p>
    - * You should have received a copy of the GNU General Public License along
    - * with this program; if not, write to the Free Software Foundation, Inc.,
    - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    - */
    -
    -package com.openkm.bean;
    -
    -import java.io.Serializable;
    -
    -public class Scripting implements Serializable {
    -	private static final long serialVersionUID = 1L;
    -	public static final String TYPE = "mix:scripting";
    -	public static final String SCRIPT_CODE = "okm:scriptCode";
    -
    -	public String toString() {
    -		StringBuilder sb = new StringBuilder();
    -		sb.append("{");
    -		sb.append("}");
    -		return sb.toString();
    -	}
    -}
    
  • src/main/java/com/openkm/servlet/admin/ActivityLogServlet.java+1 2 modified
    @@ -83,7 +83,6 @@ public class ActivityLogServlet extends BaseServlet {
     			"ADMIN_CHECK_EMAIL",
     			"ADMIN_CONFIG_CREATE", "ADMIN_CONFIG_EDIT", "ADMIN_CONFIG_DELETE",
     			"ADMIN_CRONTAB_CREATE", "ADMIN_CRONTAB_EDIT", "ADMIN_CRONTAB_DELETE", "ADMIN_CRONTAB_EXECUTE",
    -			"ADMIN_DATABASE_QUERY", "ADMIN_DATABASE_UPDATE",
     			"ADMIN_LANGUAGE_CREATE", "ADMIN_LANGUAGE_EDIT", "ADMIN_LANGUAGE_DELETE", "ADMIN_LANGUAGE_IMPORT",
     			"ADMIN_LOGCAT_LIST", "ADMIN_LOGCAT_VIEW",
     			"ADMIN_LOGGED_USERS",
    @@ -110,7 +109,7 @@ public class ActivityLogServlet extends BaseServlet {
     			"ADMIN_TOKEN_SET_NODE", "ADMIN_TOKEN_SIGNAL",
     			"ADMIN_STAMP_IMAGE_CREATE", "ADMIN_STAMP_IMAGE_EDIT", "ADMIN_STAMP_IMAGE_DELETE", "ADMIN_STAMP_IMAGE_ACTIVE",
     			"ADMIN_STAMP_TEXT_CREATE", "ADMIN_STAMP_TEXT_EDIT", "ADMIN_STAMP_TEXT_DELETE", "ADMIN_STAMP_TEXT_ACTIVE",
    -			"ADMIN_USER_CONFIG_EDIT", "ADMIN_SCRIPTING",
    +			"ADMIN_USER_CONFIG_EDIT",
     			"ADMIN_OMR_CREATE", "ADMIN_OMR_EDIT", "ADMIN_OMR_DELETE", "ADMIN_OMR_EXECUTE", "ADMIN_OMR_CHECK_TEMPLATE",
     
     			//---------------------------------
    
  • src/main/java/com/openkm/servlet/admin/ConfigServlet.java+0 1 modified
    @@ -30,7 +30,6 @@
     import com.openkm.dao.ConfigDAO;
     import com.openkm.dao.HibernateUtil;
     import com.openkm.dao.bean.Config;
    -import com.openkm.servlet.admin.DatabaseQueryServlet.WorkerUpdate;
     import com.openkm.util.*;
     import org.apache.commons.fileupload.FileItem;
     import org.apache.commons.fileupload.FileItemFactory;
    
  • src/main/java/com/openkm/servlet/admin/DatabaseQueryServlet.java+0 694 removed
    @@ -1,694 +0,0 @@
    -/**
    - * OpenKM, Open Document Management System (http://www.openkm.com)
    - * Copyright (c) 2006-2017 Paco Avila & Josep Llort
    - * <p>
    - * No bytes were intentionally harmed during the development of this application.
    - * <p>
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - * <p>
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - * <p>
    - * You should have received a copy of the GNU General Public License along
    - * with this program; if not, write to the Free Software Foundation, Inc.,
    - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    - */
    -
    -package com.openkm.servlet.admin;
    -
    -import com.openkm.bean.DbQueryGlobalResult;
    -import com.openkm.core.Config;
    -import com.openkm.core.DatabaseException;
    -import com.openkm.dao.DatabaseMetadataDAO;
    -import com.openkm.dao.HibernateUtil;
    -import com.openkm.dao.LegacyDAO;
    -import com.openkm.dao.bean.DatabaseMetadataType;
    -import com.openkm.dao.bean.DatabaseMetadataValue;
    -import com.openkm.util.DatabaseMetadataUtils;
    -import com.openkm.util.FormatUtil;
    -import com.openkm.util.UserActivity;
    -import org.apache.commons.beanutils.BeanUtils;
    -import org.apache.commons.fileupload.FileItem;
    -import org.apache.commons.fileupload.FileItemFactory;
    -import org.apache.commons.fileupload.FileUploadException;
    -import org.apache.commons.fileupload.disk.DiskFileItemFactory;
    -import org.apache.commons.fileupload.servlet.ServletFileUpload;
    -import org.hibernate.HibernateException;
    -import org.hibernate.Query;
    -import org.hibernate.Session;
    -import org.hibernate.jdbc.Work;
    -import org.hibernate.type.Type;
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -
    -import javax.servlet.ServletContext;
    -import javax.servlet.ServletException;
    -import javax.servlet.http.HttpServletRequest;
    -import javax.servlet.http.HttpServletResponse;
    -import java.io.BufferedReader;
    -import java.io.ByteArrayInputStream;
    -import java.io.IOException;
    -import java.io.InputStreamReader;
    -import java.lang.reflect.InvocationTargetException;
    -import java.sql.*;
    -import java.util.*;
    -
    -/**
    - * Database query
    - */
    -public class DatabaseQueryServlet extends BaseServlet {
    -	private static final long serialVersionUID = 1L;
    -	private static Logger log = LoggerFactory.getLogger(DatabaseQueryServlet.class);
    -
    -	@Override
    -	public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
    -		String method = request.getMethod();
    -
    -		if (checkMultipleInstancesAccess(request, response)) {
    -			if (method.equals(METHOD_GET)) {
    -				doGet(request, response);
    -			} else if (method.equals(METHOD_POST)) {
    -				doPost(request, response);
    -			}
    -		}
    -	}
    -
    -	@Override
    -	public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
    -		log.debug("doGet({}, {})", request, response);
    -		request.setCharacterEncoding("UTF-8");
    -		updateSessionManager(request);
    -		ServletContext sc = getServletContext();
    -		Session session = null;
    -
    -		try {
    -			session = HibernateUtil.getSessionFactory().openSession();
    -			sc.setAttribute("qs", null);
    -			sc.setAttribute("type", null);
    -			sc.setAttribute("showSql", null);
    -			sc.setAttribute("exception", null);
    -			sc.setAttribute("globalResults", null);
    -			sc.setAttribute("tables", listTables(session));
    -			sc.setAttribute("vtables", listVirtualTables());
    -			sc.getRequestDispatcher("/admin/database_query.jsp").forward(request, response);
    -		} catch (Exception e) {
    -			sendError(sc, request, response, e);
    -		} finally {
    -			HibernateUtil.close(session);
    -		}
    -	}
    -
    -	@Override
    -	@SuppressWarnings("unchecked")
    -	public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
    -		log.debug("doPost({}, {})", request, response);
    -		request.setCharacterEncoding("UTF-8");
    -		updateSessionManager(request);
    -		String user = request.getRemoteUser();
    -		ServletContext sc = getServletContext();
    -		Session session = null;
    -
    -		try {
    -			if (ServletFileUpload.isMultipartContent(request)) {
    -				FileItemFactory factory = new DiskFileItemFactory();
    -				ServletFileUpload upload = new ServletFileUpload(factory);
    -				List<FileItem> items = upload.parseRequest(request);
    -				boolean showSql = false;
    -				String vtable = "";
    -				String type = "";
    -				String qs = "";
    -				byte[] data = null;
    -
    -				for (FileItem item : items) {
    -					if (item.isFormField()) {
    -						if (item.getFieldName().equals("qs")) {
    -							qs = item.getString("UTF-8");
    -						} else if (item.getFieldName().equals("type")) {
    -							type = item.getString("UTF-8");
    -						} else if (item.getFieldName().equals("showSql")) {
    -							showSql = true;
    -						} else if (item.getFieldName().equals("vtables")) {
    -							vtable = item.getString("UTF-8");
    -						}
    -					} else {
    -						data = item.get();
    -					}
    -				}
    -
    -				if (!qs.equals("") && !type.equals("")) {
    -					session = HibernateUtil.getSessionFactory().openSession();
    -					sc.setAttribute("qs", qs);
    -					sc.setAttribute("type", type);
    -
    -					if (type.equals("jdbc")) {
    -						executeJdbc(session, qs, sc, request, response);
    -
    -						// Activity log
    -						UserActivity.log(user, "ADMIN_DATABASE_QUERY_JDBC", null, null, qs);
    -					} else if (type.equals("hibernate")) {
    -						executeHibernate(session, qs, showSql, sc, request, response);
    -
    -						// Activity log
    -						UserActivity.log(user, "ADMIN_DATABASE_QUERY_HIBERNATE", null, null, qs);
    -					} else if (type.equals("metadata")) {
    -						sc.setAttribute("vtable", vtable);
    -						executeMetadata(session, qs, false, sc, request, response);
    -
    -						// Activity log
    -						UserActivity.log(user, "ADMIN_DATABASE_QUERY_METADATA", null, null, qs);
    -					}
    -				} else if (data != null && data.length > 0) {
    -					sc.setAttribute("exception", null);
    -					session = HibernateUtil.getSessionFactory().openSession();
    -					executeUpdate(session, data, sc, request, response);
    -
    -					// Activity log
    -					UserActivity.log(user, "ADMIN_DATABASE_QUERY_FILE", null, null, new String(data));
    -				} else {
    -					sc.setAttribute("qs", qs);
    -					sc.setAttribute("type", type);
    -					sc.setAttribute("showSql", showSql);
    -					sc.setAttribute("exception", null);
    -					sc.setAttribute("globalResults", new ArrayList<DbQueryGlobalResult>());
    -					sc.getRequestDispatcher("/admin/database_query.jsp").forward(request, response);
    -				}
    -			} else {
    -				// Edit table cell value
    -				String action = request.getParameter("action");
    -				String vtable = request.getParameter("vtable");
    -				String column = request.getParameter("column");
    -				String value = request.getParameter("value");
    -				String id = request.getParameter("id");
    -
    -				if (action.equals("edit")) {
    -					int idx = column.indexOf('(');
    -
    -					if (idx > 0) {
    -						column = column.substring(idx + 1, idx + 6);
    -					}
    -
    -					String hql = "update DatabaseMetadataValue dmv set dmv." + column + "='" + value + "' where dmv.table='" + vtable
    -							+ "' and dmv.id=" + id;
    -					log.info("HQL: {}", hql);
    -					session = HibernateUtil.getSessionFactory().openSession();
    -					int rows = session.createQuery(hql).executeUpdate();
    -					log.info("Rows affected: {}", rows);
    -				}
    -			}
    -		} catch (FileUploadException | SQLException | HibernateException | DatabaseException | IllegalAccessException |
    -				 InvocationTargetException | NoSuchMethodException e) {
    -			sendError(sc, request, response, e);
    -		} finally {
    -			HibernateUtil.close(session);
    -		}
    -	}
    -
    -	/**
    -	 * Execute metadata query
    -	 */
    -	private void executeMetadata(Session session, String qs, boolean showSql, ServletContext sc, HttpServletRequest request,
    -			HttpServletResponse response) throws DatabaseException, ServletException, IOException, HibernateException,
    -			IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    -		StringTokenizer st = new StringTokenizer(qs, "\n\r");
    -		List<DbQueryGlobalResult> globalResults = new ArrayList<>();
    -
    -		// For each query line
    -		while (st.hasMoreTokens()) {
    -			String mds = st.nextToken();
    -			String[] parts = mds.split("\\|");
    -
    -			if (parts.length > 1) {
    -				String hql = null;
    -
    -				switch (parts[0].toUpperCase()) {
    -					case "SENTENCE":
    -						if (parts.length > 2) {
    -							List<String> tables = Arrays.asList(parts[1].split(","));
    -							hql = DatabaseMetadataUtils.replaceVirtual(tables, parts[2]);
    -							log.debug("Metadata SENTENCE: {}", hql);
    -							globalResults.add(executeHQL(session, hql, showSql, tables));
    -						}
    -						break;
    -
    -					case "SELECT":
    -						if (parts.length > 2) {
    -							hql = DatabaseMetadataUtils.buildQuery(parts[1], parts[2]);
    -						} else {
    -							// Filter parameter is optional
    -							hql = DatabaseMetadataUtils.buildQuery(parts[1], null);
    -						}
    -
    -						log.debug("Metadata SELECT: {}", hql);
    -						globalResults.add(executeHQL(session, hql, showSql, Arrays.asList(parts[1])));
    -						break;
    -
    -					case "UPDATE":
    -						if (parts.length > 3) {
    -							hql = DatabaseMetadataUtils.buildUpdate(parts[1], parts[2], parts[3]);
    -						} else if (parts.length > 2) {
    -							// Filter parameter is optional
    -							hql = DatabaseMetadataUtils.buildUpdate(parts[1], parts[2], null);
    -						} else {
    -							// Filter parameter is optional
    -							hql = DatabaseMetadataUtils.buildUpdate(parts[1], null, null);
    -						}
    -
    -						log.debug("Metadata UPDATE: {}", hql);
    -						globalResults.add(executeHQL(session, hql, showSql, Arrays.asList(parts[1])));
    -						break;
    -
    -					case "DELETE":
    -						if (parts.length > 2) {
    -							hql = DatabaseMetadataUtils.buildDelete(parts[1], parts[2]);
    -						} else {
    -							// Filter parameter is optional
    -							hql = DatabaseMetadataUtils.buildDelete(parts[1], null);
    -						}
    -
    -						log.debug("Metadata DELETE: {}", hql);
    -						globalResults.add(executeHQL(session, hql, showSql, Arrays.asList(parts[1])));
    -						break;
    -
    -					default:
    -						throw new DatabaseException("Error in metadata action");
    -				}
    -			} else {
    -				throw new DatabaseException("Error in metadata sentence parameters");
    -			}
    -		}
    -
    -		sc.setAttribute("exception", null);
    -		sc.setAttribute("showSql", showSql);
    -		sc.setAttribute("globalResults", globalResults);
    -		sc.getRequestDispatcher("/admin/database_query.jsp").forward(request, response);
    -	}
    -
    -	/**
    -	 * Execute Hibernate query
    -	 */
    -	private void executeHibernate(Session session, String qs, boolean showSql, ServletContext sc, HttpServletRequest request,
    -			HttpServletResponse response) throws ServletException, IOException, HibernateException, DatabaseException,
    -			IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    -		StringTokenizer st = new StringTokenizer(qs, "\n\r");
    -		List<DbQueryGlobalResult> globalResults = new ArrayList<>();
    -
    -		// For each query line
    -		while (st.hasMoreTokens()) {
    -			String tk = st.nextToken().trim();
    -
    -			if (tk.toUpperCase().startsWith("--") || tk.equals("") || tk.equals("\r")) {
    -				// Is a comment, so ignore it
    -			} else {
    -				if (tk.endsWith(";")) {
    -					tk = tk.substring(0, tk.length() - 1);
    -				}
    -
    -				globalResults.add(executeHQL(session, tk, showSql, null));
    -			}
    -		}
    -
    -		sc.setAttribute("exception", null);
    -		sc.setAttribute("showSql", showSql);
    -		sc.setAttribute("globalResults", globalResults);
    -		sc.getRequestDispatcher("/admin/database_query.jsp").forward(request, response);
    -	}
    -
    -	/**
    -	 * Execute hibernate sentence
    -	 */
    -	@SuppressWarnings("unchecked")
    -	private DbQueryGlobalResult executeHQL(Session session, String hql, boolean showSql, List<String> vtables) throws HibernateException,
    -			DatabaseException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    -		long begin = System.currentTimeMillis();
    -
    -		if (hql.toUpperCase().startsWith("SELECT") || hql.toUpperCase().startsWith("FROM")) {
    -			Query q = session.createQuery(hql);
    -			List<Object> ret = q.list();
    -			List<String> columns = new ArrayList<>();
    -			List<String> vcolumns = new ArrayList<>();
    -			List<List<String>> results = new ArrayList<>();
    -			Type[] rt = q.getReturnTypes();
    -			int i = 0;
    -
    -			if (vtables == null) {
    -				for (i = 0; i < rt.length; i++) {
    -					columns.add(rt[i].getName());
    -				}
    -			} else {
    -				for (String vtable : vtables) {
    -					String query = "select dmt.virtualColumn, dmt.realColumn from DatabaseMetadataType dmt where dmt.table='" + vtable
    -							+ "'";
    -					List<Object> tmp = LegacyDAO.executeQuery(query);
    -
    -					for (Object obj : tmp) {
    -						Object[] dt = (Object[]) obj;
    -						vcolumns.add(String.valueOf(dt[0]));
    -						columns.add(String.valueOf(dt[0]).concat(" (").concat(String.valueOf(dt[1])).concat(")"));
    -					}
    -				}
    -			}
    -
    -			for (Iterator<Object> it = ret.iterator(); it.hasNext() && i++ < Config.MAX_SEARCH_RESULTS; ) {
    -				List<String> row = new ArrayList<>();
    -				Object obj = it.next();
    -
    -				if (vtables == null) {
    -					if (obj instanceof Object[]) {
    -						Object[] ao = (Object[]) obj;
    -
    -						for (Object o : ao) {
    -							row.add(String.valueOf(o));
    -						}
    -					} else {
    -						row.add(String.valueOf(obj));
    -					}
    -				} else {
    -					if (obj instanceof DatabaseMetadataValue) {
    -						DatabaseMetadataValue dmv = (DatabaseMetadataValue) obj;
    -						row.add(String.valueOf(dmv.getId()));
    -
    -						for (String column : vcolumns) {
    -							row.add(DatabaseMetadataUtils.getString(dmv, column));
    -						}
    -					} else if (obj instanceof Object[]) {
    -						for (Object objChild : (Object[]) obj) {
    -							if (objChild instanceof DatabaseMetadataValue) {
    -								DatabaseMetadataValue dmvChild = (DatabaseMetadataValue) objChild;
    -								List<DatabaseMetadataType> types = DatabaseMetadataDAO.findAllTypes(dmvChild.getTable());
    -
    -								for (DatabaseMetadataType emt : types) {
    -									for (String column : vcolumns) {
    -										if (emt.getVirtualColumn().equals(column)) {
    -											row.add(BeanUtils.getProperty(dmvChild, emt.getRealColumn()));
    -										}
    -									}
    -								}
    -							} else {
    -								row.add(String.valueOf(objChild));
    -							}
    -						}
    -					} else {
    -						row.add("Query result should be instance of DatabaseMetadataValue");
    -					}
    -				}
    -
    -				results.add(row);
    -			}
    -
    -			DbQueryGlobalResult gr = new DbQueryGlobalResult();
    -			gr.setColumns(columns);
    -			gr.setResults(results);
    -			gr.setRows(null);
    -			gr.setSql(hql);
    -			gr.setExtra(showSql ? HibernateUtil.toSql(hql) : null);
    -			gr.setTime(System.currentTimeMillis() - begin);
    -			return gr;
    -		} else {
    -			DbQueryGlobalResult gr = new DbQueryGlobalResult();
    -			int rows = session.createQuery(hql).executeUpdate();
    -			gr.setColumns(null);
    -			gr.setResults(null);
    -			gr.setRows(rows);
    -			gr.setSql(hql);
    -			gr.setTime(System.currentTimeMillis() - begin);
    -			return gr;
    -		}
    -	}
    -
    -	/**
    -	 * Execute JDBC query
    -	 */
    -	private void executeJdbc(Session session, String qs, ServletContext sc, HttpServletRequest request, HttpServletResponse response)
    -			throws SQLException, ServletException, IOException {
    -		WorkerJdbc worker = new WorkerJdbc();
    -		worker.setQueryString(qs);
    -		session.doWork(worker);
    -
    -		sc.setAttribute("showSql", null);
    -		sc.setAttribute("exception", null);
    -		sc.setAttribute("globalResults", worker.getGlobalResults());
    -		sc.getRequestDispatcher("/admin/database_query.jsp").forward(request, response);
    -	}
    -
    -	/**
    -	 * Import into database
    -	 */
    -	private void executeUpdate(Session session, byte[] data, ServletContext sc, HttpServletRequest request, HttpServletResponse response)
    -			throws SQLException, ServletException, IOException {
    -		log.debug("executeUpdate({}, {}, {})", session, request, response);
    -		List<DbQueryGlobalResult> globalResults = new ArrayList<>();
    -		WorkerUpdate worker = new WorkerUpdate();
    -		worker.setData(data);
    -		session.doWork(worker);
    -
    -		DbQueryGlobalResult gr = new DbQueryGlobalResult();
    -		gr.setColumns(null);
    -		gr.setResults(null);
    -		gr.setSql(null);
    -		gr.setRows(worker.getRows());
    -		gr.setErrors(worker.getErrors());
    -		globalResults.add(gr);
    -
    -		sc.setAttribute("qs", null);
    -		sc.setAttribute("type", null);
    -		sc.setAttribute("showSql", null);
    -		sc.setAttribute("globalResults", globalResults);
    -		sc.getRequestDispatcher("/admin/database_query.jsp").forward(request, response);
    -
    -		log.debug("executeUpdate: void");
    -	}
    -
    -	/**
    -	 * List tables from database
    -	 */
    -	private List<String> listTables(Session session) {
    -		final List<String> tables = new ArrayList<>();
    -		final String[] tableTypes = {"TABLE"};
    -		final String[] tablePatterns = new String[]{"JBPM_%", "OKM_%", "DEFAULT_%", "VERSION_%", "jbpm_%", "okm_%", "default_%",
    -				"version_%"};
    -
    -		session.doWork(new Work() {
    -			@Override
    -			public void execute(Connection con) throws SQLException {
    -				DatabaseMetaData md = con.getMetaData();
    -
    -				for (String table : tablePatterns) {
    -					ResultSet rs = md.getTables(null, null, table, tableTypes);
    -
    -					while (rs.next()) {
    -						tables.add(rs.getString(3));
    -					}
    -
    -					rs.close();
    -				}
    -			}
    -		});
    -
    -		return tables;
    -	}
    -
    -	/**
    -	 * List virtual tables from database
    -	 */
    -	private List<String> listVirtualTables() throws DatabaseException {
    -		String query = "select distinct(dmv.table) from DatabaseMetadataType dmv order by dmv.table";
    -		List<Object> tmp = LegacyDAO.executeQuery(query);
    -		List<String> tables = new ArrayList<>();
    -
    -		for (Object obj : tmp) {
    -			tables.add(String.valueOf(obj));
    -		}
    -
    -		return tables;
    -	}
    -
    -	/**
    -	 * Send error to be displayed inline
    -	 */
    -	protected void sendError(ServletContext sc, HttpServletRequest request, HttpServletResponse response, Exception e)
    -			throws ServletException, IOException {
    -		sc.setAttribute("exception", e);
    -		sc.setAttribute("globalResults", null);
    -		sc.getRequestDispatcher("/admin/database_query.jsp").forward(request, response);
    -	}
    -
    -	/**
    -	 * Hibernate worker helper
    -	 */
    -	public static class WorkerJdbc implements Work {
    -		List<DbQueryGlobalResult> globalResults = new ArrayList<>();
    -		String qs = null;
    -
    -		List<DbQueryGlobalResult> getGlobalResults() {
    -			return this.globalResults;
    -		}
    -
    -		void setQueryString(String qs) {
    -			this.qs = qs;
    -		}
    -
    -		@Override
    -		public void execute(Connection con) throws SQLException {
    -			Statement stmt = null;
    -			ResultSet rs = null;
    -
    -			try {
    -				stmt = con.createStatement();
    -				StringTokenizer st = new StringTokenizer(qs, "\n\r");
    -				int ln = 0;
    -
    -				// For each query line
    -				while (st.hasMoreTokens()) {
    -					String tk = st.nextToken().trim();
    -					ln++;
    -
    -					if (tk.toUpperCase().startsWith("--") || tk.equals("") || tk.equals("\r")) {
    -						// Is a comment, so ignore it
    -					} else {
    -						if (tk.endsWith(";")) {
    -							tk = tk.substring(0, tk.length() - 1);
    -						}
    -
    -						try {
    -							long begin = System.currentTimeMillis();
    -
    -							if (tk.toUpperCase().startsWith("SELECT") || tk.toUpperCase().startsWith("DESCRIBE")) {
    -								rs = stmt.executeQuery(tk);
    -								ResultSetMetaData md = rs.getMetaData();
    -								List<String> columns = new ArrayList<>();
    -								List<List<String>> results = new ArrayList<>();
    -
    -								for (int i = 1; i < md.getColumnCount() + 1; i++) {
    -									columns.add(md.getColumnName(i));
    -								}
    -
    -								for (int i = 0; rs.next() && i++ < Config.MAX_SEARCH_RESULTS; ) {
    -									List<String> row = new ArrayList<>();
    -
    -									for (int j = 1; j < md.getColumnCount() + 1; j++) {
    -										if (Types.BLOB == md.getColumnType(j)) {
    -											row.add("BLOB");
    -										} else {
    -											if (rs.getString(j) != null) {
    -												row.add(FormatUtil.escapeHtml(rs.getString(j)));
    -											} else {
    -												row.add(rs.getString(j));
    -											}
    -										}
    -									}
    -
    -									results.add(row);
    -								}
    -
    -								DbQueryGlobalResult gr = new DbQueryGlobalResult();
    -								gr.setColumns(columns);
    -								gr.setResults(results);
    -								gr.setExtra(null);
    -								gr.setRows(null);
    -								gr.setSql(tk);
    -								gr.setTime(System.currentTimeMillis() - begin);
    -								globalResults.add(gr);
    -							} else {
    -								DbQueryGlobalResult gr = new DbQueryGlobalResult();
    -								int rows = stmt.executeUpdate(tk);
    -								gr.setColumns(null);
    -								gr.setResults(null);
    -								gr.setExtra(null);
    -								gr.setRows(rows);
    -								gr.setSql(tk);
    -								gr.setTime(System.currentTimeMillis() - begin);
    -								globalResults.add(gr);
    -							}
    -						} catch (SQLException e) {
    -							DbQueryGlobalResult gr = new DbQueryGlobalResult();
    -							List<HashMap<String, String>> errors = new ArrayList<>();
    -							HashMap<String, String> error = new HashMap<>();
    -							error.put("ln", Integer.toString(ln));
    -							error.put("sql", tk);
    -							error.put("msg", e.getMessage());
    -							errors.add(error);
    -							gr.setErrors(errors);
    -							gr.setRows(null);
    -							gr.setSql(null);
    -							gr.setExtra(null);
    -							gr.setColumns(null);
    -							gr.setResults(null);
    -							globalResults.add(gr);
    -						}
    -					}
    -				}
    -			} finally {
    -				LegacyDAO.close(rs);
    -				LegacyDAO.close(stmt);
    -			}
    -		}
    -	}
    -
    -	/**
    -	 * Hibernate worker helper
    -	 */
    -	public static class WorkerUpdate implements Work {
    -		List<HashMap<String, String>> errors = new ArrayList<>();
    -		int rows = 0;
    -		byte[] data;
    -
    -		List<HashMap<String, String>> getErrors() {
    -			return this.errors;
    -		}
    -
    -		void setData(byte[] data) {
    -			this.data = data;
    -		}
    -
    -		int getRows() {
    -			return this.rows;
    -		}
    -
    -		@Override
    -		public void execute(Connection con) throws SQLException {
    -			Statement stmt = null;
    -			ResultSet rs = null;
    -
    -			try {
    -				stmt = con.createStatement();
    -				InputStreamReader is = new InputStreamReader(new ByteArrayInputStream(data));
    -				BufferedReader br = new BufferedReader(is);
    -				String sql;
    -				int ln = 0;
    -
    -				while ((sql = br.readLine()) != null) {
    -					String tk = sql.trim();
    -					ln++;
    -
    -					if (tk.toUpperCase().startsWith("--") || tk.equals("") || tk.equals("\r")) {
    -						// Is a comment, so ignore it
    -					} else {
    -						if (tk.endsWith(";")) {
    -							tk = tk.substring(0, tk.length() - 1);
    -						}
    -
    -						try {
    -							rows += stmt.executeUpdate(tk);
    -						} catch (SQLException e) {
    -							HashMap<String, String> error = new HashMap<>();
    -							error.put("ln", Integer.toString(ln));
    -							error.put("sql", tk);
    -							error.put("msg", e.getMessage());
    -							errors.add(error);
    -						}
    -					}
    -				}
    -			} catch (IOException e) {
    -				throw new SQLException(e.getMessage(), e);
    -			} finally {
    -				LegacyDAO.close(rs);
    -				LegacyDAO.close(stmt);
    -			}
    -		}
    -	}
    -}
    
  • src/main/java/com/openkm/servlet/admin/MimeTypeServlet.java+1 6 modified
    @@ -26,11 +26,7 @@
     import com.openkm.dao.HibernateUtil;
     import com.openkm.dao.MimeTypeDAO;
     import com.openkm.dao.bean.MimeType;
    -import com.openkm.servlet.admin.DatabaseQueryServlet.WorkerUpdate;
    -import com.openkm.util.SecureStore;
    -import com.openkm.util.UserActivity;
    -import com.openkm.util.WarUtils;
    -import com.openkm.util.WebUtils;
    +import com.openkm.util.*;
     import org.apache.commons.fileupload.FileItem;
     import org.apache.commons.fileupload.FileItemFactory;
     import org.apache.commons.fileupload.FileUploadException;
    @@ -296,7 +292,6 @@ private void export(String userId, HttpServletRequest request, HttpServletRespon
     	private void importMimeTypes(String userId, HttpServletRequest request, HttpServletResponse response, final byte[] data,
     			Session dbSession) throws DatabaseException, SQLException {
     		log.debug("import({}, {}, {}, {}, {})", userId, request, response, data, dbSession);
    -
             WorkerUpdate worker = new WorkerUpdate();
             worker.setData(data);
             dbSession.doWork(worker);
    
  • src/main/java/com/openkm/servlet/admin/ScriptingServlet.java+0 154 removed
    @@ -1,154 +0,0 @@
    -/**
    - * OpenKM, Open Document Management System (http://www.openkm.com)
    - * Copyright (c) Paco Avila & Josep Llort
    - * <p>
    - * No bytes were intentionally harmed during the development of this application.
    - * <p>
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - * <p>
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    - * GNU General Public License for more details.
    - * <p>
    - * You should have received a copy of the GNU General Public License along
    - * with this program; if not, write to the Free Software Foundation, Inc.,
    - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    - */
    -
    -package com.openkm.servlet.admin;
    -
    -import bsh.Interpreter;
    -import com.openkm.util.FormatUtil;
    -import com.openkm.util.SecureStore;
    -import com.openkm.util.UserActivity;
    -import com.openkm.util.WebUtils;
    -import org.apache.commons.io.IOUtils;
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -
    -import javax.servlet.ServletContext;
    -import javax.servlet.ServletException;
    -import javax.servlet.annotation.WebServlet;
    -import javax.servlet.http.HttpServletRequest;
    -import javax.servlet.http.HttpServletResponse;
    -import java.io.*;
    -import java.util.UUID;
    -
    -/**
    - * Execute BeanShell
    - *
    - * @author sochoa
    - */
    -@WebServlet("/admin/Scripting")
    -public class ScriptingServlet extends BaseServlet {
    -    private static final long serialVersionUID = 1L;
    -    private static Logger log = LoggerFactory.getLogger(ScriptingServlet.class);
    -
    -    @Override
    -    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
    -        log.debug("doGet({}, {})", request, response);
    -        updateSessionManager(request);
    -
    -        try {
    -            String genCsrft = SecureStore.md5Encode(UUID.randomUUID().toString().getBytes());
    -            request.getSession().setAttribute("csrft", genCsrft);
    -            ServletContext sc = getServletContext();
    -            sc.setAttribute("script", null);
    -            sc.setAttribute("csrft", genCsrft);
    -            sc.setAttribute("scriptResult", null);
    -            sc.setAttribute("scriptError", null);
    -            sc.setAttribute("scriptOutput", null);
    -            sc.setAttribute("time", null);
    -            sc.getRequestDispatcher("/admin/scripting.jsp").forward(request, response);
    -        } catch (Exception e) {
    -            log.error(e.getMessage(), e);
    -            sendErrorRedirect(request, response, e);
    -        }
    -    }
    -
    -    @Override
    -    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
    -        log.debug("doPost({}, {})", request, response);
    -        updateSessionManager(request);
    -        String action = WebUtils.getString(request, "action");
    -        String script = WebUtils.getString(request, "script");
    -        String fsPath = WebUtils.getString(request, "fsPath");
    -        String reqCsrft = WebUtils.getString(request, "csrft");
    -        String sesCsrft = (String) request.getSession().getAttribute("csrft");
    -
    -        try {
    -            ByteArrayOutputStream scriptOutput = new ByteArrayOutputStream();
    -            Exception scriptError = null;
    -            Object scriptResult = null;
    -            long begin = System.currentTimeMillis();
    -
    -            if (action.equals("Load") && !fsPath.isEmpty()) {
    -                if (reqCsrft.equals(sesCsrft)) {
    -                    File file = new File(fsPath);
    -                    FileInputStream fis = new FileInputStream(file);
    -                    script = IOUtils.toString(fis);
    -                    IOUtils.closeQuietly(fis);
    -                } else {
    -                    UserActivity.log(request.getRemoteUser(), "ADMIN_SECURITY_RISK", request.getRemoteHost(), null, script);
    -                    throw new ServletException("Security risk detected");
    -                }
    -            } else if (action.equals("Save") && !fsPath.isEmpty() && !script.isEmpty()) {
    -                if (reqCsrft.equals(sesCsrft)) {
    -                    // Trim filename
    -                    File fsFile = new File(fsPath.trim());
    -                    String fsName = fsFile.getName().trim();
    -                    String fsParent = fsFile.getParent().trim();
    -                    File file = new File(fsParent, fsName);
    -
    -                    FileOutputStream fos = new FileOutputStream(file);
    -                    IOUtils.write(script, fos);
    -                    IOUtils.closeQuietly(fos);
    -                } else {
    -                    UserActivity.log(request.getRemoteUser(), "ADMIN_SECURITY_RISK", request.getRemoteHost(), null, script);
    -                    throw new ServletException("Security risk detected");
    -                }
    -            } else if (action.equals("Evaluate") && !script.isEmpty()) {
    -                if (reqCsrft.equals(sesCsrft)) {
    -                    PrintStream pout = new PrintStream(scriptOutput);
    -                    Interpreter bsh = new Interpreter(null, pout, pout, false);
    -
    -                    // set up interpreter
    -                    bsh.set("bsh.httpServletRequest", request);
    -                    bsh.set("bsh.httpServletResponse", response);
    -
    -                    try {
    -                        scriptResult = bsh.eval(script);
    -                    } catch (Exception e) {
    -                        scriptError = e;
    -                    }
    -
    -					pout.flush();
    -
    -                    // Activity log
    -                    UserActivity.log(request.getRemoteUser(), "ADMIN_SCRIPTING", request.getRemoteHost(), null, script);
    -                } else {
    -                    UserActivity.log(request.getRemoteUser(), "ADMIN_SECURITY_RISK", request.getRemoteHost(), null, script);
    -                    throw new ServletException("Security risk detected");
    -                }
    -            }
    -
    -            String genCsrft = SecureStore.md5Encode(UUID.randomUUID().toString().getBytes());
    -            request.getSession().setAttribute("csrft", genCsrft);
    -            ServletContext sc = getServletContext();
    -            sc.setAttribute("script", script);
    -            sc.setAttribute("csrft", genCsrft);
    -            sc.setAttribute("scriptResult", scriptResult);
    -            sc.setAttribute("scriptError", scriptError);
    -            sc.setAttribute("scriptOutput", scriptOutput.toString());
    -            sc.setAttribute("time", FormatUtil.formatMiliSeconds(System.currentTimeMillis() - begin));
    -            sc.getRequestDispatcher("/admin/scripting.jsp").forward(request, response);
    -        } catch (Exception e) {
    -            log.error(e.getMessage(), e);
    -            sendErrorRedirect(request, response, e);
    -        }
    -    }
    -}
    
  • src/main/java/com/openkm/util/ScriptingLock.java+0 71 removed
    @@ -1,71 +0,0 @@
    -/**
    - * OpenKM, Open Document Management System (http://www.openkm.com)
    - * Copyright (c) 2006-2017 Paco Avila & Josep Llort
    - * <p>
    - * No bytes were intentionally harmed during the development of this application.
    - * <p>
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - * <p>
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - * <p>
    - * You should have received a copy of the GNU General Public License along
    - * with this program; if not, write to the Free Software Foundation, Inc.,
    - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    - */
    -
    -package com.openkm.util;
    -
    -/**
    - * Lock
    - *
    - * @author jllort
    - */
    -public class ScriptingLock {
    -	private static ScriptingLock instance = new ScriptingLock();
    -
    -	boolean isLocked = false;
    -	Thread lockedBy = null;
    -	int lockedCount = 0;
    -
    -	/**
    -	 * getInstance
    -	 */
    -	public static ScriptingLock getInstance() {
    -		return instance;
    -	}
    -
    -	/**
    -	 * lock
    -	 */
    -	public synchronized void lock() throws InterruptedException {
    -		Thread callingThread = Thread.currentThread();
    -
    -		while (isLocked) {
    -			wait();
    -		}
    -
    -		isLocked = true;
    -		lockedCount++;
    -		lockedBy = callingThread;
    -	}
    -
    -	/**
    -	 * unlock
    -	 */
    -	public synchronized void unlock() {
    -		if (Thread.currentThread() == this.lockedBy) {
    -			lockedCount--;
    -
    -			if (lockedCount == 0) {
    -				isLocked = false;
    -				notify();
    -			}
    -		}
    -	}
    -}
    \ No newline at end of file
    
  • src/main/java/com/openkm/util/WorkerUpdate.java+79 0 added
    @@ -0,0 +1,79 @@
    +package com.openkm.util;
    +
    +import com.openkm.dao.LegacyDAO;
    +import org.hibernate.jdbc.Work;
    +
    +import java.io.BufferedReader;
    +import java.io.ByteArrayInputStream;
    +import java.io.IOException;
    +import java.io.InputStreamReader;
    +import java.sql.Connection;
    +import java.sql.ResultSet;
    +import java.sql.SQLException;
    +import java.sql.Statement;
    +import java.util.ArrayList;
    +import java.util.HashMap;
    +import java.util.List;
    +
    +/**
    + * Hibernate worker helper
    + */
    +public class WorkerUpdate implements Work {
    +	private List<HashMap<String, String>> errors = new ArrayList<>();
    +	private int rows = 0;
    +	private byte[] data;
    +
    +	public List<HashMap<String, String>> getErrors() {
    +		return this.errors;
    +	}
    +
    +	public void setData(byte[] data) {
    +		this.data = data;
    +	}
    +
    +	public int getRows() {
    +		return this.rows;
    +	}
    +
    +	@Override
    +	public void execute(Connection con) throws SQLException {
    +		Statement stmt = null;
    +		ResultSet rs = null;
    +
    +		try {
    +			stmt = con.createStatement();
    +			InputStreamReader is = new InputStreamReader(new ByteArrayInputStream(data));
    +			BufferedReader br = new BufferedReader(is);
    +			String sql;
    +			int ln = 0;
    +
    +			while ((sql = br.readLine()) != null) {
    +				String tk = sql.trim();
    +				ln++;
    +
    +				if (tk.toUpperCase().startsWith("--") || tk.equals("") || tk.equals("\r")) {
    +					// Is a comment, so ignore it
    +				} else {
    +					if (tk.endsWith(";")) {
    +						tk = tk.substring(0, tk.length() - 1);
    +					}
    +
    +					try {
    +						rows += stmt.executeUpdate(tk);
    +					} catch (SQLException e) {
    +						HashMap<String, String> error = new HashMap<>();
    +						error.put("ln", Integer.toString(ln));
    +						error.put("sql", tk);
    +						error.put("msg", e.getMessage());
    +						errors.add(error);
    +					}
    +				}
    +			}
    +		} catch (IOException e) {
    +			throw new SQLException(e.getMessage(), e);
    +		} finally {
    +			LegacyDAO.close(rs);
    +			LegacyDAO.close(stmt);
    +		}
    +	}
    +}
    
  • src/main/webapp/admin/database_query.jsp+0 373 removed
    @@ -1,373 +0,0 @@
    -<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
    -<%@ page import="com.openkm.core.Config" %>
    -<%@ page import="com.openkm.servlet.admin.BaseServlet" %>
    -<%@ page import="java.util.concurrent.TimeUnit" %>
    -<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    -<%@ taglib uri="http://www.openkm.com/tags/utils" prefix="u" %>
    -<?xml version="1.0" encoding="UTF-8" ?>
    -<!DOCTYPE html>
    -<html>
    -<head>
    -  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    -  <link rel="Shortcut icon" href="favicon.ico" />
    -  <link rel="stylesheet" type="text/css" href="js/codemirror/lib/codemirror.css" />
    -  <link rel="stylesheet" type="text/css" href="../css/chosen.css"/>
    -  <link rel="stylesheet" type="text/css" href="css/admin-style.css" />
    -  <style type="text/css">
    -    .CodeMirror { width: 700px; height: 300px; background-color: #f8f6c2; }
    -    .results .cellEditing { padding: 0; }
    -    .results .cellEditing input[type=text] { width: 100%; border: 0; background-color: rgb(255,253,210); }
    -  </style>
    -  <script type="text/javascript" src="js/codemirror/lib/codemirror.js"></script>
    -  <script type="text/javascript" src="js/codemirror/mode/sql/sql.js"></script>
    -  <script type="text/javascript" src="js/codemirror/addon/selection/active-line.js"></script>
    -  <script type="text/javascript" src="../js/jquery-1.11.3.min.js"></script>
    -  <script src="../js/chosen.jquery.js" type="text/javascript"></script>
    -  <script type="text/javascript">
    -    $(document).ready(function() {
    -      $('#scroll').height($(window).height() - 21);
    -      $('select#tables').chosen({disable_search_threshold: 10});
    -      $('select#vtables').chosen({disable_search_threshold: 10});
    -      $('select#type').chosen({disable_search_threshold: 10});
    -      
    -      var cm = CodeMirror.fromTextArea(document.getElementById('qs'), {
    -        lineNumbers: true,
    -        matchBrackets: true,
    -        styleActiveLine: true,
    -        mode: "text/x-sql",
    -        indentUnit: 4
    -      });
    -      
    -      var width = $(window).width() - 60;
    -      var height = $(window).height() / 3;
    -      cm.setSize(width, height);
    -      
    -      if ($('#type').val() == 'jdbc') {
    -        $('#divTables').show();
    -        $('#divVTables').hide();
    -        $('#divShowSql').hide();
    -      } else if ($('#type').val() == 'metadata') {
    -        $('#divTables').hide();
    -        $('#divVTables').show();
    -        $('#divShowSql').hide();
    -      } else if ($('#type').val() == 'hibernate') {
    -        $('#divTables').hide();
    -        $('#divVTables').hide();
    -        $('#divShowSql').show();
    -      } else {
    -        $('#divTables').hide();
    -        $('#divVTables').hide();
    -        $('#divShowSql').hide();
    -      }
    -      
    -      $('#type').change(function() {
    -        if ($(this).val() == 'jdbc') {
    -          $('#divTables').show();
    -          $('#divVTables').hide();
    -          $('#divShowSql').hide();
    -        } else if ($(this).val() == 'metadata') {
    -          $('#divTables').hide();
    -          $('#divVTables').show();
    -          $('#divShowSql').hide();
    -        } else if ($('#type').val() == 'hibernate') {
    -          $('#divTables').hide();
    -          $('#divVTables').hide();
    -          $('#divShowSql').show();
    -        } else {
    -          $('#divTables').hide();
    -          $('#divVTables').hide();
    -          $('#divShowSql').hide();
    -        }
    -      });
    -      
    -      $('#tables').change(function() {
    -        if ($(this).val() == '') {
    -          cm.setValue('');
    -        } else {
    -          cm.setValue('SELECT * FROM ' + $(this).val() + ';');
    -        }
    -      });
    -      
    -      $('#vtables').change(function() {
    -        if ($(this).val() == '') {
    -          cm.setValue('');
    -        } else {
    -          cm.setValue('SELECT|' + $(this).val());
    -        }
    -      });
    -      
    -      $('#edit').click(function() {
    -        var mtable = $('#vtables').val();
    -        
    -        if (mtable != '') {
    -          window.location = 'DatabaseQuery?action=list&mtable=' + mtable;
    -        } else {
    -          alert('Please, select a metatable');
    -        }
    -      });
    -
    -      $("td.result").dblclick(function () {
    -        if ($('#type').val() == 'metadata' && '${vtable}' != '' && !$(this).hasClass("cellEditing")) {
    -          var originalContent = $(this).text();
    -          var width = $(this).width() + "px";
    -
    -          $(this).addClass("cellEditing");
    -          $(this).html("<input type='text' value='" + originalContent + "' style='width: " + width + "'/>");
    -          $(this).width(width);
    -          $(this).children().first().focus();
    -          $(this).children().first().keypress(function(e) {
    -            if (e.which == 13) {
    -              var newContent = $(this).val();
    -              var parent = $(this).parent();
    -              var id = parent.closest('tr').find('td:nth-child(2)');
    -              var header = parent.closest('table').find('th').eq(parent.index());
    -              var params = { "action" : "edit", "vtable": "${vtable}", "column": header.text(), "id": id.text(), "value": newContent };
    -              
    -              $.ajax({
    -                url: 'DatabaseQuery',
    -                type: 'post',
    -                data: params
    -              }).fail(function(jqXHR, textStatus) {
    -                alert("Error updating value: " + textStatus);
    -              });
    -              
    -              $(this).parent().text(newContent);
    -              $(this).parent().removeClass("cellEditing");
    -            }
    -          });
    -
    -          $(this).children().first().blur(function() {
    -            var td = $(this).parent();
    -            td.text(originalContent);
    -            td.removeClass("cellEditing");
    -          });
    -        }
    -      });
    -    });
    -    
    -    function keepSessionAlive() {
    -    	$.ajax({ type:'GET', url:'../SessionKeepAlive', cache:false, async:false });
    -    }
    -    
    -    window.setInterval('keepSessionAlive()', <%=java.util.concurrent.TimeUnit.MINUTES.toMillis(com.openkm.core.Config.KEEP_SESSION_ALIVE_INTERVAL)%>);
    -  </script>
    -  <title>Database Query</title>
    -</head>
    -<body>
    -  <c:set var="isAdmin"><%=BaseServlet.isMultipleInstancesAdmin(request)%></c:set>
    -   <c:choose>
    -    <c:when test="${isAdmin}">
    -      <ul id="breadcrumb">
    -        <li class="path">
    -          <a href="DatabaseQuery">Database query</a>
    -        </li>
    -      </ul>
    -      <br/>
    -      <div id="scroll" style="width: 100%; height: 100%; overflow: auto;">
    -        <br />
    -        <table class="form">
    -          <tr>
    -            <td>
    -              <form action="DatabaseQuery" method="post" enctype="multipart/form-data">
    -                <table>
    -                  <tr>
    -                    <td colspan="2"><textarea cols="80" rows="25" name="qs" id="qs">${qs}</textarea></td>
    -                  </tr>
    -                  <tr>
    -                    <td align="left">
    -                      <div id="divTables">
    -                        Table <select id="tables" name="tables" style="width: 300px" data-placeholder="&nbsp;">
    -                          <option></option>
    -                          <c:forEach var="tableItem" items="${tables}">
    -                            <c:choose>
    -                              <c:when test="${tableItem == table}">
    -                                <option value="${tableItem}" selected="selected">${tableItem}</option>
    -                              </c:when>
    -                              <c:otherwise>
    -                                <option value="${tableItem}">${tableItem}</option>
    -                              </c:otherwise>
    -                            </c:choose>
    -                          </c:forEach>
    -                        </select>
    -                      </div>
    -                      <div id="divVTables">
    -                        Metatable <select id="vtables" name="vtables" style="width: 250px" data-placeholder="&nbsp;">
    -                          <option></option>
    -                          <c:forEach var="vtableItem" items="${vtables}">
    -                            <c:choose>
    -                              <c:when test="${vtableItem == vtable}">
    -                                <option value="${vtableItem}" selected="selected">${vtableItem}</option>
    -                              </c:when>
    -                              <c:otherwise>
    -                                <option value="${vtableItem}">${vtableItem}</option>
    -                              </c:otherwise>
    -                            </c:choose>
    -                          </c:forEach>
    -                        </select>
    -                      </div>
    -                      <div id="divShowSql">
    -                        <table cellpadding="0" cellspacing="0">
    -                          <tr>
    -                            <td>Show SQL</td>
    -                            <td><c:choose>
    -                                <c:when test="${showSql}">
    -                                  <input name="showSql" type="checkbox" checked="checked" style="margin: 0px 0px 0px 3px;" />
    -                                </c:when>
    -                                <c:otherwise>
    -                                  <input name="showSql" type="checkbox" style="margin: 0px 0px 0px 3px;" />
    -                                </c:otherwise>
    -                              </c:choose></td>
    -                          </tr>
    -                        </table>
    -                      </div>
    -                    </td>
    -                    <td align="right">
    -                      Type 
    -                      <select name="type" id="type" style="width: 100px" data-placeholder="&nbsp;">
    -                        <option></option>
    -                        <c:choose>
    -                          <c:when test="${type == 'jdbc'}">
    -                            <option value="jdbc" selected="selected">JDBC</option>
    -                          </c:when>
    -                          <c:otherwise>
    -                            <option value="jdbc">JDBC</option>
    -                          </c:otherwise>
    -                        </c:choose>
    -                        <c:choose>
    -                          <c:when test="${type == 'hibernate'}">
    -                            <option value="hibernate" selected="selected">Hibernate</option>
    -                          </c:when>
    -                          <c:otherwise>
    -                            <option value="hibernate">Hibernate</option>
    -                          </c:otherwise>
    -                        </c:choose>
    -                        <c:choose>
    -                          <c:when test="${type == 'metadata'}">
    -                            <option value="metadata" selected="selected">Metadata</option>
    -                          </c:when>
    -                          <c:otherwise>
    -                            <option value="metadata">Metadata</option>
    -                          </c:otherwise>
    -                        </c:choose>
    -                      </select> 
    -                      <input type="submit" value="Execute" class="executeButton" />
    -                    </td>
    -                  </tr>
    -                </table>
    -              </form>
    -            </td>
    -          </tr>
    -          <tr class="fuzzy">
    -            <td colspan="4" align="right">
    -              <form action="DatabaseQuery" method="post" enctype="multipart/form-data">
    -                <input type="hidden" name="action" value="import" />
    -                <table>
    -                  <tr>
    -                    <td><input class=":required :only_on_blur" type="file" name="sql-file" /></td>
    -                    <td><input type="submit" value="Import SQL script" class="loadButton" /></td>
    -                  </tr>
    -                </table>
    -              </form>
    -            </td>
    -          </tr>
    -        </table>
    -        <br />
    -        <c:if test="${exception != null}">
    -          <div class="error">
    -            <table align="center">
    -              <tr>
    -                <td><b>Class:</b></td>
    -                <td>${exception['class'].name}</td>
    -              </tr>
    -              <tr>
    -                <td><b>Message:</b></td>
    -                <td>${exception.message}</td>
    -              </tr>
    -              <c:if test="${exception.cause != null}">
    -                <tr>
    -                  <td><b>Cause:</b></td>
    -                  <td>${exception.cause.message}</td>
    -                </tr>
    -              </c:if>
    -            </table>
    -          </div>
    -        </c:if>
    -        <br />
    -        <c:forEach var="gResult" items="${globalResults}">
    -          <c:if test="${gResult.sql != null}">
    -            <div class="ok">
    -              <center>
    -                <c:out value="${gResult.sql}" />
    -                <br />
    -                <br />
    -                <c:if test="${gResult.extra != null}">
    -                  <div class="warn">
    -                    <center>
    -                      <c:out value="${gResult.extra}" />
    -                      <br />
    -                      <br />
    -                    </center>
    -                  </div>
    -                </c:if>
    -                Time:
    -                <u:formatMiliSeconds time="${gResult.time}" />
    -              </center>
    -            </div>
    -            <br />
    -          </c:if>
    -          <c:choose>
    -            <c:when test="${gResult.rows != null}">
    -              <div class="ok">
    -                <center>Row Count: ${gResult.rows}</center>
    -              </div>
    -              <br />
    -            </c:when>
    -            <c:when test="${not empty gResult.errors}">
    -              <table class="results-old" width="95%">
    -                <tr>
    -                  <th>Line</th>
    -                  <th>SQL</th>
    -                  <th>Error</th>
    -                </tr>
    -                <c:forEach var="error" items="${gResult.errors}" varStatus="row">
    -                  <tr class="${row.index % 2 == 0 ? 'even' : 'odd'}">
    -                    <td>${error.ln}</td>
    -                    <td>${error.sql}</td>
    -                    <td>${error.msg}</td>
    -                  </tr>
    -                </c:forEach>
    -              </table>
    -            </c:when>
    -            <c:otherwise>
    -              <table class="results-old" width="95%">
    -                <tr>
    -                  <th width="24px">#</th>
    -                  <c:if test="${type == 'metadata'}">
    -                    <th width="24px">id</th>
    -                  </c:if>
    -                  <c:forEach var="col" items="${gResult.columns}">
    -                    <th>${col}</th>
    -                  </c:forEach>
    -                </tr>
    -                <c:forEach var="result" items="${gResult.results}" varStatus="row">
    -                  <tr class="${row.index % 2 == 0 ? 'even' : 'odd'}">
    -                    <td>${row.index + 1}</td>
    -                    <c:forEach var="col" items="${result}" varStatus="status">
    -                      <td class="result">${col}</td>
    -                    </c:forEach>
    -                    <td></td>
    -                  </tr>
    -                </c:forEach>
    -              </table>
    -              <br />
    -            </c:otherwise>
    -          </c:choose>
    -        </c:forEach>
    -      </div>
    -    </c:when>
    -    <c:otherwise>
    -      <div class="error"><h3>Only admin users allowed</h3></div>
    -    </c:otherwise>
    -  </c:choose>
    -</body>
    -</html>
    \ No newline at end of file
    
  • src/main/webapp/admin/img/toolbar/database.png+0 0 removed
  • src/main/webapp/admin/img/toolbar/scripting.png+0 0 removed
  • src/main/webapp/admin/menu.jsp+0 14 modified
    @@ -28,13 +28,6 @@
             <img src="img/toolbar/stats.png">
           </a>
         </li>
    -    <c:if test="${isMultipleInstancesAdmin}">
    -      <li>
    -        <a target="frame" href="Scripting" title="Scripting">
    -          <img src="img/toolbar/scripting.png">
    -        </a>
    -      </li>
    -    </c:if>
         <li>
           <a target="frame" href="PropertyGroups" title="Metadata">
             <img src="img/toolbar/properties.png">
    @@ -50,13 +43,6 @@
             <img src="img/toolbar/profile.png">
           </a>
         </li>
    -    <c:if test="${isMultipleInstancesAdmin}">
    -      <li>
    -        <a target="frame" href="DatabaseQuery" title="Database query">
    -          <img src="img/toolbar/database.png">
    -        </a>
    -      </li>
    -    </c:if>
         <li>
           <a target="frame" href="Report" title="Reports">
             <img src="img/toolbar/report.png" title="Reports">
    
  • src/main/webapp/admin/scripting.jsp+0 112 removed
    @@ -1,112 +0,0 @@
    -<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
    -<%@ page import="com.openkm.servlet.admin.BaseServlet" %>
    -<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    -<%@ taglib uri="http://www.openkm.com/tags/utils" prefix="u" %>
    -<?xml version="1.0" encoding="UTF-8" ?>
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -<head>
    -  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    -  <link rel="Shortcut icon" href="favicon.ico" />
    -  <link rel="stylesheet" type="text/css" href="css/admin-style.css" />
    -  <link rel="stylesheet" type="text/css" href="js/codemirror/lib/codemirror.css" />
    -  <style type="text/css">
    -    .CodeMirror { width: 700px; height: 300px; background-color: #f8f6c2; }
    -  </style>
    -  <script type="text/javascript" src="js/codemirror/lib/codemirror.js"></script>
    -  <script type="text/javascript" src="js/codemirror/mode/clike/clike.js"></script>
    -  <script type="text/javascript" src="js/codemirror/addon/selection/active-line.js"></script>
    -  <script type="text/javascript" src="../js/jquery-1.11.3.min.js"></script>
    -  <script type="text/javascript" src="js/jquery.DOMWindow.js"></script>
    -  <script type="text/javascript">
    -    $(document).ready(function() {
    -      var cm = CodeMirror.fromTextArea(document.getElementById('script'), {
    -        lineNumbers: true,
    -        matchBrackets: true,
    -        styleActiveLine: true,
    -        mode: "text/x-java",
    -        indentUnit: 4
    -      });
    -
    -      var width = $(window).width() - 60;
    -      var height = $(window).height() - 300;
    -      cm.setSize(width, height);
    -
    -      dm = $('.ds').openDOMWindow({
    -        height:300, width:400,
    -        eventType:'click',
    -        overlayOpacity: '57',
    -        windowSource:'iframe', windowPadding:0
    -      });
    -    });
    -
    -    function dialogClose() {
    -		  dm.closeDOMWindow();
    -    }
    -    
    -    function keepSessionAlive() {
    -    	$.ajax({ type:'GET', url:'../SessionKeepAlive', cache:false, async:false });
    -    }
    -    
    -	window.setInterval('keepSessionAlive()', <%=java.util.concurrent.TimeUnit.MINUTES.toMillis(com.openkm.core.Config.KEEP_SESSION_ALIVE_INTERVAL)%>);
    -  </script>
    -  <title>Scripting</title>
    -</head>
    -<body>
    -  <c:set var="isAdmin"><%=BaseServlet.isMultipleInstancesAdmin(request)%></c:set>
    -  <c:choose>
    -    <c:when test="${isAdmin}">
    -      <ul id="breadcrumb">
    -        <li class="path"><a href="utilities.jsp">Utilities</a></li>
    -        <li class="path">Scripting</li>
    -      </ul>
    -      <br />
    -      <form action="Scripting" method="post">
    -        <input type="hidden" name="csrft" value="${csrft}" />
    -        <table class="form" align="center">
    -          <tr>
    -            <td colspan="4">
    -              <textarea cols="80" rows="25" name="script" id="script">${script}</textarea>
    -            </td>
    -          </tr>
    -          <tr>
    -            <td align="left" width="125px">
    -              <input type="text" size="50" name="fsPath" id="fsPath" value="${fsPath}" />
    -            </td>
    -            <td align="left" width="25px">
    -              <a class="ds" href="../extension/DataBrowser?action=fs&dst=fsPath"> 
    -              <img src="img/action/browse_fs.png" />
    -              </a>
    -            </td>
    -            <td>
    -              <input type="submit" name="action" value="Load" class="loadButton" /> 
    -              <input type="submit" name="action" value="Save" class="saveButton" />
    -            </td>
    -            <td align="right">
    -              <input type="submit" name="action" value="Evaluate" class="executeButton" />
    -            </td>
    -          </tr>
    -        </table>
    -        <br />
    -        <div class="ok" style="text-align: center">
    -          <c:if test="${!empty time}">Time: ${time}</c:if>
    -        </div>
    -        <br />
    -        <table class="results-old" width="95%">
    -          <tr><th>Script error</th></tr>
    -          <tr class="even"><td>${scriptError}</td></tr>
    -          <tr><th>Script result</th></tr>
    -          <tr class="even"><td>${scriptResult}</td></tr>
    -          <tr><th>Script output</th></tr>
    -          <tr class="even"><td>${scriptOutput}</td></tr>
    -        </table>
    -      </form>
    -    </c:when>
    -    <c:otherwise>
    -      <div class="error">
    -        <h3>Only admin users allowed</h3>
    -      </div>
    -    </c:otherwise>
    -  </c:choose>
    -</body>
    -</html>
    
  • src/main/webapp/WEB-INF/web.xml+0 8 modified
    @@ -389,10 +389,6 @@
         <servlet-name>TailServlet</servlet-name>
         <servlet-class>com.openkm.servlet.admin.TailServlet</servlet-class>
       </servlet>
    -  <servlet>
    -    <servlet-name>DatabaseQueryServlet</servlet-name>
    -    <servlet-class>com.openkm.servlet.admin.DatabaseQueryServlet</servlet-class>
    -  </servlet>
       <servlet>
         <servlet-name>AuthServlet</servlet-name>
         <servlet-class>com.openkm.servlet.admin.AuthServlet</servlet-class>
    @@ -831,10 +827,6 @@
         <servlet-name>TailServlet</servlet-name>
         <url-pattern>/admin/Tail</url-pattern>
       </servlet-mapping>
    -  <servlet-mapping>
    -    <servlet-name>DatabaseQueryServlet</servlet-name>
    -    <url-pattern>/admin/DatabaseQuery</url-pattern>
    -  </servlet-mapping>
       <servlet-mapping>
         <servlet-name>AuthServlet</servlet-name>
         <url-pattern>/admin/Auth</url-pattern>
    

Vulnerability mechanics

Root cause

"Missing path validation in the administrative file-loading workflow allows an authenticated user to read arbitrary files on the server filesystem."

Attack vector

An attacker who has already obtained administrative credentials (e.g., via default credentials, weak passwords, or the separate SQL injection vulnerability) navigates to the `/admin/Scripting` page. By supplying an attacker-controlled filesystem path through the `fsPath` parameter with `action=Load`, the server reads and returns the contents of any file readable by the OpenKM process [ref_id=1][ref_id=2]. This allows the attacker to read sensitive files such as `/etc/passwd`, configuration files containing database credentials, and JVM keystores [ref_id=1]. The attack is network-accessible (HTTP/HTTPS) and requires no additional privileges beyond an authenticated admin session [CWE-22].

Affected code

The vulnerability resides in the administrative scripting interface at `/admin/Scripting`. The file-loading workflow accepts a user-supplied `fsPath` parameter with `action=Load` and reads the specified file without any path validation or directory restriction [ref_id=1][ref_id=2]. The affected code is the administrative JSP component that handles the file-loading action [ref_id=2].

What the fix does

The patch [patch_id=2562547] introduces path validation to restrict file reads to a safe directory. The fix checks that the resolved canonical path of the requested file starts with an allowed base path, preventing directory traversal and arbitrary file access. This closes the Local File Inclusion by ensuring that even an authenticated administrator can only load files within the intended scripting workspace.

Preconditions

  • authAttacker must have a valid administrative session (e.g., okmAdmin credentials)
  • networkTarget OpenKM instance must expose the /admin/Scripting endpoint over HTTP/HTTPS
  • inputNo input validation or path restriction on the fsPath parameter before the patch

Generated on May 26, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

7

News mentions

0

No linked articles in our index yet.