UNPKG

eslint-plugin-sonarjs

Version:
134 lines (133 loc) 6.01 kB
"use strict"; /* * SonarQube JavaScript Plugin * Copyright (C) 2011-2025 SonarSource SA * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Sonar Source-Available License for more details. * * You should have received a copy of the Sonar Source-Available License * along with this program; if not, see https://sonarsource.com/license/ssal/ */ // https://sonarsource.github.io/rspec/#/rspec/S4423/javascript Object.defineProperty(exports, "__esModule", { value: true }); exports.rule = void 0; const index_js_1 = require("../helpers/index.js"); const SECURE_PROTOCOL_ALLOWED_VALUES = [ 'TLSv1_2_method', 'TLSv1_2_client_method', 'TLSv1_2_server_method', 'TLS_method', 'TLS_client_method', 'TLS_server_method', ]; exports.rule = { meta: { messages: { useMinimumTLS: "Change '{{option}}' to use at least TLS v1.2.", useSecureTLS: "Change '{{option}}' to allow only secure TLS versions.", AWSApiGateway: 'Change this code to enforce TLS 1.2 or above.', AWSOpenElasticSearch: 'Omitting "tlsSecurityPolicy" enables a deprecated version of TLS. Set it to enforce TLS 1.2 or above.', }, }, create(context) { function getValueOfProperty(objectExpression, propertyName) { const unsafeProperty = objectExpression && (0, index_js_1.getProperty)(objectExpression, propertyName, context); if (unsafeProperty) { return (0, index_js_1.getValueOfExpression)(context, unsafeProperty.value, 'Literal'); } return undefined; } function checkMinMaxVersion(propertyName, property) { if (property && (property.value === 'TLSv1.1' || property.value === 'TLSv1')) { context.report({ node: property, messageId: 'useMinimumTLS', data: { option: propertyName, }, }); } } function checkSslOptions(optionsNode) { const options = (0, index_js_1.getValueOfExpression)(context, optionsNode, 'ObjectExpression'); const minVersion = getValueOfProperty(options, 'minVersion'); const maxVersion = getValueOfProperty(options, 'maxVersion'); checkMinMaxVersion('minVersion', minVersion); checkMinMaxVersion('maxVersion', maxVersion); const secureProtocol = getValueOfProperty(options, 'secureProtocol'); const secureProtocolValue = secureProtocol?.value?.toString() ?? ''; if (secureProtocol && !SECURE_PROTOCOL_ALLOWED_VALUES.includes(secureProtocolValue)) { context.report({ node: secureProtocol, messageId: 'useMinimumTLS', data: { option: 'secureProtocol', }, }); } const secureOptions = (0, index_js_1.getProperty)(options, 'secureOptions', context); if (secureOptions && !isValidSecureOptions(secureOptions.value)) { context.report({ node: secureOptions, messageId: 'useSecureTLS', data: { option: 'secureOptions', }, }); } } function isValidSecureOptions(options) { const flags = []; collectIdentifiersFromBinary(options, flags); return (flags[0] === null || (flags.includes('SSL_OP_NO_TLSv1') && flags.includes('SSL_OP_NO_TLSv1_1'))); } function collectIdentifiersFromBinary(node, acc) { if (node.type === 'BinaryExpression') { collectIdentifiersFromBinary(node.left, acc); collectIdentifiersFromBinary(node.right, acc); } else if (node.type === 'MemberExpression' && (0, index_js_1.getFullyQualifiedName)(context, node.object) === 'constants' && node.property.type === 'Identifier') { acc.push(node.property.name); } else { // if part of expression is some complex node like function call, we set null on index 0 acc[0] = null; } } return { CallExpression: (node) => { const callExpression = node; const fqn = (0, index_js_1.getFullyQualifiedName)(context, callExpression); // https://nodejs.org/api/https.html#https_https_get_options_callback if (fqn === 'https.request') { checkSslOptions(callExpression.arguments[0]); checkSslOptions(callExpression.arguments[1]); } // https://github.com/request/request#tlsssl-protocol if (fqn === 'request.get') { checkSslOptions(callExpression.arguments[0]); } // https://nodejs.org/api/tls.html#tls_tls_connect_options_callback if (fqn === 'tls.connect') { checkSslOptions(callExpression.arguments[0]); checkSslOptions(callExpression.arguments[1]); checkSslOptions(callExpression.arguments[2]); } // https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options if (fqn === 'tls.createSecureContext') { checkSslOptions(callExpression.arguments[0]); } }, }; }, };