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

Fix security errors in lint steps #2061

Merged
merged 26 commits into from
Sep 16, 2024
Merged

Fix security errors in lint steps #2061

merged 26 commits into from
Sep 16, 2024

Conversation

tstirrat15
Copy link
Contributor

Description

A new go version was published that fixes a security vulnerability. This takes that patch.

Changes

  • Bump go version in root and magefile

Testing

Review. See that things pass.

@tstirrat15 tstirrat15 requested a review from a team as a code owner September 9, 2024 16:48
@github-actions github-actions bot added the area/dependencies Affects dependencies label Sep 9, 2024
ecordell
ecordell previously approved these changes Sep 9, 2024
Copy link
Contributor

@ecordell ecordell left a comment

Choose a reason for hiding this comment

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

there's another go.mod in e2e as well

@github-actions github-actions bot added the area/tooling Affects the dev or user toolchain (e.g. tests, ci, build tools) label Sep 10, 2024
@github-actions github-actions bot added the area/api v1 Affects the v1 API label Sep 10, 2024
@github-actions github-actions bot added the area/datastore Affects the storage system label Sep 10, 2024
@github-actions github-actions bot added area/CLI Affects the command line area/dispatch Affects dispatching of requests labels Sep 11, 2024
Copy link
Contributor Author

@tstirrat15 tstirrat15 left a comment

Choose a reason for hiding this comment

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

See additional comments

