Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When the password section in urlstr contains special characters, let Parse be able to parse it correctly #33

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
7 changes: 7 additions & 0 deletions .deepsource.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
version = 1

[[analyzers]]
name = "go"

[analyzers.meta]
import_root = "github.com/LisaIsCoding/dburl"
23 changes: 23 additions & 0 deletions .github/workflows/codesee-arch-diagram.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# This workflow was added by CodeSee. Learn more at https://codesee.io/
# This is v2.0 of this workflow file
on:
push:
branches:
- master
pull_request_target:
types: [opened, synchronize, reopened]

name: CodeSee

permissions: read-all

jobs:
codesee:
runs-on: ubuntu-latest
continue-on-error: true
name: Analyze the repo with CodeSee
steps:
- uses: Codesee-io/codesee-action@v2
with:
codesee-token: ${{ secrets.CODESEE_ARCH_DIAG_API_TOKEN }}
codesee-url: https://app.codesee.io
32 changes: 32 additions & 0 deletions dburl.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ package dburl

import (
"database/sql"
"fmt"
"net/url"
"regexp"
"strings"
)

Expand Down Expand Up @@ -70,11 +72,40 @@ type URL struct {
// "scheme://" but "scheme:"), and the database scheme does not support opaque
// components, Parse will attempt to re-process the URL as "scheme://<opaque>".
func Parse(urlstr string) (*URL, error) {
// Use regex to find and encode the password twice to handle comlicated
// password like: A7p0jch5Vj_+-,&=!@#$%^*(). Since inside the url.Parse will
// call unescape(`parse` --> `parseAuthority` --> `unescape`)
userPassRe := regexp.MustCompile(`^([^:/]*:/{2})([^:]*):(.*)@`)
prefixRe := regexp.MustCompile(`^([^:/]*:/{1,2})`)
switch {
case userPassRe.MatchString(urlstr):
urlstr = userPassRe.ReplaceAllStringFunc(urlstr, func(m string) string {
parts := userPassRe.FindStringSubmatch(m)
prefix := parts[1]
return fmt.Sprintf("%s%s:%s@", prefix, parts[2], url.QueryEscape(url.QueryEscape(parts[3])))
})
case prefixRe.MatchString(urlstr):
// no need to do anything
default:
// for strings like "file:myfile.sqlite3?loc=auto", also no need to do anything
}

// parse url
v, err := url.Parse(urlstr)
if err != nil {
return nil, err
}

// decode the password
if password, isPasswordSet := v.User.Password(); isPasswordSet {
passwordDecode, err := url.QueryUnescape(password)
if err != nil {
return nil, err
}

v.User = url.UserPassword(v.User.Username(), passwordDecode)
}

if v.Scheme == "" {
return nil, ErrInvalidDatabaseScheme
}
Expand Down Expand Up @@ -139,6 +170,7 @@ func Parse(urlstr string) (*URL, error) {
if u.DSN, u.GoDriver, err = scheme.Generator(u); err != nil {
return nil, err
}

return u, nil
}

Expand Down