Skip to content

Commit 4e23eda

Browse files
cdimasciocarmine
and
carmine
authoredSep 1, 2024··
fixes #63 string oob with ignore malformed (#74)
Co-authored-by: carmine <carmine@everco.ai>
1 parent d216732 commit 4e23eda

File tree

4 files changed

+65
-12
lines changed

4 files changed

+65
-12
lines changed
 

‎pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
<groupId>io.github.cdimascio</groupId>
1313
<artifactId>dotenv-java</artifactId>
14-
<version>3.0.1</version>
14+
<version>3.0.2</version>
1515

1616
<licenses>
1717
<license>

‎src/main/java/io/github/cdimascio/dotenv/internal/DotenvParser.java

+49-11
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,16 @@ public class DotenvParser {
3434
private final boolean throwIfMissing;
3535
private final boolean throwIfMalformed;
3636

37-
private final Predicate<String> isWhiteSpace = s -> matches(WHITE_SPACE_REGEX, s);
38-
private final Predicate<String> isComment = s -> s.startsWith("#") || s.startsWith("////");
39-
private final Predicate<String> isQuoted = s -> s.startsWith("\"") && s.endsWith("\"");
37+
private static final Predicate<String> isWhiteSpace = s -> matches(WHITE_SPACE_REGEX, s);
38+
private static final Predicate<String> isComment = s -> s.startsWith("#") || s.startsWith("////");
39+
private static final Predicate<String> isQuoted = s -> s.length() > 1 && s.startsWith("\"") && s.endsWith("\"");
4040
private final Function<String, DotenvEntry> parseLine = s -> matchEntry(DOTENV_ENTRY_REGEX, s);
4141

4242
/**
4343
* Creates a dotenv parser
44-
* @param reader the dotenv reader
45-
* @param throwIfMissing if true, throws when the .env file is missing
44+
*
45+
* @param reader the dotenv reader
46+
* @param throwIfMissing if true, throws when the .env file is missing
4647
* @param throwIfMalformed if true, throws when the .env file is malformed
4748
*/
4849
public DotenvParser(final DotenvReader reader, final boolean throwIfMissing, final boolean throwIfMalformed) {
@@ -53,6 +54,7 @@ public DotenvParser(final DotenvReader reader, final boolean throwIfMissing, fin
5354

5455
/**
5556
* (Internal) parse the .env file
57+
*
5658
* @return a list of DotenvEntries
5759
* @throws DotenvException if an error is encountered during the parse
5860
*/
@@ -77,8 +79,13 @@ private void addNewEntry(final List<DotenvEntry> entries, final String line) {
7779
return;
7880
}
7981

82+
if (!QuotedStringValidator.isValid(entry.getValue())) {
83+
if (throwIfMalformed)
84+
throw new DotenvException("Malformed entry, unmatched quotes " + line);
85+
return;
86+
}
8087
final var key = entry.getKey();
81-
final var value = normalizeValue(entry.getValue());
88+
final var value = QuotedStringValidator.stripQuotes(entry.getValue());
8289
entries.add(new DotenvEntry(key, value));
8390
}
8491

@@ -94,11 +101,6 @@ private List<String> lines() throws DotenvException {
94101
}
95102
}
96103

97-
private String normalizeValue(final String value) {
98-
final var tr = value.trim();
99-
return isQuoted.test(tr) ? tr.substring(1, value.length() - 1) : tr;
100-
}
101-
102104
private static boolean matches(final Pattern regex, final String text) {
103105
return regex.matcher(text).matches();
104106
}
@@ -114,4 +116,40 @@ private static DotenvEntry matchEntry(final Pattern regex, final String text) {
114116
private static boolean isBlank(String s) {
115117
return s == null || s.trim().isEmpty();
116118
}
119+
120+
/**
121+
* Internal: Validates quoted strings
122+
*/
123+
private static class QuotedStringValidator {
124+
private static boolean isValid(String input) {
125+
final var s = input.trim();
126+
if (!s.startsWith("\"") && !s.endsWith("\"")) {
127+
// not quoted, its valid
128+
return true;
129+
}
130+
if (input.length() == 1 || !(s.startsWith("\"") && s.endsWith("\""))) {
131+
// doesn't start and end with quote
132+
return false;
133+
}
134+
// remove start end quote
135+
var content = s.substring(1, s.length() - 1);
136+
var quotePattern = Pattern.compile("\"");
137+
var matcher = quotePattern.matcher(content);
138+
139+
// Check for unescaped quotes
140+
while (matcher.find()) {
141+
int quoteIndex = matcher.start();
142+
// Check if the quote is escaped
143+
if (quoteIndex == 0 || content.charAt(quoteIndex - 1) != '\\') {
144+
return false; // unescaped quote found
145+
}
146+
}
147+
return true; // No unescaped quotes found
148+
}
149+
private static String stripQuotes(String input) {
150+
var tr = input.trim();
151+
return isQuoted.test(tr) ? tr.substring(1, input.length() - 1) : tr;
152+
}
153+
}
117154
}
155+

‎src/test/java/tests/DotenvTests.java

+12
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,16 @@ void configureWithIgnoreMissingAndMalformed() {
101101

102102
assertNotNull(dotenv.get("PATH"));
103103
}
104+
105+
@Test
106+
void malformedWithUncloseQuote() {
107+
final var dotenv = Dotenv.configure()
108+
.directory("/unclosed.quote")
109+
.ignoreIfMalformed()
110+
.load();
111+
112+
assertNull(dotenv.get("FOO"));
113+
assertEquals(dotenv.get("BAR"), "bar");
114+
assertNull(dotenv.get("BAZ"), "baz");
115+
}
104116
}
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
FOO="
2+
BAR="bar"
3+
BAZ="baz

0 commit comments

Comments
 (0)
Please sign in to comment.