@@ -151,6 +151,7 @@
 import com.thoughtworks.xstream.mapper.XStream11XmlFriendlyMapper;
 import com.thoughtworks.xstream.security.AnyTypePermission;
 import com.thoughtworks.xstream.security.ArrayTypePermission;
+import com.thoughtworks.xstream.security.InputManipulationException;
 import com.thoughtworks.xstream.security.ExplicitTypePermission;
 import com.thoughtworks.xstream.security.InterfaceTypePermission;
 import com.thoughtworks.xstream.security.NoPermission;
@@ -295,6 +296,8 @@ public class XStream {

     // CAUTION: The sequence of the fields is intentional for an optimal XML output of a
     // self-serialization!
+    private int collectionUpdateLimit = 20;
+
     private ReflectionProvider reflectionProvider;
     private HierarchicalStreamDriver hierarchicalStreamDriver;
     private ClassLoaderReference classLoaderReference;
@@ -329,6 +332,9 @@ public class XStream {
     public static final int PRIORITY_LOW = -10;
     public static final int PRIORITY_VERY_LOW = -20;

+    public static final String COLLECTION_UPDATE_LIMIT = "XStreamCollectionUpdateLimit";
+    public static final String COLLECTION_UPDATE_SECONDS = "XStreamCollectionUpdateSeconds";
+
     private static final String ANNOTATION_MAPPER_TYPE = "com.thoughtworks.xstream.mapper.AnnotationMapper";
     private static final Pattern IGNORE_ALL = Pattern.compile(".*");

@@ -1182,6 +1188,23 @@ public void setMarshallingStrategy(MarshallingStrategy marshallingStrategy) {
         this.marshallingStrategy = marshallingStrategy;
     }

+    /**
+     * Set time limit for adding elements to collections or maps.
+     *
+     * Manipulated content may be used to create recursive hash code calculations or sort operations. An
+     * {@link InputManipulationException} is thrown, it the summed up time to add elements to collections or maps
+     * exceeds the provided limit.
+     *
+     * Note, that the time to add an individual element is calculated in seconds, not milliseconds. However, attacks
+     * typically use objects with exponential growing calculation times.
+     *
+     * @param maxSeconds limit in seconds or 0 to disable check
+     * @since upcoming
+     */
+    public void setCollectionUpdateLimit(int maxSeconds) {
+        collectionUpdateLimit = maxSeconds;
+    }
+
     /**
      * Serialize an object to a pretty-printed XML String.
      *
@@ -1388,6 +1411,13 @@ public Object unmarshal(HierarchicalStreamReader reader, Object root) {
      */
     public Object unmarshal(HierarchicalStreamReader reader, Object root, DataHolder dataHolder) {
         try {
+            if (collectionUpdateLimit >= 0) {
+                if (dataHolder == null) {
+                    dataHolder = new MapBackedDataHolder();
+                }
+                dataHolder.put(COLLECTION_UPDATE_LIMIT, new Integer(collectionUpdateLimit));
+                dataHolder.put(COLLECTION_UPDATE_SECONDS, new Integer(0));
+            }
             return marshallingStrategy.unmarshal(root, reader, dataHolder, converterLookup, mapper);
         } catch (ConversionException e) {
             Package pkg = getClass().getPackage();
@@ -2053,15 +2083,23 @@ public ObjectInputStream createObjectInputStream(final HierarchicalStreamReader
      * @see #createObjectInputStream(com.thoughtworks.xstream.io.HierarchicalStreamReader)
      * @since 1.4.10
      */
-    public ObjectInputStream createObjectInputStream(final HierarchicalStreamReader reader, final DataHolder dataHolder)
+    public ObjectInputStream createObjectInputStream(final HierarchicalStreamReader reader, DataHolder dataHolder)
             throws IOException {
+        if (collectionUpdateLimit >= 0) {
+            if (dataHolder == null) {
+                dataHolder = new MapBackedDataHolder();
+            }
+            dataHolder.put(COLLECTION_UPDATE_LIMIT, new Integer(collectionUpdateLimit));
+            dataHolder.put(COLLECTION_UPDATE_SECONDS, new Integer(0));
+        }
+        final DataHolder dh = dataHolder;
         return new CustomObjectInputStream(new CustomObjectInputStream.StreamCallback() {
             public Object readFromStream() throws EOFException {
                 if (!reader.hasMoreChildren()) {
                     throw new EOFException();
                 }
                 reader.moveDown();
-                final Object result = unmarshal(reader, null, dataHolder);
+                final Object result = unmarshal(reader, null, dh);
                 reader.moveUp();
                 return result;
             }


@@ -60,26 +60,14 @@ public VertxResteasyReactiveRequestContext(Deployment deployment, ProvidersImpl
         this.devModeTccl = devModeTccl;
         context.addHeadersEndHandler(this);
         String expect = request.getHeader(HttpHeaderNames.EXPECT);
-        ContextInternal internal = ((ConnectionBase) context.request().connection()).getContext();
-        if (!vertxContextPropsToCopy.isEmpty()) {
-            ContextInternal current = (ContextInternal) Vertx.currentContext();
-            Map<Object, Object> internalLocalContextData = internal.localContextData();
-            Map<Object, Object> currentLocalContextData = current.localContextData();
-            for (int i = 0; i < vertxContextPropsToCopy.size(); i++) {
-                String name = vertxContextPropsToCopy.get(i);
-                Object value = currentLocalContextData.get(name);
-                if (value != null) {
-                    internalLocalContextData.put(name, value);
-                }
-            }
-        }
+        ContextInternal current = (ContextInternal) Vertx.currentContext();
         if (expect != null && expect.equalsIgnoreCase(CONTINUE)) {
             continueState = ContinueState.REQUIRED;
         }
         this.contextExecutor = new Executor() {
             @Override
             public void execute(Runnable command) {
-                internal.runOnContext(new Handler<Void>() {
+                current.runOnContext(new Handler<Void>() {
                     @Override
                     public void handle(Void unused) {
                         command.run();


Reject extraneous data after SSL or GSS encryption handshake.

The server collects up to a bufferload of data whenever it reads data
from the client socket.  When SSL or GSS encryption is requested
during startup, any additional data received with the initial
request message remained in the buffer, and would be treated as
already-decrypted data once the encryption handshake completed.
Thus, a man-in-the-middle with the ability to inject data into the
TCP connection could stuff some cleartext data into the start of
a supposedly encryption-protected database session.

This could be abused to send faked SQL commands to the server,
although that would only work if the server did not demand any
authentication data.  (However, a server relying on SSL certificate
authentication might well not do so.)

To fix, throw a protocol-violation error if the internal buffer
is not empty after the encryption handshake.

Our thanks to Jacob Champion for reporting this problem.

Security: CVE-2021-23214

@@ -2110,6 +2110,18 @@ ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done)
 			return STATUS_ERROR;
 #endif

+		/*
+		 * At this point we should have no data already buffered.  If we do,
+		 * it was received before we performed the SSL handshake, so it wasn't
+		 * encrypted and indeed may have been injected by a man-in-the-middle.
+		 * We report this case to the client.
+		 */
+		if (pq_buffer_has_data())
+			ereport(FATAL,
+					(errcode(ERRCODE_PROTOCOL_VIOLATION),
+					 errmsg("received unencrypted data after SSL request"),
+					 errdetail("This could be either a client-software bug or evidence of an attempted man-in-the-middle attack.")));
+
 		/*
 		 * regular startup packet, cancel, etc packet should follow, but not
 		 * another SSL negotiation request, and a GSS request should only
@@ -2142,6 +2154,18 @@ ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done)
 			return STATUS_ERROR;
 #endif

+		/*
+		 * At this point we should have no data already buffered.  If we do,
+		 * it was received before we performed the GSS handshake, so it wasn't
+		 * encrypted and indeed may have been injected by a man-in-the-middle.
+		 * We report this case to the client.
+		 */
+		if (pq_buffer_has_data())
+			ereport(FATAL,
+					(errcode(ERRCODE_PROTOCOL_VIOLATION),
+					 errmsg("received unencrypted data after GSSAPI encryption request"),
+					 errdetail("This could be either a client-software bug or evidence of an attempted man-in-the-middle attack.")));
+
 		/*
 		 * regular startup packet, cancel, etc packet should follow, but not
 		 * another GSS negotiation request, and an SSL request should only


:lock: 修改模板引擎的默认安全策略，以防止RCE

@@ -4,6 +4,7 @@
 import org.apache.velocity.VelocityContext;
 import org.apache.velocity.app.Velocity;
 import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
+import org.apache.velocity.util.introspection.SecureUberspector;
 import org.springframework.stereotype.Component;

 import java.io.StringWriter;
@@ -22,6 +23,8 @@ public class VelocityTemplateEngine implements TemplateEngine {
 		// 设置velocity资源加载器, 保留文件加载loader
 		Properties prop = new Properties();
 		prop.put("file.resource.loader.class", ClasspathResourceLoader.class.getName());
+		// 安全处理
+		prop.put("runtime.introspector.uberspect", SecureUberspector.class.getName());
 		Velocity.init(prop);
 	}

@@ -31,7 +34,7 @@ public TemplateEngineTypeEnum type() {
 	}

 	@Override
-	public String render(String templateContent, Map<String, Object> context) {
+	public String render(String templateContent, Map<String, Object> context) throws TemplateRenderException {
 		VelocityContext velocityContext = new VelocityContext(context);
 		try (StringWriter sw = new StringWriter()) {
 			Velocity.evaluate(velocityContext, sw, "velocityTemplateEngine", templateContent);


fix some security bug (#103)

* fix: use hard-code secret

* feat: add driver class validate

* feat: optimize drvier resource code

* fix:ut failed

@@ -36,8 +36,10 @@ public boolean support(String databaseType) {

     @Override
     public Connection getConnection(Context context) throws SQLException {
-        DatabaseTypePojo type = databaseTypeDao.selectByDatabaseType(context.getDatabaseType());
-        File driverFile = driverResources.loadOrDownload(context.getDatabaseType(), type.getJdbcDriverFileUrl());
+        String databaseType = context.getDatabaseType();
+        DatabaseTypePojo type = databaseTypeDao.selectByDatabaseType(databaseType);
+        File driverFile = driverResources.loadOrDownloadByDatabaseType(databaseType, type.getJdbcDriverFileUrl());
+
         URLClassLoader loader = null;
         try {
             loader = new URLClassLoader(
@@ -55,11 +57,11 @@ public Connection getConnection(Context context) throws SQLException {
         Class<?> clazz = null;
         Driver driver = null;
         try {
-            clazz = Class.forName(type.getJdbcDriverClassName(), true, loader);
+            clazz = Class.forName(type.getJdbcDriverClassName(), false, loader);
             driver = (Driver) clazz.getConstructor().newInstance();
         } catch (ClassNotFoundException e) {
             log.error("init driver error", e);
-            throw DomainErrors.CONNECT_DATABASE_FAILED.exception("驱动初始化异常, 请检查 Driver name：" + e.getMessage());
+            throw DomainErrors.CONNECT_DATABASE_FAILED.exception("驱动初始化异常, 请检查驱动类名：" + e.getMessage());
         } catch (InvocationTargetException
                  | InstantiationException
                  | IllegalAccessException


tcos: prevent out of bounds read

Thanks oss-fuzz

https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=27719

@@ -152,7 +152,7 @@ static int insert_key(
 			sc_log(ctx, "No EF_KEYD-Record found\n");
 			return 1;
 		}
-		for (i = 0; i < r; i += 2 + buf[i + 1]) {
+		for (i = 0; i + 1 < r; i += 2 + buf[i + 1]) {
 			if (buf[i] == 0xB6)
 				can_sign++;
 			if (buf[i] == 0xB8)


Improve documentation and matching algorithm in data binders

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2020 the original author or authors.
+ * Copyright 2002-2022 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -34,6 +34,15 @@
  * the Servlet API; serves as base class for more specific DataBinder variants,
  * such as {@link org.springframework.web.bind.ServletRequestDataBinder}.
  *
+ *
WARNING: Data binding can lead to security issues by exposing
+ * parts of the object graph that are not meant to be accessed or modified by
+ * external clients. Therefore the design and use of data binding should be considered
+ * carefully with regard to security. For more details, please refer to the dedicated
+ * sections on data binding for
+ * Spring Web MVC and
+ * Spring WebFlux
+ * in the reference manual.
+ *
  *
Includes support for field markers which address a common problem with
  * HTML checkboxes and select options: detecting that a field was part of
  * the form, but did not generate a request parameter because it was empty.


#000 - Validate stage counter during upload artifacts

@@ -39,6 +39,7 @@

 import static com.thoughtworks.go.util.GoConstants.*;
 import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
 import static org.hamcrest.Matchers.*;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.mockito.Mockito.any;
@@ -120,6 +121,21 @@ public void shouldReturnHttpErrorCodeWhenChecksumFileSaveFails() throws Exceptio
         assertThat(view.getContent(), is("Error saving checksum file for the artifact at path 'some-path'"));
     }

+    @Test
+    void shouldFailToPostAndPutWhenStageCounterIsNotAPositiveInteger() throws Exception {
+        ModelAndView modelAndView = artifactsController.postArtifact("pipeline-1", "1", "stage-1", "NOT_AN_INTEGER", "job-1", 122L, "some-path", 1, null);
+        assertThat(((ResponseCodeView) modelAndView.getView()).getStatusCode(), is(SC_NOT_FOUND));
+
+        modelAndView = artifactsController.postArtifact("pipeline-1", "1", "stage-1", "-123", "job-1", 122L, "some-path", 1, null);
+        assertThat(((ResponseCodeView) modelAndView.getView()).getStatusCode(), is(SC_NOT_FOUND));
+
+        modelAndView = artifactsController.putArtifact("pipeline-1", "1", "stage-1", "NOT_AN_INTEGER", "job-1", 122L, "some-path", "1", null);
+        assertThat(((ResponseCodeView) modelAndView.getView()).getStatusCode(), is(SC_NOT_FOUND));
+
+        modelAndView = artifactsController.putArtifact("pipeline-1", "1", "stage-1", "-123", "job-1", 122L, "some-path", "1", null);
+        assertThat(((ResponseCodeView) modelAndView.getView()).getStatusCode(), is(SC_NOT_FOUND));
+    }
+
     @Test
     public void shouldFunnelAll_GET_calls() throws Exception {
         final ModelAndView returnVal = new ModelAndView();


@@ -19,6 +19,7 @@
 import java.util.List;

 import com.thoughtworks.go.server.presentation.models.HtmlRenderer;
+import org.apache.commons.text.StringEscapeUtils;

 public class HtmlElement implements HtmlRenderable {
     public static HtmlElement div(HtmlAttribute... attributes) { return new HtmlElement("div", attributes); }
@@ -37,10 +38,14 @@ private HtmlElement(String elementName, HtmlAttribute... attributes) {
         this.attributes = attributes;
     }

-    public HtmlElement content(String body) {
+    public HtmlElement unsafecontent(String body) {
         return content(new TextElement(body));
     }

+    public HtmlElement safecontent(String body) {
+        return content(new TextElement(StringEscapeUtils.escapeHtml4(body)));
+    }
+
     public HtmlElement content(HtmlRenderable... elements) {
         for (HtmlRenderable element : elements) {
             addToBody(element);
@@ -80,5 +85,4 @@ public void render(HtmlRenderer renderer) {
             renderer.append("</" + elementName + ">\n");
         }
     }
-
 }


#000 - Validate URLs provided

- Disallow URLs which are obviously not URLs.

@@ -20,7 +20,6 @@
 import com.thoughtworks.go.config.materials.Filter;
 import com.thoughtworks.go.config.materials.IgnoredFiles;
 import com.thoughtworks.go.config.materials.ScmMaterialConfig;
-import com.thoughtworks.go.config.materials.svn.SvnMaterialConfig;
 import com.thoughtworks.go.domain.materials.MaterialConfig;
 import com.thoughtworks.go.security.GoCipher;
 import com.thoughtworks.go.util.ReflectionUtil;
@@ -33,6 +32,8 @@

 import static com.thoughtworks.go.helper.MaterialConfigsMother.p4;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.Mockito.*;

 class P4MaterialConfigTest {
@@ -111,17 +112,17 @@ void setConfigAttributes_shouldUpdatePasswordWhenPasswordChangedBooleanChanged()
         assertThat(materialConfig.getEncryptedPassword()).isEqualTo(new GoCipher().encrypt("secret"));

         //Dont change
-        map.put(SvnMaterialConfig.PASSWORD, "Hehehe");
-        map.put(SvnMaterialConfig.PASSWORD_CHANGED, "0");
+        map.put(P4MaterialConfig.PASSWORD, "Hehehe");
+        map.put(P4MaterialConfig.PASSWORD_CHANGED, "0");
         materialConfig.setConfigAttributes(map);

         assertThat(ReflectionUtil.getField(materialConfig, "password")).isNull();
         assertThat(materialConfig.getPassword()).isEqualTo("secret");
         assertThat(materialConfig.getEncryptedPassword()).isEqualTo(new GoCipher().encrypt("secret"));

         //Dont change
-        map.put(SvnMaterialConfig.PASSWORD, "");
-        map.put(SvnMaterialConfig.PASSWORD_CHANGED, "1");
+        map.put(P4MaterialConfig.PASSWORD, "");
+        map.put(P4MaterialConfig.PASSWORD_CHANGED, "1");
         materialConfig.setConfigAttributes(map);

         assertThat(materialConfig.getPassword()).isNull();
@@ -141,6 +142,23 @@ void shouldNotSetUseTicketsIfNotInConfigAttributesMap() {
         assertThat(p4MaterialConfig.getUseTickets()).isFalse();
     }

+    @Nested
+    class Validate {
+        @Test
+        void rejectsObviouslyWrongURL() {
+            assertTrue(validating(p4("-url-not-starting-with-an-alphanumeric-character", "view")).errors().containsKey(P4MaterialConfig.SERVER_AND_PORT));
+            assertTrue(validating(p4("_url-not-starting-with-an-alphanumeric-character", "view")).errors().containsKey(P4MaterialConfig.SERVER_AND_PORT));
+            assertTrue(validating(p4("@url-not-starting-with-an-alphanumeric-character", "view")).errors().containsKey(P4MaterialConfig.SERVER_AND_PORT));
+
+            assertFalse(validating(p4("url-starting-with-an-alphanumeric-character", "view")).errors().containsKey(P4MaterialConfig.SERVER_AND_PORT));
+        }
+
+        private P4MaterialConfig validating(P4MaterialConfig p4) {
+            p4.validate(new ConfigSaveValidationContext(null));
+            return p4;
+        }
+
+    }
     @Nested
     class ValidateTree {
         @Test


#000 - Allow parameters to be used as URLs

Relates to commit 6fa9fb7a7c91e

@@ -176,6 +176,7 @@ void rejectsObviouslyWrongURL() {
             assertTrue(validating(tfs("@url-not-starting-with-an-alphanumeric-character")).errors().containsKey(TfsMaterialConfig.URL));

             assertFalse(validating(tfs("url-starting-with-an-alphanumeric-character")).errors().containsKey(TfsMaterialConfig.URL));
+            assertFalse(validating(tfs("#{url}")).errors().containsKey(TfsMaterialConfig.URL));
         }

         private TfsMaterialConfig validating(TfsMaterialConfig tfs) {


@@ -22,9 +22,7 @@
 import java.util.jar.Attributes.Name;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
-import javax.naming.InitialContext;
 import javax.naming.NamingException;
-import javax.naming.NoInitialContextException;
 import javax.sql.DataSource;
 import org.geotools.metadata.i18n.ErrorKeys;
 import org.geotools.metadata.i18n.Errors;
@@ -91,28 +89,12 @@ public class ThreadedEpsgFactory extends DeferredAuthorityFactory
      */
     public static final String DATASOURCE_NAME = "java:comp/env/jdbc/EPSG";

-    /**
-     * {@code true} if automatic registration of {@link #datasourceName} is allowed. Set to {@code
-     * false} for now because the registration has not been correctly tested in JEE environment.
-     *
-     * @todo Consider removing completly the code related to JNDI binding. In such case, this field
-     *     and the {@link #registerInto} field would be removed.
-     */
-    private static final boolean ALLOW_REGISTRATION = false;
-
     /** The default priority level for this factory. */
     protected static final int PRIORITY = MAXIMUM_PRIORITY - 10;

     /** The factories to be given to the backing store. */
     private final ReferencingFactoryContainer factories;

-    /**
-     * The context where to register {@link #datasource}, or {@code null} if it should not be
-     * registered. This is used only as a way to pass "hiden" return value between {@link
-     * #createDataSource()} and {@link #createBackingStore()}.
-     */
-    private transient InitialContext registerInto;
-
     /**
      * The data source name. If it was not specified by the {@link Hints#EPSG_DATA_SOURCE
      * EPSG_DATA_SOURCE} hint, then this is the {@value #DATASOURCE_NAME} value.
@@ -301,15 +283,10 @@ public synchronized void setDataSource(final DataSource datasource) throws SQLEx
      * @throws SQLException if an error occured while creating the data source.
      */
     protected DataSource createDataSource() throws SQLException {
-        InitialContext context = null;
         DataSource source = null;
         try {
-            context = GeoTools.getInitialContext();
-            source = (DataSource) context.lookup(datasourceName);
-        } catch (IllegalArgumentException | NoInitialContextException exception) {
-            // Fall back on 'return null' below.
-        } catch (NamingException exception) {
-            registerInto = context;
+            source = (DataSource) GeoTools.jndiLookup(datasourceName);
+        } catch (IllegalArgumentException | NamingException exception) {
             // Fall back on 'return null' below.
         }
         return source;
@@ -380,14 +357,7 @@ private AbstractAuthorityFactory createBackingStore0() throws FactoryException,
          * Try to gets the DataSource from JNDI. In case of success, it will be tried
          * for a connection before any DataSource declared in META-INF/services/.
          */
-        DataSource source;
-        final InitialContext context;
-        try {
-            source = createDataSource();
-            context = registerInto;
-        } finally {
-            registerInto = null;
-        }
+        DataSource source = createDataSource();
         if (source == null) {
             throw new FactoryNotFoundException(Errors.format(ErrorKeys.NO_DATA_SOURCE));
         }
@@ -398,27 +368,6 @@ private AbstractAuthorityFactory createBackingStore0() throws FactoryException,
         } finally {
             datasource = null;
         }
-        /*
-         * We now have a working connection. If a naming directory is running but didn't contains
-         * the "jdbc/EPSG" entry, add it now. In such case, a message is prepared and logged.
-         */
-        LogRecord record;
-        if (ALLOW_REGISTRATION && context != null) {
-            try {
-                context.bind(datasourceName, source);
-                record =
-                        Loggings.format(
-                                Level.FINE,
-                                LoggingKeys.CREATED_DATASOURCE_ENTRY_$1,
-                                datasourceName);
-            } catch (NamingException exception) {
-                record =
-                        Loggings.format(
-                                Level.WARNING, LoggingKeys.CANT_BIND_DATASOURCE_$1, datasourceName);
-                record.setThrown(exception);
-            }
-            log(record);
-        }
         this.datasource = source; // Stores the data source only after success.
         return factory;
     }


Escape/encode values when building search filters.

@@ -38,7 +38,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.*;

-class LdapTest {
+class ApacheDsLdapClientTest {

     @Test
     void shouldBeAbleToSearchUsers() throws ParseException {
@@ -65,6 +65,26 @@ void shouldBeAbleToSearchUsers() throws ParseException {
         assertThat(searchRequest.getTimeLimit()).isEqualTo(10);
     }

+    @Test
+    void shouldEscapeSearchFilterValues() throws ParseException {
+        final LdapConfiguration ldapConfiguration = new LdapConfigurationBuilder()
+                .withSearchTimeout(10)
+                .withSearchBases("ou=foo,dc=bar")
+                .build();
+
+        final LdapConnectionTemplate ldapConnectionTemplate = mock(LdapConnectionTemplate.class);
+        final ApacheDsLdapClient ldap = new ApacheDsLdapClient(ldapConfiguration, ldapConnectionTemplate);
+        final ArgumentCaptor<SearchRequest> argumentCaptor = ArgumentCaptor.forClass(SearchRequestImpl.class);
+
+        when(ldapConnectionTemplate.search(argumentCaptor.capture(), ArgumentMatchers.<EntryMapper<Entry>>any())).thenReturn(Collections.singletonList(new DefaultEntry()));
+
+        String injectionUserName = "*)(objectclass=*";
+        ldap.search("(uid={0})", new String[]{injectionUserName}, 1);
+
+        assertThat(argumentCaptor.getValue().getFilter())
+                .isEqualTo(FilterParser.parse("(uid=\\2A\\29\\28objectclass=\\2A)"));
+    }
+
     @Test
     void shouldAbleToFetchResultsFromMultipleSearchBase() {
         final LdapConfiguration ldapConfiguration = new LdapConfigurationBuilder()


Merge pull request from GHSA-qm6v-cg9v-53j3

This patch fixes the issue that users can pass URLs from other tenants
to the ingest service which will check only against the other
organization but not against the one currently active. This allows users
to easily ingest media from other tenants.

@@ -129,15 +129,13 @@
 import java.util.Dictionary;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;

 import javax.management.ObjectInstance;

@@ -1568,16 +1566,7 @@ protected URI addContentToRepo(MediaPackage mp, String elementId, URI uri) throw
     try {
       if (uri.toString().startsWith("http")) {
         HttpGet get = new HttpGet(uri);
-        List<String> clusterUrls = new LinkedList<>();
-        try {
-          // Note that we are not checking ports here.
-          clusterUrls = organizationDirectoryService.getOrganization(uri.toURL()).getServers()
-                          .keySet()
-                          .stream()
-                          .collect(Collectors.toUnmodifiableList());
-        } catch (NotFoundException e) {
-          logger.warn("Unable to determine cluster members, will not be able to authenticate any downloads from them", e);
-        }
+        var clusterUrls = securityService.getOrganization().getServers().keySet();

         if (uri.toString().matches(downloadSource)) {
           //NB: We're creating a new client here with *different* auth than the system auth creds


Fixed an issue affecting recursions in JIT


@@ -210,6 +210,14 @@ public String toString() {
 		addMagic(SName.usecase);
 		addMagic(SName.map);
 		addMagic(SName.archimate);
+
+
+//		addConvert("nodeStereotypeFontSize", PName.FontSize, SName.node, SName.stereotype);
+//		addConvert("sequenceStereotypeFontSize", PName.FontSize, SName.stereotype);
+//		addConvert("sequenceStereotypeFontStyle", PName.FontStyle, SName.stereotype);
+//		addConvert("sequenceStereotypeFontColor", PName.FontColor, SName.stereotype);
+//		addConvert("sequenceStereotypeFontName", PName.FontName, SName.stereotype);
+

 	}

@@ -221,8 +229,14 @@ private static void addMagic(SName sname) {
 		addConvert(cleanName + "RoundCorner", PName.RoundCorner, sname);
 		addConvert(cleanName + "DiagonalCorner", PName.DiagonalCorner, sname);
 		addConvert(cleanName + "BorderStyle", PName.LineStyle, sname);
-		addConvert(cleanName + "StereotypeFontColor", PName.FontColor, SName.stereotype, sname);
 		addConFont(cleanName, sname);
+		addConvert(cleanName + "Shadowing", PName.Shadowing, sname);
+
+		addConvert(cleanName + "StereotypeFontSize", PName.FontSize, SName.stereotype, sname);
+		addConvert(cleanName + "StereotypeFontStyle", PName.FontStyle, SName.stereotype, sname);
+		addConvert(cleanName + "StereotypeFontColor", PName.FontColor, SName.stereotype, sname);
+		addConvert(cleanName + "StereotypeFontName", PName.FontName, SName.stereotype, sname);
+
 	}

 	private final List<Style> styles = new ArrayList<>();
@@ -234,7 +248,7 @@ public FromSkinparamToStyle(String key) {
 		if (key.contains("<<")) {
 			final StringTokenizer st = new StringTokenizer(key, "<>");
 			this.key = st.nextToken();
-			this.stereo = st.hasMoreTokens() ? st.nextToken() : null;
+			this.stereo = st.hasMoreTokens() ? st.nextToken().trim() : null;
 		} else {
 			this.key = key;
 			this.stereo = null;
@@ -243,6 +257,12 @@ public FromSkinparamToStyle(String key) {
 	}

 	public void convertNow(String value, final AutomaticCounter counter) {
+		if (key.endsWith("shadowing")) {
+			if (value.equalsIgnoreCase("false"))
+				value = "0";
+			else if (value.equalsIgnoreCase("true"))
+				value = "3";
+		}
 		if (value.equalsIgnoreCase("right:right"))
 			value = "right";
 		if (value.equalsIgnoreCase("dotted"))


@@ -36,7 +36,6 @@
 package net.sourceforge.plantuml.svek;

 import net.sourceforge.plantuml.awt.geom.Dimension2D;
-
 import net.sourceforge.plantuml.graphic.AbstractTextBlock;
 import net.sourceforge.plantuml.graphic.StringBounder;
 import net.sourceforge.plantuml.graphic.TextBlock;


@@ -35,10 +35,9 @@
  */
 package net.sourceforge.plantuml.svek;

-import net.sourceforge.plantuml.awt.geom.Dimension2D;
-
 import net.sourceforge.plantuml.Dimension2DDouble;
 import net.sourceforge.plantuml.Url;
+import net.sourceforge.plantuml.awt.geom.Dimension2D;
 import net.sourceforge.plantuml.graphic.AbstractTextBlock;
 import net.sourceforge.plantuml.graphic.StringBounder;
 import net.sourceforge.plantuml.graphic.TextBlock;


@@ -35,12 +35,11 @@
  */
 package net.sourceforge.plantuml.svek.image;

-import net.sourceforge.plantuml.awt.geom.Dimension2D;
-
 import net.sourceforge.plantuml.Dimension2DDouble;
 import net.sourceforge.plantuml.FontParam;
 import net.sourceforge.plantuml.Guillemet;
 import net.sourceforge.plantuml.ISkinParam;
+import net.sourceforge.plantuml.awt.geom.Dimension2D;
 import net.sourceforge.plantuml.cucadiagram.Display;
 import net.sourceforge.plantuml.cucadiagram.ILeaf;
 import net.sourceforge.plantuml.cucadiagram.Stereotype;


@@ -35,9 +35,6 @@
  */
 package net.sourceforge.plantuml.svek.image;

-import java.util.EnumMap;
-import java.util.Map;
-
 import net.sourceforge.plantuml.ColorParam;
 import net.sourceforge.plantuml.CornerParam;
 import net.sourceforge.plantuml.Dimension2DDouble;
@@ -76,7 +73,6 @@
 import net.sourceforge.plantuml.ugraphic.Shadowable;
 import net.sourceforge.plantuml.ugraphic.UGraphic;
 import net.sourceforge.plantuml.ugraphic.UGraphicStencil;
-import net.sourceforge.plantuml.ugraphic.UGroupType;
 import net.sourceforge.plantuml.ugraphic.ULayoutGroup;
 import net.sourceforge.plantuml.ugraphic.URectangle;
 import net.sourceforge.plantuml.ugraphic.UStroke;


@@ -35,14 +35,14 @@
  */
 package net.sourceforge.plantuml.svek.image;

-import net.sourceforge.plantuml.awt.geom.Dimension2D;
 import java.awt.geom.Point2D;

 import net.sourceforge.plantuml.ColorParam;
 import net.sourceforge.plantuml.FontParam;
 import net.sourceforge.plantuml.ISkinParam;
 import net.sourceforge.plantuml.SkinParamUtils;
 import net.sourceforge.plantuml.UseStyle;
+import net.sourceforge.plantuml.awt.geom.Dimension2D;
 import net.sourceforge.plantuml.cucadiagram.EntityPosition;
 import net.sourceforge.plantuml.cucadiagram.ILeaf;
 import net.sourceforge.plantuml.graphic.color.ColorType;


@@ -34,11 +34,10 @@
  */
 package net.sourceforge.plantuml.timingdiagram;

-import net.sourceforge.plantuml.FontParam;
 import net.sourceforge.plantuml.ISkinParam;
-import net.sourceforge.plantuml.UseStyle;
 import net.sourceforge.plantuml.command.Position;
 import net.sourceforge.plantuml.cucadiagram.Display;
+import net.sourceforge.plantuml.cucadiagram.Stereotype;
 import net.sourceforge.plantuml.graphic.FontConfiguration;
 import net.sourceforge.plantuml.graphic.HorizontalAlignment;
 import net.sourceforge.plantuml.graphic.StringBounder;
@@ -49,10 +48,10 @@
 import net.sourceforge.plantuml.style.PName;
 import net.sourceforge.plantuml.style.SName;
 import net.sourceforge.plantuml.style.Style;
+import net.sourceforge.plantuml.style.StyleSignature;
 import net.sourceforge.plantuml.style.StyleSignatureBasic;
 import net.sourceforge.plantuml.ugraphic.UStroke;
 import net.sourceforge.plantuml.ugraphic.color.HColor;
-import net.sourceforge.plantuml.ugraphic.color.HColorUtils;

 public abstract class Player implements TimeProjected {

@@ -61,8 +60,10 @@ public abstract class Player implements TimeProjected {
 	private final boolean compact;
 	private final Display title;
 	protected int suggestedHeight;
+	protected final Stereotype stereotype;

-	public Player(String title, ISkinParam skinParam, TimingRuler ruler, boolean compact) {
+	public Player(String title, ISkinParam skinParam, TimingRuler ruler, boolean compact, Stereotype stereotype) {
+		this.stereotype = stereotype;
 		this.skinParam = skinParam;
 		this.compact = compact;
 		this.ruler = ruler;
@@ -73,23 +74,15 @@ public boolean isCompact() {
 		return compact;
 	}

-	protected abstract StyleSignatureBasic getStyleSignature();
-
-	protected abstract SymbolContext getContextLegacy();
-//	private StyleSignature getStyleSignature() {
-//		return StyleSignature.of(SName.root, SName.element, SName.timingDiagram);
-//	}
+	protected abstract StyleSignature getStyleSignature();

 	final protected Style getStyle() {
 		return getStyleSignature().getMergedStyle(skinParam.getCurrentStyleBuilder());
-
 	}

 	final protected FontConfiguration getFontConfiguration() {
-		if (UseStyle.useBetaStyle() == false)
-			return FontConfiguration.create(skinParam, FontParam.TIMING, null);
-		return FontConfiguration.create(skinParam, StyleSignatureBasic.of(SName.root, SName.element, SName.timingDiagram)
-				.getMergedStyle(skinParam.getCurrentStyleBuilder()));
+		return FontConfiguration.create(skinParam, StyleSignatureBasic
+				.of(SName.root, SName.element, SName.timingDiagram).getMergedStyle(skinParam.getCurrentStyleBuilder()));
 	}

 	final protected UStroke getStroke() {
@@ -98,8 +91,6 @@ final protected UStroke getStroke() {
 	}

 	final protected SymbolContext getContext() {
-		if (UseStyle.useBetaStyle() == false)
-			return getContextLegacy();

 		final Style style = getStyleSignature().getMergedStyle(skinParam.getCurrentStyleBuilder());
 		final HColor lineColor = style.value(PName.LineColor).asColor(skinParam.getThemeStyle(),


@@ -34,10 +34,9 @@
  */
 package net.sourceforge.plantuml.timingdiagram.graphic;

-import net.sourceforge.plantuml.awt.geom.Dimension2D;
-
 import net.sourceforge.plantuml.ISkinParam;
 import net.sourceforge.plantuml.UseStyle;
+import net.sourceforge.plantuml.awt.geom.Dimension2D;
 import net.sourceforge.plantuml.graphic.TextBlock;
 import net.sourceforge.plantuml.style.PName;
 import net.sourceforge.plantuml.style.SName;


OC-17139: code  changes after code review

@@ -27,7 +27,6 @@
 import org.apache.commons.fileupload.FileItem;
 import org.apache.commons.fileupload.disk.DiskFileItemFactory;
 import org.apache.commons.fileupload.servlet.ServletFileUpload;
-import org.apache.commons.io.FilenameUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.exception.ExceptionUtils;
 import org.slf4j.Logger;
@@ -194,16 +193,25 @@ private boolean mayProceed(String studyOid, StudySubjectBean ssBean) throws Exce
         return accessPermission;
     }

-    public static String getAttachedFilePath(String inputStudyOid) {
-    	// Using a standard library to validate/Sanitize user inputs which will be used in path expression to prevent from path traversal
-    	String studyOid =  FilenameUtils.getName(inputStudyOid);
+    public static String getAttachedFilePath(String studyOid) throws Exception {
         String attachedFilePath = CoreResources.getField("attached_file_location");
         if (attachedFilePath == null || attachedFilePath.length() <= 0) {
-            attachedFilePath = CoreResources.getField("filePath") + "attached_files" + File.separator + studyOid + File.separator;
-        } else {
-            attachedFilePath += studyOid + File.separator;
+            attachedFilePath = CoreResources.getField("filePath") + "attached_files" + File.separator;
+        }
+        File tempFile =  new File(attachedFilePath,studyOid);
+        String canonicalPath= tempFile.getCanonicalPath();
+
+        if (canonicalPath.startsWith(attachedFilePath)) {
+        	if (attachedFilePath == null || attachedFilePath.length() <= 0) {
+                attachedFilePath = CoreResources.getField("filePath") + "attached_files" + File.separator + studyOid + File.separator;
+            } else {
+                attachedFilePath += studyOid + File.separator;
+            }
+            return attachedFilePath;
+        }else {
+        	throw new RuntimeException("Traversal attempt - file path not allowed " + studyOid);
         }
-        return attachedFilePath;
+
     }

     private File processUploadedFile(FileItem item, String dirToSaveUploadedFileIn) {


@@ -4,12 +4,18 @@

 import com.alibaba.fastjson.JSON;

+import java.util.Map;
+
 public class Bug_for_Exception extends TestCase {
 	public void test_exception() throws Exception {
 		RuntimeException ex = new RuntimeException("e1");
 		String text = JSON.toJSONString(ex);
 		System.out.println(text);
-
-		RuntimeException ex2 = (RuntimeException) JSON.parse(text);
+
+
+		Object obj = JSON.parse(text);
+		assertTrue(obj instanceof Map);
+
+		RuntimeException ex2 = (RuntimeException) JSON.parseObject(text, Throwable.class);
 	}
 }


Merge pull request from GHSA-52vp-f7hj-cj92

@@ -27,9 +27,12 @@
  */
 package org.hisp.dhis.commons.collection;

+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.function.Consumer;
@@ -57,6 +60,22 @@ public static <T> Collection<T> intersection( Collection<T> c1, Collection<T> c2
         set1.retainAll( new HashSet<>( c2 ) );
         return set1;
     }
+
+    /**
+     * Returns all elements which are contained by {@code collection1} but not
+     * contained by {@code collection2} as an immutable list.
+     *
+     * @param <T>
+     * @param collection1 the first collection.
+     * @param collection2 the second collection.
+     * @return all elements in {@code collection1} not in {@code collection2}.
+     */
+    public static <A> List<A> difference( Collection<A> collection1, Collection<A> collection2 )
+    {
+        List<A> list = new ArrayList<>( collection1 );
+        list.removeAll( collection2 );
+        return Collections.unmodifiableList( list );
+    }

     /**
      * Searches for and returns the first string which starts with the given


Merge pull request from GHSA-52vp-f7hj-cj92

* fix: Add validation for programs org unit associations [DHIS2-13056]

* fix compilation failure in test class

* Fix one more compilation failure in another test class

Co-authored-by: Lars Helge Øverland <lars@dhis2.org>
Co-authored-by: Ameen <ameen@dhis2.org>

@@ -27,9 +27,12 @@
  */
 package org.hisp.dhis.commons.collection;

+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.function.Consumer;
@@ -58,6 +61,22 @@ public static <T> Collection<T> intersection( Collection<T> c1, Collection<T> c2
         return set1;
     }

+    /**
+     * Returns all elements which are contained by {@code collection1} but not
+     * contained by {@code collection2} as an immutable list.
+     *
+     * @param <T>
+     * @param list1 the first collection.
+     * @param list2 the second collection.
+     * @return all elements in {@code collection1} not in {@code collection2}.
+     */
+    public static <A> List<A> difference( Collection<A> collection1, Collection<A> collection2 )
+    {
+        List<A> list = new ArrayList<>( collection1 );
+        list.removeAll( collection2 );
+        return Collections.unmodifiableList( list );
+    }
+
     /**
      * Searches for and returns the first string which starts with the given
      * prefix. Removes the match from the collection.


Avoid clients exchanging tokens using tokens issued to other clients (#11542)

@@ -16,6 +16,7 @@
  */
 package org.keycloak.services.resources.admin.permissions;

+import org.keycloak.models.ClientModel;
 import org.keycloak.models.UserModel;

 import java.util.Map;
@@ -40,8 +41,8 @@ public interface UserPermissionEvaluator {

     void requireImpersonate(UserModel user);
     boolean canImpersonate();
-    boolean canImpersonate(UserModel user);
-    boolean isImpersonatable(UserModel user);
+    boolean canImpersonate(UserModel user, ClientModel requester);
+    boolean isImpersonatable(UserModel user, ClientModel requester);

     Map<String, Boolean> getAccess(UserModel user);


Restrict nested depth for collections to avoid DoS attacks

@@ -118,20 +118,22 @@ public void parseManyAliasesForCollections() {
     }

     @Test
-    public void referencesWithRestrictedAliases() {
+    public void referencesWithRestrictedNesting() {
         // without alias restriction this size should occupy tons of CPU, memory and time to parse
-        String bigYAML = createDump(35);
+        int depth = 35;
+        String bigYAML = createDump(depth);
         // Load
         long time1 = System.currentTimeMillis();
         LoaderOptions settings = new LoaderOptions();
-        settings.setMaxAliasesForCollections(40);
+        settings.setMaxAliasesForCollections(1000);
         settings.setAllowRecursiveKeys(true);
+        settings.setNestingDepthLimit(depth);
         Yaml yaml = new Yaml(settings);
         try {
             yaml.load(bigYAML);
             fail();
         } catch (Exception e) {
-            assertEquals("Number of aliases for non-scalar nodes exceeds the specified max=40", e.getMessage());
+            assertEquals("Nesting Depth exceeded max 35", e.getMessage());
         }
         long time2 = System.currentTimeMillis();
         float duration = (time2 - time1) / 1000;


[UNDERTOW-1979] CVE-2021-3859 continuation frames are not read correctly

@@ -0,0 +1,310 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2021 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package io.undertow.client.http;
+
+import io.undertow.Undertow;
+import io.undertow.UndertowOptions;
+import io.undertow.client.ClientCallback;
+import io.undertow.client.ClientConnection;
+import io.undertow.client.ClientExchange;
+import io.undertow.client.ClientRequest;
+import io.undertow.client.ClientResponse;
+import io.undertow.client.UndertowClient;
+import io.undertow.connector.ByteBufferPool;
+import io.undertow.io.Receiver;
+import io.undertow.io.Sender;
+import io.undertow.protocols.ssl.UndertowXnioSsl;
+import io.undertow.server.DefaultByteBufferPool;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.server.handlers.PathHandler;
+import io.undertow.server.protocol.http2.Http2UpgradeHandler;
+import io.undertow.testutils.DebuggingSlicePool;
+import io.undertow.testutils.DefaultServer;
+import io.undertow.testutils.HttpOneOnly;
+import io.undertow.util.AttachmentKey;
+import io.undertow.util.HeaderValues;
+import io.undertow.util.Headers;
+import io.undertow.util.HttpString;
+import io.undertow.util.Methods;
+import io.undertow.util.StatusCodes;
+import io.undertow.util.StringReadChannelListener;
+import io.undertow.util.StringWriteChannelListener;
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xnio.ChannelListeners;
+import org.xnio.IoUtils;
+import org.xnio.OptionMap;
+import org.xnio.Options;
+import org.xnio.Xnio;
+import org.xnio.XnioWorker;
+import org.xnio.channels.StreamSinkChannel;
+
+/**
+ *
Test that uses H2C upgrade and tries to send different number of headers
+ * for a GET/POST request. The idea is that the byte buffer used in the client
+ * and server is small. That way when sending a big number of headers the
+ * HEADERS frame is not enough to contain all the data and some CONTINUATION
+ * frames are needed. The test method also tries with different sizes of DATA
+ * to force several DATA frames to be sent when using POST method.

+ *
+ * @author rmartinc
+ */
+@RunWith(DefaultServer.class)
+@HttpOneOnly
+public class H2CUpgradeContinuationTestCase {
+
+    private static final String HEADER_PREFFIX = "custom-header-";
+    private static final String ECHO_PATH = "/echo";
+    private static final AttachmentKey<String> RESPONSE_BODY = AttachmentKey.create(String.class);
+
+    private static ByteBufferPool smallPool;
+    private static XnioWorker worker;
+    private static Undertow server;
+
+    /**
+     * Just a handler that receives the request and sends back all the custom
+     * headers received and (if data received) returns the same data (empty
+     * response otherwise).
+     * @param exchange The HttpServerExchange
+     */
+    private static void sendEchoResponse(final HttpServerExchange exchange) {
+        exchange.setStatusCode(StatusCodes.OK);
+        // add the custom headers received
+        for (HeaderValues header : exchange.getRequestHeaders()) {
+            if (header.getFirst().startsWith(HEADER_PREFFIX)) {
+                exchange.getResponseHeaders().putAll(header.getHeaderName(), header.subList(0, header.size()));
+            }
+        }
+        // response using echo or empty string
+        if (exchange.getRequestContentLength() > 0) {
+            exchange.getRequestReceiver().receiveFullString(new Receiver.FullStringCallback() {
+                @Override
+                public void handle(HttpServerExchange exchange, String message) {
+                    exchange.getResponseSender().send(message);
+                }
+            });
+        } else {
+            final Sender sender = exchange.getResponseSender();
+            sender.send("");
+        }
+    }
+
+    /**
+     * Initializes the server with the H2C handler and adds the echo handler to
+     * manage the requests.
+     * @throws IOException Some error
+     */
+    @BeforeClass
+    public static void beforeClass() throws IOException {
+        // server and client pool is using 1024 for the buffer size
+        smallPool = new DebuggingSlicePool(new DefaultByteBufferPool(true, 1024, 1000, 10, 100));
+
+        final PathHandler path = new PathHandler()
+                .addExactPath(ECHO_PATH, new HttpHandler() {
+                    @Override
+                    public void handleRequest(HttpServerExchange exchange) throws Exception {
+                        sendEchoResponse(exchange);
+                    }
+                });
+
+        server = Undertow.builder()
+                .setByteBufferPool(smallPool)
+                .addHttpListener(DefaultServer.getHostPort("default") + 1, DefaultServer.getHostAddress("default"), new Http2UpgradeHandler(path))
+                .setSocketOption(Options.REUSE_ADDRESSES, true)
+                .build();
+        server.start();
+
+        // Create xnio worker
+        final Xnio xnio = Xnio.getInstance();
+        final XnioWorker xnioWorker = xnio.createWorker(null, OptionMap.builder()
+                .set(Options.WORKER_IO_THREADS, 8)
+                .set(Options.TCP_NODELAY, true)
+                .set(Options.KEEP_ALIVE, true)
+                .getMap());
+        worker = xnioWorker;
+    }
+
+    /**
+     * Stops server and worker.
+     */
+    @AfterClass
+    public static void afterClass() {
+        if (server != null) {
+            server.stop();
+        }
+        if (worker != null) {
+            worker.shutdown();
+        }
+        if (smallPool != null) {
+            smallPool.close();
+            smallPool = null;
+        }
+    }
+
+    /**
+     * Method that sends a GET or POST request adding count number of custom
+     * headers and sending contentLength data. GET is used if no content length
+     * is passed, POST if contentLength is greater than 0.
+     * @param connection The connection to use
+     * @param requestCount The number of requests to send
+     * @param headersCount The number of custom headers to send
+     * @param contentLength The content length to send (POST method used if >0)
+     * @throws Exception Some error
+     */
+    private void sendRequest(ClientConnection connection, int requestCount, int headersCount, int contentLength) throws Exception {
+        final CountDownLatch latch = new CountDownLatch(requestCount);
+        final List<ClientResponse> responses = new CopyOnWriteArrayList<>();
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < contentLength; i++) {
+            sb.append(i % 10);
+        }
+        final String content = sb.length() > 0? sb.toString() : null;
+        connection.getIoThread().execute(new Runnable() {
+            @Override
+            public void run() {
+                for (int i = 0; i < requestCount; i++) {
+                    final ClientRequest request = new ClientRequest()
+                            .setMethod(contentLength > 0 ? Methods.POST : Methods.GET)
+                            .setPath(ECHO_PATH);
+                    request.getRequestHeaders().put(Headers.HOST, DefaultServer.getHostAddress());
+                    if (contentLength > 0) {
+                        request.getRequestHeaders().put(Headers.CONTENT_LENGTH, contentLength);
+                    }
+                    for (int j = 0; j < headersCount; j++) {
+                        request.getRequestHeaders().put(new HttpString(HEADER_PREFFIX + j), HEADER_PREFFIX + j);
+                    }
+                    connection.sendRequest(request, createClientCallback(responses, latch, content));
+                }
+            }
+        });
+
+        latch.await(10, TimeUnit.SECONDS);
+
+        Assert.assertEquals("No responses received from server in 10s", requestCount, responses.size());
+        for (int i = 0; i < requestCount; i++) {
+            Assert.assertEquals("Response " + i + " code was not OK", StatusCodes.OK, responses.get(i).getResponseCode());
+            Assert.assertEquals("Incorrect data received for response " + i, contentLength > 0 ? content : "", responses.get(i).getAttachment(RESPONSE_BODY));
+            int headersReturned = 0;
+            for (HeaderValues header : responses.get(i).getResponseHeaders()) {
+                if (header.getFirst().startsWith(HEADER_PREFFIX)) {
+                    headersReturned += header.size();
+                }
+            }
+            Assert.assertEquals("Incorrect number of headers returned for response " + i, headersCount, headersReturned);
+        }
+    }
+
+    /**
+     * The real test that sends several GET and POST requests with different
+     * number of headers and different content length.
+     * @throws Exception  Some error
+     */
+    @Test
+    public void testDifferentSizes() throws Exception {
+        final UndertowClient client = UndertowClient.getInstance();
+
+        // the client connection uses the small byte-buffer of 1024 to force the continuation frames
+        final ClientConnection connection = client.connect(
+                new URI("http://" + DefaultServer.getHostAddress() + ":" + (DefaultServer.getHostPort("default") + 1)),
+                worker, new UndertowXnioSsl(worker.getXnio(), OptionMap.EMPTY, DefaultServer.getClientSSLContext()),
+                smallPool, OptionMap.create(UndertowOptions.ENABLE_HTTP2, true)).get();
+        try {
+            // the first request triggers the upgrade to H2C
+            sendRequest(connection, 1, 0, 0);
+            // send several requests with different sizes for headers and data
+            sendRequest(connection, 10, 10, 0);
+            sendRequest(connection, 10, 100, 0);
+            sendRequest(connection, 10, 150, 0);
+            sendRequest(connection, 10, 1, 10);
+            sendRequest(connection, 10, 0, 2000);
+            sendRequest(connection, 10, 150, 2000);
+        } finally {
+            IoUtils.safeClose(connection);
+        }
+    }
+
+    /**
+     * Create the callback to receive the response and assign it to the list.
+     * @param responses The list where the response will be added
+     * @param latch The latch to count down when the response is received
+     * @param message The message to send if it's a POST message (if null nothing is send)
+     * @return The created callback
+     */
+    private static ClientCallback<ClientExchange> createClientCallback(final List<ClientResponse> responses, final CountDownLatch latch, String message) {
+        return new ClientCallback<ClientExchange>() {
+            @Override
+            public void completed(ClientExchange result) {
+                if (message != null) {
+                    new StringWriteChannelListener(message).setup(result.getRequestChannel());
+                }
+                result.setResponseListener(new ClientCallback<ClientExchange>() {
+                    @Override
+                    public void completed(final ClientExchange result) {
+                        responses.add(result.getResponse());
+                        new StringReadChannelListener(result.getConnection().getBufferPool()) {
+
+                            @Override
+                            protected void stringDone(String string) {
+                                result.getResponse().putAttachment(RESPONSE_BODY, string);
+                                latch.countDown();
+                            }
+
+                            @Override
+                            protected void error(IOException e) {
+                                e.printStackTrace();
+                                latch.countDown();
+                            }
+                        }.setup(result.getResponseChannel());
+                    }
+
+                    @Override
+                    public void failed(IOException e) {
+                        e.printStackTrace();
+                        latch.countDown();
+                    }
+                });
+                try {
+                    result.getRequestChannel().shutdownWrites();
+                    if (!result.getRequestChannel().flush()) {
+                        result.getRequestChannel().getWriteSetter().set(ChannelListeners.<StreamSinkChannel>flushingChannelListener(null, null));
+                        result.getRequestChannel().resumeWrites();
+                    }
+                } catch (IOException e) {
+                    e.printStackTrace();
+                    latch.countDown();
+                }
+            }
+
+            @Override
+            public void failed(IOException e) {
+                e.printStackTrace();
+                latch.countDown();
+            }
+        };
+    }
+}


Fix memory leak on each TLS connection

Each TLS connection is leaking a bunch of data that isn't in the heap
and so after 25k requests Tomcat uses about 2.5GB resident memory.

There are large number of relationships that point at each other and we
need to break the cycle so JSSEngineReferenceImpl's finalizer can run
and clear all the native resources these point at.

The lowest impact place to break the cycle was at SSLAlertEvent.engine.
This relationship doesn't seem to be used anywhere.  Once the cycle is
broken, JSSEngineReferenceImpl can be garbage collected and the
finalizer can run.

Signed-off-by: Chris Kelley <ckelley@redhat.com>

@@ -1,6 +1,5 @@
 package org.mozilla.jss.nss;

-import java.lang.IllegalArgumentException;
 import java.util.ArrayList;

 import org.mozilla.jss.crypto.X509Certificate;
@@ -42,13 +41,14 @@ public void SetClientCert(X509Certificate cert) throws IllegalArgumentException

     @Override
     protected synchronized void releaseNativeResources() throws Exception {
-        synchronized (globalRef) {
-            if (globalRef != null) {
-                try {
-                    globalRef.close();
-                } finally {
-                    globalRef = null;
-                }
+
+        super.releaseNativeResources();
+
+        if (globalRef != null) {
+            try {
+                globalRef.close();
+            } finally {
+                globalRef = null;
             }
         }
     }


KEYCLOAK-19177 Disable ECP flow by default for all Saml clients; ecp flow creates only transient users sessions

@@ -61,6 +61,11 @@ public String getForceNameIDFormat() {
         return getAttributes().get(SamlConfigAttributes.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE);
     }

+    public String getAllowEcpFlow() {
+        if (getAttributes() == null) return null;
+        return getAttributes().get(SamlConfigAttributes.SAML_ALLOW_ECP_FLOW);
+    }
+
     public String getSamlArtifactBinding() {
         if (getAttributes() == null) return null;
         return getAttributes().get(SamlConfigAttributes.SAML_ARTIFACT_BINDING);


Merge pull request from GHSA-r38f-c4h4-hqq2

Fixes SQL generated in PgResultSet.refresh() to escape column identifiers so as to prevent SQL injection.

Previously, the column names for both key and data columns in the table were copied as-is into the generated
SQL. This allowed a malicious table with column names that include statement terminator to be parsed and
executed as multiple separate commands.

Also adds a new test class ResultSetRefreshTest to verify this change.

@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2022, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.test.jdbc2;
+
+import static org.junit.Assert.assertTrue;
+
+import org.postgresql.test.TestUtil;
+
+import org.junit.Test;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+public class ResultSetRefreshTest extends BaseTest4 {
+  @Test
+  public void testWithDataColumnThatRequiresEscaping() throws Exception {
+    TestUtil.dropTable(con, "refresh_row_bad_ident");
+    TestUtil.execute(con, "CREATE TABLE refresh_row_bad_ident (id int PRIMARY KEY, \"1 FROM refresh_row_bad_ident; SELECT 2; SELECT *\" int)");
+    TestUtil.execute(con, "INSERT INTO refresh_row_bad_ident (id) VALUES (1), (2), (3)");
+
+    Statement stmt = con.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
+    ResultSet rs = stmt.executeQuery("SELECT * FROM refresh_row_bad_ident");
+    assertTrue(rs.next());
+    try {
+      rs.refreshRow();
+    } catch (SQLException ex) {
+      throw new RuntimeException("ResultSet.refreshRow() did not handle escaping data column identifiers", ex);
+    }
+    rs.close();
+    stmt.close();
+  }
+
+  @Test
+  public void testWithKeyColumnThatRequiresEscaping() throws Exception {
+    TestUtil.dropTable(con, "refresh_row_bad_ident");
+    TestUtil.execute(con, "CREATE TABLE refresh_row_bad_ident (\"my key\" int PRIMARY KEY)");
+    TestUtil.execute(con, "INSERT INTO refresh_row_bad_ident VALUES (1), (2), (3)");
+
+    Statement stmt = con.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
+    ResultSet rs = stmt.executeQuery("SELECT * FROM refresh_row_bad_ident");
+    assertTrue(rs.next());
+    try {
+      rs.refreshRow();
+    } catch (SQLException ex) {
+      throw new RuntimeException("ResultSet.refreshRow() did not handle escaping key column identifiers", ex);
+    }
+    rs.close();
+    stmt.close();
+  }
+}


fix: ensure the payload format is valid

This commit should prevent some NPE issues encountered after the
parsing of the packet.

Related:

- https://github.com/socketio/socket.io-client-java/issues/642
- https://github.com/socketio/socket.io-client-java/issues/609
- https://github.com/socketio/socket.io-client-java/issues/505

@@ -63,5 +63,8 @@ public void decodeInError() throws JSONException {
         Helpers.testDecodeError(Parser.EVENT + "2sd");
         // event with invalid json data
         Helpers.testDecodeError(Parser.EVENT + "2[\"a\",1,{asdf}]");
+        Helpers.testDecodeError(Parser.EVENT + "2{}");
+        Helpers.testDecodeError(Parser.EVENT + "2[]");
+        Helpers.testDecodeError(Parser.EVENT + "2[null]");
     }
 }


[DS-4383] Request Item Servlet escape HTML

@@ -118,7 +118,7 @@ protected static String getNewToken(Context context, int bitstreamId
      * @exception SQLExeption
      *
      */
-    public static String getLinkTokenEmail(Context context, String bitstreamId
+    public static String getLinkTokenEmail(Context context, int bitstreamId
             , int itemID, String reqEmail, String reqName, boolean allfiles)
             throws SQLException
     {
@@ -128,7 +128,7 @@ public static String getLinkTokenEmail(Context context, String bitstreamId
                 base.endsWith("/") ? "" : "/").append(
                 "request-item").append("?step=" + RequestItemServlet.ENTER_TOKEN)
                 .append("&token=")
-                .append(getNewToken(context, Integer.parseInt(bitstreamId), itemID, reqEmail, reqName, allfiles))
+                .append(getNewToken(context, bitstreamId, itemID, reqEmail, reqName, allfiles))
                 .toString();

         return specialLink;


Fix security vulnerability issue regarding site/* access

@@ -18,7 +18,7 @@ public BrandLogoPanel(String id) {
 	}

 	private File getCustomLogoFile() {
-		return new File(Bootstrap.getSiteDir(), "logo.png");
+		return new File(Bootstrap.getSiteDir(), "assets/logo.png");
 	}

 	@Override


Fix the docker sock mount security vulnerability

@@ -4078,4 +4078,27 @@ private void migrate85(File dataDir, Stack<Integer> versions) {
 	private void migrate86(File dataDir, Stack<Integer> versions) {
 	}

+	private void migrate87(File dataDir, Stack<Integer> versions) {
+		for (File file: dataDir.listFiles()) {
+			if (file.getName().startsWith("Settings.xml")) {
+				VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file);
+				for (Element element: dom.getRootElement().elements()) {
+					String key = element.elementTextTrim("key");
+					if (key.equals("JOB_EXECUTORS")) {
+						Element valueElement = element.element("value");
+						if (valueElement != null) {
+							for (Element executorElement: valueElement.elements()) {
+								if (executorElement.getName().contains("DockerExecutor"))
+									executorElement.addElement("mountDockerSock").setText("false");
+								else if (executorElement.getName().contains("KubernetesExecutor"))
+									executorElement.addElement("mountContainerSock").setText("false");
+							}
+						}
+					}
+				}
+				dom.writeToFile(file, false);
+			}
+		}
+	}
+
 }


XWIKI-19549: Disallow template override for login, register and skin

* Also allow `registerinline` template for RegisterAction.

@@ -19,6 +19,9 @@
  */
 package com.xpn.xwiki.web;

+import java.util.Arrays;
+import java.util.List;
+
 import javax.inject.Named;
 import javax.inject.Singleton;
 import javax.script.ScriptContext;
@@ -55,17 +58,20 @@ public class RegisterAction extends XWikiAction
     /** Space where the registration config and class are stored. */
     private static final String WIKI_SPACE = "XWiki";

+    /** Allowed templates for this action. */
+    private static final List<String> ALLOWED_TEMPLATES = Arrays.asList(REGISTER, "registerinline");
+
     @Override
     public boolean action(XWikiContext context) throws XWikiException
     {
         XWiki xwiki = context.getWiki();
         XWikiRequest request = context.getRequest();
         XWikiResponse response = context.getResponse();

-        // Disallow template override with xpage parameter.
-        if (!REGISTER.equals(Utils.getPage(context.getRequest(), REGISTER))) {
+        // Limit template overrides with xpage to allowed templates.
+        if (!ALLOWED_TEMPLATES.contains(Utils.getPage(context.getRequest(), REGISTER))) {
             throw new XWikiException(XWikiException.MODULE_XWIKI, XWikiException.ERROR_XWIKI_ACCESS_DENIED,
-                String.format("Template may not be overriden with 'xpage' in [%s] action.", REGISTER));
+                String.format("Forbidden template override with 'xpage' in [%s] action.", REGISTER));
         }

         String register = request.getParameter(REGISTER);


XWIKI-19559: Refactor handling of users in oldcore

  * Add property for inactive users in XWikiContext
  * Ensure that all calls to checkAuth in XS goes through
    XWiki#checkAuth so that inactive users are handled
  * Improve code of XWiki#prepareDocuments to simplify it
  * Ensure that the login page is not displayed if a inactive user
    logged in, in XWikiCachingRightService
  * Improve a bit the code of the drawer to display the logout link in
    case of logged in inactive user

@@ -2910,7 +2910,7 @@ public String getCounter(String name)
      */
     public XWikiUser checkAuth() throws XWikiException
     {
-        return this.context.getWiki().getAuthService().checkAuth(this.context);
+        return this.context.getWiki().checkAuth(this.context);
     }

     /**
@@ -2925,7 +2925,13 @@ public XWikiUser checkAuth() throws XWikiException
      */
     public XWikiUser checkAuth(String username, String password, String rememberme) throws XWikiException
     {
-        return this.context.getWiki().getAuthService().checkAuth(username, password, rememberme, this.context);
+        XWikiUser user =
+            this.context.getWiki().getAuthService().checkAuth(username, password, rememberme, this.context);
+        if (user.isDisabled(this.context)) {
+            this.context.put(XWikiContext.INACTIVE_USER_REFERENCE, user.getUserReference());
+            user = null;
+        }
+        return user;
     }

     /**


@@ -60,7 +60,7 @@ private File download(String driverFileUrl, String parentDir) {
         try {
             Files.createDirectories(parentDirPath);
         } catch (IOException e) {
-            log.error("下载驱动时创建目录失败", e);
+            log.error("create directory for driver failed", e);
             throw DomainErrors.DOWNLOAD_DRIVER_ERROR.exception(e);
         }

@@ -97,8 +97,9 @@ private File download(String driverFileUrl, String parentDir) {
                 }
             });
         } catch (RestClientException e) {
-            log.error(parentDir + " download driver error", e);
-            throw DomainErrors.DOWNLOAD_DRIVER_ERROR.exception(e.getMessage());
+            String msg = String.format("download driver from %s to %s failed", driverFileUrl, parentDir);
+            log.error(msg, e);
+            throw DomainErrors.DOWNLOAD_DRIVER_ERROR.exception(msg);
         }
     }


SCMMaterial changes #000

* SCMMaterial unlike SCMMaterialConfig objects are used for polling,
  they do not need to encrypt the password. Hence removing the
  encryptedPassword attribute.

@@ -68,8 +68,8 @@ public class TfsMaterialTest {
     @BeforeEach
     void setUp() {
         GoCipher goCipher = mock(GoCipher.class);
-        tfsMaterialFirstCollectionFirstProject = new TfsMaterial(goCipher, new UrlArgument(TFS_FIRST_COLLECTION_URL), USERNAME, DOMAIN, PASSWORD, TFS_FIRST_PROJECT);
-        tfsMaterialFirstCollectionSecondProject = new TfsMaterial(goCipher, new UrlArgument(TFS_FIRST_COLLECTION_URL), USERNAME, DOMAIN, PASSWORD, TFS_SECOND_PROJECT);
+        tfsMaterialFirstCollectionFirstProject = new TfsMaterial(new UrlArgument(TFS_FIRST_COLLECTION_URL), USERNAME, DOMAIN, PASSWORD, TFS_FIRST_PROJECT);
+        tfsMaterialFirstCollectionSecondProject = new TfsMaterial(new UrlArgument(TFS_FIRST_COLLECTION_URL), USERNAME, DOMAIN, PASSWORD, TFS_SECOND_PROJECT);
     }

     @Test
@@ -104,7 +104,7 @@ void shouldLoadAllModificationsSinceAGivenRevision() throws IOException {

     @Test
     void shouldInjectAllRelevantAttributesInSqlCriteriaMap() {
-        TfsMaterial tfsMaterial = new TfsMaterial(new GoCipher(), new UrlArgument("my-url"), "loser", DOMAIN, "foo_bar_baz", "/dev/null");
+        TfsMaterial tfsMaterial = new TfsMaterial(new UrlArgument("my-url"), "loser", DOMAIN, "foo_bar_baz", "/dev/null");
         assertThat(tfsMaterial.getSqlCriteria()).isEqualTo(m(
                 SQL_CRITERIA_TYPE, (Object) "TfsMaterial",
                 "url", "my-url",
@@ -114,7 +114,7 @@ void shouldInjectAllRelevantAttributesInSqlCriteriaMap() {

     @Test
     void shouldInjectAllRelevantAttributesInAttributeMap() {
-        TfsMaterial tfsMaterial = new TfsMaterial(new GoCipher(), new UrlArgument("my-url"), "loser", DOMAIN, "foo_bar_baz", "/dev/null");
+        TfsMaterial tfsMaterial = new TfsMaterial(new UrlArgument("my-url"), "loser", DOMAIN, "foo_bar_baz", "/dev/null");
         assertThat(tfsMaterial.getAttributesForXml()).isEqualTo(m(
                 AbstractMaterial.SQL_CRITERIA_TYPE, (Object) "TfsMaterial",
                 "url", "my-url",
@@ -124,56 +124,31 @@ void shouldInjectAllRelevantAttributesInAttributeMap() {

     @Test
     void shouldReturnUrlForCommandLine_asUrl_IfSet() {
-        TfsMaterial tfsMaterial = new TfsMaterial(new GoCipher(), new UrlArgument("http://foo:bar@my-url.com"), "loser", DOMAIN, "foo_bar_baz", "/dev/null"
+        TfsMaterial tfsMaterial = new TfsMaterial(new UrlArgument("http://foo:bar@my-url.com"), "loser", DOMAIN, "foo_bar_baz", "/dev/null"
         );
         assertThat(tfsMaterial.getUrl()).isEqualTo("http://foo:bar@my-url.com");

-        tfsMaterial = new TfsMaterial(new GoCipher(), null, "loser", DOMAIN, "foo_bar_baz", "/dev/null");
+        tfsMaterial = new TfsMaterial(null, "loser", DOMAIN, "foo_bar_baz", "/dev/null");
         assertThat(tfsMaterial.getUrl()).isNull();
     }

     @Test
     void shouldReturnUrlForCommandLine_asLocation_IfSet() {
-        TfsMaterial tfsMaterial = new TfsMaterial(new GoCipher(), new UrlArgument("http://foo:bar@my-url.com"), "loser", DOMAIN, "foo_bar_baz", "/dev/null"
+        TfsMaterial tfsMaterial = new TfsMaterial(new UrlArgument("http://foo:bar@my-url.com"), "loser", DOMAIN, "foo_bar_baz", "/dev/null"
         );
         assertThat(tfsMaterial.getLocation()).isEqualTo("http://foo:******@my-url.com");

-        tfsMaterial = new TfsMaterial(new GoCipher(), null, "loser", DOMAIN, "foo_bar_baz", "/dev/null");
+        tfsMaterial = new TfsMaterial(null, "loser", DOMAIN, "foo_bar_baz", "/dev/null");
         assertThat(tfsMaterial.getLocation()).isNull();
     }

-    @Test
-    void shouldEncryptTfsPasswordAndMarkPasswordAsNull() throws Exception {
-        GoCipher mockGoCipher = mock(GoCipher.class);
-        when(mockGoCipher.encrypt("password")).thenReturn("encrypted");
-
-        TfsMaterial tfsMaterial = new TfsMaterial(mockGoCipher, new UrlArgument("/foo"), "username", DOMAIN, "password", "");
-        tfsMaterial.ensureEncrypted();
-
-        assertThat(tfsMaterial.getPassword()).isNull();
-        assertThat(tfsMaterial.getEncryptedPassword()).isEqualTo("encrypted");
-    }
-
-    @Test
-    void shouldDecryptTfsPassword() throws Exception {
-        GoCipher mockGoCipher = mock(GoCipher.class);
-        when(mockGoCipher.decrypt("encrypted")).thenReturn("password");
-
-        TfsMaterial tfsMaterial = new TfsMaterial(mockGoCipher, new UrlArgument("/foo"), "username", DOMAIN, null, "");
-
-        ReflectionUtil.setField(tfsMaterial, "encryptedPassword", "encrypted");
-
-        tfsMaterial.ensureEncrypted();
-        assertThat(tfsMaterial.getPassword()).isEqualTo("password");
-    }
-
     @Test
     void shouldNotDecryptPasswordIfPasswordIsNotNull() throws Exception {
         GoCipher mockGoCipher = mock(GoCipher.class);
         when(mockGoCipher.encrypt("password")).thenReturn("encrypted");
         when(mockGoCipher.decrypt("encrypted")).thenReturn("password");

-        TfsMaterial material = new TfsMaterial(mockGoCipher, new UrlArgument("/foo"), "username", DOMAIN, "password", "");
+        TfsMaterial material = new TfsMaterial(new UrlArgument("/foo"), "username", DOMAIN, "password", "");
         material.ensureEncrypted();
         when(mockGoCipher.encrypt("new_password")).thenReturn("new_encrypted");
         material.setPassword("new_password");
@@ -182,33 +157,6 @@ void shouldNotDecryptPasswordIfPasswordIsNotNull() throws Exception {
         assertThat(material.getPassword()).isEqualTo("new_password");
     }

-    @Test
-    void shouldErrorOutIfDecryptionFails() throws CryptoException {
-        GoCipher mockGoCipher = mock(GoCipher.class);
-        String fakeCipherText = "fake cipher text";
-        when(mockGoCipher.decrypt(fakeCipherText)).thenThrow(new CryptoException("exception"));
-        TfsMaterial material = new TfsMaterial(mockGoCipher, new UrlArgument("/foo"), "username", DOMAIN, "password", "");
-        ReflectionUtil.setField(material, "encryptedPassword", fakeCipherText);
-        try {
-            material.getPassword();
-            fail("Should have thrown up");
-        } catch (Exception e) {
-            assertThat(e.getMessage()).isEqualTo("Could not decrypt the password to get the real password");
-        }
-    }
-
-    @Test
-    void shouldErrorOutIfEncryptionFails() throws Exception {
-        GoCipher mockGoCipher = mock(GoCipher.class);
-        when(mockGoCipher.encrypt("password")).thenThrow(new CryptoException("exception"));
-        try {
-            new TfsMaterial(mockGoCipher, new UrlArgument("/foo"), "username", DOMAIN, "password", "");
-            fail("Should have thrown up");
-        } catch (Exception e) {
-            assertThat(e.getMessage()).isEqualTo("Password encryption failed. Please verify your cipher key.");
-        }
-    }
-
     @Test
     void shouldBePasswordAware() {
         assertThat(PasswordAwareMaterial.class.isAssignableFrom(TfsMaterial.class)).isTrue();
@@ -237,13 +185,13 @@ void shouldCheckConnection() {

     @Test
     void shouldGetLongDescriptionForMaterial() {
-        TfsMaterial material = new TfsMaterial(new GoCipher(), new UrlArgument("http://url/"), "user", "domain", "password", "$project/path/");
+        TfsMaterial material = new TfsMaterial(new UrlArgument("http://url/"), "user", "domain", "password", "$project/path/");
         assertThat(material.getLongDescription()).isEqualTo("URL: http://url/, Username: user, Domain: domain, ProjectPath: $project/path/");
     }

     @Test
     void shouldCopyOverPasswordWhenConvertingToConfig() throws Exception {
-        TfsMaterial material = new TfsMaterial(new GoCipher(), new UrlArgument("http://url/"), "user", "domain", "password", "$project/path/");
+        TfsMaterial material = new TfsMaterial(new UrlArgument("http://url/"), "user", "domain", "password", "$project/path/");

         TfsMaterialConfig config = (TfsMaterialConfig) material.config();

@@ -253,7 +201,7 @@ void shouldCopyOverPasswordWhenConvertingToConfig() throws Exception {

     @Test
     void shouldGetAttributesWithSecureFields() {
-        TfsMaterial material = new TfsMaterial(new GoCipher(), new UrlArgument("http://username:password@tfsrepo.com"), "username", "domain", "password", "$project/path/");
+        TfsMaterial material = new TfsMaterial(new UrlArgument("http://username:password@tfsrepo.com"), "username", "domain", "password", "$project/path/");
         Map<String, Object> attributes = material.getAttributes(true);

         assertThat(attributes.get("type")).isEqualTo("tfs");
@@ -267,7 +215,7 @@ void shouldGetAttributesWithSecureFields() {

     @Test
     void shouldGetAttributesWithoutSecureFields() {
-        TfsMaterial material = new TfsMaterial(new GoCipher(), new UrlArgument("http://username:password@tfsrepo.com"), "username", "domain", "password", "$project/path/");
+        TfsMaterial material = new TfsMaterial(new UrlArgument("http://username:password@tfsrepo.com"), "username", "domain", "password", "$project/path/");
         Map<String, Object> attributes = material.getAttributes(false);

         assertThat(attributes.get("type")).isEqualTo("tfs");
@@ -283,14 +231,14 @@ void shouldGetAttributesWithoutSecureFields() {
     class passwordForCommandLine {
         @Test
         void shouldReturnPasswordAsConfigured_IfNotDefinedAsSecretParam() {
-            TfsMaterial tfsMaterial = new TfsMaterial(new GoCipher(), new UrlArgument("some-url"), null, null, "badger", null);
+            TfsMaterial tfsMaterial = new TfsMaterial(new UrlArgument("some-url"), null, null, "badger", null);

             assertThat(tfsMaterial.passwordForCommandLine()).isEqualTo("badger");
         }

         @Test
         void shouldReturnAResolvedPassword_IfPasswordDefinedAsSecretParam() {
-            TfsMaterial tfsMaterial = new TfsMaterial(new GoCipher(), new UrlArgument("some-url"), null, null, "{{SECRET:[secret_config_id][lookup_pass]}}", null);
+            TfsMaterial tfsMaterial = new TfsMaterial(new UrlArgument("some-url"), null, null, "{{SECRET:[secret_config_id][lookup_pass]}}", null);

             tfsMaterial.getSecretParams().findFirst("lookup_pass").ifPresent(secretParam -> secretParam.setValue("resolved_password"));

@@ -299,7 +247,7 @@ void shouldReturnAResolvedPassword_IfPasswordDefinedAsSecretParam() {

         @Test
         void shouldErrorOutWhenCalledOnAUnResolvedSecretParam_IfPasswordDefinedAsSecretParam() {
-            TfsMaterial tfsMaterial = new TfsMaterial(new GoCipher(), new UrlArgument("some-url"), null, null, "{{SECRET:[secret_config_id][lookup_pass]}}", null);
+            TfsMaterial tfsMaterial = new TfsMaterial(new UrlArgument("some-url"), null, null, "{{SECRET:[secret_config_id][lookup_pass]}}", null);

             assertThatCode(tfsMaterial::passwordForCommandLine)
                     .isInstanceOf(UnresolvedSecretParamException.class)
@@ -311,7 +259,7 @@ void shouldErrorOutWhenCalledOnAUnResolvedSecretParam_IfPasswordDefinedAsSecretP
     class setPassword {
         @Test
         void shouldParsePasswordString_IfDefinedAsSecretParam() {
-            TfsMaterial tfsMaterial = new TfsMaterial(new GoCipher(), new UrlArgument("some-url"), null, null, "{{SECRET:[secret_config_id][lookup_pass]}}", null);
+            TfsMaterial tfsMaterial = new TfsMaterial(new UrlArgument("some-url"), null, null, "{{SECRET:[secret_config_id][lookup_pass]}}", null);

             assertThat(tfsMaterial.getSecretParams())
                     .hasSize(1)
@@ -339,7 +287,7 @@ void populateEnvContextShouldSetMaterialEnvVars() {

     @Test
     void shouldOnlyPopulateDomainEnvVarIfPresent() {
-        TfsMaterial material = new TfsMaterial(mock(GoCipher.class), new UrlArgument(TFS_FIRST_COLLECTION_URL), USERNAME, "", PASSWORD, TFS_FIRST_PROJECT);
+        TfsMaterial material = new TfsMaterial(new UrlArgument(TFS_FIRST_COLLECTION_URL), USERNAME, "", PASSWORD, TFS_FIRST_PROJECT);
         EnvironmentVariableContext ctx = new EnvironmentVariableContext();
         final ArrayList<Modification> modifications = new ArrayList<>();


@@ -25,7 +25,6 @@
 import com.thoughtworks.go.domain.materials.tfs.TfsCommand;
 import com.thoughtworks.go.domain.materials.tfs.TfsCommandFactory;
 import com.thoughtworks.go.domain.materials.tfs.TfsMaterialInstance;
-import com.thoughtworks.go.security.GoCipher;
 import com.thoughtworks.go.util.GoConstants;
 import com.thoughtworks.go.util.command.ConsoleOutputStreamConsumer;
 import com.thoughtworks.go.util.command.EnvironmentVariableContext;
@@ -54,12 +53,12 @@ public class TfsMaterial extends ScmMaterial implements PasswordAwareMaterial, P
     private String domain = "";
     private String projectPath;

-    public TfsMaterial(GoCipher goCipher) {
-        super(TYPE, goCipher);
+    public TfsMaterial() {
+        super(TYPE);
     }

-    public TfsMaterial(GoCipher goCipher, UrlArgument url, String userName, String domain, String password, String projectPath) {
-        this(goCipher);
+    public TfsMaterial(UrlArgument url, String userName, String domain, String password, String projectPath) {
+        this();
         this.url = url;
         this.userName = userName;
         this.domain = domain;
@@ -68,7 +67,7 @@ public TfsMaterial(GoCipher goCipher, UrlArgument url, String userName, String d
     }

     public TfsMaterial(TfsMaterialConfig config) {
-        this(config.getGoCipher(), new UrlArgument(config.getUrl()), config.getUserName(), config.getDomain(), config.getPassword(), config.getProjectPath());
+        this(new UrlArgument(config.getUrl()), config.getUserName(), config.getDomain(), config.getPassword(), config.getProjectPath());
         this.autoUpdate = config.getAutoUpdate();
         this.filter = config.rawFilter();
         this.invertFilter = config.getInvertFilter();


Access Token APIs  (#5797)

* Define a AuthTokenSqlMapDao to store and retrieve token information from DB

* Introduce Auth Token API to manage auth tokens

* Store the hashed token instead of storing the raw value

* Some tests

* Store information of the user who has created the auth token

* Use SHA-256 instead of MD5 for hashing token value

* Change username data type from VARCHAR to VARCHAR_IGNORECASE

* Introduce AuthTokenMother to create authToken in tests

* Fix auth token controller specs

* Remove unused class variable from test

* Introduce auth token index endpoint to fetch all the tokens belonging to me

* Add extra attributes to auth token
* Salt Id
* Salt Value
* Auth Config Id
* Revoked At

* Add support for revoking access tokens
Add a method to validate and fetch access token object provided access token

* Rename controller package and module

* rename service and dao

* Rename auth token package

* Rename rules under url rewrite

* Renamed few more occurences of auth token to acccess tokens

@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019 ThoughtWorks, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.thoughtworks.go.helper;
+
+import com.thoughtworks.go.domain.AccessToken;
+import org.apache.commons.lang3.RandomStringUtils;
+
+import java.util.Date;
+
+public class AccessTokenMother {
+    public static AccessToken accessTokenWithName(String tokenName) {
+        String tokenValue = RandomStringUtils.randomAlphanumeric(32).toUpperCase();
+        String tokenDescription = RandomStringUtils.randomAlphanumeric(512).toUpperCase();
+        String saltId = RandomStringUtils.randomAlphanumeric(8).toUpperCase();
+        String saltValue = RandomStringUtils.randomAlphanumeric(255).toUpperCase();
+        String authConfigId = RandomStringUtils.randomAlphanumeric(255).toUpperCase();
+
+        AccessToken accessToken = new AccessToken(tokenName, tokenValue, tokenDescription, false, new Date(), null);
+        accessToken.setSaltId(saltId);
+        accessToken.setSaltValue(saltValue);
+        accessToken.setAuthConfigId(authConfigId);
+        accessToken.setUsername("Bob");
+
+        return accessToken;
+    }
+
+    public static AccessToken accessTokenWithNameForUser(String tokenName, String username) {
+        AccessToken accessToken = accessTokenWithName(tokenName);
+        accessToken.setUsername(username);
+
+        return accessToken;
+    }
+}


@@ -433,4 +433,19 @@ public static class Users {
     public class ArtifactStores {
         public static final String SPA_BASE = "/admin/artifact_stores";
     }
+
+    public static class AccessToken {
+        public static final String BASE = "/api/access_token";
+        public static final String TOKEN_NAME = "/:token_name";
+        public static final String USERNAME = "/:username";
+        public static final String DOC = apiDocsUrl("#access_token");
+
+        public static String find() {
+            return BASE + TOKEN_NAME;
+        }
+
+        public static String name(String tokenName) {
+            return find().replaceAll(":token_name", tokenName);
+        }
+    }
 }


Removed News-Channel.
Fixed Cross-Server Channel Exploit.
Fixed Temporal-Voice setup.

@@ -864,19 +864,6 @@ public void onSelectMenuInteraction(@NotNull SelectMenuInteractionEvent event) {
                         event.editMessageEmbeds(embedBuilder.build()).setActionRow(new SelectMenuImpl("setupWelcomeMenu", "Select your Action", 1, 1, false, optionList)).queue();
                     }

-                    case "news" -> {
-                        optionList.add(SelectOption.of("Setup", "newsSetup"));
-
-                        if (Main.getInstance().getSqlConnector().getSqlWorker().isNewsSetup(event.getGuild().getId()))
-                            optionList.add(SelectOption.of("Delete", "newsDelete"));
-
-                        optionList.add(SelectOption.of("Back to Menu", "backToSetupMenu"));
-
-                        embedBuilder.setDescription("You can set up our own Ree6-News!\nBy setting up Ree6-News on a specific channel you will get a Message in the given Channel, when ever Ree6 gets an update!");
-
-                        event.editMessageEmbeds(embedBuilder.build()).setActionRow(new SelectMenuImpl("setupNewsMenu", "Select your Action", 1, 1, false, optionList)).queue();
-                    }
-
                     case "autorole" -> {
                         embedBuilder.setDescription("You can set up our own Autorole-System!\nYou can select Roles that Users should get upon joining the Server!");

@@ -886,7 +873,7 @@ public void onSelectMenuInteraction(@NotNull SelectMenuInteractionEvent event) {
                     case "tempvoice" -> {
                         optionList.add(SelectOption.of("Setup", "tempVoiceSetup"));

-                        if (Main.getInstance().getSqlConnector().getSqlWorker().isNewsSetup(event.getGuild().getId()))
+                        if (Main.getInstance().getSqlConnector().getSqlWorker().getEntity(TemporalVoicechannel.class, "SELECT * FROM TemporalVoicechannel WHERE GID=?", event.getGuild().getId()).isSuccess())
                             optionList.add(SelectOption.of("Delete", "tempVoiceDelete"));

                         optionList.add(SelectOption.of("Back to Menu", "backToSetupMenu"));
@@ -1220,62 +1207,6 @@ public void onSelectMenuInteraction(@NotNull SelectMenuInteractionEvent event) {

             }

-            case "setupNewsMenu" -> {
-
-                if (checkPerms(event.getMember(), event.getChannel())) {
-                    return;
-                }
-
-                EmbedBuilder embedBuilder = new EmbedBuilder(event.getMessage().getEmbeds().get(0));
-
-                List<SelectOption> optionList = new ArrayList<>();
-
-                switch (event.getInteraction().getValues().get(0)) {
-
-                    case "backToSetupMenu" -> sendDefaultChoice(event);
-
-                    case "newsSetup" -> {
-                        for (TextChannel channel : event.getGuild().getTextChannels()) {
-                            optionList.add(SelectOption.of(channel.getName(), channel.getId()));
-                        }
-
-                        embedBuilder.setDescription("Which Channel do you want to use as News-Channel?");
-
-                        event.editMessageEmbeds(embedBuilder.build()).setActionRow(new SelectMenuImpl("setupNewsChannel", "Select a Channel!", 1, 1, false, optionList)).queue();
-                    }
-
-                    default -> {
-                        if (event.getMessage().getEmbeds().isEmpty() || event.getMessage().getEmbeds().get(0) == null)
-                            return;
-
-                        embedBuilder.setDescription("You somehow selected a Invalid Option? Are you a Wizard?");
-                        event.editMessageEmbeds(embedBuilder.build()).queue();
-                    }
-                }
-            }
-
-            case "setupNewsChannel" -> {
-                if (checkPerms(event.getMember(), event.getChannel())) {
-                    return;
-                }
-
-                EmbedBuilder embedBuilder = new EmbedBuilder(event.getMessage().getEmbeds().get(0));
-
-                TextChannel textChannel = event.getGuild().getTextChannelById(event.getInteraction().getValues().get(0));
-
-                if (textChannel != null) {
-                    textChannel.createWebhook("Ree6-News").queue(webhook -> {
-                        Main.getInstance().getSqlConnector().getSqlWorker().setNewsWebhook(event.getGuild().getId(), webhook.getId(), webhook.getToken());
-                        embedBuilder.setDescription("Successfully changed the News-Channel, nice work!");
-                        embedBuilder.setColor(Color.GREEN);
-                        event.editMessageEmbeds(embedBuilder.build()).setComponents(new ArrayList<>()).queue();
-                    });
-                } else {
-                    embedBuilder.setDescription("The given Channel doesn't exists, how did you select it? Are you a Wizard?");
-                    event.editMessageEmbeds(embedBuilder.build()).queue();
-                }
-            }
-
             default -> {
                 if (event.getMessage().getEmbeds().isEmpty() || event.getMessage().getEmbeds().get(0) == null) return;

@@ -1298,7 +1229,6 @@ public void sendDefaultChoice(SelectMenuInteractionEvent event) {
         List<SelectOption> optionList = new ArrayList<>();
         optionList.add(SelectOption.of("Audit-Logging", "log"));
         optionList.add(SelectOption.of("Welcome-channel", "welcome"));
-        optionList.add(SelectOption.of("News-channel", "news"));
         optionList.add(SelectOption.of("Autorole", "autorole"));
         optionList.add(SelectOption.of("Temporal-Voice", "tempvoice"));
         optionList.add(SelectOption.of("Statistics", "statistics"));


ICU-20246 Fixing another integer overflow in number parsing.

@@ -239,7 +239,15 @@ protected BigDecimal bcdToBigDecimal() {
                 tempLong = tempLong * 10 + getDigitPos(shift);
             }
             BigDecimal result = BigDecimal.valueOf(tempLong);
-            result = result.scaleByPowerOfTen(scale);
+            try {
+                result = result.scaleByPowerOfTen(scale);
+            } catch (ArithmeticException e) {
+                if (e.getMessage().contains("Underflow")) {
+                    result = BigDecimal.ZERO;
+                } else {
+                    throw e;
+                }
+            }
             if (isNegative())
                 result = result.negate();
             return result;


@@ -1,13 +1,12 @@
 package com.fasterxml.jackson.databind.deser.dos;

 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;

 public class DeepArrayWrappingForDeser3582Test extends BaseMapTest
 {
-    // 23-Aug-2022, tatu: Before fix, fails with 5000
-    //    (but passes with 2000)
-//    private final static int TOO_DEEP_NESTING = 4999;
-    private final static int TOO_DEEP_NESTING = 999;
+    // 23-Aug-2022, tatu: Before fix, failed with 5000
+    private final static int TOO_DEEP_NESTING = 9999;

     private final ObjectMapper MAPPER = jsonMapperBuilder()
             .enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
@@ -16,8 +15,14 @@ public class DeepArrayWrappingForDeser3582Test extends BaseMapTest
     public void testArrayWrapping() throws Exception
     {
         final String doc = _nestedDoc(TOO_DEEP_NESTING, "[ ", "] ", "{}");
-        Point p = MAPPER.readValue(doc, Point.class);
-        assertNotNull(p);
+        try {
+            MAPPER.readValue(doc, Point.class);
+            fail("Should not pass");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Cannot deserialize");
+            verifyException(e, "nested JSON Array");
+            verifyException(e, "only single");
+        }
     }

     private String _nestedDoc(int nesting, String open, String close, String content) {


Fix temporary directory hijacking or temporary directory information disclosure (#1621)

This replaces and closes #1617.

Fix a vulnerability which allows a malicious user access files created in a temp directory if it was created using IOUtils.createTempDir.
The implementation has been changed to use the more secure Files.createTemporaryDirectory(). This has a side effect of making the second "suffix" argument no longer usable and it is now simply appended to the existing prefix.
Deprecating IOUtils.createTempDir(String, String) and adding a new single argument version instead which returns a Path.

This potentially exposed data created by CoordinateSortedPairInfoMaps on shared systems to a malicious users with access to the global temp directory.

It is recommended that user upgrade if they use CoordinateSortedPairInfoMap or IOUTils.createTempDir() in their code.

* vuln-fix: Temporary Directory Hijacking or Information Disclosure

This fixes either Temporary Directory Hijacking, or Temporary Directory Local Information Disclosure.

Weakness: CWE-379: Creation of Temporary File in Directory with Insecure Permissions
Severity: High
CVSSS: 7.3
Detection: CodeQL & OpenRewrite (https://public.moderne.io/recipes/org.openrewrite.java.security.UseFilesCreateTempDirectory)

Reported-by: Jonathan Leitschuh <Jonathan.Leitschuh@gmail.com>
Signed-off-by: Jonathan Leitschuh <Jonathan.Leitschuh@gmail.com>

Bug-tracker: https://github.com/JLLeitschuh/security-research/issues/10

Co-authored-by: Jonathan Leitschuh <Jonathan.Leitschuh@gmail.com>
Co-authored-by: Moderne <team@moderne.io>

@@ -312,7 +312,7 @@ public void testGetDefaultTmpDirPath() throws Exception {

     @Test(dataProvider = "fileNamesForDelete")
     public void testDeletePathLocal(final List<String> fileNames) throws Exception {
-        final File tmpDir = IOUtil.createTempDir("testDeletePath", "");
+        final Path tmpDir = IOUtil.createTempDir("testDeletePath");
         final List<Path> paths = createLocalFiles(tmpDir, fileNames);
         testDeletePaths(paths);
     }
@@ -341,7 +341,7 @@ public void testDeletePathJims(final List<String> fileNames) throws Exception {

     @Test(dataProvider = "fileNamesForDelete")
     public void testDeleteArrayPathLocal(final List<String> fileNames) throws Exception {
-        final File tmpDir = IOUtil.createTempDir("testDeletePath", "");
+        final Path tmpDir = IOUtil.createTempDir("testDeletePath");
         final List<Path> paths = createLocalFiles(tmpDir, fileNames);
         testDeletePathArray(paths);
     }
@@ -365,12 +365,11 @@ private static void testDeletePathArray(final List<Path> paths) {
         paths.forEach(p -> Assert.assertFalse(Files.exists(p)));
     }

-    private static List<Path> createLocalFiles(final File tmpDir, final List<String> fileNames) throws Exception {
+    private static List<Path> createLocalFiles(final Path tmpDir, final List<String> fileNames) throws Exception {
         final List<Path> paths = new ArrayList<>(fileNames.size());
         for (final String f: fileNames) {
-            final File file = new File(tmpDir, f);
-            Assert.assertTrue(file.createNewFile(), "failed to create test file" +file);
-            paths.add(file.toPath());
+            final Path file = Files.createFile(tmpDir.resolve(f));
+            paths.add(file);
         }
         return paths;
     }