Extract credential-stripping into a tested helper · Entire

Extract credential-stripping into a tested helper

5d7f827→main·

Soph·1mo ago·2 files·+87 added/-5 removed

Pull the parse-and-rebuild logic out of reportRepoEnabled into a pure
cleanRemoteURLForReport helper and cover it with a table-driven test:
embedded https tokens and user:password, query params, scp-style ssh
users are all stripped, the .git suffix is normalized, and unparseable
single-segment paths error. Guards assert no known secret survives in
the cleaned URL.

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

Sessions

5046baaca8a6View transcript

Changes

2

951 unmodified lines

952
953
954
955
956
957
958
955
956
957
958
959
963
960
961
962
7 unmodified lines

970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988

951 unmodified lines

return
    }

// Never send the raw remote: it can carry embedded credentials
    // (https://token@host/...) or query params. Parse and rebuild a clean,
    // credential-free URL; skip entirely if it isn't parseable.
    info, err := gitremote.ParseURL(rawURL)
    cleanURL, err := cleanRemoteURLForReport(rawURL)
    if err != nil {
        logging.Debug(ctx, "skipping enable report: unparseable origin remote", "error", err)
        return
    }
    cleanURL := fmt.Sprintf("https://%s/%s/%s.git", info.Host, info.Owner, info.Repo)

client, err := NewAuthenticatedAPIClient(ctx, insecureHTTPAuth)
    if err != nil {
7 unmodified lines

}
}

// cleanRemoteURLForReport turns a raw git remote URL into a clean,
// credential-free HTTPS URL safe to send to the backend. The raw remote can
// carry embedded credentials (https://token@host/...) or query params, so we
// never forward it verbatim: parse it and rebuild from host/owner/repo alone.
// Returns an error if the URL can't be parsed (the caller skips reporting).
func cleanRemoteURLForReport(rawURL string) (string, error) {
    info, err := gitremote.ParseURL(rawURL)
    if err != nil {
        return "", fmt.Errorf("parse remote URL: %w", err)
    }
    return fmt.Sprintf("https://%s/%s/%s.git", info.Host, info.Owner, info.Repo), nil
}

func newDisableCmd() *cobra.Command {
    var useProjectSettings bool
    var uninstall bool

Mcmd/entire/cli/setup.go+14/-5


3088 unmodified lines

3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164

3088 unmodified lines

t.Errorf("expected hint pointing at 'entire enable', got stderr: %s", stderr.String())
    }
}

func TestCleanRemoteURLForReport(t *testing.T) {
    t.Parallel()

tests := []struct {
        name    string
        rawURL  string
        want    string
        wantErr bool
    }{
        {
            name:   "https without credentials is normalized",
            rawURL: "https://github.com/entireio/cli.git",
            want:   "https://github.com/entireio/cli.git",
        },
        {
            name:   "https token credentials are stripped",
            rawURL: "https://ghp_secrettoken@github.com/entireio/cli.git",
            want:   "https://github.com/entireio/cli.git",
        },
        {
            name:   "https user:password credentials are stripped",
            rawURL: "https://x-access-token:ghp_secret@github.com/entireio/cli.git",
            want:   "https://github.com/entireio/cli.git",
        },
        {
            name:   "query parameters are dropped",
            rawURL: "https://github.com/entireio/cli.git?token=secret",
            want:   "https://github.com/entireio/cli.git",
        },
        {
            name:   "scp-style ssh remote is normalized to https and the user is dropped",
            rawURL: "git@github.com:entireio/cli.git",
            want:   "https://github.com/entireio/cli.git",
        },
        {
            name:   "missing .git suffix is added",
            rawURL: "https://github.com/entireio/cli",
            want:   "https://github.com/entireio/cli.git",
        },
        {
            name:    "unparseable single-segment path errors",
            rawURL:  "https://github.com/onlyowner.git",
            wantErr: true,
        },
    }

for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            t.Parallel()

got, err := cleanRemoteURLForReport(tt.rawURL)
            if tt.wantErr {
                if err == nil {
                    t.Fatalf("expected error for %q, got %q", tt.rawURL, got)
                }
                return
            }
            if err != nil {
                t.Fatalf("unexpected error for %q: %v", tt.rawURL, err)
            }
            if got != tt.want {
                t.Errorf("cleanRemoteURLForReport(%q) = %q, want %q", tt.rawURL, got, tt.want)
            }
            // The cleaned URL must never carry the original credentials.
            for _, secret := range []string{"ghp_secrettoken", "ghp_secret", "x-access-token", "token=secret"} {
                if strings.Contains(got, secret) {
                    t.Errorf("cleaned URL %q leaked credential %q", got, secret)
                }
            }
        })
    }
}