Enforce HTTPS / Prevent Reverse Tabnabbing (#773)
* working version of test custom rule config * setting no imports to false so tests will pass * adding anchor blank noopener rule, rule currently off to allow tests to pass * removing copied code from tslint-microsoft-contrib * adding tslint-microsoft-contrib to dev deps * extending tslint for external http rule * locking tslint-microsoft-contrib version and turning on target blank noopener rule * final fixes for pull #663 * add noopener noreferrer as needed * fixing false positives for a tags without href * really fix linting errors * fix imports * remove accidently(?) added LedgerNano duplicate file
This commit is contained in:
parent
6e2b74c79a
commit
26619e28cc
|
@ -93,14 +93,22 @@ export default class AccountInfo extends React.Component<Props, State> {
|
|||
<ul className="AccountInfo-list">
|
||||
{!!blockExplorer && (
|
||||
<li className="AccountInfo-list-item">
|
||||
<a href={blockExplorer.address(address)} target="_blank">
|
||||
<a
|
||||
href={blockExplorer.address(address)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{`${network.name} (${blockExplorer.name})`}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
{!!tokenExplorer && (
|
||||
<li className="AccountInfo-list-item">
|
||||
<a href={tokenExplorer.address(address)} target="_blank">
|
||||
<a
|
||||
href={tokenExplorer.address(address)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{`Tokens (${tokenExplorer.name})`}
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
@ -58,6 +58,7 @@ export default class Promos extends React.Component<{}, State> {
|
|||
className="Promos-promo"
|
||||
key={promo.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={promo.href}
|
||||
style={{ backgroundColor: promo.color }}
|
||||
>
|
||||
|
|
|
@ -19,7 +19,7 @@ const ErrorScreen: React.SFC<Props> = ({ error }) => {
|
|||
Please contact{' '}
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
rel="noopener noreferrer"
|
||||
href={`mailto:support@myetherwallet.com?Subject=${SUBJECT}&body=${DESCRIPTION}`}
|
||||
>
|
||||
support@myetherwallet.com
|
||||
|
|
|
@ -14,7 +14,12 @@ const TransactionSucceeded = ({ txHash, blockExplorer }: TransactionSucceededPro
|
|||
return (
|
||||
<div>
|
||||
<p>{translateRaw('SUCCESS_3') + txHash}</p>
|
||||
<a className="btn btn-xs btn-info string" href={txHashLink} target="_blank" rel="noopener">
|
||||
<a
|
||||
className="btn btn-xs btn-info string"
|
||||
href={txHashLink}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Verify Transaction
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -61,7 +61,11 @@ export default class GasPriceDropdown extends Component<Props> {
|
|||
<code>21 GWEI</code>.
|
||||
</p>
|
||||
<p>
|
||||
<a href={`${knowledgeBaseURL}/gas/what-is-gas-ethereum`} target="_blank">
|
||||
<a
|
||||
href={`${knowledgeBaseURL}/gas/what-is-gas-ethereum`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Read more
|
||||
</a>
|
||||
</p>
|
||||
|
|
|
@ -35,7 +35,13 @@ class NavigationLink extends React.Component<Props, {}> {
|
|||
|
||||
const linkEl =
|
||||
link.external || !link.to ? (
|
||||
<a className={linkClasses} href={link.to} aria-label={linkLabel} target="_blank">
|
||||
<a
|
||||
className={linkClasses}
|
||||
href={link.to}
|
||||
aria-label={linkLabel}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{translate(link.name)}
|
||||
</a>
|
||||
) : (
|
||||
|
|
|
@ -292,7 +292,11 @@ class DeterministicWalletsModalClass extends React.Component<Props, State> {
|
|||
)}
|
||||
</td>
|
||||
<td>
|
||||
<a target="_blank" href={`https://ethplorer.io/address/${wallet.address}`}>
|
||||
<a
|
||||
target="_blank"
|
||||
href={`https://ethplorer.io/address/${wallet.address}`}
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<i className="DWModal-addresses-table-more" />
|
||||
</a>
|
||||
</td>
|
||||
|
@ -310,7 +314,9 @@ function mapStateToProps(state: AppState) {
|
|||
};
|
||||
}
|
||||
|
||||
export const DeterministicWalletsModal = connect(mapStateToProps, {
|
||||
const DeterministicWalletsModal = connect(mapStateToProps, {
|
||||
getDeterministicWallets,
|
||||
setDesiredToken
|
||||
})(DeterministicWalletsModalClass);
|
||||
|
||||
export default DeterministicWalletsModal;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import './LedgerNano.scss';
|
||||
import React, { Component } from 'react';
|
||||
import translate, { translateRaw } from 'translations';
|
||||
import { DeterministicWalletsModal } from './DeterministicWalletsModal';
|
||||
import DeterministicWalletsModal from './DeterministicWalletsModal';
|
||||
import { LedgerWallet } from 'libs/wallet';
|
||||
import Ledger3 from 'vendor/ledger3';
|
||||
import LedgerEth from 'vendor/ledger-eth';
|
||||
|
@ -81,7 +81,7 @@ export class LedgerNanoSDecrypt extends Component<Props, State> {
|
|||
className="LedgerDecrypt-buy btn btn-sm btn-default"
|
||||
href="https://www.ledgerwallet.com/r/fa4b?path=/products/"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{translate('Don’t have a Ledger? Order one now!')}
|
||||
</a>
|
||||
|
@ -92,9 +92,9 @@ export class LedgerNanoSDecrypt extends Component<Props, State> {
|
|||
Guides:
|
||||
<div>
|
||||
<a
|
||||
href="http://support.ledgerwallet.com/knowledge_base/topics/how-to-use-myetherwallet-with-ledger"
|
||||
href="https://support.ledgerwallet.com/knowledge_base/topics/how-to-use-myetherwallet-with-ledger"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
How to use MyEtherWallet with your Nano S
|
||||
</a>
|
||||
|
@ -103,7 +103,7 @@ export class LedgerNanoSDecrypt extends Component<Props, State> {
|
|||
<a
|
||||
href="https://ledger.groovehq.com/knowledge_base/topics/how-to-secure-your-eth-tokens-augur-rep-dot-dot-dot-with-your-nano-s"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
How to secure your tokens with your Nano S
|
||||
</a>
|
||||
|
|
|
@ -2,7 +2,7 @@ import { mnemonicToSeed, validateMnemonic } from 'bip39';
|
|||
import DPATHS from 'config/dpaths';
|
||||
import React, { Component } from 'react';
|
||||
import translate, { translateRaw } from 'translations';
|
||||
import { DeterministicWalletsModal } from './DeterministicWalletsModal';
|
||||
import DeterministicWalletsModal from './DeterministicWalletsModal';
|
||||
import { formatMnemonic } from 'utils/formatters';
|
||||
|
||||
const DEFAULT_PATH = DPATHS.MNEMONIC[0].value;
|
||||
|
|
|
@ -3,7 +3,7 @@ import { TrezorWallet } from 'libs/wallet';
|
|||
import React, { Component } from 'react';
|
||||
import translate, { translateRaw } from 'translations';
|
||||
import TrezorConnect from 'vendor/trezor-connect';
|
||||
import { DeterministicWalletsModal } from './DeterministicWalletsModal';
|
||||
import DeterministicWalletsModal from './DeterministicWalletsModal';
|
||||
import './Trezor.scss';
|
||||
import { Spinner } from 'components/ui';
|
||||
const DEFAULT_PATH = DPATHS.TREZOR[0].value;
|
||||
|
@ -53,7 +53,7 @@ export class TrezorDecrypt extends Component<Props, State> {
|
|||
className="TrezorDecrypt-buy btn btn-sm btn-default"
|
||||
href="https://trezor.io/?a=myetherwallet.com"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{translate('Don’t have a TREZOR? Order one now!')}
|
||||
</a>
|
||||
|
@ -65,7 +65,7 @@ export class TrezorDecrypt extends Component<Props, State> {
|
|||
<a
|
||||
href="https://blog.trezor.io/trezor-integration-with-myetherwallet-3e217a652e08"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
How to use TREZOR with MyEtherWallet
|
||||
</a>
|
||||
|
|
|
@ -11,7 +11,7 @@ interface Props {
|
|||
|
||||
const Help = ({ size = 'x1', link }: Props) => {
|
||||
return (
|
||||
<a href={link} className={`Help Help-${size}`} target={'_blank'}>
|
||||
<a href={link} className={`Help Help-${size}`} target="_blank" rel="noopener noreferrer">
|
||||
<img src={icon} />
|
||||
</a>
|
||||
);
|
||||
|
|
|
@ -36,7 +36,7 @@ interface NewTabLinkProps extends AAttributes {
|
|||
}
|
||||
|
||||
const NewTabLink = ({ content, children, ...rest }: NewTabLinkProps) => (
|
||||
<a target="_blank" rel="noopener" {...rest}>
|
||||
<a target="_blank" rel="noopener noreferrer" {...rest}>
|
||||
{content || children}
|
||||
</a>
|
||||
);
|
||||
|
|
|
@ -17,6 +17,7 @@ const Help = () => (
|
|||
<a
|
||||
href="https://www.reddit.com/r/ethereum/comments/47nkoi/psa_check_your_ethaddressorg_wallets_and_any/d0eo45o"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<span className="text-danger">{translate('HELP_Warning')}</span>
|
||||
</a>
|
||||
|
@ -25,7 +26,7 @@ const Help = () => (
|
|||
<li>
|
||||
<h3>
|
||||
This page is deprecated. Please check out our more up-to-date and searchable{' '}
|
||||
<a href={knowledgeBaseURL} target="_blank">
|
||||
<a href={knowledgeBaseURL} target="_blank" rel="noopener noreferrer">
|
||||
Knowledge Base.{' '}
|
||||
</a>
|
||||
</h3>
|
||||
|
|
|
@ -21,7 +21,7 @@ export default class BitcoinQR extends Component<Props, {}> {
|
|||
Orders that take too long will have to be processed manually & and may delay the
|
||||
amount of time it takes to receive your coins.
|
||||
<br />
|
||||
<a href="https://shapeshift.io/#/btcfee" target="_blank" rel="noopener">
|
||||
<a href="https://shapeshift.io/#/btcfee" target="_blank" rel="noopener noreferrer">
|
||||
Please use the recommended TX fees seen here.
|
||||
</a>
|
||||
</p>
|
||||
|
|
|
@ -88,7 +88,12 @@ export default class CurrentRates extends Component<Props> {
|
|||
|
||||
<section className="SwapRates-panel row">
|
||||
{children}
|
||||
<a className="SwapRates-panel-logo" href={providerURL} target="_blank">
|
||||
<a
|
||||
className="SwapRates-panel-logo"
|
||||
href={providerURL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<img src={providerLogo} width={120} height={49} />
|
||||
</a>
|
||||
</section>
|
||||
|
|
|
@ -27,7 +27,12 @@ export default class SwapInfoHeaderTitle extends Component<SwapInfoHeaderTitlePr
|
|||
<h3 className="SwapInfo-top-title">{translate('SWAP_information')}</h3>
|
||||
</div>
|
||||
<div className="col-xs-3">
|
||||
<a className="SwapInfo-top-logo" href={bityReferralURL} target="_blank" rel="noopener">
|
||||
<a
|
||||
className="SwapInfo-top-logo"
|
||||
href={bityReferralURL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<img className="SwapInfo-top-logo-img" src={logoToRender} />
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -49,7 +49,7 @@ export default class SwapProgress extends Component<Props, State> {
|
|||
if (destinationId !== 'BTC') {
|
||||
link = bityConfig.ETHTxExplorer(outputTx);
|
||||
linkElement = (
|
||||
<a href={link} target="_blank" rel="noopener">
|
||||
<a href={link} target="_blank" rel="noopener noreferrer">
|
||||
{notificationMessage}
|
||||
</a>
|
||||
);
|
||||
|
@ -57,7 +57,7 @@ export default class SwapProgress extends Component<Props, State> {
|
|||
} else {
|
||||
link = bityConfig.BTCTxExplorer(outputTx);
|
||||
linkElement = (
|
||||
<a href={link} target="_blank" rel="noopener">
|
||||
<a href={link} target="_blank" rel="noopener noreferrer">
|
||||
{notificationMessage}
|
||||
</a>
|
||||
);
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
</p>
|
||||
<p>
|
||||
If you are not sure why you are seeing this message, or are unsure of how to enable Javascript, please visit
|
||||
<a href="https://www.enable-javascript.com/" rel="noopener" target="_blank">enable-javascript.com</a>
|
||||
<a href="https://www.enable-javascript.com/" rel="noopener noreferrer" target="_blank">enable-javascript.com</a>
|
||||
to learn more.
|
||||
</p>
|
||||
</div>
|
||||
|
@ -41,18 +41,18 @@
|
|||
to a laptop or computer to continue using MyEtherWallet.
|
||||
</p>
|
||||
<div class="BadBrowser-content-browsers is-desktop">
|
||||
<a class="BadBrowser-content-browsers-browser firefox" href="https://www.mozilla.org/en-US/firefox/new/" rel="noopener" target="_blank">
|
||||
<a class="BadBrowser-content-browsers-browser firefox" href="https://www.mozilla.org/en-US/firefox/new/" rel="noopener noreferrer" target="_blank">
|
||||
<span class="BadBrowser-content-browsers-browser-name">
|
||||
Firefox
|
||||
</span>
|
||||
</a>
|
||||
<a class="BadBrowser-content-browsers-browser chrome" href="https://www.google.com/chrome/browser/desktop/index.html" rel="noopener"
|
||||
<a class="BadBrowser-content-browsers-browser chrome" href="https://www.google.com/chrome/browser/desktop/index.html" rel="noopener noreferrer"
|
||||
target="_blank">
|
||||
<span class="BadBrowser-content-browsers-browser-name">
|
||||
Chrome
|
||||
</span>
|
||||
</a>
|
||||
<a class="BadBrowser-content-browsers-browser opera" href="http://www.opera.com/" rel="noopener" target="_blank">
|
||||
<a class="BadBrowser-content-browsers-browser opera" href="http://www.opera.com/" rel="noopener noreferrer" target="_blank">
|
||||
<span class="BadBrowser-content-browsers-browser-name">
|
||||
Opera
|
||||
</span>
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
"use strict";
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
||||
return function (d, b) {
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
exports.__esModule = true;
|
||||
var ts = require("typescript");
|
||||
var Lint = require("tslint");
|
||||
var ErrorTolerantWalker_1 = require("../node_modules/tslint-microsoft-contrib/utils/ErrorTolerantWalker");
|
||||
var JsxAttribute_1 = require("../node_modules/tslint-microsoft-contrib/utils/JsxAttribute");
|
||||
var FAILURE_STRING = 'Anchor tags with an external link must use https';
|
||||
/**
|
||||
* Implementation of the no-external-http-link rule.
|
||||
*/
|
||||
var Rule = /** @class */ (function (_super) {
|
||||
__extends(Rule, _super);
|
||||
function Rule() {
|
||||
return _super !== null && _super.apply(this, arguments) || this;
|
||||
}
|
||||
Rule.prototype.apply = function (sourceFile) {
|
||||
if (sourceFile.languageVariant === ts.LanguageVariant.JSX) {
|
||||
return this.applyWithWalker(new NoExternalHttpLinkRuleWalker(sourceFile, this.getOptions()));
|
||||
}
|
||||
else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
Rule.metadata = {
|
||||
ruleName: 'tno-external-http-link',
|
||||
type: 'functionality',
|
||||
description: 'Anchor tags with an external link must use https',
|
||||
options: null,
|
||||
optionsDescription: '',
|
||||
typescriptOnly: true,
|
||||
issueClass: 'SDL',
|
||||
issueType: 'Error',
|
||||
severity: 'Critical',
|
||||
level: 'Mandatory',
|
||||
group: 'Security',
|
||||
commonWeaknessEnumeration: '242,676'
|
||||
};
|
||||
return Rule;
|
||||
}(Lint.Rules.AbstractRule));
|
||||
exports.Rule = Rule;
|
||||
var NoExternalHttpLinkRuleWalker = /** @class */ (function (_super) {
|
||||
__extends(NoExternalHttpLinkRuleWalker, _super);
|
||||
function NoExternalHttpLinkRuleWalker() {
|
||||
return _super !== null && _super.apply(this, arguments) || this;
|
||||
}
|
||||
NoExternalHttpLinkRuleWalker.prototype.visitJsxElement = function (node) {
|
||||
var openingElement = node.openingElement;
|
||||
this.validateOpeningElement(openingElement);
|
||||
_super.prototype.visitJsxElement.call(this, node);
|
||||
};
|
||||
NoExternalHttpLinkRuleWalker.prototype.visitJsxSelfClosingElement = function (node) {
|
||||
this.validateOpeningElement(node);
|
||||
_super.prototype.visitJsxSelfClosingElement.call(this, node);
|
||||
};
|
||||
NoExternalHttpLinkRuleWalker.prototype.validateOpeningElement = function (openingElement) {
|
||||
if (openingElement.tagName.getText() === 'a') {
|
||||
var allAttributes = JsxAttribute_1.getJsxAttributesFromJsxElement(openingElement);
|
||||
var href = allAttributes.href;
|
||||
if (href !== null && !isSafeHrefAttributeValue(href) && JsxAttribute_1.getStringLiteral(href) !== 'undefined') {
|
||||
this.addFailureAt(openingElement.getStart(), openingElement.getWidth(), FAILURE_STRING);
|
||||
}
|
||||
}
|
||||
};
|
||||
return NoExternalHttpLinkRuleWalker;
|
||||
}(ErrorTolerantWalker_1.ErrorTolerantWalker));
|
||||
function isSafeHrefAttributeValue(attribute) {
|
||||
if (JsxAttribute_1.isEmpty(attribute)) {
|
||||
return false;
|
||||
}
|
||||
if (attribute.initializer.kind === ts.SyntaxKind.JsxExpression) {
|
||||
var expression = attribute.initializer;
|
||||
if (expression.expression !== null &&
|
||||
expression.expression.kind !== ts.SyntaxKind.StringLiteral) {
|
||||
return true; // attribute value is not a string literal, so do not validate
|
||||
}
|
||||
}
|
||||
var stringValue = JsxAttribute_1.getStringLiteral(attribute);
|
||||
if (stringValue === '#') {
|
||||
return true;
|
||||
}
|
||||
else if (stringValue === null || stringValue.length === 0) {
|
||||
return false;
|
||||
}
|
||||
return stringValue.indexOf('https://') >= 0;
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
import * as ts from 'typescript';
|
||||
import * as Lint from 'tslint';
|
||||
|
||||
import { ErrorTolerantWalker } from '../node_modules/tslint-microsoft-contrib/utils/ErrorTolerantWalker';
|
||||
import { ExtendedMetadata } from '../node_modules/tslint-microsoft-contrib/utils/ExtendedMetadata';
|
||||
import { Utils } from '../node_modules/tslint-microsoft-contrib/utils/Utils';
|
||||
|
||||
import {
|
||||
getJsxAttributesFromJsxElement,
|
||||
getStringLiteral,
|
||||
isEmpty
|
||||
} from '../node_modules/tslint-microsoft-contrib/utils/JsxAttribute';
|
||||
|
||||
const FAILURE_STRING = 'Anchor tags with an external link must use https';
|
||||
|
||||
/**
|
||||
* Implementation of the no-external-http-link rule.
|
||||
*/
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
public static metadata: ExtendedMetadata = {
|
||||
ruleName: 'tno-external-http-link',
|
||||
type: 'functionality',
|
||||
description: 'Anchor tags with an external link must use https',
|
||||
options: null,
|
||||
optionsDescription: '',
|
||||
typescriptOnly: true,
|
||||
issueClass: 'SDL',
|
||||
issueType: 'Error',
|
||||
severity: 'Critical',
|
||||
level: 'Mandatory',
|
||||
group: 'Security',
|
||||
commonWeaknessEnumeration: '242,676'
|
||||
};
|
||||
|
||||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
if (sourceFile.languageVariant === ts.LanguageVariant.JSX) {
|
||||
return this.applyWithWalker(new NoExternalHttpLinkRuleWalker(sourceFile, this.getOptions()));
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NoExternalHttpLinkRuleWalker extends ErrorTolerantWalker {
|
||||
protected visitJsxElement(node: ts.JsxElement): void {
|
||||
const openingElement: ts.JsxOpeningElement = node.openingElement;
|
||||
this.validateOpeningElement(openingElement);
|
||||
super.visitJsxElement(node);
|
||||
}
|
||||
|
||||
protected visitJsxSelfClosingElement(node: ts.JsxSelfClosingElement): void {
|
||||
this.validateOpeningElement(node);
|
||||
super.visitJsxSelfClosingElement(node);
|
||||
}
|
||||
|
||||
private validateOpeningElement(openingElement: ts.JsxOpeningLikeElement): void {
|
||||
if (openingElement.tagName.getText() === 'a') {
|
||||
const allAttributes: { [propName: string]: ts.JsxAttribute } = getJsxAttributesFromJsxElement(
|
||||
openingElement
|
||||
);
|
||||
const href: ts.JsxAttribute = allAttributes.href;
|
||||
if (
|
||||
href !== null &&
|
||||
!isSafeHrefAttributeValue(href) &&
|
||||
getStringLiteral(href) !== 'undefined'
|
||||
) {
|
||||
this.addFailureAt(openingElement.getStart(), openingElement.getWidth(), FAILURE_STRING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isSafeHrefAttributeValue(attribute: ts.JsxAttribute): boolean {
|
||||
if (isEmpty(attribute)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (attribute.initializer.kind === ts.SyntaxKind.JsxExpression) {
|
||||
const expression: ts.JsxExpression = <ts.JsxExpression>attribute.initializer;
|
||||
if (
|
||||
expression.expression !== null &&
|
||||
expression.expression.kind !== ts.SyntaxKind.StringLiteral
|
||||
) {
|
||||
return true; // attribute value is not a string literal, so do not validate
|
||||
}
|
||||
}
|
||||
|
||||
const stringValue = getStringLiteral(attribute);
|
||||
if (stringValue === '#') {
|
||||
return true;
|
||||
} else if (stringValue === null || stringValue.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return stringValue.indexOf('https://') >= 0;
|
||||
}
|
|
@ -109,6 +109,7 @@
|
|||
"ts-loader": "3.2.0",
|
||||
"tslint": "5.8.0",
|
||||
"tslint-config-prettier": "1.6.0",
|
||||
"tslint-microsoft-contrib": "5.0.1",
|
||||
"tslint-react": "3.3.3",
|
||||
"types-rlp": "0.0.1",
|
||||
"typescript": "2.6.2",
|
||||
|
|
|
@ -23,7 +23,9 @@
|
|||
"no-var-requires": false,
|
||||
"jsx-wrap-multiline": false,
|
||||
"comment-format": false,
|
||||
"ordered-imports": false
|
||||
"ordered-imports": false,
|
||||
"react-anchor-blank-noopener": true,
|
||||
"no-external-http-link": true
|
||||
},
|
||||
"rulesDirectory": []
|
||||
"rulesDirectory": ["node_modules/tslint-microsoft-contrib", "custom_linting_rules"]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue