You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2021/09/15 14:48:40 UTC

[brooklyn-server] branch master updated (397d3ac -> 4365221)

This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git.


    from 397d3ac  do a docker logout as part of jenkins build
     new 029f3b5  allow sensitive fields token and plaintext blocking option to be set from brooklyn properties
     new 373e575  return the sensitive field settings to the UI
     new 10096f2  block deployment if passwords in plain text
     new 5397739  only include 4 bytes (8 chars) of the md5 checksum
     new 74a9520  sanitize blueprint contents logged during deployment
     new fa13b25  fix tests for revised md5 sanitizer
     new a2f65bf  sanitizer more polymorphic, suppress values in maps (don't replace by xxxxxx)
     new 8ae26bd  sanitize the plan data on error
     new 638627f  block security in more places, fix some logic, add tests; and more sanitizing of secrets
     new af54075  add support for sanitizing ConfigBag
     new 4365221  This closes #1258

The 11 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../BrooklynComponentTemplateResolver.java         |   8 +
 .../creation/BrooklynEntityDecorationResolver.java |   4 +
 .../spi/creation/CampTypePlanTransformer.java      |   9 +-
 .../brooklyn/camp/brooklyn/ConfigYamlTest.java     |  42 +++++
 .../brooklyn/catalog/CatalogYamlEntityTest.java    |  31 +++-
 .../catalog/internal/BasicBrooklynCatalog.java     |  13 +-
 .../org/apache/brooklyn/core/config/Sanitizer.java | 188 ++++++++++++++++-----
 .../brooklyn/core/server/BrooklynServerConfig.java |   7 +-
 .../core/typereg/AbstractTypePlanTransformer.java  |  37 +++-
 .../core/typereg/TypePlanTransformers.java         |   3 +-
 .../org/apache/brooklyn/util/core/osgi/Osgis.java  |  23 ++-
 .../apache/brooklyn/core/config/SanitizerTest.java |  32 +++-
 .../org/apache/brooklyn/rest/api/ServerApi.java    |   3 +-
 .../rest/resources/ApplicationResource.java        |   5 +-
 .../brooklyn/rest/resources/ServerResource.java    |   8 +-
 .../rest/resources/EffectorUtilsRestTest.java      |   2 +-
 .../rest/resources/EntityConfigResourceTest.java   |   4 +-
 .../base/VanillaSoftwareProcessStreamsTest.java    |   6 +-
 18 files changed, 350 insertions(+), 75 deletions(-)

[brooklyn-server] 10/11: add support for sanitizing ConfigBag

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit af54075c0e7f6ff883243227695ca34851694764
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Wed Sep 15 11:39:20 2021 +0100

    add support for sanitizing ConfigBag
---
 .../brooklyn/core/catalog/internal/BasicBrooklynCatalog.java   | 10 +++++-----
 .../main/java/org/apache/brooklyn/core/config/Sanitizer.java   |  2 ++
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
index d5ad8b4..a739c0e 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
@@ -563,7 +563,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         }
         Map<?,?> catalogMetadata = getFirstAsMap(itemDef, "brooklyn.catalog").orNull();
         if (catalogMetadata==null)
-            log.warn("No `brooklyn.catalog` supplied in catalog request; using legacy mode for "+itemDef);
+            log.warn("No `brooklyn.catalog` supplied in catalog request; using legacy mode for "+Sanitizer.sanitize(itemDef));
         catalogMetadata = MutableMap.copyOf(catalogMetadata);
 
         collectCatalogItemsFromItemMetadataBlock(Yamls.getTextOfYamlAtPath(yaml, "brooklyn.catalog").getMatchedYamlTextOrWarn(), 
@@ -813,7 +813,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
                 // TODO we should let the plan transformer give us this
                 symbolicName = setFromItemIfUnset(symbolicName, itemAsMap, "template_name");
                 if (Strings.isBlank(symbolicName)) {
-                    log.error("Can't infer catalog item symbolicName from the following plan:\n" + sourceYaml);
+                    log.error("Can't infer catalog item symbolicName from the following plan:\n" + Sanitizer.sanitizeJsonTypes(sourceYaml));
                     throw new IllegalStateException("Can't infer catalog item symbolicName from catalog item metadata");
                 }
             }
@@ -871,7 +871,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
                 if (Strings.isNonBlank(symbolicName)) {
                     id = symbolicName;
                 } else {
-                    log.error("Can't infer catalog item id from the following plan:\n" + sourceYaml);
+                    log.error("Can't infer catalog item id from the following plan:\n" + Sanitizer.sanitizeJsonTypes(sourceYaml));
                     throw new IllegalStateException("Can't infer catalog item id from catalog item metadata");
                 }
             }
