pgweb/pkg/client/util.go
Dan Sosedoff 4c40eef99a
Perform client version validation before executing pg_dump command (#614)
* Add func to parse out pg_dump version
* Perform client vs server version checking before dump exports
* Fix dump tests
* Add extra test to validate against empty server version
* Fix attachment filenames cleanup function
* Add extra test
* Fix small typos in comments
* Drop third-party package to deal with versions
* Tweak the pg dump incompatibility error message
* Run CI on pull requests
2022-12-12 15:09:12 -06:00

102 lines
2.7 KiB
Go

package client
import (
"fmt"
"regexp"
"strings"
)
var (
// List of keywords that are not allowed in read-only mode
reRestrictedKeywords = regexp.MustCompile(`(?mi)\s?(CREATE|INSERT|DROP|DELETE|TRUNCATE|GRANT|OPEN|IMPORT|COPY)\s`)
// Comment regular expressions
reSlashComment = regexp.MustCompile(`(?m)/\*.+\*/`)
reDashComment = regexp.MustCompile(`(?m)--.+`)
// Postgres version signature
postgresSignature = regexp.MustCompile(`(?i)postgresql ([\d\.]+)\s?`)
postgresDumpSignature = regexp.MustCompile(`\s([\d\.]+)\s?`)
postgresType = "PostgreSQL"
// Cockroach version signature
cockroachSignature = regexp.MustCompile(`(?i)cockroachdb ccl v([\d\.]+)\s?`)
cockroachType = "CockroachDB"
)
// Get major and minor version components
// Example: 10.2.3.1 -> 10.2
func getMajorMinorVersion(str string) (major int, minor int) {
chunks := strings.Split(str, ".")
fmt.Sscanf(chunks[0], "%d", &major)
if len(chunks) > 1 {
fmt.Sscanf(chunks[1], "%d", &minor)
}
return
}
// Get short version from the string
// Example: 10.2.3.1 -> 10.2
func getMajorMinorVersionString(str string) string {
major, minor := getMajorMinorVersion(str)
return fmt.Sprintf("%d.%d", major, minor)
}
func detectServerTypeAndVersion(version string) (bool, string, string) {
version = strings.TrimSpace(version)
// Detect postgresql
matches := postgresSignature.FindAllStringSubmatch(version, 1)
if len(matches) > 0 {
return true, postgresType, matches[0][1]
}
// Detect cockroachdb
matches = cockroachSignature.FindAllStringSubmatch(version, 1)
if len(matches) > 0 {
return true, cockroachType, matches[0][1]
}
return false, "", ""
}
// detectDumpVersion parses out version from `pg_dump -V` command.
func detectDumpVersion(version string) (bool, string) {
matches := postgresDumpSignature.FindAllStringSubmatch(version, 1)
if len(matches) > 0 {
return true, matches[0][1]
}
return false, ""
}
func checkVersionRequirement(client, server string) bool {
clientMajor, clientMinor := getMajorMinorVersion(client)
serverMajor, serverMinor := getMajorMinorVersion(server)
if serverMajor < 10 {
return clientMajor >= serverMajor && clientMinor >= serverMinor
}
return clientMajor >= serverMajor
}
// containsRestrictedKeywords returns true if given keyword is not allowed in read-only mode
func containsRestrictedKeywords(str string) bool {
str = reSlashComment.ReplaceAllString(str, "")
str = reDashComment.ReplaceAllString(str, "")
return reRestrictedKeywords.MatchString(str)
}
func hasBinary(data string, checkLen int) bool {
for idx, chr := range data {
if int(chr) < 32 || int(chr) > 126 {
return true
}
if idx >= checkLen {
break
}
}
return false
}