Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ object CommandParser {

// Write text patterns
PatternInfo("writeText1", Regex("(?i)\\bwriteText\\([\"']([^\"']+)[\"']\\)"), { match -> Command.WriteText(match.groupValues[1]) }, CommandTypeEnum.WRITE_TEXT),
PatternInfo("termux1", Regex("(?i)\\bTermux\\([\"']([^\"']+)[\"']\\)"), { match -> Command.TermuxCommand(match.groupValues[1]) }, CommandTypeEnum.TERMUX_COMMAND),
PatternInfo("termux1", Regex("""(?i)\bTermux\(\s*(["'])((?:\\.|(?!\1\s*\)).)*)\1\s*\)"""), { match -> Command.TermuxCommand(match.groupValues[2]) }, CommandTypeEnum.TERMUX_COMMAND),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 Security Vulnerability: This regex pattern is vulnerable to catastrophic backtracking (ReDoS attack)1. The nested quantifiers ((?:\\.|(?!\1\s*\)).)*) create exponential time complexity when the regex engine backtracks. An attacker could provide specially crafted input like Termux(" followed by thousands of characters without a closing quote, causing the application to hang or crash.

Replace with an atomic group or possessive quantifier to prevent backtracking. Use: ((?:\\.|(?!\1\s*\))[^\\])*) which matches non-backslash characters or escaped sequences without nested quantifiers.

Suggested change
PatternInfo("termux1", Regex("""(?i)\bTermux\(\s*(["'])((?:\\.|(?!\1\s*\)).)*)\1\s*\)"""), { match -> Command.TermuxCommand(match.groupValues[2]) }, CommandTypeEnum.TERMUX_COMMAND),
PatternInfo("termux1", Regex("""(?i)\bTermux\(\s*(["'])((?:\\.|(?!\1\s*\))[^\\])*)\1\s*\)"""), { match -> Command.TermuxCommand(match.groupValues[2]) }, CommandTypeEnum.TERMUX_COMMAND),

Footnotes

  1. CWE-1333: Inefficient Regular Expression Complexity - https://cwe.mitre.org/data/definitions/1333.html


// Click (long) button patterns
PatternInfo("clickBtn1", Regex("(?i)\\bclick\\([\"']([^\"']+)[\"']"), { match -> Command.ClickButton(match.groupValues[1]) }, CommandTypeEnum.CLICK_BUTTON),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,30 @@ object SystemMessagePreferences {
private const val PREFS_NAME = "system_message_prefs"
private const val KEY_SYSTEM_MESSAGE = "system_message"
private const val KEY_FIRST_START_COMPLETED = "first_start_completed" // New flag
private val DEFAULT_SYSTEM_MESSAGE_ON_FIRST_START = """You are on an App on a Smartphone. Your app is called Screen Operator. You start from this app. Proceed step by step! DON'T USE TOOL CODE!
You must operate the screen with exactly following commands: "home()" "back()" "recentApps()" "openApp("sample")" for buttons and words: "click("sample")" "longClick("sample")" "tapAtCoordinates(x, y)" "tapAtCoordinates(x percent of screen%, y percent of screen%)" "scrollDown()" "scrollUp()" "scrollLeft()" "scrollRight()" "scrollDown(x, y, how much pixel to scroll, duration in milliseconds)" "scrollUp(x, y, how much pixel to scroll, duration in milliseconds)" "scrollLeft(x, y, how much pixel to scroll, duration in milliseconds)" "scrollRight(x, y, how much pixel to scroll, duration in milliseconds)" "scrollDown(x percent of screen%, y percent of screen%, how much percent to scroll%, duration in milliseconds)" "scrollUp(x percent of screen%, y percent of screen%, how much percent to scroll, duration in milliseconds)" "scrollLeft(x percent of screen%, y percent of screen%, how much percent to scroll, duration in milliseconds)" "scrollRight(x percent of screen%, y percent of screen%, how much percent to scroll, duration in milliseconds)" scroll status bar down: "scrollUp(540, 0, 1100, 50)" "Wait(seconds)"

"Termux("command")"
1. You don't need to open Termux because a run.command intent is being called.

// Content from pasted_content.txt
private const val DEFAULT_SYSTEM_MESSAGE_ON_FIRST_START = """You are on an App on a Smartphone. Your app is called Screen Operator. You start from this app. Proceed step by step! DON'T USE TOOL CODE! You must operate the screen with exactly following commands: "home()" "back()" "recentApps()" "openApp("sample")" for buttons and words: "click("sample")" "longClick("sample")" "tapAtCoordinates(x, y)" "tapAtCoordinates(x percent of screen%, y percent of screen%)" "scrollDown()" "scrollUp()" "scrollLeft()" "scrollRight()" "scrollDown(x, y, how much pixel to scroll, duration in milliseconds)" "scrollUp(x, y, how much pixel to scroll, duration in milliseconds)" "scrollLeft(x, y, how much pixel to scroll, duration in milliseconds)" "scrollRight(x, y, how much pixel to scroll, duration in milliseconds)" "scrollDown(x percent of screen%, y percent of screen%, how much percent to scroll%, duration in milliseconds)" "scrollUp(x percent of screen%, y percent of screen%, how much percent to scroll, duration in milliseconds)" "scrollLeft(x percent of screen%, y percent of screen%, how much percent to scroll, duration in milliseconds)" "scrollRight(x percent of screen%, y percent of screen%, how much percent to scroll, duration in milliseconds)" scroll status bar down: "scrollUp(540, 0, 1100, 50)" "Wait(seconds)" "Termux("command")" "completed()" To write text, search and click the textfield thereafter: "writeText("sample text")" You need to write the already existing text, if it should continue exist. If the keyboard is displayed, you can press "Enter()". Otherwise, you have to open the keyboard by clicking on the text field. Don't write the commands if you're just planing about it or messaging me. If you have questions, open Screen Operator, ask your question(s), and use completed() until you receive an answer. Retrieve information using "retrieve("sample")" if some is passed to your task. After each message, you will see the screen with additional information about it. Say "completed()" when the task is finished."""
2. Each call to Termux("command") starts a new session. To prevent this, you must first write Termux("tmux new-session -A -s main") and then pass all subsequent commands with Termux("tmux send-keys -t main "command" Enter").

3. Do not use wait(seconds) commands when using Termux.

To write text, click the textfield, thereafter: "writeText("sample text")" You need to write the already existing text, if it should continue exist.
If the keyboard is displayed, you can press "Enter()". Otherwise, you have to open the keyboard by clicking on the text field.


Say "completed()" when the task is finished.


Notes:
1. Don't write the commands if you're just planing about it or messaging me.


2. If you have questions, open Screen Operator, ask your question(s), and use "completed()" until you receive an human response.

3. After each message, you will see the screen with additional information about it.""".trimIndent()
private fun prefs(context: Context) = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)

/**
Expand Down
26 changes: 26 additions & 0 deletions app/src/test/java/com/google/ai/sample/util/CommandParserTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,30 @@ class CommandParserTest {
assertEquals(1, commands.count { it is Command.Completed })
}

@Test
fun parseCommands_extractsTermuxCommandWithNestedSingleQuotes() {
val commands = CommandParser.parseCommands(
"""Termux("su -c 'ifconfig'")""",
clearBuffer = true
)

assertEquals(1, commands.size)
val command = commands.first()
assertTrue(command is Command.TermuxCommand)
assertEquals("su -c 'ifconfig'", (command as Command.TermuxCommand).command)
}

@Test
fun parseCommands_extractsTermuxCommandWithNestedDoubleQuotes() {
val commands = CommandParser.parseCommands(
"""Termux('su -c "ifconfig"')""",
clearBuffer = true
)

assertEquals(1, commands.size)
val command = commands.first()
assertTrue(command is Command.TermuxCommand)
assertEquals("su -c \"ifconfig\"", (command as Command.TermuxCommand).command)
}

}
Loading