Skip to content

feat(js-x-ray): add weak-argon2 detection probe#592

Open
HoyeongJeon wants to merge 2 commits intoNodeSecure:masterfrom
HoyeongJeon:feat/weak-argon2
Open

feat(js-x-ray): add weak-argon2 detection probe#592
HoyeongJeon wants to merge 2 commits intoNodeSecure:masterfrom
HoyeongJeon:feat/weak-argon2

Conversation

@HoyeongJeon
Copy link
Copy Markdown
Contributor

Adds an optional weak-argon2 probe to detect insecure usage of crypto.argon2() / crypto.argon2Sync().

Detection targets:

  • Wrong algorithm (argon2d or argon2i instead of argon2id) — OWASP recommends argon2id as it combines side-channel resistance and GPU cracking resistance
  • Weak parameters (memory/passes/parallelism below OWASP minimum) — validates against five OWASP-recommended parameter combinations trading memory for iterations
  • Hardcoded nonce (string literal as nonce argument) — salt must be randomly generated, not hardcoded

The warning is optional and disabled by default.

Partially addresses #506 (Argon2 part)

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Mar 28, 2026

🦋 Changeset detected

Latest commit: efc68e4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@nodesecure/js-x-ray Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Comment thread workspaces/js-x-ray/src/probes/isWeakArgon2.ts Outdated
function main(node: ESTree.CallExpression, ctx: ProbeContext) {
const { sourceFile } = ctx;
const algorithm = node.arguments.at(0);
if (isLiteral(algorithm)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

the isLiteral case will be the most common one for sure, but imo we should also check the case where it is an identifier:

const algo = "argon2d";

crypto.argon2(algo, {
  message: "password",
  nonce: crypto.randomBytes(16),
  memory: 47104,
  passes: 1,
  parallelism: 1,
  tagLength: 64
});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

you can achieve that like this:

// when the type is "Identifier"
const literalIdentifier = tracer.literalIdentifiers.get(node.name);

Comment thread docs/weak-argon2.md

## Introduction

Detect usage of **weak Argon2** parameters with the Node.js core `crypto.argon2()` / `crypto.argon2Sync()` functions. This probe checks for:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
Detect usage of **weak Argon2** parameters with the Node.js core `crypto.argon2()` / `crypto.argon2Sync()` functions. This probe checks for:
Detect usage of **weak Argon2** parameters with the Node.js core `crypto.argon2()` / `crypto.argon2Sync()` functions.
Checks for:

I don't think we should use the term "probe" since it is an implementation detail of Js-x-ray

(prop) => prop.key.type === "Identifier" && prop.key.name === "nonce"
);

if (nonce && isLiteral(nonce.value)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same here we should check for identifier that are hardcoded

const properties = parameters.properties.filter(
(prop): prop is ESTree.Property => prop.type === "Property"
);
const memory = extractNumericParam(properties, ["memory"]);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

i guess a better way to do it would be to extract each properties in one loop then extract the numeric value

): number | null {
for (const prop of properties) {
if (
prop.key.type === "Identifier" &&
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same here, it would be nice to check for identifier that are literal as well

[7168, 5, 1]
];

const tracedFunctions = new Set(["crypto.argon2", "crypto.argon2Sync"]);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

i guess, just creating an array is ok here, you already know that there will be no duplicate

@clemgbld clemgbld requested a review from fraxken March 29, 2026 08:40
}
}

const nonce = properties.find(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same here it should be part of just one loop rather than iterating once again

if (isWeakArgon2Params(memory, iteration, parallelism)) {
sourceFile.warnings.push(
generateWarning("weak-argon2", {
value: "weak-parameters",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

in the value it would be also more helpful to add the type of the parameters that is weak:
```value: weak-parameter: ${type}`;

…k-argon2 warningadd weak-argon2 detection probe
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