Skip to content

Commit

Permalink
[crypt] Better isEncrypted implementation to avoid to decrypt not enc…
Browse files Browse the repository at this point in the history
…rypted values
  • Loading branch information
rmannibucau committed Jul 5, 2023
1 parent 0b5337f commit 0074eae
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
import java.security.SecureRandom;
import java.time.Instant;
import java.util.Base64;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.IntStream;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
Expand All @@ -41,7 +41,7 @@
// mimic and is compatible with maven password encryption for now
// todo: support stronger algorithms
public class SimpleCodec implements Codec {
private static final Pattern ENCRYPTED_PATTERN = Pattern.compile(".*?[^\\\\]?\\{(.*?[^\\\\])\\}.*");
private static final Pattern ENCRYPTED_PATTERN = Pattern.compile(".*?[^\\\\]?\\{(?<value>.*?[^\\\\])\\}.*", Pattern.DOTALL);
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
private static final String KEY_ALGORITHM = "AES";
private static final String DIGEST_ALGORITHM = "SHA-256";
Expand All @@ -59,7 +59,31 @@ public SimpleCodec(final SimpleCodecConfiguration configuration) {

@Override
public boolean isEncrypted(final String value) {
return ENCRYPTED_PATTERN.matcher(value).matches();
final var matcher = ENCRYPTED_PATTERN.matcher(value);
if (!matcher.matches()) {
return false;
}

final var bare = matcher.group("value");
if (value.startsWith("${env.") || value.startsWith("${")) {
return true;
}

// mime decoder - the RFC behind - ignores unknown chars, this is not bad but means next test can be a false positive
if (IntStream.range(0, bare.length())
.map(bare::charAt)
.anyMatch(i -> !(Character.isAlphabetic(i) || Character.isDigit(i) ||
i == '\r' || i == '\n' ||
i == '/' || i == '+' || i == '='))) {
return false;
}

try {
Base64.getMimeDecoder().decode(bare);
return true;
} catch (final RuntimeException re) {
return false;
}
}

@Override
Expand Down Expand Up @@ -123,17 +147,17 @@ public String encrypt(final String input) {
allEncryptedBytes[8] = padLen;

System.arraycopy(encryptedBytes, 0, allEncryptedBytes, 8 + 1, len);
return '{' + Base64.getEncoder().encodeToString(allEncryptedBytes) + '}';
return '{' + Base64.getMimeEncoder().encodeToString(allEncryptedBytes) + '}';
}

@Override
public String decrypt(final String value) {
final Matcher matcher = ENCRYPTED_PATTERN.matcher(value);
final var matcher = ENCRYPTED_PATTERN.matcher(value);
if (!matcher.matches() && !matcher.find()) {
return value; // not encrypted, just use it
}

final String bare = matcher.group(1);
final var bare = matcher.group("value");
if (value.startsWith("${env.")) {
final String key = bare.substring("env.".length());
return ofNullable(System.getenv(key)).orElseGet(() -> System.getProperty(bare));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,12 @@
public class CryptPropertiesMojo extends BaseCryptPropertiesMojo {
@Override
protected void transform(final Codec codec, final Properties from, final Properties to) {
to.putAll(from.stringPropertyNames().stream()
.collect(toMap(identity(), e -> {
final var value = from.getProperty(e, "");
if (codec.isEncrypted(value)) {
return value;
}
return codec.encrypt(value);
})));
to.putAll(from.stringPropertyNames().stream().collect(toMap(identity(), e -> {
final var value = from.getProperty(e, "");
if (codec.isEncrypted(value)) {
return value;
}
return codec.encrypt(value);
})));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@
public class DecryptPropertiesMojo extends BaseCryptPropertiesMojo {
@Override
protected void transform(final Codec codec, final Properties from, final Properties to) {
to.putAll(from.stringPropertyNames().stream()
.collect(toMap(identity(), e -> codec.decrypt(from.getProperty(e, "")))));
to.putAll(from.stringPropertyNames().stream().collect(toMap(identity(), e -> {
final var property = from.getProperty(e, "");
if (codec.isEncrypted(property)) {
return codec.decrypt(property);
}
return property;
})));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
Expand Down Expand Up @@ -52,7 +53,16 @@ public void load(final BufferedReader reader, final boolean usePlainProperties)
public Properties toWorkProperties() {
return lines.isEmpty() ? properties : lines.stream()
.filter(it -> it.key != null)
.collect(Collector.of(Properties::new, (p, l) -> p.setProperty(l.key, l.value), (p1, p2) -> {
.collect(Collector.of(Properties::new, (p, l) -> {
final var tmp = new Properties();
try (final var r = new StringReader(l.key + '=' + l.value)) { // unescape
tmp.load(r);
} catch (final IOException e) {
throw new IllegalStateException(e);
}
final var key = tmp.stringPropertyNames().iterator().next();
p.setProperty(key, tmp.getProperty(key));
}, (p1, p2) -> {
p1.putAll(p2);
return p1;
}));
Expand Down Expand Up @@ -103,15 +113,15 @@ private void doLoad(final BufferedReader reader) {
break;
case ':':
case '=':
var value = line.substring(i + 1);
if (value.endsWith("\\") && iterator.hasNext()) {
value += '\n' + iterator.next();
final var value = new StringBuilder(line.substring(i + 1));
while (value.toString().endsWith("\\") && iterator.hasNext()) {
value.append('\n').append(iterator.next());
}
lines.add(new Line(
String.join("\n", comments),
line.substring(0, i).strip(),
Character.toString(line.charAt(i)),
value.strip()));
value.toString()));
comments.clear();
found = true;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,30 @@
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Properties;

import static org.junit.jupiter.api.Assertions.assertEquals;

class LightPropertiesTest {
@Test
void unescape() throws IOException {
final var props = new LightProperties(new SystemStreamLog());
try (final var r = new BufferedReader(new StringReader("" +
"escaped = yes\\=true\n" +
"split = foo\\\n" +
" bar\\\n" +
" du\\=mmy\n" +
"multilines = foo\\n\\\n" +
" bar\\n\\\n" +
" du\\=mmy"))) {
props.load(r, false);
}
final var loaded = props.toWorkProperties();
assertEquals("yes=true", loaded.getProperty("escaped"));
assertEquals("foobardu=mmy", loaded.getProperty("split"));
assertEquals("foo\nbar\ndu=mmy", loaded.getProperty("multilines"));
}

@Test
void rewrite() throws IOException {
final var props = new LightProperties(new SystemStreamLog());
Expand Down

0 comments on commit 0074eae

Please sign in to comment.