Skip to content

Commit 05739bb

Browse files
fix TS problems
1 parent e1f167d commit 05739bb

File tree

2 files changed

+148
-142
lines changed

2 files changed

+148
-142
lines changed

react_on_rails_pro/packages/node-renderer/src/shared/licenseValidator.ts

Lines changed: 98 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ interface LicenseData {
1010
plan?: string; // Optional: license plan (e.g., "free", "paid")
1111
issued_by?: string; // Optional: who issued the license
1212
// Allow additional fields
13-
[key: string]: any;
13+
[key: string]: unknown;
1414
}
1515

1616
// Module-level state for caching
@@ -19,54 +19,88 @@ let cachedLicenseData: LicenseData | undefined;
1919
let cachedValidationError: string | undefined;
2020

2121
/**
22-
* Validates the license and exits the process if invalid.
23-
* Caches the result after first validation.
24-
*
25-
* @throws Exits process if license is invalid
22+
* Handles invalid license by logging error and exiting.
23+
* @private
2624
*/
27-
export function validateLicense(): void {
28-
if (cachedValid !== undefined) {
29-
return;
30-
}
31-
32-
cachedValid = performValidation();
25+
function handleInvalidLicense(message: string): never {
26+
const fullMessage = `[React on Rails Pro] ${message}`;
27+
console.error(fullMessage);
28+
// Validation errors should prevent the application from starting
29+
process.exit(1);
3330
}
3431

3532
/**
36-
* Gets the decoded license data.
37-
*
38-
* @returns Decoded license data or undefined if no license
33+
* Logs license information for analytics.
34+
* @private
3935
*/
40-
export function getLicenseData(): LicenseData | undefined {
41-
if (!cachedLicenseData) {
42-
loadAndDecodeLicense();
36+
function logLicenseInfo(license: LicenseData): void {
37+
const { plan, issued_by: issuedBy } = license;
38+
39+
if (plan) {
40+
console.log(`[React on Rails Pro] License plan: ${plan}`);
41+
}
42+
if (issuedBy) {
43+
console.log(`[React on Rails Pro] Issued by: ${issuedBy}`);
4344
}
44-
return cachedLicenseData;
4545
}
4646

4747
/**
48-
* Gets the validation error message if validation failed.
49-
*
50-
* @returns Error message or undefined
48+
* Loads the license string from environment variable or config file.
49+
* @private
5150
*/
52-
export function getValidationError(): string | undefined {
53-
return cachedValidationError;
51+
// eslint-disable-next-line consistent-return
52+
function loadLicenseString(): string | never {
53+
// First try environment variable
54+
const envLicense = process.env.REACT_ON_RAILS_PRO_LICENSE;
55+
if (envLicense) {
56+
return envLicense;
57+
}
58+
59+
// Then try config file (relative to project root)
60+
try {
61+
const configPath = path.join(process.cwd(), 'config', 'react_on_rails_pro_license.key');
62+
if (fs.existsSync(configPath)) {
63+
return fs.readFileSync(configPath, 'utf8').trim();
64+
}
65+
} catch {
66+
// File doesn't exist or can't be read
67+
}
68+
69+
cachedValidationError =
70+
'No license found. Please set REACT_ON_RAILS_PRO_LICENSE environment variable ' +
71+
'or create config/react_on_rails_pro_license.key file. ' +
72+
'Get a FREE evaluation license at https://shakacode.com/react-on-rails-pro';
73+
74+
handleInvalidLicense(cachedValidationError);
5475
}
5576

5677
/**
57-
* Resets all cached validation state (primarily for testing).
78+
* Loads and decodes the license from environment or file.
79+
* @private
5880
*/
59-
export function reset(): void {
60-
cachedValid = undefined;
61-
cachedLicenseData = undefined;
62-
cachedValidationError = undefined;
81+
function loadAndDecodeLicense(): LicenseData {
82+
const licenseString = loadLicenseString();
83+
84+
const decoded = jwt.verify(licenseString, PUBLIC_KEY, {
85+
// Enforce RS256 algorithm only to prevent "alg=none" and downgrade attacks.
86+
// Adding other algorithms to the whitelist (e.g., ['RS256', 'HS256']) can introduce vulnerabilities:
87+
// If the public key is mistakenly used as a secret for HMAC algorithms (like HS256), attackers could forge tokens.
88+
// Always carefully review algorithm changes to avoid signature bypass risks.
89+
algorithms: ['RS256'],
90+
// Disable automatic expiration verification so we can handle it manually with custom logic
91+
ignoreExpiration: true,
92+
}) as LicenseData;
93+
94+
cachedLicenseData = decoded;
95+
return decoded;
6396
}
6497

6598
/**
6699
* Performs the actual license validation logic.
67100
* @private
68101
*/
69-
function performValidation(): boolean {
102+
// eslint-disable-next-line consistent-return
103+
function performValidation(): boolean | never {
70104
try {
71105
const license = loadAndDecodeLicense();
72106

@@ -92,93 +126,67 @@ function performValidation(): boolean {
92126
logLicenseInfo(license);
93127

94128
return true;
95-
} catch (error: any) {
96-
if (error.name === 'JsonWebTokenError') {
129+
} catch (error: unknown) {
130+
if (error instanceof Error && error.name === 'JsonWebTokenError') {
97131
cachedValidationError =
98132
`Invalid license signature: ${error.message}. ` +
99133
'Your license file may be corrupted. ' +
100134
'Get a FREE evaluation license at https://shakacode.com/react-on-rails-pro';
101-
} else {
135+
} else if (error instanceof Error) {
102136
cachedValidationError =
103137
`License validation error: ${error.message}. ` +
104138
'Get a FREE evaluation license at https://shakacode.com/react-on-rails-pro';
139+
} else {
140+
cachedValidationError =
141+
'License validation error: Unknown error. ' +
142+
'Get a FREE evaluation license at https://shakacode.com/react-on-rails-pro';
105143
}
106144
handleInvalidLicense(cachedValidationError);
107145
}
108146
}
109147

110148
/**
111-
* Loads and decodes the license from environment or file.
112-
* @private
149+
* Validates the license and exits the process if invalid.
150+
* Caches the result after first validation.
151+
*
152+
* @returns true if license is valid
153+
* @throws Exits process if license is invalid
113154
*/
114-
function loadAndDecodeLicense(): LicenseData {
115-
const licenseString = loadLicenseString();
116-
117-
const decoded = jwt.verify(licenseString, PUBLIC_KEY, {
118-
// Enforce RS256 algorithm only to prevent "alg=none" and downgrade attacks.
119-
// Adding other algorithms to the whitelist (e.g., ['RS256', 'HS256']) can introduce vulnerabilities:
120-
// If the public key is mistakenly used as a secret for HMAC algorithms (like HS256), attackers could forge tokens.
121-
// Always carefully review algorithm changes to avoid signature bypass risks.
122-
algorithms: ['RS256'],
123-
// Disable automatic expiration verification so we can handle it manually with custom logic
124-
ignoreExpiration: true,
125-
}) as LicenseData;
155+
export function validateLicense(): boolean {
156+
if (cachedValid !== undefined) {
157+
return cachedValid;
158+
}
126159

127-
cachedLicenseData = decoded;
128-
return decoded;
160+
cachedValid = performValidation();
161+
return cachedValid;
129162
}
130163

131164
/**
132-
* Loads the license string from environment variable or config file.
133-
* @private
165+
* Gets the decoded license data.
166+
*
167+
* @returns Decoded license data or undefined if no license
134168
*/
135-
function loadLicenseString(): string {
136-
// First try environment variable
137-
const envLicense = process.env.REACT_ON_RAILS_PRO_LICENSE;
138-
if (envLicense) {
139-
return envLicense;
140-
}
141-
142-
// Then try config file (relative to project root)
143-
try {
144-
const configPath = path.join(process.cwd(), 'config', 'react_on_rails_pro_license.key');
145-
if (fs.existsSync(configPath)) {
146-
return fs.readFileSync(configPath, 'utf8').trim();
147-
}
148-
} catch (error) {
149-
// File doesn't exist or can't be read
169+
export function getLicenseData(): LicenseData | undefined {
170+
if (!cachedLicenseData) {
171+
loadAndDecodeLicense();
150172
}
151-
152-
cachedValidationError =
153-
'No license found. Please set REACT_ON_RAILS_PRO_LICENSE environment variable ' +
154-
'or create config/react_on_rails_pro_license.key file. ' +
155-
'Get a FREE evaluation license at https://shakacode.com/react-on-rails-pro';
156-
157-
handleInvalidLicense(cachedValidationError);
173+
return cachedLicenseData;
158174
}
159175

160176
/**
161-
* Handles invalid license by logging error and exiting.
162-
* @private
177+
* Gets the validation error message if validation failed.
178+
*
179+
* @returns Error message or undefined
163180
*/
164-
function handleInvalidLicense(message: string): never {
165-
const fullMessage = `[React on Rails Pro] ${message}`;
166-
console.error(fullMessage);
167-
// Validation errors should prevent the application from starting
168-
process.exit(1);
181+
export function getValidationError(): string | undefined {
182+
return cachedValidationError;
169183
}
170184

171185
/**
172-
* Logs license information for analytics.
173-
* @private
186+
* Resets all cached validation state (primarily for testing).
174187
*/
175-
function logLicenseInfo(license: LicenseData): void {
176-
const { plan, issued_by: issuedBy } = license;
177-
178-
if (plan) {
179-
console.log(`[React on Rails Pro] License plan: ${plan}`);
180-
}
181-
if (issuedBy) {
182-
console.log(`[React on Rails Pro] Issued by: ${issuedBy}`);
183-
}
188+
export function reset(): void {
189+
cachedValid = undefined;
190+
cachedLicenseData = undefined;
191+
cachedValidationError = undefined;
184192
}

0 commit comments

Comments
 (0)