-
-
Notifications
You must be signed in to change notification settings - Fork 18
Fix yarn audit vulnerabilities across backend and frontend #1692
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -1,9 +1,19 @@ | ||||||||
| import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms'; | ||||||||
| import is_ip_private from 'private-ip'; | ||||||||
| import * as ipaddr from 'ipaddr.js'; | ||||||||
| import isFQDN from 'validator/es/lib/isFQDN'; | ||||||||
| import isIP from 'validator/es/lib/isIP'; | ||||||||
| import { DBtype } from '../models/connection'; | ||||||||
|
|
||||||||
| const PRIVATE_RANGES = new Set(['private', 'loopback', 'linkLocal', 'unspecified', 'carrierGradeNat', 'uniqueLocal']); | ||||||||
|
|
||||||||
| function isPrivateIP(ip: string): boolean { | ||||||||
| try { | ||||||||
| return PRIVATE_RANGES.has(ipaddr.process(ip).range()); | ||||||||
| } catch { | ||||||||
| return false; | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| export function hostnameValidation(dbType: DBtype): ValidatorFn { | ||||||||
| return (control: AbstractControl): ValidationErrors | null => { | ||||||||
| if (control.value) { | ||||||||
|
|
@@ -21,7 +31,7 @@ export function hostnameValidation(dbType: DBtype): ValidatorFn { | |||||||
| hostname = hostname.replace(/^mongodb\+srv:\/\//, ''); | ||||||||
| } | ||||||||
|
|
||||||||
| if (control.value === 'localhost' || (isIP(control.value) && is_ip_private(control.value))) | ||||||||
| if (control.value === 'localhost' || (isIP(control.value) && isPrivateIP(control.value))) | ||||||||
|
||||||||
| if (control.value === 'localhost' || (isIP(control.value) && isPrivateIP(control.value))) | |
| const normalizedHostname = hostname.toLowerCase(); | |
| if (normalizedHostname === 'localhost' || (isIP(hostname) && isPrivateIP(hostname))) |
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.
Security: Private IP check bypassed for DynamoDB connections.
The private IP validation at line 34 uses control.value (raw input), but for DynamoDB the hostname is extracted from the https:// prefixed URL. When a user enters https://192.168.1.1:
isIP("https://192.168.1.1")returnsfalse, skipping the private IP checkhostname = "192.168.1.1"passes theisIP(hostname)check at line 36
This defeats the SSRF mitigation this PR intends to address. The check should use the processed hostname variable.
🔒 Proposed fix to check processed hostname
- if (control.value === 'localhost' || (isIP(control.value) && isPrivateIP(control.value)))
+ if (hostname === 'localhost' || (isIP(hostname) && isPrivateIP(hostname)))
return { isLocalhost: true };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/app/validators/hostname.validator.ts` around lines 34 - 36, The
private IP/localhost check currently uses control.value which can include a
scheme and bypass the SSRF check; update the conditional to use the processed
hostname variable instead (e.g., replace uses of control.value in the first if
with hostname) so that you call isIP(hostname) && isPrivateIP(hostname) and
compare hostname === 'localhost' before validating isIP(hostname) ||
isFQDN(hostname); keep the existing calls to isIP, isPrivateIP and isFQDN and
ensure the check occurs prior to the final validity check.
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.
🧩 Analysis chain
🏁 Script executed:
Repository: rocket-admin/rocketadmin
Length of output: 904
🏁 Script executed:
Repository: rocket-admin/rocketadmin
Length of output: 117
🏁 Script executed:
Repository: rocket-admin/rocketadmin
Length of output: 91
🏁 Script executed:
Repository: rocket-admin/rocketadmin
Length of output: 1545
🌐 Web query:
ipaddr.js range() method IPv6-mapped addresses behavior documentation💡 Result:
The ipaddr.js library provides a range method on both IPv4 and IPv6 address instances. This method returns a predefined name (e.g., "unicast", "reserved", "private") for special IP ranges defined by protocols, based on matching the address against hardcoded CIDR ranges in the source code. For IPv6 addresses, including IPv4-mapped addresses (e.g., ::ffff:192.168.1.1), the range method uses IPv6-specific special ranges listed in the source. One explicit range is "ipv4Mapped": [[new IPv6([0, 0, 0, 0, 0, 0xffff, 0, 0]), 96]], covering ::ffff:0:0/96 per RFC 4291. An IPv4-mapped IPv6 address like ipaddr.parse('::ffff:192.168.1.1').range will match the "ipv4Mapped" range if it fits ::ffff:0:0/96, returning "ipv4Mapped". It does not automatically convert to the underlying IPv4 address and use IPv4 ranges (e.g., "private" for 192.168.1.1); that behavior is only in the separate global ipaddr.process method for parsing. The exact ranges are defined in the library source at https://github.com/whitequark/ipaddr.js/blob/main/lib/ipaddr.js (IPv6 around line 530, IPv4 around line 182 as noted in README). Full list for IPv6 includes: unspecified, linkLocal, multicast, loopback, uniqueLocal, ipv4Mapped, rfc6145, rfc6052, 6to4, teredo, as112v6, orchid2, droneRemoteIdProtocolEntityTags, reserved, benchmarking, amt, discard, unicast (default). No official documentation explicitly details IPv4-mapped behavior for range, but source code confirms it treats them as IPv6 addresses matching IPv6 special ranges, specifically identifying them via the "ipv4Mapped" range.
Citations:
Add
ipv4MappedtoPRIVATE_RANGESto prevent IPv6-wrapped private IP bypass.IPv4-mapped IPv6 addresses (e.g.,
::ffff:192.168.1.1) are recognized by ipaddr.js with the rangeipv4Mapped, which is not inPRIVATE_RANGES. This allows private IP addresses wrapped in IPv6 format to bypass the private IP restriction on line 34.Fix to include ipv4Mapped range
🤖 Prompt for AI Agents