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

refactor(ses): getenv detects more errors #2690

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

erights
Copy link
Contributor

@erights erights commented Jan 13, 2025

Closes: #XXXX
Refs: #1710

Description

#1710 enhanced getEnvironmentOption with an optional third argument listing all the other allowed choices aside from the default choice. Previously, we had only manual code to detect and complain of unrecognized environment option values. #1710 provides a more declarative form, and reuses the checking-and-complaining logic. However, following #1710 we did not retire much of the previous ad-hoc manual code, nor did we make enough use of this declarative alternative. This PR fixes that.

Security Considerations

An advantage to the old ad-hoc manual style is that the enumeration of all possible alternatives was textually close to the dispatch on the alternative. This makes it easy to keep the two in sync. Thus, a comparative disadvantage of this PR is their separation, leading to maintenance hazards if one is updated but not the other. For example, if the declarative form adds another option that the dispatch code does not take into account, that other option might fall into the dispatch's fall-through case. This may or may not be good, depending on whether the fall through case is carefully chosen to also be appropriate for new not-previously-recognized options.

Scaling Considerations

none

Documentation Considerations

none

Testing Considerations

The text of the error messages for unrecognized options will likely be different. But aside from that there should be no observable difference. Since we do not consider a change to the text of an error message a correctness concern, we consider this PR to effectively be a pure refactor.

Compatibility and Upgrade Considerations

Both before and after this PR, we generally reject unrecognized environment options settings. This makes it difficult to grow existing options with new settings that are ignored if seen by previous code. This hinders plausible development patterns. This PR by itself does not change that. But by centralizing the detect-and-complaint logic and making it more declarative, perhaps we become better set up to address this issue later.

@erights erights self-assigned this Jan 13, 2025
@erights erights force-pushed the markm-getenv-detects-more-errors branch from 573a6d7 to 5a3139b Compare January 18, 2025 02:15
@erights erights force-pushed the markm-getenv-detects-more-errors branch from 5a3139b to 3d29d44 Compare January 27, 2025 21:16
@erights erights marked this pull request as ready for review January 27, 2025 21:37
@erights erights requested review from kriskowal and mhofman January 27, 2025 21:37
@erights erights force-pushed the markm-getenv-detects-more-errors branch from 3d29d44 to 5adc8ee Compare February 17, 2025 03:04
Copy link
Contributor

@gibson042 gibson042 left a comment

Choose a reason for hiding this comment

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

Rather than manually typing the options, another possibility is updating getEnvironmentOption to do it, albeit at the cost of casting its third argument to @type {const}, as visible by hovering over bindings in the final region of this demonstration.

An advantage to the old ad-hoc manual style is that the enumeration of all possible alternatives was textually close to the dispatch on the alternative. This makes it easy to keep the two in sync. Thus, a comparative disadvantage of this PR is their separation, leading to maintenance hazards if one is updated but not the other.

I think typing can address this too, as also included in the above demonstration.

/** @typedef {(typeof overrideTamingOptions)[number]} OverrideTaming */
const overrideTamingOptions = /** @type {const} */ (['moderate', 'min', 'severe']);

const overrideTaming = getenv('LOCKDOWN_OVERRIDE_TAMING', 'moderate', overrideTamingOptions);

/**
 * @param {OverrideTaming} overrideTaming
 */
function useOverrideTaming(overrideTaming) {
  if (!['moderate', 'min', 'severe'].includes(overrideTaming)) throw Error();
}

Comment on lines +165 to +168
errorTaming = getenv('LOCKDOWN_ERROR_TAMING', 'safe', [
'unsafe',
'unsafe-debug',
]),
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
errorTaming = getenv('LOCKDOWN_ERROR_TAMING', 'safe', [
'unsafe',
'unsafe-debug',
]),
errorTaming = /** @type {'safe' | 'unsafe' | 'unsafe-debug'} */ (
getenv('LOCKDOWN_ERROR_TAMING', 'safe', ['unsafe', 'unsafe-debug'])
),

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi @gibson042 , I like the idea, but I'd like to do it while avoiding redundancy with types.d.ts. For this example

errorTaming?: 'safe' | 'unsafe' | 'unsafe-debug';

What's the best way for me to do both?

Copy link
Contributor

Choose a reason for hiding this comment

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

The way to avoid repetition within TypeScript is by referencing the types of types.d.ts:

Suggested change
errorTaming = getenv('LOCKDOWN_ERROR_TAMING', 'safe', [
'unsafe',
'unsafe-debug',
]),
errorTaming = /** @type {Required<LockdownOptions>['errorTaming']} */ (
getenv('LOCKDOWN_ERROR_TAMING', 'safe', ['unsafe', 'unsafe-debug'])
),

But note that you won't get full type benefits unless the points of consumption are also updated, e.g. packages/ses/src/error/tame-error-constructor.js

@@ -31,6 +31,11 @@ const tamedMethods = {
 };
 let initialGetStackString = tamedMethods.getStackString;
 
+/** @import {LockdownOptions} from '../../types.js' */
+/**
+ * @param {LockdownOptions['errorTaming']} [errorTaming]
+ * @param {LockdownOptions['stackFiltering']} [stackFiltering]
+ */
 export default function tameErrorConstructor(
   errorTaming = 'safe',
   stackFiltering = 'concise',

However, since these values have runtime effects, they must necessarily be defined in JavaScript. So the least repetitive approach would be to reflect that definition into TypeScript as suggested at #2690 (review), e.g.

/** @typedef {(typeof errorTamingOptions)[number]} ErrorTaming */
const errorTamingOptions = /** @type {const} */ (
  ['safe', 'unsafe', 'unsafe-debug']
);

Comment on lines +183 to +184
regExpTaming = getenv('LOCKDOWN_REGEXP_TAMING', 'safe', ['unsafe']),
localeTaming = getenv('LOCKDOWN_LOCALE_TAMING', 'safe', ['unsafe']),
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
regExpTaming = getenv('LOCKDOWN_REGEXP_TAMING', 'safe', ['unsafe']),
localeTaming = getenv('LOCKDOWN_LOCALE_TAMING', 'safe', ['unsafe']),
regExpTaming = /** @type {'safe' | 'unsafe'} */ (
getenv('LOCKDOWN_REGEXP_TAMING', 'safe', ['unsafe'])
),
localeTaming = /** @type {'safe' | 'unsafe'} */ (
getenv('LOCKDOWN_LOCALE_TAMING', 'safe', ['unsafe'])
),

Comment on lines 192 to 202
stackFiltering = getenv('LOCKDOWN_STACK_FILTERING', 'concise', ['verbose']),
domainTaming = getenv('LOCKDOWN_DOMAIN_TAMING', 'safe', ['unsafe']),
evalTaming = getenv('LOCKDOWN_EVAL_TAMING', 'safeEval', [
'unsafeEval',
'noEval',
]),
overrideDebug = arrayFilter(
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
stackFiltering = getenv('LOCKDOWN_STACK_FILTERING', 'concise', ['verbose']),
domainTaming = getenv('LOCKDOWN_DOMAIN_TAMING', 'safe', ['unsafe']),
evalTaming = getenv('LOCKDOWN_EVAL_TAMING', 'safeEval', [
'unsafeEval',
'noEval',
]),
overrideDebug = arrayFilter(
stackFiltering = /** @type {'concise' | 'verbose'} */ (
getenv('LOCKDOWN_STACK_FILTERING', 'concise', ['verbose'])
),
domainTaming = /** @type {'unsafe' | 'safe'} */ (
getenv('LOCKDOWN_DOMAIN_TAMING', 'safe', ['unsafe'])
),
evalTaming = /** @type {'safeEval' | 'unsafeEval' | 'noEval'} */ (
getenv('LOCKDOWN_EVAL_TAMING', 'safeEval', [
'unsafeEval',
'noEval',
])
),
/** @type {string[]} */
overrideDebug = arrayFilter(

Comment on lines 203 to 211
legacyRegeneratorRuntimeTaming = getenv(
'LOCKDOWN_LEGACY_REGENERATOR_RUNTIME_TAMING',
'safe',
['unsafe-ignore'],
),
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
legacyRegeneratorRuntimeTaming = getenv(
'LOCKDOWN_LEGACY_REGENERATOR_RUNTIME_TAMING',
'safe',
['unsafe-ignore'],
),
legacyRegeneratorRuntimeTaming = /** @type {'safe' | 'unsafe-ignore'} */ (
getenv(
'LOCKDOWN_LEGACY_REGENERATOR_RUNTIME_TAMING',
'safe',
['unsafe-ignore'],
)
),

),
__hardenTaming__ = getenv('LOCKDOWN_HARDEN_TAMING', 'safe'),
__hardenTaming__ = getenv('LOCKDOWN_HARDEN_TAMING', 'safe', ['unsafe']),
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
__hardenTaming__ = getenv('LOCKDOWN_HARDEN_TAMING', 'safe', ['unsafe']),
__hardenTaming__ = /** @type {'safe' | 'unsafe'} */ (
getenv('LOCKDOWN_HARDEN_TAMING', 'safe', ['unsafe'])
),

@erights erights force-pushed the markm-getenv-detects-more-errors branch from 5adc8ee to b26dc32 Compare March 12, 2025 20:15
@erights erights force-pushed the markm-getenv-detects-more-errors branch from b26dc32 to 84234ce Compare March 12, 2025 21:37
@erights erights changed the base branch from master to markm-lockdown-options-kebob-case March 12, 2025 21:39
Base automatically changed from markm-lockdown-options-kebob-case to master March 12, 2025 23:16
erights added a commit that referenced this pull request Mar 12, 2025
Closes: #XXXX
Refs: #961 #2690 #2723

## Description

#961 deviated from our general convention that lockdown option values be
kebob-case, instead adding `evalTaming:` option values `safeEval`,
`unsafeEval`, `noEval`. (I approved #961 at the time, apparently without
noticing this discrepancy.) This PR fixes those to be `safe-eval`,
`unsafe-eval`, and `no-eval`. But to avoid breaking any old usage, this
PR ensure the only names continue to work for now, but always commented
in the code as "deprecated".

This PR does only that. Other changes to the relevant lockdown option or
relevant lockdown options machinery are left to #2723 or #2690
respectively. I request that this PR go first, with those others
adjusting to this one.

### Security Considerations

none
### Scaling Considerations

non
### Documentation Considerations

This PR simply changes the documentation to use the new names without
ever mentioning the deprecated old names. That seems like an appropriate
simplification for the docs.

### Testing Considerations

With a bit of duplication and renaming, we now test the new names and
the old deprecated names.

### Compatibility Considerations

To avoid breaking any old usage, this PR ensure the only names continue
to work for now, but always commented in the code as "deprecated". It
would be very nice to eventually be able to retire the deprecated names,
but I find it hard to imagine how we'd test that it is safe to do so.

### Upgrade Considerations

Nothing BREAKING, since the old deprecated names continue to work.

- [x] Update `NEWS.md` for user-facing changes.
@erights erights force-pushed the markm-getenv-detects-more-errors branch from 84234ce to e6d8423 Compare March 12, 2025 23:26
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