@@ -68,7 +68,7 @@ func (gc *fakeGC) DeleteBeforeTx(_ context.Context, rev datastore.Revision) (Del

revInt := rev.(revisions.TransactionIDRevision).TransactionID()

return gc.deleter.DeleteBeforeTx(int64(revInt))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This seemed like a type mismatch, so I propagated it through. I'd welcome a check of that assumption.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, seems so

Comment on lines 123 to 127
// We checked for underflow above, so the current byte size
// should fit in a uint64
currentByteSize, _ := safecast.ToUint64(ch.currentByteSize)

if currentByteSize > ch.maxByteSize {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

As I was going through these, I tended to go one of four ways:

  1. Something about the context makes it obvious that the cast is safe, so I used safecast to mark it as such by ignoring the error
  2. There's potential for a miscast and it seems like the situation would represent a bug, so I return a MustBugF
  3. There's potential for a miscast and it seems like it could realistically happen, so I return a normal error
  4. It was possible to redeclare the code in such a way that largely avoided casting issues

Copy link
Member

Choose a reason for hiding this comment

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

I think we should return the error as a MustBugf even if we know it can be safe; it is "extra" code but it means if anything changes we're guaranteed to catch the error vs having a silent bug appear

Comment on lines +90 to +97
for seed == 0 {
// Sum64 returns a uint64, and safecast will return 0 if it's not castable,
// which will happen about half the time (?). We just keep running it until
// we get a seed that fits in the box.
// Subtracting math.MaxInt64 should mean that we retain the entire range of
// possible values.
seed, _ = safecast.ToInt64(new(maphash.Hash).Sum64() - math.MaxInt64)
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This felt a little wonky but seemed to capture the desired effect.

Copy link
Member

Choose a reason for hiding this comment

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

I think we should just make the seed a uint64 then? @ecordell?

Copy link
Contributor

@ecordell ecordell Sep 16, 2024

Choose a reason for hiding this comment

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

NewSource() and Seed() both take an int64, which is what this value is used for.

interpreting the uint64 bits as int64 bits doesn't really matter for this application. the requirements for this use of rand are super minimal, we really just want "don't do the same exact thing on every replica 100% of the time".

can we just divide it by 2 and then cast? or is there a way to mark this specific "unsafe" cast as "I don't care about wraparound at all"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, we can // ignore gosec for this particular case, or we can use the approach I came up with. I'm fine with either.

Comment on lines +306 to +310
maxConns, err := safecast.ToInt32(*opts.MaxOpenConns)
if err != nil {
return err
}
pgxConfig.MaxConns = maxConns
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This would require a user to put a very specific value in, but we might as well catch it.

xminInt := int64(decoded.Xmin)
xminInt, err := safecast.ToInt64(decoded.Xmin)
if err != nil {
return datastore.NoRevision, spiceerrors.MustBugf("Could not cast xmin to int64")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This seemed like it would be a bug since we were the ones who stored the revision in the first place

Comment on lines +706 to +707
uintPageSize, err := safecast.ToUint32(pageSize)
require.NoError(err)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made a bunch of these changes to tests. They're overkill, especially since the values are statically defined in most cases, so I can walk these back out if desired.

Comment on lines +73 to +76
// We make an assumption that if the cast fails, it's because the value
// was too big, so we set to maxint in that case.
uintCost = math.MaxUint32
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please check this

Copy link
Member

Choose a reason for hiding this comment

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

Log it as well, but this seems fine

Comment on lines +473 to +476
maxRetries, err := safecast.ToUint8(opts.MaxRetries)
if err != nil {
return nil, errors.New("max-retries could not be cast to uint8")
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Another branch that we're unlikely to hit, but it could result in very weird behavior if we do.

@@ -42,14 +43,18 @@ func runAssertions(devContext *DevContext, assertions []blocks.Assertion, expect
for _, assertion := range assertions {
tpl := tuple.MustFromRelationship[*v1t.ObjectReference, *v1t.SubjectReference, *v1t.ContextualizedCaveat](assertion.Relationship)

// NOTE: zeroes are fine here to mean "unknown"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If safecast can't successfully cast, it returns zero. For pointing at line numbers, I think this should be fine (especially since you'd have to do something weird to end up out of bounds here).

Copy link
Member

Choose a reason for hiding this comment

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

We should either log the error or have it panic

@@ -13,16 +17,18 @@ func MustEnsureUInt32(value int) uint32 {

// EnsureUInt32 ensures that the specified value can be represented as a uint32.
func EnsureUInt32(value int) (uint32, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These functions are basically doing safecast things anyway, so I rewrote them in terms of safecast to silence the errors. We can upstream it later.

@github-actions github-actions bot added the area/schema Affects the Schema Language label Sep 12, 2024
if ch.currentByteSize > int64(ch.maxByteSize) {
// We checked for underflow above, so the current byte size
// should fit in a uint64
currentByteSize, _ := safecast.ToUint64(ch.currentByteSize)
Copy link
Member

Choose a reason for hiding this comment

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

return the error even if we think it cannot be hit; its safer than ignoring it

Comment on lines 123 to 127
// We checked for underflow above, so the current byte size
// should fit in a uint64
currentByteSize, _ := safecast.ToUint64(ch.currentByteSize)

if currentByteSize > ch.maxByteSize {
Copy link
Member

Choose a reason for hiding this comment

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

I think we should return the error as a MustBugf even if we know it can be safe; it is "extra" code but it means if anything changes we're guaranteed to catch the error vs having a silent bug appear

@@ -68,7 +68,7 @@ func (gc *fakeGC) DeleteBeforeTx(_ context.Context, rev datastore.Revision) (Del

revInt := rev.(revisions.TransactionIDRevision).TransactionID()

return gc.deleter.DeleteBeforeTx(int64(revInt))
Copy link
Member

Choose a reason for hiding this comment

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

Yeah, seems so

Comment on lines +90 to +97
for seed == 0 {
// Sum64 returns a uint64, and safecast will return 0 if it's not castable,
// which will happen about half the time (?). We just keep running it until
// we get a seed that fits in the box.
// Subtracting math.MaxInt64 should mean that we retain the entire range of
// possible values.
seed, _ = safecast.ToInt64(new(maphash.Hash).Sum64() - math.MaxInt64)
}
Copy link
Member

Choose a reason for hiding this comment

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

I think we should just make the seed a uint64 then? @ecordell?

@@ -141,12 +142,16 @@ func (p *RetryPool) ID() string {

// MaxConns returns the MaxConns configured on the underlying pool
func (p *RetryPool) MaxConns() uint32 {
return uint32(p.pool.Config().MaxConns)
// This should be non-negative
maxConns, _ := safecast.ToUint32(p.pool.Config().MaxConns)
Copy link
Member

Choose a reason for hiding this comment

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

If we have enough of these examples, we should add our own MustToUint32 method and just have it panic if it fails

if delLimit > 0 && uint64(modified.RowsAffected()) == delLimit {
rowsAffected, err := safecast.ToUint64(modified.RowsAffected())
if err != nil {
return false, spiceerrors.MustBugf("could not cast RowsAffected to uint64")
Copy link
Member

Choose a reason for hiding this comment

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

always wrap the error when using MustBugf

Copy link
Member

Choose a reason for hiding this comment

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

In fact, we should probably add a custom linter to ensure this at some point. Might be worth filing an issue

Comment on lines +73 to +76
// We make an assumption that if the cast fails, it's because the value
// was too big, so we set to maxint in that case.
uintCost = math.MaxUint32
}
Copy link
Member

Choose a reason for hiding this comment

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

Log it as well, but this seems fine

@@ -42,14 +43,18 @@ func runAssertions(devContext *DevContext, assertions []blocks.Assertion, expect
for _, assertion := range assertions {
tpl := tuple.MustFromRelationship[*v1t.ObjectReference, *v1t.SubjectReference, *v1t.ContextualizedCaveat](assertion.Relationship)

// NOTE: zeroes are fine here to mean "unknown"
Copy link
Member

Choose a reason for hiding this comment

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

We should either log the error or have it panic

Copy link
Member

@josephschorr josephschorr left a comment

Choose a reason for hiding this comment

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

LGTM

@tstirrat15 tstirrat15 added this pull request to the merge queue Sep 16, 2024
@tstirrat15 tstirrat15 merged commit d9162eb into main Sep 16, 2024
22 checks passed
@tstirrat15 tstirrat15 deleted the fix-security-errors branch September 16, 2024 17:03
@github-actions github-actions bot locked and limited conversation to collaborators Sep 16, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area/api v1 Affects the v1 API area/CLI Affects the command line area/datastore Affects the storage system area/dependencies Affects dependencies area/dispatch Affects dispatching of requests area/schema Affects the Schema Language area/tooling Affects the dev or user toolchain (e.g. tests, ci, build tools)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants