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

Move eval to parseInt #3328

Closed
wants to merge 4 commits into from
Closed

Conversation

mawburn
Copy link
Sponsor Contributor

@mawburn mawburn commented Jul 11, 2024

Summary

Using parseInt is preferred over eval for handling environment variables because parseInt safely converts a string to an integer without executing any code. In contrast, eval can execute arbitrary code, which poses a significant security risk if the environment variable contains malicious input. By using parseInt, we mitigate the risk of code injection attacks and ensure that only numerical values are processed.

While this isn't used in a whole lot of places and the chances of a malicious environment variable is low, it's also a significant performance boost.

Running this script:

const { performance } = require('perf_hooks')

const numStr = '12345'
const iterations = 10000000

const measureTime = (fn) => {
  const start = performance.now()
  fn()
  const end = performance.now()
  return end - start
}

const evalTest = () => {
  for (let i = 0; i < iterations; i++) {
    eval(numStr)
  }
}

const parseIntTest = () => {
  for (let i = 0; i < iterations; i++) {
    parseInt(numStr, 10)
  }
}

const evalTime = measureTime(evalTest)
const parseIntTime = measureTime(parseIntTest)

console.log(`eval time: ${evalTime.toFixed(2)} ms`)
console.log(`parseInt time: ${parseIntTime.toFixed(2)} ms`)
console.log(`parseInt is ${(evalTime / parseIntTime).toFixed(2)} times faster than eval`)

I get this output on my local machine (MBA M1-series first gen):

eval time: 1745.03 ms
parseInt time: 39.69 ms
parseInt is 43.97 times faster than eval

Change Type

Please delete any irrelevant options.

  • Bug fix (non-breaking change which fixes an issue)

Testing

Tests should pass as normal and no changes should be seen within the app. It should work the same, unless there were environment variable based code being injected.

Checklist

Please delete any irrelevant options.

  • My code adheres to this project's style guidelines
  • I have performed a self-review of my own code
  • Local unit tests pass with my changes

Copilot Summary

This pull request includes changes to improve the security and robustness of the codebase. The most important changes involve replacing the eval function with parseInt in several files to parse environment variables. This change improves security by avoiding the potential execution of malicious code.

Changes to environment variable parsing:

Changes to utility functions:

  • api/server/utils/math.js: Replaced eval with parseInt in the math function and updated the error message to reflect the change.

Comment on lines +35 to +37
const value = parseInt(str, 10);

if (typeof value !== 'number') {
if (isNaN(value)) {
Copy link

Choose a reason for hiding this comment

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

I assume that eval is correct in this place since there is a regex above checking for a valid string containing only digits and mathematical characters, i.e. numerical equation.

Copy link
Sponsor Contributor Author

Choose a reason for hiding this comment

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

The parseInt will turn it into a base 10 number and isNan will check if it succeeded.

@Qubhis
Copy link

Qubhis commented Aug 18, 2024

The general issue with this is to change the .env values for REFRESH_TOKEN_EXPIRY and SESSION_EXPIRY as they are written as calculation, e.g. 1000 * 60 * 15

While this is more readable in env, I would rather include comments to the .env file than execute eval in unsafe places. Ot at least used the math function which checks for the string validity

@danny-avila
Copy link
Owner

While I think this update is necessary I'm concerned of backwards compatibility. Thinking how to handle this gracefully so it doesn't create so many tickets.

@Qubhis
Copy link

Qubhis commented Aug 18, 2024

I see. So, what about keeping only the math function and use it instead of eval. Then, the math function gonna check for the string if it contains only digits and then it uses parseInt, otherwise it would use the eval (as there is the regex checking for the valid string equation). There gonna be an announcement that users should migrate their env values to contain only digits, for performance improvements. And the breaking change would be then introduced only when releasing a major version (in future). ?

@mawburn
Copy link
Sponsor Contributor Author

mawburn commented Aug 19, 2024

The general issue with this is to change the .env values for REFRESH_TOKEN_EXPIRY and SESSION_EXPIRY as they are written as calculation, e.g. 1000 * 60 * 15

I honestly didn't think of that. Good catch. This change isn't really worth it in that case. The alternatives are slower, this is only executed a couple times during the lifecycle of the app, and the attack vector is low.

It's not bad to use 1000 * 60 * 15 when setting a variable, but it's bad practice putting it in ENV files... but it's already established in the ENV files that this is ok.

If your ENV vars are a legitimate attack vector, then you have much more serious problems. 🤣

@mawburn mawburn closed this Aug 19, 2024
@mawburn mawburn deleted the mawburn/fix-eval branch August 19, 2024 13:53
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.

3 participants