@@ -1687,7 +1687,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     
     @Override
     public List<? extends CatalogItem<?,?>> addItems(String yaml, ManagedBundle bundle, boolean forceUpdate) {
-        log.debug("Adding catalog item to "+mgmt+": "+yaml);
+        log.debug("Adding catalog item to "+mgmt+": "+Sanitizer.sanitizeJsonTypes(yaml));
         checkNotNull(yaml, "yaml");
         List<CatalogItemDtoAbstract<?, ?>> result = MutableList.of();
         collectCatalogItemsFromCatalogBomRoot("caller-supplied YAML", yaml, bundle, result, null, true, ImmutableMap.of(), 0, forceUpdate, true);
@@ -1718,7 +1718,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     
     @Override @Beta
     public void addTypesFromBundleBom(String yaml, ManagedBundle bundle, boolean forceUpdate, Map<RegisteredType, RegisteredType> result) {
-        log.debug("Catalog load, adding registered types to "+mgmt+" for bundle "+bundle+": "+yaml);
+        log.debug("Catalog load, adding registered types to "+mgmt+" for bundle "+bundle+": "+Sanitizer.sanitizeJsonTypes(yaml));
         checkNotNull(yaml, "yaml");
         if (result==null) result = MutableMap.of();
         collectCatalogItemsFromCatalogBomRoot("bundle BOM in "+bundle, yaml, bundle, null, result, false, MutableMap.of(), 0, forceUpdate, false);
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/Sanitizer.java b/core/src/main/java/org/apache/brooklyn/core/config/Sanitizer.java
index 515f069..f340a37 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/Sanitizer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/Sanitizer.java
@@ -269,6 +269,8 @@ public final class Sanitizer {
             return (K) applySet((Set) input, visited);
         } else if (input instanceof String) {
             return (K) sanitizeMultilineString((String) input);
+        } else if (input instanceof ConfigBag) {
+            return (K) ConfigBag.newInstance( applyMap( ((ConfigBag)input).getAllConfig(), visited) );
         } else {
             return (K) input;
         }

[brooklyn-server] 09/11: block security in more places, fix some logic, add tests; and more sanitizing of secrets

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 638627f9ad976561e69521f1dd3bc5f09aa7771a
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Wed Sep 15 09:49:24 2021 +0100

    block security in more places, fix some logic, add tests; and more sanitizing of secrets
---
 .../BrooklynComponentTemplateResolver.java         |  8 +++++
 .../creation/BrooklynEntityDecorationResolver.java |  4 +++
 .../spi/creation/CampTypePlanTransformer.java      | 34 +-----------------
 .../brooklyn/camp/brooklyn/ConfigYamlTest.java     | 42 ++++++++++++++++++++++
 .../brooklyn/catalog/CatalogYamlEntityTest.java    | 31 +++++++++++++++-
 .../catalog/internal/BasicBrooklynCatalog.java     |  3 +-
 .../org/apache/brooklyn/core/config/Sanitizer.java |  5 +--
 .../core/typereg/AbstractTypePlanTransformer.java  | 37 ++++++++++++++++++-
 .../apache/brooklyn/core/config/SanitizerTest.java | 14 +++-----
 .../rest/resources/EffectorUtilsRestTest.java      |  2 +-
 10 files changed, 132 insertions(+), 48 deletions(-)

diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
index 018e293..f8c9844 100644
--- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
+++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
@@ -63,6 +63,7 @@ import org.apache.brooklyn.core.mgmt.EntityManagementUtils;
 import org.apache.brooklyn.core.mgmt.ManagementContextInjectable;
 import org.apache.brooklyn.core.mgmt.classloading.JavaBrooklynClassLoadingContext;
 import org.apache.brooklyn.core.resolve.entity.EntitySpecResolver;
+import org.apache.brooklyn.core.typereg.AbstractTypePlanTransformer;
 import org.apache.brooklyn.core.typereg.RegisteredTypeLoadingContexts;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
@@ -283,6 +284,13 @@ public class BrooklynComponentTemplateResolver {
         new BrooklynEntityDecorationResolver.TagsResolver(yamlLoader).decorate(spec, attrs, encounteredRegisteredTypeIds);
 
         configureEntityConfig(spec, encounteredRegisteredTypeIds);
+
+        // check security. we probably used the catalog resolver which will have delegated to the transformer;
+        // but if we used the java resolver or one of the others, then:
+        // - we won't have all the right source tags, but that's okay
+        // - depth will come from the containing transformer and so be ignored here (which is fine, none of them should have children)
+        // - secure fields won't be scanned - need to fix that; just check again here on the spec, and above on the spec decorations
+        AbstractTypePlanTransformer.checkSecuritySensitiveFields(spec);
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
index eb89faa..438e9f4 100644
--- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
+++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
@@ -45,6 +45,7 @@ import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
 import org.apache.brooklyn.core.mgmt.BrooklynTags;
 import org.apache.brooklyn.core.objs.BasicSpecParameter;
 import org.apache.brooklyn.core.resolve.jackson.BeanWithTypeUtils;
+import org.apache.brooklyn.core.typereg.AbstractTypePlanTransformer;
 import org.apache.brooklyn.core.typereg.RegisteredTypeLoadingContexts;
 import org.apache.brooklyn.core.typereg.RegisteredTypes;
 import org.apache.brooklyn.util.collections.MutableList;
@@ -148,6 +149,9 @@ public abstract class BrooklynEntityDecorationResolver<DT> {
                 spec = classFactory.apply((Class<DTInterface>)type).parameters(BasicSpecParameter.fromClass(mgmt, type));
             }
             spec.configure(decoLoader.getConfigMap());
+
+            AbstractTypePlanTransformer.checkSecuritySensitiveFields(spec);
+
             return spec;
         }
 
diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampTypePlanTransformer.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampTypePlanTransformer.java
index 09c3834..84717fc 100644
--- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampTypePlanTransformer.java
+++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampTypePlanTransformer.java
@@ -109,10 +109,7 @@ public class CampTypePlanTransformer extends AbstractTypePlanTransformer {
     @Override
     protected AbstractBrooklynObjectSpec<?, ?> createSpec(RegisteredType type, RegisteredTypeLoadingContext context) throws Exception {
         try {
-            return decorateWithCommonTags(
-                    checkSecuritySensitiveFields(
-                            new CampResolver(mgmt, type, context).createSpec()
-                    ),
+            return decorateWithCommonTags(new CampResolver(mgmt, type, context).createSpec(),
                     type, null, null, prevHeadSpecSummary -> "Based on "+prevHeadSpecSummary);
 
         } catch (Exception e) {
@@ -140,35 +137,6 @@ public class CampTypePlanTransformer extends AbstractTypePlanTransformer {
         }
     }
 
-    @Beta
-    public static AbstractBrooklynObjectSpec<?,?> checkSecuritySensitiveFields(AbstractBrooklynObjectSpec<?,?> spec) {
-        if (Sanitizer.isSensitiveFieldsPlaintextBlocked()) {
-            // if blocking plaintext values, check them before instantiating
-            Predicate<Object> predicate = Sanitizer.IS_SECRET_PREDICATE;
-            spec.getConfig().forEach( (key,val) -> failOnInsecureValueForSensitiveNamedField(predicate, key.getName(), val) );
-            spec.getFlags().forEach( (key,val) -> failOnInsecureValueForSensitiveNamedField(predicate, key, val) );
-        }
-        return spec;
-    }
-
-    public static void failOnInsecureValueForSensitiveNamedField(Predicate<Object> tokens, String key, Object val) {
-        if (val instanceof BrooklynDslDeferredSupplier || val==null) {
-            // value allowed; key is irrelevant
-            return;
-        }
-        if (!tokens.apply(key)) {
-            // not a sensitive named key
-            return;
-        }
-
-        // sensitive named key
-        if (val instanceof String || Boxing.isPrimitiveOrBoxedClass(val.getClass()) || val instanceof Number) {
-            // value
-            throw new IllegalStateException("Insecure value supplied for '"+key+"'; external suppliers must be used here");
-        }
-        // complex values allowed
-    }
-
     @Override
     protected Object createBean(RegisteredType type, RegisteredTypeLoadingContext context) throws Exception {
         // beans not supported by this?
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigYamlTest.java
index 645155c..90d2feb 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigYamlTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigYamlTest.java
@@ -18,10 +18,15 @@
  */
 package org.apache.brooklyn.camp.brooklyn;
 
+import com.google.common.annotations.Beta;
 import java.util.Map;
+import org.apache.brooklyn.core.config.Sanitizer;
+import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.core.server.BrooklynServerConfig;
 import org.apache.brooklyn.util.internal.BrooklynSystemProperties;
 import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
 import org.apache.brooklyn.util.yaml.Yamls;
+import org.testng.Assert;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
@@ -552,4 +557,41 @@ public class ConfigYamlTest extends AbstractYamlTest {
                 e -> e.toString().contains("1.5"));
     }
 
+    @Test
+    public void testSensitiveConfigFailsIfConfigured() throws Exception {
+        Asserts.assertFailsWith(() -> {
+            return withSensitiveFieldsBlocked(() -> {
+                String yaml = Joiner.on("\n").join(
+                        "services:",
+                        "- type: org.apache.brooklyn.core.test.entity.TestEntity",
+                        "  brooklyn.config:",
+                        "    secret1: myval");
+
+                return createStartWaitAndLogApplication(yaml);
+            });
+        }, e -> {
+            Asserts.expectedFailureContainsIgnoreCase(e, "secret1");
+            Asserts.expectedFailureDoesNotContain(e, "myval");
+            return true;
+        });
+    }
+
+    @Beta
+    public static <T> T withSensitiveFieldsBlocked(Callable<T> r) throws Exception {
+        String oldValue =
+                //((BrooklynProperties) mgmt().getConfig()).put(BrooklynServerConfig.SENSITIVE_FIELDS_PLAINTEXT_BLOCKED, true);
+                System.setProperty(BrooklynServerConfig.SENSITIVE_FIELDS_PLAINTEXT_BLOCKED.getName(), "true");
+        Sanitizer.getSensitiveFieldsTokens(true);
+        Assert.assertTrue( Sanitizer.isSensitiveFieldsPlaintextBlocked() );
+
+        try {
+
+            return r.call();
+
+        } finally {
+            //((BrooklynProperties) mgmt().getConfig()).put(BrooklynServerConfig.SENSITIVE_FIELDS_PLAINTEXT_BLOCKED, oldValue);
+            System.setProperty(BrooklynServerConfig.SENSITIVE_FIELDS_PLAINTEXT_BLOCKED.getName(), oldValue!=null ? oldValue : "");
+            Sanitizer.getSensitiveFieldsTokens(true);
+        }
+    }
 }
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
index a33329d..ae01172 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
@@ -24,10 +24,13 @@ import java.util.Collection;
 import java.util.Set;
 
 import org.apache.brooklyn.api.objs.BrooklynObject;
+import org.apache.brooklyn.camp.brooklyn.ConfigYamlTest;
 import org.apache.brooklyn.core.entity.Dumper;
 import org.apache.brooklyn.core.mgmt.BrooklynTags;
 import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecSummary;
 import org.apache.brooklyn.util.collections.MutableSet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
@@ -66,7 +69,9 @@ import com.google.common.collect.Iterables;
 public class CatalogYamlEntityTest extends AbstractYamlTest {
 
     protected static final String TEST_VERSION_SNAPSHOT = TEST_VERSION + "-SNAPSHOT";
-    
+
+    private static final Logger LOG = LoggerFactory.getLogger(CatalogYamlEntityTest.class);
+
     @Test
     public void testAddCatalogItemVerySimple() throws Exception {
         String symbolicName = "my.catalog.app.id.load";
@@ -204,6 +209,30 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
     }
 
     @Test
+    public void testAddCatalogItemWithPlaintextValueForSensitiveNamedFieldBlocked() throws Exception {
+        String id = "sensitive-fields-1";
+        Asserts.assertFailsWith(() -> {
+            return ConfigYamlTest.withSensitiveFieldsBlocked(() -> {
+                addCatalogItems(
+                        "brooklyn.catalog:",
+                        "  name: " + id,
+                        "  itemType: entity",
+                        "  item:",
+                        "    type: "+ BasicEntity.class.getName(),
+                        "    brooklyn.config:",
+                        "      secret1: myval");
+                return null;
+            });
+        }, e -> {
+            LOG.info("Got error (as expected): "+e);
+            Asserts.expectedFailureContainsIgnoreCase(e, "secret1");
+            Asserts.expectedFailureDoesNotContain(e, "myval");
+            return true;
+        });
+    }
+
+
+    @Test
     public void testLaunchApplicationReferencingCatalog() throws Exception {
         String symbolicName = "myitem";
         addCatalogEntity(IdAndVersion.of(symbolicName, TEST_VERSION), TestEntity.class.getName());
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
index 71098f3..d5ad8b4 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
@@ -53,6 +53,7 @@ import org.apache.brooklyn.api.typereg.RegisteredTypeLoadingContext;
 import org.apache.brooklyn.core.catalog.CatalogPredicates;
 import org.apache.brooklyn.core.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
 import org.apache.brooklyn.core.config.ConfigUtils;
+import org.apache.brooklyn.core.config.Sanitizer;
 import org.apache.brooklyn.core.mgmt.BrooklynTags;
 import org.apache.brooklyn.core.mgmt.classloading.OsgiBrooklynClassLoadingContext;
 import org.apache.brooklyn.core.mgmt.ha.OsgiBundleInstallationResult;
@@ -1736,7 +1737,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         }
 
         // often in tests we don't have osgi and so it acts as follows
-        log.debug("Catalog load, adding registered types to "+mgmt+": "+catalogYaml);
+        log.debug("Catalog load, adding registered types to "+mgmt+": "+ Sanitizer.sanitizeMultilineString(catalogYaml));
         if (result==null) result = MutableMap.of();
         collectCatalogItemsFromCatalogBomRoot("unbundled catalog definition", catalogYaml, null, null, result, false, MutableMap.of(), 0, forceUpdate, true);
 
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/Sanitizer.java b/core/src/main/java/org/apache/brooklyn/core/config/Sanitizer.java
index 07881dc..515f069 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/Sanitizer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/Sanitizer.java
@@ -68,7 +68,8 @@ public final class Sanitizer {
     private static long LAST_SENSITIVE_FIELDS_CACHE_MILLIS = 60*1000;
 
     private static final void refreshProperties(Boolean refresh) {
-        if (Boolean.FALSE.equals(refresh) || LAST_SENSITIVE_FIELDS_LOAD_TIME + LAST_SENSITIVE_FIELDS_CACHE_MILLIS > System.currentTimeMillis()) {
+        if (Boolean.FALSE.equals(refresh) ||
+                (refresh==null && (LAST_SENSITIVE_FIELDS_LOAD_TIME + LAST_SENSITIVE_FIELDS_CACHE_MILLIS > System.currentTimeMillis()))) {
             return;
         }
         synchronized (Sanitizer.class) {
@@ -98,7 +99,7 @@ public final class Sanitizer {
                 }
 
                 if (plaintextBlocked==null) {
-                    StringSystemProperty plaintextSP = new StringSystemProperty(SENSITIVE_FIELDS_TOKENS.getName());
+                    StringSystemProperty plaintextSP = new StringSystemProperty(SENSITIVE_FIELDS_PLAINTEXT_BLOCKED.getName());
                     if (plaintextSP.isNonEmpty()) {
                         plaintextBlocked = TypeCoercions.coerce(plaintextSP.getValue(), SENSITIVE_FIELDS_PLAINTEXT_BLOCKED.getTypeToken());
                     }
diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java b/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
index 97832b7..2716770 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
@@ -18,7 +18,8 @@
  */
 package org.apache.brooklyn.core.typereg;
 
-import com.google.common.reflect.TypeToken;
+import com.google.common.annotations.Beta;
+import com.google.common.base.Predicate;
 import java.util.List;
 import java.util.function.Function;
 import java.util.function.Supplier;
@@ -29,12 +30,15 @@ import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.api.typereg.RegisteredTypeLoadingContext;
 import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
+import org.apache.brooklyn.core.config.Sanitizer;
 import org.apache.brooklyn.core.mgmt.BrooklynTags;
 import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecSummary;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.task.DeferredSupplier;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.javalang.Boxing;
 import org.apache.brooklyn.util.javalang.JavaClassNames;
 import org.apache.brooklyn.util.text.Strings;
 import org.slf4j.Logger;
@@ -206,6 +210,8 @@ public abstract class AbstractTypePlanTransformer implements BrooklynTypePlanTra
             addDepthTagsWhereMissing( ((EntitySpec<?>)spec).getChildren(), 1 );
         }
 
+        checkSecuritySensitiveFields(spec);
+
         return spec;
     }
 
@@ -219,4 +225,33 @@ public abstract class AbstractTypePlanTransformer implements BrooklynTypePlanTra
         });
     }
 
+    @Beta
+    public static AbstractBrooklynObjectSpec<?,?> checkSecuritySensitiveFields(AbstractBrooklynObjectSpec<?,?> spec) {
+        if (Sanitizer.isSensitiveFieldsPlaintextBlocked()) {
+            // if blocking plaintext values, check them before instantiating
+            Predicate<Object> predicate = Sanitizer.IS_SECRET_PREDICATE;
+            spec.getConfig().forEach( (key,val) -> failOnInsecureValueForSensitiveNamedField(predicate, key.getName(), val) );
+            spec.getFlags().forEach( (key,val) -> failOnInsecureValueForSensitiveNamedField(predicate, key, val) );
+        }
+        return spec;
+    }
+
+    public static void failOnInsecureValueForSensitiveNamedField(Predicate<Object> tokens, String key, Object val) {
+        if (val instanceof DeferredSupplier || val==null) {
+            // value allowed; key is irrelevant
+            return;
+        }
+        if (!tokens.apply(key)) {
+            // not a sensitive named key
+            return;
+        }
+
+        // sensitive named key
+        if (val instanceof String || Boxing.isPrimitiveOrBoxedClass(val.getClass()) || val instanceof Number) {
+            // value
+            throw new IllegalStateException("Insecure value supplied for '"+key+"'; external suppliers must be used here");
+        }
+        // complex values allowed
+    }
+
 }
diff --git a/core/src/test/java/org/apache/brooklyn/core/config/SanitizerTest.java b/core/src/test/java/org/apache/brooklyn/core/config/SanitizerTest.java
index b697e95..17c2215 100644
--- a/core/src/test/java/org/apache/brooklyn/core/config/SanitizerTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/config/SanitizerTest.java
@@ -18,21 +18,17 @@
  */
 package org.apache.brooklyn.core.config;
 
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
 import java.io.ByteArrayInputStream;
-import org.apache.brooklyn.util.stream.Streams;
-import org.apache.brooklyn.util.text.Strings;
-import static org.testng.Assert.assertEquals;
-
 import java.util.Map;
-
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.stream.Streams;
+import org.apache.brooklyn.util.text.Strings;
+import static org.testng.Assert.assertEquals;
 import org.testng.annotations.Test;
 
-import com.google.common.base.Functions;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-
 public class SanitizerTest {
 
     @Test
diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/EffectorUtilsRestTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/EffectorUtilsRestTest.java
index d7157fd..1ef0c34 100644
--- a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/EffectorUtilsRestTest.java
+++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/EffectorUtilsRestTest.java
@@ -89,7 +89,7 @@ public class EffectorUtilsRestTest extends BrooklynAppUnitTestSupport {
         assertEquals(
                 summary.getDescription(),
                 "Invoking effector resetPassword on "+TestEntityWithEffectors.class.getSimpleName()+":"+entity.getId().substring(0,4)
-                    +" with parameters {"+sensitiveField1+"=xxxxxxxx, "+sensitiveField2+"=xxxxxxxx}",
+                    +" with parameters {"+sensitiveField1+"=<suppressed> (MD5 hash: 5E70B271), "+sensitiveField2+"=<suppressed> (MD5 hash: 81DC9BDB)}",
                 "Task summary must hide sensitive parameters");
     }
 

[brooklyn-server] 11/11: This closes #1258

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 4365221d9c999004aba9f0a83cfdb3a829ada816
Merge: 397d3ac af54075
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Wed Sep 15 15:48:35 2021 +0100

    This closes #1258

 .../BrooklynComponentTemplateResolver.java         |   8 +
 .../creation/BrooklynEntityDecorationResolver.java |   4 +
 .../spi/creation/CampTypePlanTransformer.java      |   9 +-
 .../brooklyn/camp/brooklyn/ConfigYamlTest.java     |  42 +++++
 .../brooklyn/catalog/CatalogYamlEntityTest.java    |  31 +++-
 .../catalog/internal/BasicBrooklynCatalog.java     |  13 +-
 .../org/apache/brooklyn/core/config/Sanitizer.java | 188 ++++++++++++++++-----
 .../brooklyn/core/server/BrooklynServerConfig.java |   7 +-
 .../core/typereg/AbstractTypePlanTransformer.java  |  37 +++-
 .../core/typereg/TypePlanTransformers.java         |   3 +-
 .../org/apache/brooklyn/util/core/osgi/Osgis.java  |  23 ++-
 .../apache/brooklyn/core/config/SanitizerTest.java |  32 +++-
 .../org/apache/brooklyn/rest/api/ServerApi.java    |   3 +-
 .../rest/resources/ApplicationResource.java        |   5 +-
 .../brooklyn/rest/resources/ServerResource.java    |   8 +-
 .../rest/resources/EffectorUtilsRestTest.java      |   2 +-
 .../rest/resources/EntityConfigResourceTest.java   |   4 +-
 .../base/VanillaSoftwareProcessStreamsTest.java    |   6 +-
 18 files changed, 350 insertions(+), 75 deletions(-)

[brooklyn-server] 04/11: only include 4 bytes (8 chars) of the md5 checksum

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 5397739d8b6da371d1d0815f8b2104a0cb173f80
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Tue Sep 14 23:01:15 2021 +0100

    only include 4 bytes (8 chars) of the md5 checksum
---
 .../org/apache/brooklyn/core/config/Sanitizer.java    | 19 +++++++------------
 .../apache/brooklyn/core/config/SanitizerTest.java    |  2 +-
 2 files changed, 8 insertions(+), 13 deletions(-)

diff --git a/core/src/main/java/org/apache/brooklyn/core/config/Sanitizer.java b/core/src/main/java/org/apache/brooklyn/core/config/Sanitizer.java
index f123c20..e2e6c16 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/Sanitizer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/Sanitizer.java
@@ -18,33 +18,27 @@
  */
 package org.apache.brooklyn.core.config;
 
-import com.google.common.reflect.TypeToken;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
 import java.io.ByteArrayInputStream;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
 import java.util.stream.Collectors;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.server.BrooklynServerConfig;
 import org.apache.brooklyn.util.core.config.ConfigBag;
-
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
 import org.apache.brooklyn.util.core.osgi.Osgis;
-import org.apache.brooklyn.util.internal.BrooklynSystemProperties;
 import org.apache.brooklyn.util.internal.StringSystemProperty;
 import org.apache.brooklyn.util.stream.Streams;
 import org.apache.brooklyn.util.text.StringEscapes.BashStringEscapes;
 import org.apache.brooklyn.util.text.Strings;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.ServiceReference;
 
 public final class Sanitizer {
 
@@ -145,7 +139,8 @@ public final class Sanitizer {
     }
 
     public static String suppress(Object value) {
-        String md5Checksum = Streams.getMd5Checksum(new ByteArrayInputStream(("" + value).getBytes()));
+        // only include the first few chars so that malicious observers can't uniquely brute-force discover the source
+        String md5Checksum = Strings.maxlen(Streams.getMd5Checksum(new ByteArrayInputStream(("" + value).getBytes())), 8);
         return "<suppressed> (MD5 hash: " + md5Checksum + ")";
     }
 
diff --git a/core/src/test/java/org/apache/brooklyn/core/config/SanitizerTest.java b/core/src/test/java/org/apache/brooklyn/core/config/SanitizerTest.java
index 4b8e428..9b75034 100644
--- a/core/src/test/java/org/apache/brooklyn/core/config/SanitizerTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/config/SanitizerTest.java
@@ -84,7 +84,7 @@ public class SanitizerTest {
                 "  allowedOnNewLine"
             )), Strings.lines(
                 "public: password",
-                "private: <suppressed> (MD5 hash: " + hashPassword2 + ")",
+                "private: <suppressed> (MD5 hash: " + hashPassword2.substring(0, 8) + ")",
                 "private: ",
                 "  allowedOnNewLine"
             ));

[brooklyn-server] 03/11: block deployment if passwords in plain text

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 10096f2e796c2dc59bf331cb0978f3c4b28264b1
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Tue Sep 14 22:53:07 2021 +0100

    block deployment if passwords in plain text
---
 .../spi/creation/CampTypePlanTransformer.java      | 41 ++++++++++++++++++++--
 1 file changed, 39 insertions(+), 2 deletions(-)

diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampTypePlanTransformer.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampTypePlanTransformer.java
index c6d0cb3..09c3834 100644
--- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampTypePlanTransformer.java
+++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampTypePlanTransformer.java
@@ -18,6 +18,8 @@
  */
 package org.apache.brooklyn.camp.brooklyn.spi.creation;
 
+import com.google.common.annotations.Beta;
+import com.google.common.base.Predicate;
 import java.util.List;
 import java.util.Map;
 import java.util.function.BiFunction;
@@ -27,11 +29,14 @@ import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry.RegisteredTypeKind;
 import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.api.typereg.RegisteredType.TypeImplementationPlan;
 import org.apache.brooklyn.api.typereg.RegisteredTypeLoadingContext;
+import org.apache.brooklyn.camp.brooklyn.spi.dsl.BrooklynDslDeferredSupplier;
+import org.apache.brooklyn.core.config.Sanitizer;
 import org.apache.brooklyn.core.typereg.*;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
 
 import com.google.common.collect.ImmutableList;
+import org.apache.brooklyn.util.javalang.Boxing;
 
 public class CampTypePlanTransformer extends AbstractTypePlanTransformer {
 
@@ -104,8 +109,11 @@ public class CampTypePlanTransformer extends AbstractTypePlanTransformer {
     @Override
     protected AbstractBrooklynObjectSpec<?, ?> createSpec(RegisteredType type, RegisteredTypeLoadingContext context) throws Exception {
         try {
-            return decorateWithCommonTags(new CampResolver(mgmt, type, context).createSpec(), type, null, null,
-                    prevHeadSpecSummary -> "Based on "+prevHeadSpecSummary);
+            return decorateWithCommonTags(
+                    checkSecuritySensitiveFields(
+                            new CampResolver(mgmt, type, context).createSpec()
+                    ),
+                    type, null, null, prevHeadSpecSummary -> "Based on "+prevHeadSpecSummary);
 
         } catch (Exception e) {
             Exceptions.propagateIfFatal(e);
@@ -132,6 +140,35 @@ public class CampTypePlanTransformer extends AbstractTypePlanTransformer {
         }
     }
 
+    @Beta
+    public static AbstractBrooklynObjectSpec<?,?> checkSecuritySensitiveFields(AbstractBrooklynObjectSpec<?,?> spec) {
+        if (Sanitizer.isSensitiveFieldsPlaintextBlocked()) {
+            // if blocking plaintext values, check them before instantiating
+            Predicate<Object> predicate = Sanitizer.IS_SECRET_PREDICATE;
+            spec.getConfig().forEach( (key,val) -> failOnInsecureValueForSensitiveNamedField(predicate, key.getName(), val) );
+            spec.getFlags().forEach( (key,val) -> failOnInsecureValueForSensitiveNamedField(predicate, key, val) );
+        }
+        return spec;
+    }
+
+    public static void failOnInsecureValueForSensitiveNamedField(Predicate<Object> tokens, String key, Object val) {
+        if (val instanceof BrooklynDslDeferredSupplier || val==null) {
+            // value allowed; key is irrelevant
+            return;
+        }
+        if (!tokens.apply(key)) {
+            // not a sensitive named key
+            return;
+        }
+
+        // sensitive named key
+        if (val instanceof String || Boxing.isPrimitiveOrBoxedClass(val.getClass()) || val instanceof Number) {
+            // value
+            throw new IllegalStateException("Insecure value supplied for '"+key+"'; external suppliers must be used here");
+        }
+        // complex values allowed
+    }
+
     @Override
     protected Object createBean(RegisteredType type, RegisteredTypeLoadingContext context) throws Exception {
         // beans not supported by this?

[brooklyn-server] 08/11: sanitize the plan data on error

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 8ae26bd1618e4ac02e6d29a58e50b7f6ecfffb41
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Wed Sep 15 00:16:09 2021 +0100

    sanitize the plan data on error
---
 .../java/org/apache/brooklyn/core/typereg/TypePlanTransformers.java    | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/TypePlanTransformers.java b/core/src/main/java/org/apache/brooklyn/core/typereg/TypePlanTransformers.java
index b710d23..94d2b57 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/TypePlanTransformers.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/TypePlanTransformers.java
@@ -28,6 +28,7 @@ import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry;
 import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.api.typereg.RegisteredTypeLoadingContext;
 import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
+import org.apache.brooklyn.core.config.Sanitizer;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.exceptions.PropagatedRuntimeException;
@@ -159,7 +160,7 @@ public class TypePlanTransformers {
             String prefix = Strings.isBlank(type.getPlan().getPlanFormat()) ? "Invalid plan" : "Invalid '"+type.getPlan().getPlanFormat()+"' plan";
             if (transformers.isEmpty()) {
                 result = new UnsupportedTypePlanException(prefix + "; format could not be recognized, none of the available transformers "+all(mgmt)+" support "+
-                    (type.getId()!=null ? type.getId() : "plan:\n"+type.getPlan().getPlanData()));
+                    (type.getId()!=null ? type.getId() : "plan:\n"+ Sanitizer.sanitizeJsonTypes(type.getPlan().getPlanData())));
             } else {
                 result = new UnsupportedTypePlanException(prefix + "; potentially applicable transformers "+transformers+" do not support it, and other available transformers "+
 //                    // the removeAll call below won't work until "all" caches it

[brooklyn-server] 02/11: return the sensitive field settings to the UI

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 373e575d89dd27107e975d8c77a5f816635e049c
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Tue Sep 14 22:52:37 2021 +0100

    return the sensitive field settings to the UI
---
 .../src/main/java/org/apache/brooklyn/rest/api/ServerApi.java     | 3 ++-
 .../java/org/apache/brooklyn/rest/resources/ServerResource.java   | 8 +++++++-
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/ServerApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/ServerApi.java
index 096a928..868f0f2 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/ServerApi.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/ServerApi.java
@@ -108,7 +108,8 @@ public interface ServerApi {
     @GET
     @Path("/up/extended")
     @ApiOperation(value = "Returns extended server-up information, a map including up (/up), shuttingDown (/shuttingDown), healthy (/healthy), and ha (/ha/states) (qv)"
-        + "; also forces a session, so a useful general-purpose call for a UI client to do when starting")
+            + " as well as selected settings such as sensitive field treatment"
+            + "; also forces a session, so a useful general-purpose call for a UI client to do when starting")
     public Map<String,Object> getUpExtended();
 
     @GET
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ServerResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ServerResource.java
index 835ff39..1706b80 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ServerResource.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ServerResource.java
@@ -48,6 +48,7 @@ import org.apache.brooklyn.api.mgmt.rebind.mementos.BrooklynMementoRawData;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.BrooklynVersion;
 import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.config.Sanitizer;
 import org.apache.brooklyn.core.entity.Attributes;
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.entity.StartableApplication;
@@ -395,7 +396,12 @@ public class ServerResource extends AbstractBrooklynRestResource implements Serv
             "up", isUp(),
             "shuttingDown", isShuttingDown(),
             "healthy", isHealthy(),
-            "ha", getHighAvailabilityPlaneStates());
+            "ha", getHighAvailabilityPlaneStates(),
+            "brooklyn.security.sensitive.fields",
+                MutableMap.of(
+                        "tokens", Sanitizer.getSensitiveFieldsTokens(),
+                        "plaintext.blocked", Sanitizer.isSensitiveFieldsPlaintextBlocked()
+                ));
     }
 
     @Override

[brooklyn-server] 06/11: fix tests for revised md5 sanitizer

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit fa13b259671401e0f070bf2a2d7906f3ef02b052
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Tue Sep 14 23:24:27 2021 +0100

    fix tests for revised md5 sanitizer
---
 .../apache/brooklyn/rest/resources/EntityConfigResourceTest.java    | 4 ++--
 .../entity/software/base/VanillaSoftwareProcessStreamsTest.java     | 6 +++---
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/EntityConfigResourceTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/EntityConfigResourceTest.java
index 16dc385..d11b283 100644
--- a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/EntityConfigResourceTest.java
+++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/EntityConfigResourceTest.java
@@ -150,7 +150,7 @@ public class EntityConfigResourceTest extends BrooklynRestResourceTest {
                         .get(String.class);
 
         assertEquals(getSecretWithQueryParams.apply(""), JavaStringEscapes.wrapJavaString("hello-world"));
-        assertEquals(getSecretWithQueryParams.apply("suppressSecrets=true"), JavaStringEscapes.wrapJavaString("<suppressed> (MD5 hash: 2095312189753DE6AD47DFE20CBE97EC)"));
+        assertEquals(getSecretWithQueryParams.apply("suppressSecrets=true"), JavaStringEscapes.wrapJavaString("<suppressed> (MD5 hash: 20953121)"));
         assertEquals(getSecretWithQueryParams.apply("skipResolution=true"), JavaStringEscapes.wrapJavaString(HELLO_WORLD_DSL));
         assertEquals(getSecretWithQueryParams.apply("suppressSecrets=true&skipResolution=true"), JavaStringEscapes.wrapJavaString(HELLO_WORLD_DSL));
     }
@@ -165,7 +165,7 @@ public class EntityConfigResourceTest extends BrooklynRestResourceTest {
                     .get(String.class);
 
         assertEquals(getSecretWithQueryParams.apply(""), "hello-world");
-        assertEquals(getSecretWithQueryParams.apply("suppressSecrets=true"), "<suppressed> (MD5 hash: 2095312189753DE6AD47DFE20CBE97EC)");
+        assertEquals(getSecretWithQueryParams.apply("suppressSecrets=true"), "<suppressed> (MD5 hash: 20953121)");
         assertEquals(getSecretWithQueryParams.apply("skipResolution=true"), HELLO_WORLD_DSL);
         assertEquals(getSecretWithQueryParams.apply("suppressSecrets=true&skipResolution=true"), HELLO_WORLD_DSL);
     }
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessStreamsTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessStreamsTest.java
index ff375e6..4b9dfe0 100644
--- a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessStreamsTest.java
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessStreamsTest.java
@@ -71,7 +71,7 @@ public class VanillaSoftwareProcessStreamsTest extends AbstractSoftwareProcessSt
         // Prepare expected environment variables, secret names are keys with values that should be masked in env stream
         Map<String, String> expectedEnv = new ImmutableMap.Builder<String, String>()
                 .put("KEY1", "VAL1")
-                .putAll(Sanitizer.SECRET_NAMES.stream().collect(Collectors.toMap(item -> item, item -> item)))
+                .putAll(Sanitizer.DEFAULT_SENSITIVE_FIELDS_TOKENS.stream().collect(Collectors.toMap(item -> item, item -> item)))
                 .build();
 
         // Create configuration
@@ -115,9 +115,9 @@ public class VanillaSoftwareProcessStreamsTest extends AbstractSoftwareProcessSt
         // Calculate MD5 hash for all keys that are expected to be masked and verify them displayed masked in env stream
         Map<String, String> expectedMaskedEnv = new ImmutableMap.Builder<String, String>()
                 .put("KEY1", "VAL1") // this key must appear unmasked, it is not in the list of SECRET NAMES to mask
-                .putAll(Sanitizer.SECRET_NAMES.stream().collect(Collectors.toMap(
+                .putAll(Sanitizer.DEFAULT_SENSITIVE_FIELDS_TOKENS.stream().collect(Collectors.toMap(
                         item -> item, // key and expected masked (suppressed) value for a SECRET NAME with MD5 hash
-                        item -> "<suppressed> (MD5 hash: " + Streams.getMd5Checksum(new ByteArrayInputStream(item.getBytes())) + ")")))
+                        Sanitizer::suppress)))
                 .build();
         assertEnvStream(entity, expectedMaskedEnv);
     }

[brooklyn-server] 07/11: sanitizer more polymorphic, suppress values in maps (don't replace by xxxxxx)

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit a2f65bfa8e7acfd5ae3845097266201a14bc17c6
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Wed Sep 15 00:15:24 2021 +0100

    sanitizer more polymorphic, suppress values in maps (don't replace by xxxxxx)
---
 .../org/apache/brooklyn/core/config/Sanitizer.java | 62 +++++++++++++---------
 .../apache/brooklyn/core/config/SanitizerTest.java |  2 +-
 2 files changed, 37 insertions(+), 27 deletions(-)

diff --git a/core/src/main/java/org/apache/brooklyn/core/config/Sanitizer.java b/core/src/main/java/org/apache/brooklyn/core/config/Sanitizer.java
index e2e6c16..07881dc 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/Sanitizer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/Sanitizer.java
@@ -32,6 +32,7 @@ import java.util.stream.Collectors;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.server.BrooklynServerConfig;
+import org.apache.brooklyn.util.collections.MutableSet;
 import org.apache.brooklyn.util.core.config.ConfigBag;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
 import org.apache.brooklyn.util.core.osgi.Osgis;
@@ -181,6 +182,11 @@ public final class Sanitizer {
         }
     }
 
+    /** applies to strings, sets, lists, maps */
+    public static <K> K sanitizeJsonTypes(K obj) {
+        return Sanitizer.newInstance().apply(obj);
+    }
+
     private static class IsSecretPredicate implements Predicate<Object> {
         @Override
         public boolean apply(Object name) {
@@ -231,7 +237,7 @@ public final class Sanitizer {
     }
 
     static <K> Map<K, Object> sanitize(Map<K, ?> input, Set<Object> visited) {
-        return (input == null) ? null : newInstance().apply(input, visited);
+        return (input == null) ? null : (Map) newInstance().apply(input, visited);
     }
     
     private Predicate<Object> predicate;
@@ -240,35 +246,41 @@ public final class Sanitizer {
         predicate = sanitizingNeededCheck;
     }
 
-    public <K> Map<K, Object> apply(Map<K, ?> input) {
-        return (input == null) ? null : apply(input, Sets.newHashSet());
+    public <K> K apply(K input) {
+        return (input == null) ? null : apply(input, Sets.newLinkedHashSet());
+    }
+
+    private <K> K apply(K input, Set<Object> visited) {
+        if (input==null) return null;
+
+        // avoid endless loops if object is self-referential
+        if (visited.contains(System.identityHashCode(input))) {
+            return input;
+        }
+
+        visited.add(System.identityHashCode(input));
+
+        if (input instanceof Map) {
+            return (K) applyMap((Map)input, visited);
+        } else if (input instanceof List) {
+            return (K) applyList((List<?>) input, visited);
+        } else if (input instanceof Set) {
+            return (K) applySet((Set) input, visited);
+        } else if (input instanceof String) {
+            return (K) sanitizeMultilineString((String) input);
+        } else {
+            return (K) input;
+        }
     }
 
-    private <K> Map<K, Object> apply(Map<K, ?> input, Set<Object> visited) {
+    private <K> Map<K, Object> applyMap(Map<K, ?> input, Set<Object> visited) {
         Map<K, Object> result = Maps.newLinkedHashMap();
         for (Map.Entry<K, ?> e : input.entrySet()) {
             if (e.getKey() != null && predicate.apply(e.getKey())){
-                result.put(e.getKey(), "xxxxxxxx");
+                result.put(e.getKey(), suppress(e.getValue()));
                 continue;
             } 
-            
-            // need to compare object reference, not equality since we may miss some.
-            // not a perfect identifier, but very low probability of collision.
-            if (visited.contains(System.identityHashCode(e.getValue()))) {
-                result.put(e.getKey(), e.getValue());
-                continue;
-            }
-
-            visited.add(System.identityHashCode(e.getValue()));
-            if (e.getValue() instanceof Map) {
-                result.put(e.getKey(), apply((Map<?, ?>) e.getValue(), visited));
-            } else if (e.getValue() instanceof List) {
-                result.put(e.getKey(), applyList((List<?>) e.getValue(), visited));
-            } else if (e.getValue() instanceof Set) {
-                result.put(e.getKey(), applySet((Set<?>) e.getValue(), visited));
-            } else {
-                result.put(e.getKey(), e.getValue());
-            }
+            result.put(e.getKey(), apply(e.getValue(), visited));
         }
         return result;
     }
@@ -301,8 +313,6 @@ public final class Sanitizer {
     }
     
     private Set<Object> applySet(Set<?> input, Set<Object> visited) {
-        Set<Object> result = Sets.newLinkedHashSet();
-        result.addAll(applyIterable(input, visited));
-        return result;
+        return MutableSet.copyOf(applyIterable(input, visited));
     }
 }
diff --git a/core/src/test/java/org/apache/brooklyn/core/config/SanitizerTest.java b/core/src/test/java/org/apache/brooklyn/core/config/SanitizerTest.java
index 9b75034..b697e95 100644
--- a/core/src/test/java/org/apache/brooklyn/core/config/SanitizerTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/config/SanitizerTest.java
@@ -49,7 +49,7 @@ public class SanitizerTest {
                 .put("mykey", "myval")
                 .build();
         Map<String, Object> expected = MutableMap.<String, Object>builder()
-                .putAll(Maps.transformValues(map, Functions.constant("xxxxxxxx")))
+                .putAll(Maps.transformValues(map, Sanitizer::suppress))
                 .put("mykey", "myval")
                 .build();
         

[brooklyn-server] 01/11: allow sensitive fields token and plaintext blocking option to be set from brooklyn properties

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 029f3b5b09213a05eb250adc04d06b20eb36fbe5
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Tue Sep 14 22:13:04 2021 +0100

    allow sensitive fields token and plaintext blocking option to be set from brooklyn properties
---
 .../org/apache/brooklyn/core/config/Sanitizer.java | 112 ++++++++++++++++++++-
 .../brooklyn/core/server/BrooklynServerConfig.java |   7 +-
 .../org/apache/brooklyn/util/core/osgi/Osgis.java  |  23 +++--
 .../apache/brooklyn/core/config/SanitizerTest.java |  20 ++++
 4 files changed, 150 insertions(+), 12 deletions(-)

diff --git a/core/src/main/java/org/apache/brooklyn/core/config/Sanitizer.java b/core/src/main/java/org/apache/brooklyn/core/config/Sanitizer.java
index 825ec8f..f123c20 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/Sanitizer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/Sanitizer.java
@@ -18,11 +18,17 @@
  */
 package org.apache.brooklyn.core.config;
 
+import com.google.common.reflect.TypeToken;
 import java.io.ByteArrayInputStream;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import java.util.stream.Collectors;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.server.BrooklynServerConfig;
 import org.apache.brooklyn.util.core.config.ConfigBag;
 
 import com.google.common.base.Predicate;
@@ -30,16 +36,26 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.core.osgi.Osgis;
+import org.apache.brooklyn.util.internal.BrooklynSystemProperties;
+import org.apache.brooklyn.util.internal.StringSystemProperty;
 import org.apache.brooklyn.util.stream.Streams;
 import org.apache.brooklyn.util.text.StringEscapes.BashStringEscapes;
+import org.apache.brooklyn.util.text.Strings;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
 
 public final class Sanitizer {
 
+    public static final ConfigKey<List<String>> SENSITIVE_FIELDS_TOKENS = BrooklynServerConfig.SENSITIVE_FIELDS_TOKENS;
+    public static final ConfigKey<Boolean> SENSITIVE_FIELDS_PLAINTEXT_BLOCKED = BrooklynServerConfig.SENSITIVE_FIELDS_PLAINTEXT_BLOCKED;
+
     /**
      * Names that, if they appear anywhere in an attribute/config/field
      * indicates that it may be private, so should not be logged etc.
      */
-    public static final List<String> SECRET_NAMES = ImmutableList.of(
+    public static final List<String> DEFAULT_SENSITIVE_FIELDS_TOKENS = ImmutableList.of(
             "password", 
             "passwd", 
             "credential", 
@@ -48,6 +64,77 @@ public final class Sanitizer {
             "access.cert", 
             "access.key");
 
+    /** @deprecated since 1.1 use {@link #DEFAULT_SENSITIVE_FIELDS_TOKENS} or {@link #getSensitiveFieldsTokens(Boolean)} */
+    public static final List<String> SECRET_NAMES = DEFAULT_SENSITIVE_FIELDS_TOKENS;
+
+    private static List<String> LAST_SENSITIVE_FIELDS_TOKENS = null;
+    private static Boolean LAST_SENSITIVE_FIELDS_PLAINTEXT_BLOCKED = null;
+    private static long LAST_SENSITIVE_FIELDS_LOAD_TIME = -1;
+    private static long LAST_SENSITIVE_FIELDS_CACHE_MILLIS = 60*1000;
+
+    private static final void refreshProperties(Boolean refresh) {
+        if (Boolean.FALSE.equals(refresh) || LAST_SENSITIVE_FIELDS_LOAD_TIME + LAST_SENSITIVE_FIELDS_CACHE_MILLIS > System.currentTimeMillis()) {
+            return;
+        }
+        synchronized (Sanitizer.class) {
+            if (LAST_SENSITIVE_FIELDS_LOAD_TIME < 0) {
+                refresh = true;
+            }
+            if (refresh == null) {
+                refresh = LAST_SENSITIVE_FIELDS_LOAD_TIME + LAST_SENSITIVE_FIELDS_CACHE_MILLIS < System.currentTimeMillis();
+            }
+            if (refresh) {
+                ManagementContext mgmt = Osgis.getManagementContext();
+                List<String> tokens = null;
+                Boolean plaintextBlocked = null;
+                if (mgmt != null) {
+                    tokens = mgmt.getConfig().getConfig(SENSITIVE_FIELDS_TOKENS);
+                    plaintextBlocked = mgmt.getConfig().getConfig(SENSITIVE_FIELDS_PLAINTEXT_BLOCKED);
+                }
+
+                if (tokens==null) {
+                    StringSystemProperty tokensSP = new StringSystemProperty(SENSITIVE_FIELDS_TOKENS.getName());
+                    if (tokensSP.isNonEmpty()) {
+                        tokens = TypeCoercions.coerce(tokensSP.getValue(), SENSITIVE_FIELDS_TOKENS.getTypeToken());
+                    }
+                }
+                if (tokens==null) {
+                    tokens = DEFAULT_SENSITIVE_FIELDS_TOKENS;
+                }
+
+                if (plaintextBlocked==null) {
+                    StringSystemProperty plaintextSP = new StringSystemProperty(SENSITIVE_FIELDS_TOKENS.getName());
+                    if (plaintextSP.isNonEmpty()) {
+                        plaintextBlocked = TypeCoercions.coerce(plaintextSP.getValue(), SENSITIVE_FIELDS_PLAINTEXT_BLOCKED.getTypeToken());
+                    }
+                }
+                if (plaintextBlocked==null) {
+                    plaintextBlocked = Boolean.FALSE;
+                }
+
+                LAST_SENSITIVE_FIELDS_TOKENS = tokens.stream().map(String::toLowerCase).collect(Collectors.toList());
+                LAST_SENSITIVE_FIELDS_PLAINTEXT_BLOCKED = plaintextBlocked;
+                LAST_SENSITIVE_FIELDS_LOAD_TIME = System.currentTimeMillis();
+            }
+        }
+    }
+
+    public static List<String> getSensitiveFieldsTokens() {
+        return getSensitiveFieldsTokens(null);
+    }
+    public static List<String> getSensitiveFieldsTokens(Boolean refresh) {
+        refreshProperties(refresh);
+        return LAST_SENSITIVE_FIELDS_TOKENS;
+    }
+
+    public static boolean isSensitiveFieldsPlaintextBlocked() {
+        return isSensitiveFieldsPlaintextBlocked(null);
+    }
+    public static boolean isSensitiveFieldsPlaintextBlocked(Boolean refresh) {
+        refreshProperties(refresh);
+        return LAST_SENSITIVE_FIELDS_PLAINTEXT_BLOCKED;
+    }
+
     public static final Predicate<Object> IS_SECRET_PREDICATE = new IsSecretPredicate();
 
     public static Object suppressIfSecret(Object key, Object value) {
@@ -69,6 +156,23 @@ public final class Sanitizer {
         return value;
     }
 
+    /** replace any line matching  'secret: value' or 'secret = value' with eg 'secret = <suppressed> [MD5 hash: ... ]' */
+    public static String sanitizeMultilineString(String input) {
+        if (input==null) return null;
+        return Arrays.stream(input.split("\n")).map(line -> {
+            Integer first = Arrays.asList(line.indexOf("="), line.indexOf(":")).stream().filter(x -> x>0).min(Integer::compare).orElse(null);
+            if (first!=null) {
+                String key = line.substring(0, first);
+                if (IS_SECRET_PREDICATE.apply(key)) {
+                    String value = line.substring(first+1);
+                    return key + line.substring(first, first+1) +
+                            (Strings.isBlank(value) ? value : " " + suppress(value.trim()));
+                }
+            }
+            return line;
+        }).collect(Collectors.joining("\n"));
+    }
+
     public static void sanitizeMapToString(Map<?, ?> env, StringBuilder sb) {
         if (env!=null) {
             for (Map.Entry<?, ?> kv : env.entrySet()) {
@@ -87,13 +191,13 @@ public final class Sanitizer {
         public boolean apply(Object name) {
             if (name == null) return false;
             String lowerName = name.toString().toLowerCase();
-            for (String secretName : SECRET_NAMES) {
+            for (String secretName : getSensitiveFieldsTokens()) {
                 if (lowerName.contains(secretName))
                     return true;
             }
             return false;
         }
-    };
+    }
 
     /**
      * Kept only in case this anonymous inner class has made it into any persisted state.
@@ -107,7 +211,7 @@ public final class Sanitizer {
         public boolean apply(Object name) {
             if (name == null) return false;
             String lowerName = name.toString().toLowerCase();
-            for (String secretName : SECRET_NAMES) {
+            for (String secretName : getSensitiveFieldsTokens()) {
                 if (lowerName.contains(secretName))
                     return true;
             }
diff --git a/core/src/main/java/org/apache/brooklyn/core/server/BrooklynServerConfig.java b/core/src/main/java/org/apache/brooklyn/core/server/BrooklynServerConfig.java
index 4ded90c..058ab78 100644
--- a/core/src/main/java/org/apache/brooklyn/core/server/BrooklynServerConfig.java
+++ b/core/src/main/java/org/apache/brooklyn/core/server/BrooklynServerConfig.java
@@ -170,5 +170,10 @@ public class BrooklynServerConfig {
     public static Maybe<URI> getBrooklynWebUri(ManagementContext mgmt) {
         return mgmt.getManagementNodeUri();
     }
-    
+
+    public static final ConfigKey<List<String>> SENSITIVE_FIELDS_TOKENS = ConfigKeys.newConfigKey(new TypeToken<List<String>>() {}, "brooklyn.security.sensitive.fields.tokens",
+            "List of tokens which get treated as sensitive-named fields and suppressed in many places");
+    public static final ConfigKey<Boolean> SENSITIVE_FIELDS_PLAINTEXT_BLOCKED = ConfigKeys.newBooleanConfigKey("brooklyn.security.sensitive.fields.plaintext.blocked",
+            "Whether plaintext values for sensitive-named fields are blocked");
+
 }
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/osgi/Osgis.java b/core/src/main/java/org/apache/brooklyn/util/core/osgi/Osgis.java
index 6d7651a..cc254f1 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/osgi/Osgis.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/osgi/Osgis.java
@@ -18,6 +18,11 @@
  */
 package org.apache.brooklyn.util.core.osgi;
 
+import com.google.common.annotations.Beta;
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -28,8 +33,8 @@ import java.util.Comparator;
 import java.util.List;
 import java.util.jar.JarInputStream;
 import java.util.jar.Manifest;
-
 import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.core.ResourceUtils;
 import org.apache.brooklyn.util.exceptions.Exceptions;
@@ -45,18 +50,13 @@ import org.apache.brooklyn.util.text.Strings;
 import org.apache.brooklyn.util.text.VersionComparator;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceReference;
 import org.osgi.framework.Version;
 import org.osgi.framework.VersionRange;
 import org.osgi.framework.launch.Framework;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.annotations.Beta;
-import com.google.common.base.Joiner;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-
 /** 
  * utilities for working with osgi.
  * osgi support is in early days (June 2014) so this class is beta, subject to change,
@@ -407,4 +407,13 @@ public class Osgis {
         Bundle bundle = org.osgi.framework.FrameworkUtil.getBundle(clazz);
         return Optional.fromNullable(bundle);
     }
+
+    public static ManagementContext getManagementContext() {
+        Bundle bundle = Osgis.getBundleOf(Osgis.class).orNull();
+        if (bundle == null) return null;
+        ServiceReference<ManagementContext> svc = bundle.getBundleContext().getServiceReference(ManagementContext.class);
+        if (svc == null) return null;
+        return bundle.getBundleContext().getService(svc);
+    }
+
 }
diff --git a/core/src/test/java/org/apache/brooklyn/core/config/SanitizerTest.java b/core/src/test/java/org/apache/brooklyn/core/config/SanitizerTest.java
index 2ca4883..4b8e428 100644
--- a/core/src/test/java/org/apache/brooklyn/core/config/SanitizerTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/config/SanitizerTest.java
@@ -18,6 +18,9 @@
  */
 package org.apache.brooklyn.core.config;
 
+import java.io.ByteArrayInputStream;
+import org.apache.brooklyn.util.stream.Streams;
+import org.apache.brooklyn.util.text.Strings;
 import static org.testng.Assert.assertEquals;
 
 import java.util.Map;
@@ -70,4 +73,21 @@ public class SanitizerTest {
         assertEquals(Sanitizer.sanitize((Map<?,?>)null), null);
         assertEquals(Sanitizer.newInstance().apply((Map<?,?>)null), null);
     }
+
+    @Test
+    public void testSanitizeMultiline() throws Exception {
+        String hashPassword2 = "6CB75F652A9B52798EB6CF2201057C73";
+        assertEquals(Sanitizer.sanitizeMultilineString(Strings.lines(
+                "public: password",
+                "private: password2",
+                "private: ",
+                "  allowedOnNewLine"
+            )), Strings.lines(
+                "public: password",
+                "private: <suppressed> (MD5 hash: " + hashPassword2 + ")",
+                "private: ",
+                "  allowedOnNewLine"
+            ));
+        assertEquals(hashPassword2, Streams.getMd5Checksum(new ByteArrayInputStream(("password2").getBytes())));
+    }
 }

[brooklyn-server] 05/11: sanitize blueprint contents logged during deployment

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 74a9520e802c89e27f94c98728b225b33219991c
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Tue Sep 14 23:07:27 2021 +0100

    sanitize blueprint contents logged during deployment
---
 .../java/org/apache/brooklyn/rest/resources/ApplicationResource.java | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
index 3b0d94a..1ebff8f 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
@@ -23,6 +23,7 @@ import static javax.ws.rs.core.Response.created;
 import static javax.ws.rs.core.Response.status;
 import static javax.ws.rs.core.Response.Status.ACCEPTED;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.config.Sanitizer;
 import static org.apache.brooklyn.rest.util.WebResourceUtils.serviceAbsoluteUriBuilder;
 
 import java.net.URI;
@@ -444,7 +445,7 @@ public class ApplicationResource extends AbstractBrooklynRestResource implements
             }
         }
 
-        log.debug("Creating app from yaml:\n{}", yaml);
+        log.debug("Creating app from yaml:\n{}", Sanitizer.sanitizeMultilineString(yaml));
 
         EntitySpec<? extends Application> spec;
         try {
@@ -498,7 +499,7 @@ public class ApplicationResource extends AbstractBrooklynRestResource implements
             CreationResult<Application,Void> result = EntityManagementUtils.start(app);
             waitForStart(app, Duration.millis(100));
 
-            log.info("Launched from plan: " + planForReference + " -> " + app + " (" + result.task() + ")");
+            log.info("Launched from plan: " + Sanitizer.sanitizeMultilineString(planForReference) + " -> " + app + " (" + result.task() + ")");
 
             URI ref = serviceAbsoluteUriBuilder(ui.getBaseUriBuilder(), ApplicationApi.class, "get").build(app.getApplicationId());
             ResponseBuilder response = created(ref);