Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,9 +274,9 @@ func UpdateExtensions(w http.ResponseWriter, r *http.Request) {
host = "update.googleapis.com"
}
if jsonRequest {
http.Redirect(w, r, "https://"+host+"/service/update2/json"+queryString, http.StatusTemporaryRedirect)
http.Redirect(w, r, extension.ConstructURL(host, "/service/update2/json"+queryString), http.StatusTemporaryRedirect)

Choose a reason for hiding this comment

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

reported by reviewdog 🐶
[semgrep] An HTTP redirect was found to be crafted from user-input r. This can lead to open redirect vulnerabilities, potentially allowing attackers to redirect users to malicious web sites. It is recommend where possible to not allow user-input to craft the redirect URL. When user-input is necessary to craft the request, it is recommended to follow OWASP best practices to restrict the URL to domains in an allowlist.

Source: https://semgrep.dev/r/go.lang.security.injection.open-redirect.open-redirect


Cc @thypon @kdenhartog

Copy link
Member

Choose a reason for hiding this comment

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

seems like these 2 are concerns that need to be addressed.

Also, please encode the query params

} else {
http.Redirect(w, r, "https://"+host+"/service/update2"+queryString, http.StatusTemporaryRedirect)
http.Redirect(w, r, extension.ConstructURL(host, "/service/update2"+queryString), http.StatusTemporaryRedirect)

Choose a reason for hiding this comment

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

reported by reviewdog 🐶
[semgrep] An HTTP redirect was found to be crafted from user-input r. This can lead to open redirect vulnerabilities, potentially allowing attackers to redirect users to malicious web sites. It is recommend where possible to not allow user-input to craft the redirect URL. When user-input is necessary to craft the request, it is recommended to follow OWASP best practices to restrict the URL to domains in an allowlist.

Source: https://semgrep.dev/r/go.lang.security.injection.open-redirect.open-redirect


Cc @thypon @kdenhartog

}
return
}
Expand Down
30 changes: 30 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
networks:
main:

services:
localstack:
image: localstack/localstack
ports:
- "127.0.0.1:4566:4566"
- "127.0.0.1:4510-4559:4510-4559"
networks:
- main
volumes:
- ./misc/create_in_localstack.sh:/etc/localstack/init/ready.d/create.sh
app:
build: .
networks:
- main
environment:
DYNAMODB_ENDPOINT: http://localstack:4566
AWS_REGION: us-west-2
AWS_ACCESS_KEY_ID: test
AWS_SECRET_ACCESS_KEY: test
COMPONENT_UPDATER_HOST: http://localhost:8192
S3_EXTENSIONS_BUCKET_HOST: http://localhost:4566/brave-core-ext
ENVIRONMENT: local
depends_on:
- localstack
restart: on-failure
ports:
- "127.0.0.1:8192:8192"
6 changes: 3 additions & 3 deletions extension/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ func (updateResponse *UpdateResponse) MarshalJSON() ([]byte, error) {
patchInfo, pInfoFound := extension.PatchList[extension.FP]
app.UpdateCheck = UpdateCheck{Status: GetUpdateStatus(extension)}
extensionName := "extension_" + strings.Replace(extension.Version, ".", "_", -1) + ".crx"
url := "https://" + GetS3ExtensionBucketHost(extension.ID) + "/release/" + extension.ID + "/" + extensionName
diffURL := "https://" + GetS3ExtensionBucketHost(extension.ID) + "/release/" + extension.ID + "/patches/" + extension.SHA256 + "/"
url := ConstructURL(GetS3ExtensionBucketHost(extension.ID), "/release/"+extension.ID+"/"+extensionName)
diffURL := ConstructURL(GetS3ExtensionBucketHost(extension.ID), "/release/"+extension.ID+"/patches/"+extension.SHA256+"/")
if app.UpdateCheck.Status == "ok" {
if app.UpdateCheck.URLs == nil {
app.UpdateCheck.URLs = &URLs{
Expand Down Expand Up @@ -135,7 +135,7 @@ func (updateResponse *WebStoreUpdateResponse) MarshalJSON() ([]byte, error) {
Status: "ok",
SHA256: extension.SHA256,
Version: extension.Version,
Codebase: "https://" + GetS3ExtensionBucketHost(extension.ID) + "/release/" + extension.ID + "/" + extensionName,
Codebase: ConstructURL(GetS3ExtensionBucketHost(extension.ID), "/release/"+extension.ID+"/"+extensionName),
},
}
response.Apps = append(response.Apps, app)
Expand Down
15 changes: 15 additions & 0 deletions extension/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package extension

import (
"os"
"strings"
)

var torClientMacExtensionID = "cldoidikboihgcjfkhdeidbpclkineef"
Expand Down Expand Up @@ -39,6 +40,10 @@ func lookupEnvFallback(key, fallback string) string {
return fallback
}

func GetEnvironment() string {
return lookupEnvFallback("ENVIRONMENT", "production")
}

// GetS3ExtensionBucketHost returns the url to use for accessing crx files
func GetS3ExtensionBucketHost(id string) string {
if isTorExtension(id) {
Expand All @@ -65,3 +70,13 @@ func GetUpdateStatus(extension Extension) string {
func GetComponentUpdaterHost() string {
return lookupEnvFallback("COMPONENT_UPDATER_HOST", "componentupdater.brave.com")
}

func ConstructURL(host, path string) string {
if strings.HasPrefix(host, "http://") {
if GetEnvironment() != "local" {
panic("Cannot use http:// in non-local environment")
}
return host + path
}
return "https://" + host + path
}
4 changes: 2 additions & 2 deletions extension/xml.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func (updateResponse *UpdateResponse) MarshalXML(e *xml.Encoder, _ xml.StartElem
app := App{AppID: extension.ID}
app.UpdateCheck = UpdateCheck{Status: GetUpdateStatus(extension)}
extensionName := "extension_" + strings.Replace(extension.Version, ".", "_", -1) + ".crx"
url := "https://" + GetS3ExtensionBucketHost(extension.ID) + "/release/" + extension.ID + "/" + extensionName
url := ConstructURL(GetS3ExtensionBucketHost(extension.ID), "/release/"+extension.ID+"/"+extensionName)
if app.UpdateCheck.Status == "ok" {
if app.UpdateCheck.URLs == nil {
app.UpdateCheck.URLs = &URLs{
Expand Down Expand Up @@ -116,7 +116,7 @@ func (updateResponse *WebStoreUpdateResponse) MarshalXML(e *xml.Encoder, _ xml.S
Status: "ok",
SHA256: extension.SHA256,
Version: extension.Version,
Codebase: "https://" + GetS3ExtensionBucketHost(extension.ID) + "/release/" + extension.ID + "/" + extensionName,
Codebase: ConstructURL(GetS3ExtensionBucketHost(extension.ID), "/release/"+extension.ID+"/"+extensionName),
},
}
response.Apps = append(response.Apps, app)
Expand Down
13 changes: 13 additions & 0 deletions misc/create_in_localstack.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/sh

export AWS_DEFAULT_REGION=us-west-2
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test

awslocal dynamodb create-table \
--table-name Extensions \
--attribute-definitions AttributeName=ID,AttributeType=S \
--key-schema AttributeName=ID,KeyType=HASH \
--provisioned-throughput ReadCapacityUnits=10,WriteCapacityUnits=10 || true
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we (optionally) add some example data?

Copy link
Collaborator Author

@DJAndries DJAndries Mar 19, 2025

Choose a reason for hiding this comment

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

I was thinking that simply invoking the brave-core-crx-packager would be sufficient, rather than adding some insert commands and including a crx blob in the repo. thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

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

I wouldn't include the crx blob itself, just the metadata in DDB for testing the API (since go-update doesn't handle the actual blobs).

For a full test, the crx could still be built/uploaded separately.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this would allow the user to curl the API, but it wouldn't allow a user to test the solution e2e using the browser


awslocal s3 mb s3://brave-core-ext || true