8000 fix: Validate Access-Control-Max-Age is non-negative (Fetch spec §3.2.1) by kawasima · Pull Request #80 · enkan/enkan · GitHub
[go: up one dir, main page]

Skip to content

fix: Validate Access-Control-Max-Age is non-negative (Fetch spec §3.2.1) - #80

Merged
kawasima merged 4 commits into
developfrom
fix/rfc-cors-maxage-validation
Mar 8, 2026
Merged

kawasima merged 4 commits into
developfrom
fix/rfc-cors-maxage-validation

Conversation

@kawasima
@kawasima kawasima commented Mar 8, 2026
Copy link
Copy Markdown
Collaborator

Summary

  • setMaxage() now throws MisconfigurationException with code core.INVALID_ARGUMENT when a negative value is passed
  • setMaxage(0L) is accepted (no Access-Control-Max-Age header is emitted, per the existing > 0 guard)
  • Added core.INVALID_ARGUMENT error code to misconfiguration.properties

Test plan

  • setMaxageNegativeThrowsMisconfigurationException — setMaxage(-1L) throws MisconfigurationException
  • setMaxageZeroIsAccepted — setMaxage(0L) does not throw
  • setMaxagePositiveEmitsHeader — setMaxage(3600L) emits Access-Control-Max-Age: 3600 in preflight response

Closes #72

🤖 Generated with Claude Code

kawasima and others added 3 commits March 8, 2026 12:14
setMaxage() now throws MisconfigurationException for negative values.
Zero is accepted (no header emitted per the existing > 0 guard).
Adds core.INVALID_ARGUMENT error code to misconfiguration.properties.

Closes #72

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…GUMENT

- Rename field/setter to maxAge (camelCase, consistent with JavaBeans convention and the HTTP header name)
- Fix core.INVALID_ARGUMENT.solution to be self-contained in properties file instead of passing free-text as {2} argument (enables i18n)
- Update test method names and calls accordingly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ge null

- Remove the free-text 4th argument from MisconfigurationException (solution is now self-contained in properties)
- Add null guard in handle() to prevent NPE on unboxing when maxAge is null
- Add setMaxAgeNullIsAccepted test to cover the null case

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR adds validation to CorsMiddleware.setMaxAge() to enforce that the Access-Control-Max-Age value is non-negative, per the Fetch specification §3.2.1. It also renames the public setter from setMaxage() to setMaxAge() for proper camelCase naming, and introduces a new general-purpose core.INVALID_ARGUMENT error code in misconfiguration.properties.

Changes:

  • CorsMiddleware.setMaxAge() now throws MisconfigurationException with code core.INVALID_ARGUMENT for negative values, and a null-check guards the maxAge > 0L header-emission condition
  • New core.INVALID_ARGUMENT error code (problem + solution) added to META-INF/misconfiguration.properties
  • Four new tests covering the null, negative, zero, and positive cases for setMaxAge()

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
enkan-web/src/main/java/enkan/middleware/CorsMiddleware.java Renames field/setter to camelCase maxAge, adds null-safety to the header guard, and adds negative-value validation with MisconfigurationException
enkan-web/src/test/java/enkan/middleware/CorsMiddlewareTest.java Adds four new test cases covering the null, negative, zero, and positive paths of setMaxAge()
enkan-core/src/main/resources/META-INF/misconfiguration.properties Introduces the new core.INVALID_ARGUMENT error code with problem/solution messages

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +259 to +275
void setMaxAgeNullIsAccepted() {
CorsMiddleware sut = new CorsMiddleware();
sut.setMaxAge(null); // no exception; header will not be emitted
}

@Test
void setMaxAgeNegativeThrowsMisconfigurationException() {
CorsMiddleware sut = new CorsMiddleware();
assertThatThrownBy(() -> sut.setMaxAge(-1L))
.isInstanceOf(MisconfigurationException.class);
}

@Test
void setMaxAgeZeroIsAccepted() {
CorsMiddleware sut = new CorsMiddleware();
sut.setMaxAge(0L); // no exception; header will not be emitted (> 0 guard)
}
Copilot AI Mar 8, 2026
Copy link

Choose a reason for hiding this comment

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

The setMaxAgeNullIsAccepted and setMaxAgeZeroIsAccepted tests only verify that no exception is thrown, but neither performs any end-to-end assertion that the Access-Control-Max-Age header is absent from a preflight response when maxAge is null or 0. Since the comment in the test body explicitly states "header will not be emitted", it would be more thorough to also assert doesNotContainKey("Access-Control-Max-Age") after issuing a preflight request, mirroring the approach used in setMaxAgePositiveEmitsHeader.

Copilot uses AI. Check for mistakes.
*/
public void setMaxage(Long maxage) {
this.maxage = maxage;
public void setMaxAge(Long maxAge) {
Copilot AI Mar 8, 2026
Copy link

Choose a reason for hiding this comment

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

The PR description's Summary and Test plan consistently refer to setMaxage() (lowercase 'age') and test names like setMaxageNegativeThrowsMisconfigurationException, but the actual code renames the method to setMaxAge() (camelCase 'Age') and the test methods are named setMaxAgeNegativeThrowsMisconfigurationException, setMaxAgeZeroIsAccepted, and setMaxAgePositiveEmitsHeader. The PR description should be updated to reflect the actual method name setMaxAge() and the accurate test names.

Copilot uses AI. Check for mistakes.
*/
public void setMaxage(Long maxage) {
this.maxage = maxage;
public void setMaxAge(Long maxAge) {
Copilot AI Mar 8, 2026
Copy link

Choose a reason for hiding this comment

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

Renaming the public method setMaxage(Long) to setMaxAge(Long) is a breaking change for external consumers of this API. Any code outside this repository that calls setMaxage() will fail to compile after this change. If backward compatibility is required, consider keeping the old setMaxage() method as a deprecated delegate to the new setMaxAge() method.

Copilot uses AI. Check for mistakes.
core.MISSING_IMPLEMENTATION.problem={0}
core.MISSING_IMPLEMENTATION.solution={0}
core.INVALID_ARGUMENT.problem=Argument `{0}` has an invalid value: {1}.
core.INVALID_ARGUMENT.solution=Provide a valid value for `{0}`. See the documentation for allowed values.
Copilot AI Mar 8, 2026
Copy link

Choose a reason for hiding this comment

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

The solution message "See the documentation for allowed values" is less actionable than other entries in this file, which provide specific guidance (e.g., core.NULL_ARGUMENT.solution says "Provide a non-null value for {0}"). Since the only constraint this error code currently enforces is a non-negative value, the solution could include a hint such as "must be a non-negative integer" directly in the message, or use an additional placeholder so callers can supply the constraint description alongside the argument name and value.

Suggested change
core.INVALID_ARGUMENT.solution=Provide a valid value for `{0}`. See the documentation for allowed values.
core.INVALID_ARGUMENT.solution=Provide a non-negative integer value for `{0}`.

Copilot uses AI. Check for mistakes.
- core.INVALID_ARGUMENT.solution: use more actionable message with constraint hint
- setMaxAgeNullIsAccepted / setMaxAgeZeroIsAccepted: add preflight assertion that Access-Control-Max-Age header is absent

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@kawasima
kawasima merged commit a939e64 into B81A develop Mar 8, 2026
@kawasima
kawasima deleted the fix/rfc-cors-maxage-validation branch March 8, 2026 03:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0