-
Notifications
You must be signed in to change notification settings - Fork 438
Implement systematic parameter validation testing across all server routes #1528
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
base: development
Are you sure you want to change the base?
Implement systematic parameter validation testing across all server routes #1528
Conversation
|
Hi @huss, I created this draft PR to get some feedback on the approach before continuing further. Temporary accommodations made:
Few questions I had:
I just wanted to make sure I'm on the right track before continuing, thanks! |
huss
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks to @omarraf for another contribution. First, I would like to verify you are the only one who worked on this. I only see commits from you so just double checking. Second, I think the work is good. I read through some of the files to see how it is generally set up. I've made some comments in these files to give my thoughts. Given this is a draft, I wanted to get my thoughts back sooner rather than later and that some overall changes may be made, I did not do a full review. I hope what I did is useful and please let me know if there are specific files you would like me to look at that I did not. If anything is unclear, you have thoughts or need help then please just let me know.
|
|
||
| mocha.describe('Error Handling', () => { | ||
| mocha.it('should handle database connection errors gracefully', async () => { | ||
| const res = await chai.request(app) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you help me understand how a DB error would happen so this actually tests that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test as written doesn't actually simulate a real database error. It's just making a normal request and expecting it to work. To properly test database error handling, I'd need to either mock the database connection to throw an error or temporarily break the connection somehow. I'll remove this test since it's not actually testing error handling as intended.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe change the description strings so they more accurately reflect the test?
| }); | ||
|
|
||
| mocha.it('should reject invalid meter ID patterns', async () => { | ||
| const invalidPatterns = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this apply across several routes so should be centralized? Also, could the map pattern be the base for this pattern with the added ones?
|
Hi @huss, I really appreciate you getting back so quickly, I'm going to address all of the comments soon. Just to answer your question real quick:
Yes, I ended up working on this alone, as the rest of my team was pulled onto other priorities. |
|
Thanks again for taking the time to give your feedback @huss. I'll work through improving the PR and let you know if I need any help or need a review. |
…ts to include 429
…tests to use defined constants
…tring length and improve extra fields validation
… to include 429 for rate limiting
… and oversized parameters until proper handling is implemented
|
Hi @huss, I've addressed the comments you provided, let me know if I'm missing anything or need me to clarify. Changes:
Some web/integration tests are failing in CI, which I didn't address in this PR, didn't do enough digging to figure out why |
|
First, I want to apologize to @omarraf for the long delay in getting to this review. I made a mistake in my task tracking system and lost track of this PR. I'm very sorry this happened. Second, there are 27 failing tests where most are for Obvius. I think many (all?) are due to changes in route checking. Would it be okay if I try to address them and push out changes? |
|
No problem at all @huss. Feel free to make any changes |
- Make them alphabetical in declaration. - Rename string so it comes first so all go together. - Add more HTTP_CODE const responses and use in some places. - Some formatting.
- Use limits in test so always stay in sync. - formatting
Did not change comments. This does not fix other places such as routes. Also, possible some lines might be too long now.
Also formatting.
The new param checking was stricter so the historical use of email instead of the newer username was causing issues. This uses username no matter which is used in the request.
Note other values should be tested but another PR already deals with that.
|
I have seen periodic failure of tests on GitHub and the latest tests have 13 failures that are slightly different. The last 7 are 429/too many requests and I suspect the first 6 are OED's rate limiting error (the message is truncated so not certain). The first 6 might be because the new tests runs so fast and there are so many that it is hitting the limit for the first time. The last 7 are less clear but I did see that src/server/routes/obvius.js has a new limit on multer but I have not checked that possibility out. Note these tests do not fail locally for me. This is going to have to be tracked down and I'm open to ideas. |
huss
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks to @omarraf for the updated code. There are a lot of new tests and I appreciate the work in doing this. Overall, this is working well. I've tried to clear any resolved comments and added some more. I have not checked every test vs the route to verify all is being checked. I think that is a future task either after any changes are done or after this PR. One area with several comments is if tests could be generalized and the basic code reused across tests. For example, notes and maybe many strings are similar across params and routes. I don't know if you want to go into this now and it would be fine if it is done after this PR (not necessarily by you). Another point is that the code from the pending PR that started this process such as src/server/test/util/validationHelpers.js is not showing the git record of their having done the work. Did you change any of it or can it be included via that PR?
Please let me know your thoughts on the big picture items so we can decide how they will be addressed. There are other ones (such as using the HTTP codes throughout the coded as now done with route testing) that I plan to post as issues in the future and not in any comments on this PR.
Finally, I welcome your comments on the commits I made. I changed more than I might normally do because you have put in so much work and my review took so long. I hope this was okay.
| expect(res.status).to.equal(400); | ||
| }); | ||
|
|
||
| mocha.it.skip('should validate curr_start parameter', async () => { // TODO: re-enable once compareReadings rejects invalid ISO values without hitting DB |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OED prefers comments on the line above the code. Applies in several places.
| // Response should not reveal whether user exists or not | ||
| expect([400, 401]).to.include(res1.status); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm unclear how this tests rate limiting. Could you help me understand? Also, it raised another questing in my mind if we should rate limit in app.js for login attempts with a lower limit. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah looking back at it I think the comment is a bit misleading, it doesn't actually test rate limiting but just checks that all 10 requests fail with 400/401. I can either update the comment or add assertions for 429. Also I think adding a rate limit for logins is a good idea. We could add a seperate rate limiter instance in app.js just for /api/login with a lower threshold. I'm happy to add it in this PR or in a seperate issue.
| // Response should not reveal whether user exists or not | ||
| expect([400, 401]).to.include(res1.status); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There seems a lot of similarities to the user test above. Do you think it would be possible to create a more general test to handle both?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There definitely is overlap, both test username/password validation, malicious inputs, and field length limits. I kept them separate mainly because of different context and test organization. Authenticator tests validate middleware behavior (login endpoint), while user tests validate CRUD operations (/create, /edit, /delete)
That said, we could extract the shared test data (malicious username patterns, SQL injection strings, etc.) into a shared fixture file in src/server/test/util/. I can refactor it that way if need be
| const ISO_DURATION_REGEX = /^P(?!$)(\d+Y)?(\d+M)?(\d+D)?(T(\d+H)?(\d+M)?(\d+(\.\d+)?S)?)?$/; | ||
|
|
||
| function isValidIsoDateTime(value) { | ||
| return typeof value === 'string' && moment.parseZone(value, moment.ISO_8601, true).isValid(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see the value of checking if it is a string. However, doesn't the param validation do this before this runs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I think you're right, I just added it to be safe but I can remove
| expect([400, 500]).to.include(res.status); | ||
| }); | ||
| }); | ||
| }); No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these endpoints should be used in the .get and .describe as possible.
| const currEndRaw = req.query.curr_end; | ||
| const shiftRaw = req.query.shift; | ||
|
|
||
| if (!isValidIsoDateTime(currStartRaw) || !isValidIsoDateTime(currEndRaw) || !isValidIsoDuration(shiftRaw)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have a problem with the new checks but I want to record that we don't do them in other open routes. This could be made into an issue for the future if you agree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah sounds good, I'd be open to working on the issue as well if need be
|
|
||
| mocha.describe('Error Handling', () => { | ||
| mocha.it('should handle database connection errors gracefully', async () => { | ||
| const res = await chai.request(app) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe change the description strings so they more accurately reflect the test?
| const res = await chai.request(app) | ||
| .get('/api/ciks'); | ||
|
|
||
| expect([HTTP_CODE.OK, HTTP_CODE.INTERNAL_SERVER_ERROR]).to.include(res.status); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering why this and the next test would expect an error code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I added this by mistake, it was probably because I was running the tests locally very frequently so some failed with server errors. I'll clean this up
|
Thanks for the thorough review and commits you made @huss , they're very helpful and I appreciate your time to improve the code. The changes you made look good to me and I'll address the rest of your requested changes soon. Some of my thoughts on your questions:
Definitely think it makes sense to consolidate similar validation patterns that could share test logic, but I think it'd be a great follow-up task rather than doing it in this PR since it would be a significant refactor and a seperate PR may be easier to review.
Yeah, the validationHelpers.js I added directly from the previous PR on units.js when I first started this issue. I had left a comment at the top of the file conveying the original author and how the current file would be replaced by that PR. Since then I have built on their work and added several new helper functions (validateCommaSeparatedIdPatterns, expectValidCommaSeparatedIds, validateNoExtraFields, and modified testInvalidField to support multiple expected status codes). I want them to get credit for building the foundation so we can potentially take their original commit and add my modifications on top, or however you'd prefer to handle it. For the bigger picture items I think we should focus on addressing the rate-limiting problem in CI and doing a thorough audit of all tests vs routes making sure we have complete coverage. For future work we can open up a new issue to do the test generalization and http codes throughout the codebase, but I'm happy to implement in this PR if need be. |
…ting Merge remote-tracking branch 'origin/development' into systematic-testing
…r improved readability
|
@huss I addressed some of the comments you left, let me know if there's anything else I can change or clarify. Since this PR has been open for a while and covers a lot of ground, I'd like to wrap it up soon if there aren't any blocking issues. For the bigger refactoring items we discussed, I'm totally open to tackling those in follow-up issues. Same goes for the systematic http code audit and rate-limiting improvements in CI. Let me know if there's anything else you'd like me to address in this PR. Happy to keep working on this effort either way. |
|
Regarding the CI test failures, since most appear to be related to rate limiting, would it be possible to configure higher rate limits specifically for the test environment, or is there a preferred approach for handling this in CI? I wanted to check whether that would be considered good practice here. |
Description
This PR implements a comprehensive parameter validation testing framework across all routes in
src/server/routesas part of a major security initiative. The goal is to address these limitations via systematic testing using OED's standard Chai/Mocha frameworks.This work builds upon #1512, which established the foundational methodology for the
units.jsroute.Fixes #1497
Type of change
Checklist
Limitations