GradleRepositoryParser.kt

package com.depanalyzer.parser

import com.depanalyzer.repository.ProjectRepository
import com.depanalyzer.security.InputSafety
import java.io.File

class GradleRepositoryParser(
    private val envProvider: (String) -> String? = { System.getenv(it) },
    private val trustedCredentialHosts: Set<String> =
        InputSafety.parseTrustedCredentialHosts(envProvider(InputSafety.CREDENTIAL_HOST_ALLOWLIST_ENV))
) {
    fun parse(buildFile: File): List<ProjectRepository> {
        val content = buildFile.readText()
        val repositoriesBody =
            extractBlockBody(content, "repositories") ?: return listOf(ProjectRepository.MAVEN_CENTRAL)

        val repos = mutableListOf<ProjectRepository>()

        // Standard repositories
        if (repositoriesBody.contains("mavenCentral()")) {
            repos.add(ProjectRepository.MAVEN_CENTRAL)
        }
        if (repositoriesBody.contains("google()")) {
            repos.add(ProjectRepository(id = "google", url = "https://maven.google.com"))
        }
        if (repositoriesBody.contains("jcenter()")) {
            repos.add(ProjectRepository(id = "jcenter", url = "https://jcenter.bintray.com"))
        }
        if (repositoriesBody.contains("mavenLocal()")) {
            // mavenLocal is usually ~/.m2/repository, we can use a placeholder or local file URL
            repos.add(
                ProjectRepository(
                    id = "mavenLocal",
                    url = "file://${System.getProperty("user.home")}/.m2/repository"
                )
            )
        }

        // Custom maven repositories
        val mavenBlocks = extractMavenBlocks(repositoriesBody)
        mavenBlocks.forEach { block ->
            val url = extractUrl(block)
            if (url != null && InputSafety.isAllowedRepositoryUrl(url)) {
                val id = url.substringAfterLast("/").takeIf { it.isNotBlank() } ?: "maven"
                val (username, password) = extractCredentials(block, url)
                repos.add(
                    ProjectRepository(
                        id = id,
                        url = url,
                        username = username,
                        password = password
                    )
                )
            }
        }

        return if (repos.isEmpty()) listOf(ProjectRepository.MAVEN_CENTRAL) else repos
    }

    private fun extractBlockBody(content: String, blockName: String): String? {
        val startRegex = Regex("""\b$blockName\s*\{""")
        val startMatch = startRegex.find(content) ?: return null
        val start = startMatch.range.first
        val openBrace = content.indexOf('{', start)
        if (openBrace == -1) return null

        var depth = 0
        var index = openBrace
        while (index < content.length) {
            when (content[index]) {
                '{' -> depth++
                '}' -> {
                    depth--
                    if (depth == 0) {
                        return content.substring(openBrace + 1, index)
                    }
                }
            }
            index++
        }
        return null
    }

    private fun extractMavenBlocks(content: String): List<String> {
        val blocks = mutableListOf<String>()
        val startRegex = Regex("""\bmaven\s*\{""")
        var currentMatch = startRegex.find(content)

        while (currentMatch != null) {
            val start = currentMatch.range.first
            val openBrace = content.indexOf('{', start)
            if (openBrace != -1) {
                var depth = 0
                var index = openBrace
                while (index < content.length) {
                    when (content[index]) {
                        '{' -> depth++
                        '}' -> {
                            depth--
                            if (depth == 0) {
                                blocks.add(content.substring(openBrace + 1, index))
                                break
                            }
                        }
                    }
                    index++
                }
            }
            currentMatch = startRegex.find(content, currentMatch.range.last)
        }
        return blocks
    }

    private fun extractUrl(block: String): String? {
        val urlRegex = Regex("""url\s*(?:=|\s*)\s*(?:uri\s*\(\s*)?['"]([^'"]+)['"]""")
        return urlRegex.find(block)?.groupValues?.get(1)
    }

    private fun extractCredentials(block: String, repositoryUrl: String): Pair<String?, String?> {
        if (!InputSafety.isTrustedCredentialDestination(repositoryUrl, trustedCredentialHosts)) {
            return null to null
        }

        val credsBody = extractBlockBody(block, "credentials") ?: return null to null

        val userRegex =
            Regex("""username\s*(?:=|\s*)\s*(?:System\.getenv\s*\(\s*['"]([^'"]+)['"]\s*\)|['"]([^'"]+)['"])""")
        val passRegex =
            Regex("""password\s*(?:=|\s*)\s*(?:System\.getenv\s*\(\s*['"]([^'"]+)['"]\s*\)|['"]([^'"]+)['"])""")

        val userMatch = userRegex.find(credsBody)
        val username = when {
            userMatch?.groupValues?.get(1)?.isNotBlank() == true -> envProvider(userMatch.groupValues[1])
            userMatch?.groupValues?.get(2)?.isNotBlank() == true -> userMatch.groupValues[2]
            else -> null
        }

        val passMatch = passRegex.find(credsBody)
        val password = when {
            passMatch?.groupValues?.get(1)?.isNotBlank() == true -> envProvider(passMatch.groupValues[1])
            passMatch?.groupValues?.get(2)?.isNotBlank() == true -> passMatch.groupValues[2]
            else -> null
        }

        return username to password
    }
}