Skip to content

Conversation

@ummon-org
Copy link

Implemented Subaru Solterra support for EU.

Tested and it works on my system, not sure if I need to create a PR for website too, basically configuration is the same as Toyota (they share same platform).

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

Blocking issues:

  • Detected a Generic API Key, potentially exposing access to various services and sensitive operations. (link)

General comments:

  • Avoid embedding client secrets (AppAuthorization, ApiKey, ClientRefKey) directly in source—consider loading these from configuration or environment variables.
  • The recursive authenticate method has no depth limit—add a guard or error condition to prevent potential infinite recursion if the auth flow doesn’t progress as expected.
  • There’s a lot of overlap between the Subaru and Toyota OAuth flows—extract shared logic into a common package to reduce duplication and ease future maintenance.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Avoid embedding client secrets (AppAuthorization, ApiKey, ClientRefKey) directly in source—consider loading these from configuration or environment variables.
- The recursive authenticate method has no depth limit—add a guard or error condition to prevent potential infinite recursion if the auth flow doesn’t progress as expected.
- There’s a lot of overlap between the Subaru and Toyota OAuth flows—extract shared logic into a common package to reduce duplication and ease future maintenance.

## Individual Comments

### Comment 1
<location> `vehicle/subaru/identity.go:38` </location>
<code_context>
+	}
+}
+
+func (v *Identity) authenticate(auth Auth, user, password string, passwordSet bool) (*Token, error) {
+	uri := fmt.Sprintf("%s/%s", BaseUrl, AuthenticationPath)
+
</code_context>

<issue_to_address>
**issue (complexity):** Consider refactoring the `authenticate` function by splitting it into helper functions for callback handling and request processing, and replacing recursion with a loop.

Consider breaking `authenticate` into three focused helpers—one to apply credentials to callbacks, one to send the request and parse either an `Auth` or `Token` response, and a simple loop in place of recursion. For example:

```go
// apply user/password to callbacks, return updated passwordSet
func applyCallbacks(auth *Auth, user, pass string, pwdSet bool) bool {
    for i, cb := range auth.Callbacks {
        switch cb.Type {
        case "NameCallback":
            if out, ok := cb.Output[0].Value.(string); ok && out == "User Name" {
                auth.Callbacks[i].Input[0].Value = user
            }
        case "PasswordCallback":
            auth.Callbacks[i].Input[0].Value = pass
            pwdSet = true
        }
    }
    return pwdSet
}
```

```go
// send auth request, return either a token or the next Auth step
func (v *Identity) sendStep(auth Auth, pwdSet bool) (*Token, *Auth, error) {
    uri := fmt.Sprintf("%s/%s", BaseUrl, AuthenticationPath)
    req, err := request.New(http.MethodPost, uri, request.MarshalJSON(auth), request.JSONEncoding)
    if err != nil {
        return nil, nil, err
    }

    if pwdSet {
        var tok Token
        if err := v.DoJSON(req, &tok); err != nil {
            return nil, nil, err
        }
        return &tok, nil, nil
    }

    var next Auth
    if err := v.DoJSON(req, &next); err != nil {
        return nil, nil, err
    }
    return nil, &next, nil
}
```

```go
// loop instead of recurse
func (v *Identity) authenticate(initial Auth, user, pass string) (*Token, error) {
    auth := initial
    pwdSet := false

    for {
        pwdSet = applyCallbacks(&auth, user, pass, pwdSet)

        tok, nextAuth, err := v.sendStep(auth, pwdSet)
        if err != nil {
            return nil, err
        }
        if tok != nil {
            return tok, nil
        }
        auth = *nextAuth
    }
}
```

This keeps identical behavior but flattens recursion and separates concerns.
</issue_to_address>

### Comment 2
<location> `vehicle/subaru/api.go:26` </location>
<code_context>
tTZipv6liF74PwMfk9Ed68AQ0bISswwf3iHQdqcF
</code_context>

<issue_to_address>
**security (generic-api-key):** Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

*Source: gitleaks*
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

AuthorizationPath = "oauth2/realms/root/realms/alliance-subaru/authorize?client_id=8c4921b0b08901fef389ce1af49c4e10.subaru.com&scope=openid+profile+write&response_type=code&redirect_uri=com.subaru.oneapp:/oauth2Callback&code_challenge=plain&code_challenge_method=plain"
VehicleGuidPath = "v2/vehicle/guid"
RemoteElectricStatusPath = "v1/global/remote/electric/status"
ApiKey = "tTZipv6liF74PwMfk9Ed68AQ0bISswwf3iHQdqcF"
Copy link
Contributor

Choose a reason for hiding this comment

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

security (generic-api-key): Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

Source: gitleaks

Copy link
Author

Choose a reason for hiding this comment

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

This is a shared API Key that was used for Toyota implementation too, pretty easy to get that from the official app.

Copy link
Author

Choose a reason for hiding this comment

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

@andig as you can see this is the only point sorcery is complaining about but it's an information hardcoded everywhere in toyota/subaru apps. In codebase there's exactly same implementation for Toyota already.

@andig andig added the vehicles Specific vehicle support label Oct 9, 2025
@ummon-org ummon-org force-pushed the subaru-solterra-support branch from bf825e0 to 9d73e26 Compare October 9, 2025 11:11
@ummon-org ummon-org force-pushed the subaru-solterra-support branch from 9d73e26 to 8f85434 Compare October 10, 2025 13:07
@github-actions github-actions bot added the stale Outdated and ready to close label Oct 17, 2025
@github-actions github-actions bot closed this Oct 22, 2025
@ummon-org
Copy link
Author

@andig just to understand better for future contributions, why this PR was discarded? Thanks.

@andig
Copy link
Member

andig commented Oct 22, 2025

Seems it timed out waiting to pass the quality checks?

@ummon-org
Copy link
Author

ummon-org commented Oct 23, 2025

Seems it timed out waiting to pass the quality checks?

Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

This is referring to an api key that is hardcoded inside Subaru phone app. It's very similar to Toyota code (they share same ecosystem even if in slight different way). How I'm supposed to fix this?
(moreover there's also a comment on that line I answered and set as resolved)

@andig andig reopened this Oct 23, 2025
@andig
Copy link
Member

andig commented Oct 23, 2025

Let's restart

@github-actions github-actions bot removed the stale Outdated and ready to close label Oct 23, 2025
Comment on lines +62 to +67
"Accept": request.JSONContent,
"x-guid": v.identity.uuid,
"x-api-key": ApiKey,
"x-client-ref": v.clientRef,
"x-appversion": ClientRefKey,
"X-Appbrand": "S",
Copy link
Member

Choose a reason for hiding this comment

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

Are these for every api request? Then this should become a headers decorator.

"github.com/stretchr/testify/require"
)

func TestAPI(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

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

Since this test is only useful for you I'd suggest to remove it


var res struct {
oauth2.Token
IDToken string `json:"id_token"`
Copy link
Member

Choose a reason for hiding this comment

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

You can get the idtoken using oauth2.Token.Extra("id_token"), see cardata.

return nil
}

func (v *Identity) RefreshToken(token *oauth2.Token) (*oauth2.Token, error) {
Copy link
Member

Choose a reason for hiding this comment

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

this looks a lot like normal OAuth flow. If possible it would be nice to use an oauth2.Config and associated functions. The headers could be set using a headers decorator on the transport.

@github-actions github-actions bot added the stale Outdated and ready to close label Oct 30, 2025
@github-actions github-actions bot closed this Nov 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale Outdated and ready to close vehicles Specific vehicle support

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants