Tighten Google Chat webhook validation and settings response
Restrict the decoded key and token to a URL-safe ASCII alphabet so the 16-character minimum and the four-character masked tail measure the same thing; a token of a few multi-byte characters could otherwise be shown in full (MQ-165-01). Validate the raw query as exactly key=…&token=…, refusing empty segments, a second "?", reordered, extra or repeated parameters (MQ-165-02). Drop configured and updated_at from the settings response so it is the four-field contract: space_label, url_masked, last_test_at, last_test_status; url_masked is null exactly when nothing is stored (MQ-165-03). Task: 919e9422-3a41-4f1f-8967-77da17f5f9f3 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -59,9 +59,8 @@ var (
|
||||
)
|
||||
|
||||
// Settings is the stored webhook as the API shows it: never the URL, only
|
||||
// its masked form.
|
||||
// its masked form. URLMasked is nil when no webhook is stored.
|
||||
type Settings struct {
|
||||
Configured bool `json:"configured"`
|
||||
SpaceLabel *string `json:"space_label"`
|
||||
// URLMasked is the webhook URL without its key and with only the last
|
||||
// few characters of its token.
|
||||
@@ -70,7 +69,6 @@ type Settings struct {
|
||||
// test message is sent to this webhook.
|
||||
LastTestAt *string `json:"last_test_at"`
|
||||
LastTestStatus *string `json:"last_test_status"`
|
||||
UpdatedAt *string `json:"updated_at"`
|
||||
}
|
||||
|
||||
// TestResult is the outcome of a test message.
|
||||
@@ -143,8 +141,8 @@ func NewService(st *store.Store, box *secretbox.Box, publicURL *url.URL, transpo
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Get returns the stored webhook's settings; Configured is false and every
|
||||
// other field nil before one is saved.
|
||||
// Get returns the stored webhook's settings; every field is nil before one
|
||||
// is saved.
|
||||
func (s *Service) Get(ctx context.Context) (Settings, error) {
|
||||
row, err := db.New(s.st.Reader).GetChatSettings(ctx)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
@@ -440,14 +438,12 @@ func (s *Service) timestamp() string {
|
||||
}
|
||||
|
||||
func settingsFrom(row db.ChatSetting, hook Webhook) Settings {
|
||||
masked, updated := hook.Masked(), row.UpdatedAt
|
||||
masked := hook.Masked()
|
||||
return Settings{
|
||||
Configured: true,
|
||||
SpaceLabel: nullString(row.SpaceLabel),
|
||||
URLMasked: &masked,
|
||||
LastTestAt: nullString(row.LastTestAt),
|
||||
LastTestStatus: nullString(row.LastTestStatus),
|
||||
UpdatedAt: &updated,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user