From a9b5737eed24aba58e6eec824bf25957da90361f Mon Sep 17 00:00:00 2001 From: carmine Date: Sun, 1 Sep 2024 14:14:06 -0400 Subject: [PATCH 1/2] fixes #63 string oob with ignore malformed --- pom.xml | 2 +- .../dotenv/internal/DotenvParser.java | 60 +++++++++++++++---- src/test/java/tests/DotenvTests.java | 12 ++++ src/test/resources/unclosed.quote/.env | 3 + 4 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 src/test/resources/unclosed.quote/.env diff --git a/pom.xml b/pom.xml index ae21e3f..1b12f04 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ io.github.cdimascio dotenv-java - 3.0.1 + 3.0.2 diff --git a/src/main/java/io/github/cdimascio/dotenv/internal/DotenvParser.java b/src/main/java/io/github/cdimascio/dotenv/internal/DotenvParser.java index 38ad017..d1dc881 100644 --- a/src/main/java/io/github/cdimascio/dotenv/internal/DotenvParser.java +++ b/src/main/java/io/github/cdimascio/dotenv/internal/DotenvParser.java @@ -34,15 +34,16 @@ public class DotenvParser { private final boolean throwIfMissing; private final boolean throwIfMalformed; - private final Predicate isWhiteSpace = s -> matches(WHITE_SPACE_REGEX, s); - private final Predicate isComment = s -> s.startsWith("#") || s.startsWith("////"); - private final Predicate isQuoted = s -> s.startsWith("\"") && s.endsWith("\""); + private static final Predicate isWhiteSpace = s -> matches(WHITE_SPACE_REGEX, s); + private static final Predicate isComment = s -> s.startsWith("#") || s.startsWith("////"); + private static final Predicate isQuoted = s -> s.length() > 1 && s.startsWith("\"") && s.endsWith("\""); private final Function parseLine = s -> matchEntry(DOTENV_ENTRY_REGEX, s); /** * Creates a dotenv parser - * @param reader the dotenv reader - * @param throwIfMissing if true, throws when the .env file is missing + * + * @param reader the dotenv reader + * @param throwIfMissing if true, throws when the .env file is missing * @param throwIfMalformed if true, throws when the .env file is malformed */ public DotenvParser(final DotenvReader reader, final boolean throwIfMissing, final boolean throwIfMalformed) { @@ -53,6 +54,7 @@ public DotenvParser(final DotenvReader reader, final boolean throwIfMissing, fin /** * (Internal) parse the .env file + * * @return a list of DotenvEntries * @throws DotenvException if an error is encountered during the parse */ @@ -77,8 +79,13 @@ private void addNewEntry(final List entries, final String line) { return; } + if (!QuotedStringValidator.isValid(entry.getValue())) { + if (throwIfMalformed) + throw new DotenvException("Malformed entry, unmatched quotes " + line); + return; + } final var key = entry.getKey(); - final var value = normalizeValue(entry.getValue()); + final var value = QuotedStringValidator.stripQuotes(entry.getValue()); entries.add(new DotenvEntry(key, value)); } @@ -94,11 +101,6 @@ private List lines() throws DotenvException { } } - private String normalizeValue(final String value) { - final var tr = value.trim(); - return isQuoted.test(tr) ? tr.substring(1, value.length() - 1) : tr; - } - private static boolean matches(final Pattern regex, final String text) { return regex.matcher(text).matches(); } @@ -114,4 +116,40 @@ private static DotenvEntry matchEntry(final Pattern regex, final String text) { private static boolean isBlank(String s) { return s == null || s.trim().isEmpty(); } + + /** + * Internal: Validates quoted strings + */ + private static class QuotedStringValidator { + private static boolean isValid(String input) { + final var s = input.trim(); + if (!s.startsWith("\"") && !s.endsWith("\"")) { + // not quoted, its valid + return true; + } + if (input.length() == 1 || !(s.startsWith("\"") && s.endsWith("\""))) { + // doesn't start and end with quote + return false; + } + // remove start end quote + var content = s.substring(1, s.length() - 1); + var quotePattern = Pattern.compile("\""); + var matcher = quotePattern.matcher(content); + + // Check for unescaped quotes + while (matcher.find()) { + int quoteIndex = matcher.start(); + // Check if the quote is escaped + if (quoteIndex == 0 || content.charAt(quoteIndex - 1) != '\\') { + return false; // unescaped quote found + } + } + return true; // No unescaped quotes found + } + private static String stripQuotes(String input) { + var tr = input.trim(); + return isQuoted.test(tr) ? tr.substring(1, input.length() - 1) : tr; + } + } } + diff --git a/src/test/java/tests/DotenvTests.java b/src/test/java/tests/DotenvTests.java index b6f6314..6f89936 100644 --- a/src/test/java/tests/DotenvTests.java +++ b/src/test/java/tests/DotenvTests.java @@ -101,4 +101,16 @@ void configureWithIgnoreMissingAndMalformed() { assertNotNull(dotenv.get("PATH")); } + + @Test + void malformedWithUncloseQuote() { + final var dotenv = Dotenv.configure() + .directory("/unclosed.quote") + .ignoreIfMalformed() + .load(); + + assertNull(dotenv.get("FOO")); + assertEquals(dotenv.get("BAR"), "bar"); + assertNull(dotenv.get("BAZ"), "baz"); + } } diff --git a/src/test/resources/unclosed.quote/.env b/src/test/resources/unclosed.quote/.env new file mode 100644 index 0000000..5a1c3f6 --- /dev/null +++ b/src/test/resources/unclosed.quote/.env @@ -0,0 +1,3 @@ +FOO=" +BAR="bar" +BAZ="baz From 83f7860a521af548c9f6887630447353d4794479 Mon Sep 17 00:00:00 2001 From: carmine Date: Sun, 1 Sep 2024 14:40:01 -0400 Subject: [PATCH 2/2] update README and CONTRIBUTING --- CONTRIBUTING.md | 15 +++++++++------ README.md | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8144235..6104d7d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,11 +50,6 @@ Build sources and javadoc mvn clean test jacoco:report package ``` -Generate signed artifacts locally -```shell -mvn verify -P release-sign-artifacts -DperformRelease=true -``` - ### Publish to Maven Central Deploy @@ -68,6 +63,12 @@ When first publishing to staging repos, you most close and release from OSS Sona - press the `release` button #### Artifacts upload + +- Generate signed artifacts locally + ```shell + mvn verify -P release-sign-artifacts -DperformRelease=true + ``` + - Upload change log ```shell gh release create v3.0.1 -F CHANGELOG.md @@ -77,6 +78,7 @@ When first publishing to staging repos, you most close and release from OSS Sona ```shell gh release upload target/*.jar.asc --clobber ``` + ### Publish to Github Packages _Note: This step can only be run by maintainers._ @@ -141,6 +143,7 @@ https://docs.github.com/en/packages/using-github-packages-with-your-projects-eco ### OpenSSF Security Scorecard - Get Analysis Result: https://api.securityscorecards.dev/#/results/getResult + - enter platform=github.com, org=cdimascio, repo=dotenv-java - Step Security - Secure Your Repo Analysis + auto PR - https://app.stepsecurity.io/securerepo - Step Security - For Repo - https://app.stepsecurity.io/github/cdimascio/actions/dashboard - +- OpenSSF Badget Analysis https://www.bestpractices.dev/en/projects/9407 diff --git a/README.md b/README.md index 70a0276..795025a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 🗝️ dotenv-java -![Build Status](https://github.com/cloudsimplus/cloudsimplus/actions/workflows/build.yml/badge.svg) [![Maven Central](https://img.shields.io/maven-central/v/io.github.cdimascio/dotenv-java.svg?label=Maven%20Central)](https://search.maven.org/artifact/io.github.cdimascio/dotenv-java) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/66b8195f0da544f1ad9ed1352c0ea66f)](https://app.codacy.com/app/cdimascio/dotenv-java?utm_source=github.com&utm_medium=referral&utm_content=cdimascio/dotenv-java&utm_campaign=Badge_Grade_Dashboard) ![](https://img.shields.io/ossf-scorecard/github.com/cdimascio/dotenv-java?label=openssf%20scorecard&style=flat) [![](https://img.shields.io/gitter/room/cdimascio-oss/community?color=%23eb205a)](https://gitter.im/cdimascio-oss/community) [![All Contributors](https://img.shields.io/badge/all_contributors-6-orange.svg?style=flat-square)](#contributors-) [![](https://img.shields.io/badge/doc-javadoc-blue)](https://cdimascio.github.io/dotenv-java/docs/javadoc/index.html) ![](https://img.shields.io/badge/license-Apache%202.0-blue.svg) +![Build Status](https://github.com/cloudsimplus/cloudsimplus/actions/workflows/build.yml/badge.svg) [![Maven Central](https://img.shields.io/maven-central/v/io.github.cdimascio/dotenv-java.svg?label=Maven%20Central)](https://search.maven.org/artifact/io.github.cdimascio/dotenv-java) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/66b8195f0da544f1ad9ed1352c0ea66f)](https://app.codacy.com/app/cdimascio/dotenv-java?utm_source=github.com&utm_medium=referral&utm_content=cdimascio/dotenv-java&utm_campaign=Badge_Grade_Dashboard) ![](https://img.shields.io/ossf-scorecard/github.com/cdimascio/dotenv-java?label=openssf%20scorecard&style=flat) [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/9407/badge)](https://www.bestpractices.dev/projects/9407) [![](https://img.shields.io/gitter/room/cdimascio-oss/community?color=%23eb205a)](https://gitter.im/cdimascio-oss/community) [![All Contributors](https://img.shields.io/badge/all_contributors-6-orange.svg?style=flat-square)](#contributors-) [![](https://img.shields.io/badge/doc-javadoc-blue)](https://cdimascio.github.io/dotenv-java/docs/javadoc/index.html) ![](https://img.shields.io/badge/license-Apache%202.0-blue.svg) A no-dependency, pure Java port of the Ruby dotenv project. Load environment variables from a `.env` file.