Skip to content

Commit 7365162

Browse files
committed
Added Timeout and length check for pattern matching
1 parent f19f44a commit 7365162

File tree

2 files changed

+54
-36
lines changed

2 files changed

+54
-36
lines changed

plugins/amazonq/chat/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/FeatureDevSessionContextTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class FeatureDevSessionContextTest : FeatureDevTestBase(HeavyJavaCodeInsightTest
8585
"settings.gradle",
8686
"build.gradle",
8787
"gradle/wrapper/gradle-wrapper.properties",
88-
"builder/GetTestBuilder.java", //check for false positives
88+
"builder/GetTestBuilder.java", // check for false positives
8989
".aws-sam/build/function1",
9090
".gem/specs.rb",
9191
"archive.zip",

plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/FeatureDevSessionContext.kt

Lines changed: 53 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import kotlinx.coroutines.flow.channelFlow
1515
import kotlinx.coroutines.launch
1616
import kotlinx.coroutines.runBlocking
1717
import kotlinx.coroutines.withContext
18+
import kotlinx.coroutines.withTimeout
1819
import org.apache.commons.codec.digest.DigestUtils
1920
import org.apache.commons.io.FileUtils
2021
import software.aws.toolkits.jetbrains.core.coroutines.getCoroutineBgContext
@@ -97,7 +98,7 @@ class FeatureDevSessionContext(val project: Project, val maxProjectSizeBytes: Lo
9798
addAll(additionalGitIgnoreRules.map { convertGitIgnorePatternToRegex(it) })
9899
addAll(parseGitIgnore())
99100
}.mapNotNull { pattern ->
100-
runCatching { Regex(pattern) }.getOrNull()
101+
runCatching { pattern?.let { Regex(it) } }.getOrNull()
101102
}
102103
} catch (e: Exception) {
103104
emptyList()
@@ -136,7 +137,13 @@ class FeatureDevSessionContext(val project: Project, val maxProjectSizeBytes: Lo
136137
// entries against them by adding a trailing /.
137138
// TODO: Add unit tests for gitignore matching
138139
val relative = if (path.startsWith(projectRootPath.toString())) Paths.get(path).relativeTo(projectRootPath) else path
139-
async { pattern.matches("$relative/") }
140+
async {
141+
try {
142+
withTimeout(REGEX_TIMEOUT_MS) { pattern.matches("$relative/") }
143+
} catch (e: Exception) {
144+
false
145+
}
146+
}
140147
}
141148
}
142149

@@ -240,7 +247,7 @@ class FeatureDevSessionContext(val project: Project, val maxProjectSizeBytes: Lo
240247
tempFilePath
241248
}
242249

243-
private fun parseGitIgnore(): Set<String> {
250+
private fun parseGitIgnore(): Set<String?> {
244251
if (!gitIgnoreFile.exists()) {
245252
return emptySet()
246253
}
@@ -252,46 +259,57 @@ class FeatureDevSessionContext(val project: Project, val maxProjectSizeBytes: Lo
252259
}
253260

254261
// gitignore patterns are not regex, method update needed.
255-
fun convertGitIgnorePatternToRegex(pattern: String): String = pattern
256-
// Escape special regex characters except * and ?
257-
.replace(".", "\\.")
258-
.replace("+", "\\+")
259-
.replace("(", "\\(")
260-
.replace(")", "\\)")
261-
.replace("[", "\\[")
262-
.replace("]", "\\]")
263-
.replace("{", "\\{")
264-
.replace("}", "\\}")
265-
.replace(",", "\\,")
266-
.replace("^", "\\^")
267-
.replace("$", "\\$")
268-
.replace("|", "\\|")
269-
// Convert gitignore glob patterns to regex
270-
.replace("**", ".*?") // Match any directory depth
271-
.replace("*", "[^/]*?") // Match any character except path separator
272-
.replace("?", "[^/]") // Match single character except path separator
273-
.let { pattern ->
274-
when {
275-
// If pattern starts with '/', anchor it to the start of the path
276-
pattern.startsWith("/") -> "^${pattern.substring(1)}"
277-
// If pattern doesn't start with '/', it can match anywhere in the path
278-
else -> "(?:^|.*/?)$pattern"
279-
}
262+
fun convertGitIgnorePatternToRegex(pattern: String): String? {
263+
// Skip invalid patterns for length check
264+
if (pattern.length > MAX_PATTERN_LENGTH) {
265+
return null
280266
}
281-
.let { pattern ->
282-
when {
283-
// If pattern ends with '/', it should match directories
284-
pattern.endsWith("/") -> "$pattern.*"
285-
// Otherwise match exactly or with a trailing slash for directories
286-
else -> "$pattern(?:/.*)?$"
267+
return pattern
268+
// Escape special regex characters except * and ?
269+
.replace(".", "\\.")
270+
.replace("+", "\\+")
271+
.replace("(", "\\(")
272+
.replace(")", "\\)")
273+
.replace("[", "\\[")
274+
.replace("]", "\\]")
275+
.replace("{", "\\{")
276+
.replace("}", "\\}")
277+
.replace(",", "\\,")
278+
.replace("^", "\\^")
279+
.replace("$", "\\$")
280+
.replace("|", "\\|")
281+
// Convert gitignore glob patterns to regex
282+
.replace("**", ".*?") // Match any directory depth
283+
.replace("*", "[^/]*?") // Match any character except path separator
284+
.replace("?", "[^/]") // Match single character except path separator
285+
.let { pattern ->
286+
when {
287+
// If pattern starts with '/', anchor it to the start of the path
288+
pattern.startsWith("/") -> "^${pattern.substring(1)}"
289+
// If pattern doesn't start with '/', it can match anywhere in the path
290+
else -> "(?:^|.*/?)$pattern"
291+
}
287292
}
288-
}
293+
.let { pattern ->
294+
when {
295+
// If pattern ends with '/', it should match directories
296+
pattern.endsWith("/") -> "$pattern.*"
297+
// Otherwise match exactly or with a trailing slash for directories
298+
else -> "$pattern(?:/.*)?$"
299+
}
300+
}
301+
}
289302

290303
var selectedSourceFolder: VirtualFile
291304
set(newRoot) {
292305
_selectedSourceFolder = newRoot
293306
}
294307
get() = _selectedSourceFolder
308+
309+
companion object {
310+
private const val MAX_PATTERN_LENGTH = 256 // Maximum allowed pattern length
311+
private const val REGEX_TIMEOUT_MS = 100L // Timeout for regex operations in milliseconds
312+
}
295313
}
296314

297315
data class ZipCreationResult(val payload: File, val checksum: String, val contentLength: Long)

0 commit comments

Comments
 (0)