forked from phoedos/pmd
Merge branch 'master' into improve-method-naming-conventions-rule
This commit is contained in:
@ -1,16 +1,11 @@
|
||||
dist: trusty
|
||||
sudo: false
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- oracle-java9-installer
|
||||
ssh_known_hosts:
|
||||
- web.sourceforge.net
|
||||
|
||||
language: java
|
||||
|
||||
jdk: oraclejdk9
|
||||
|
||||
env:
|
||||
global:
|
||||
- secure: KBEuB6U1p5RQXSYe157AwydFr/zpXQPA0IChVCgZV+X1mMyy9ZtrjH1J1AXuviseDDXDbaT25sRnsvpl82rfRw2xOkMGXHy4N95/ylTSr8DjHxTao71BhXsvFycNobFva5y2EGNWqDvpS8I2oSZo7Qk4la3yep3rcJQvcy6RDbbhpDTbL1QMFyadunIBm0WtqbunrMqtjSqaoPsXz8TiQuxHvX4vEXzVbaxV1QQt79Vi+daa6wAV3mRQAugnx+UffsC8JqMxgm06usWeJgCJzxgm8E7clZCLmf53B2TL8dK6bIYbqyvOY3uFxitsTG0d8Z0GOJwXBgZNgbniTRO8ZJSty5eZP8LBybbjVLSL25DNTWtCjADUL/uySnXIEidlMt2N/3QmH7zrGAfAk/tIwKpdRca2GLLydeXf6PSkiahnPEkIY/QupcsOLELhdifpdOjb8QW1OenA+vUbNM9dccLwKnX6Fj9cu4VQG601AcYDr2eyhq8WYkr3wYdw/6KdUa3hmplowTBs+qguppP+eOSgGuEsy38KLtqnvm6WlHy6tcLmcVYKG3DmR1b7TWXsOXC6/VMH8BHBkvsF1QdRg9+Cgx07vX3Hw7roPiYzmaO9Ajs20ATsUfRskMuWCTeTSK5pN8X27veRCZlhFjeKQMDdmfVwzpAfRgKsl3TEn1I=
|
||||
@ -31,9 +26,11 @@ matrix:
|
||||
fast_finish: true
|
||||
|
||||
before_install:
|
||||
- wget https://github.com/sormuras/bach/raw/master/install-jdk.sh
|
||||
- bash .travis/setup-secrets.sh
|
||||
- bash .travis/configure-maven.sh
|
||||
install: true
|
||||
# Install OracleJDK 10 - see https://sormuras.github.io/blog/2018-03-20-jdk-matrix.html
|
||||
install: . ./install-jdk.sh -F 10 -L BCL
|
||||
before_script: true
|
||||
script: source .travis/build-$BUILD.sh
|
||||
after_success: true
|
||||
|
@ -172,6 +172,9 @@ entries:
|
||||
- title: Performance
|
||||
output: web, pdf
|
||||
url: /pmd_rules_java_performance.html
|
||||
- title: Security
|
||||
output: web, pdf
|
||||
url: /pmd_rules_java_security.html
|
||||
- title: null
|
||||
output: web, pdf
|
||||
subfolders:
|
||||
|
@ -323,6 +323,12 @@ folder: pmd/rules
|
||||
* [UseStringBufferForStringAppends](pmd_rules_java_performance.html#usestringbufferforstringappends): The use of the '+=' operator for appending strings causes the JVM to create and use an internal S...
|
||||
* [UseStringBufferLength](pmd_rules_java_performance.html#usestringbufferlength): Use StringBuffer.length() to determine StringBuffer length rather than using StringBuffer.toStrin...
|
||||
|
||||
## Security
|
||||
|
||||
{% include callout.html content="Rules that flag potential security flaws." %}
|
||||
|
||||
* [InsecureCryptoIv](pmd_rules_java_security.html#insecurecryptoiv): Do not use hard coded initialization vector in cryptographic operations. Please use a randomly ge...
|
||||
|
||||
## Additional rulesets
|
||||
|
||||
* Android (`rulesets/java/android.xml`):
|
||||
|
46
docs/pages/pmd/rules/java/security.md
Normal file
46
docs/pages/pmd/rules/java/security.md
Normal file
@ -0,0 +1,46 @@
|
||||
---
|
||||
title: Security
|
||||
summary: Rules that flag potential security flaws.
|
||||
permalink: pmd_rules_java_security.html
|
||||
folder: pmd/rules/java
|
||||
sidebaractiveurl: /pmd_rules_java.html
|
||||
editmepath: ../pmd-java/src/main/resources/category/java/security.xml
|
||||
keywords: Security, InsecureCryptoIv
|
||||
language: Java
|
||||
---
|
||||
## InsecureCryptoIv
|
||||
|
||||
**Since:** PMD 6.3.0
|
||||
|
||||
**Priority:** Medium (3)
|
||||
|
||||
Do not use hard coded initialization vector in cryptographic operations. Please use a randomly generated IV.
|
||||
|
||||
**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.security.InsecureCryptoIvRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/security/InsecureCryptoIvRule.java)
|
||||
|
||||
**Example(s):**
|
||||
|
||||
``` java
|
||||
public class Foo {
|
||||
void good() {
|
||||
SecureRandom random = new SecureRandom();
|
||||
byte iv[] = new byte[16];
|
||||
random.nextBytes(bytes);
|
||||
}
|
||||
|
||||
void bad() {
|
||||
byte[] iv = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, };
|
||||
}
|
||||
|
||||
void alsoBad() {
|
||||
byte[] iv = "secret iv in here".getBytes();
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
**Use this rule by referencing it:**
|
||||
``` xml
|
||||
<rule ref="category/java/security.xml/InsecureCryptoIv" />
|
||||
```
|
||||
|
@ -438,7 +438,8 @@ Here's a screenshot of CPD after running on the JDK 8 java.lang package:
|
||||
|
||||
## Suppression
|
||||
|
||||
Arbitrary blocks of code can be ignored through comments on **Java** by including the keywords `CPD-OFF` and `CPD-ON`.
|
||||
Arbitrary blocks of code can be ignored through comments on **Java**, **C/C++**, **Javascript**, **Matlab**,
|
||||
**Objective-C**, **PL/SQL** and **Python** by including the keywords `CPD-OFF` and `CPD-ON`.
|
||||
|
||||
public Object someParameterizedFactoryMethod(int x) throws Exception {
|
||||
// some unignored code
|
||||
|
@ -57,7 +57,8 @@ After you add these references it’ll look something like this:
|
||||
<!-- Now we'll customize a rule's property value -->
|
||||
<rule ref="category/java/design.xml/CyclomaticComplexity">
|
||||
<properties>
|
||||
<property name="reportLevel" value="5"/>
|
||||
<property name="classReportLevel" value="40"/>
|
||||
<property name="methodReportLevel" value="5"/>
|
||||
</properties>
|
||||
</rule>
|
||||
|
||||
|
@ -13,8 +13,11 @@ This is a minor release.
|
||||
### Table Of Contents
|
||||
|
||||
* [New and noteworthy](#new-and-noteworthy)
|
||||
* [Tree transversal revision](#tree-transversal-revision)
|
||||
* [Naming rules enhancements](#naming-rules-enhancements)
|
||||
* [Tree Traversal Revision](#tree-traversal-revision)
|
||||
* [Naming Rules Enhancements](#naming-rules-enhancements)
|
||||
* [CPD Suppression](#cpd-suppression)
|
||||
* [Swift 4.1 Support](#swift-41-support)
|
||||
* [New Rules](#new-rules)
|
||||
* [Modified Rules](#modified-rules)
|
||||
* [Fixed Issues](#fixed-issues)
|
||||
* [API Changes](#api-changes)
|
||||
@ -23,7 +26,7 @@ This is a minor release.
|
||||
|
||||
### New and noteworthy
|
||||
|
||||
#### Tree transversal revision
|
||||
#### Tree Traversal Revision
|
||||
|
||||
As described in [#904](https://github.com/pmd/pmd/issues/904), when searching for child nodes of the AST methods
|
||||
such as `hasDescendantOfType`, `getFirstDescendantOfType` and `findDescendantsOfType` were found to behave inconsistently,
|
||||
@ -33,9 +36,9 @@ find boundaries.
|
||||
|
||||
This change implies several false positives / unexpected results (ie: `ASTBlockStatement` falsely returning `true` to `isAllocation()`)
|
||||
have been fixed; and lots of searches are now restricted to smaller search areas, which improves performance (depending on the project,
|
||||
we have measured up to 10% improvements during Type Resolution, Symbol Table analysis, and some rule's application).
|
||||
we have measured up to 10% improvements during Type Resolution, Symbol Table analysis, and some rules' application).
|
||||
|
||||
#### Naming rules enhancements
|
||||
#### Naming Rules Enhancements
|
||||
|
||||
* [`ClassNamingConventions`](pmd_rules_java_codestyle.html#classnamingconventions)
|
||||
has been enhanced to allow granular configuration of naming
|
||||
@ -47,10 +50,70 @@ we have measured up to 10% improvements during Type Resolution, Symbol Table ana
|
||||
* [`MethodNamingConventions`](pmd_rules_java_codestyle.html#methodnamingconventions)
|
||||
has been enhanced in the same way.
|
||||
|
||||
#### CPD Suppression
|
||||
|
||||
Back in PMD 5.6.0 we introduced the ability to suppress CPD warnings in Java using comments, by
|
||||
including `CPD-OFF` (to start ignoring code), or `CPD-ON` (to resume analysis) during CPD execution.
|
||||
This has proved to be much more flexible and versatile than the old annotation-based approach,
|
||||
and has since been the preferred way to suppress CPD warnings.
|
||||
|
||||
On this ocassion, we are extending support for comment-based suppressions to many other languages:
|
||||
|
||||
* C/C++
|
||||
* Ecmascript / Javascript
|
||||
* Matlab
|
||||
* Objective-C
|
||||
* PL/SQL
|
||||
* Python
|
||||
|
||||
So for instance, in Python we could now do:
|
||||
|
||||
```python
|
||||
class BaseHandler(object):
|
||||
def __init__(self):
|
||||
# some unignored code
|
||||
|
||||
# tell cpd to start ignoring code - CPD-OFF
|
||||
|
||||
# mission critical code, manually loop unroll
|
||||
GoDoSomethingAwesome(x + x / 2);
|
||||
GoDoSomethingAwesome(x + x / 2);
|
||||
GoDoSomethingAwesome(x + x / 2);
|
||||
GoDoSomethingAwesome(x + x / 2);
|
||||
GoDoSomethingAwesome(x + x / 2);
|
||||
GoDoSomethingAwesome(x + x / 2);
|
||||
|
||||
# resume CPD analysis - CPD-ON
|
||||
|
||||
# further code will *not* be ignored
|
||||
```
|
||||
|
||||
Other languages are equivalent.
|
||||
|
||||
#### Swift 4.1 Support
|
||||
|
||||
Thanks to major contributions from [kenji21](https://github.com/kenji21) the Swift grammar has been updated to support Swift 4.1.
|
||||
This is a major update, since the old grammar was quite dated, and we are sure all iOS developers will enjoy it.
|
||||
|
||||
Unfortunately, this change is not compatible. The grammar elements that have been removed (ie: the keywords `__FILE__`,
|
||||
`__LINE__`, `__COLUMN__` and `__FUNCTION__`) are no longer supported. We don't usually introduce such drastic / breaking
|
||||
changes in minor releases, however, given that the whole Swift ecosystem pushes hard towards always using the latest
|
||||
versions, and that Swift needs all code and libraries to be currently compiling against the same Swift version,
|
||||
we felt strongly this change was both safe and necessary to be shipped as soon as possible. We had great feedback
|
||||
from the comunity during the processm but if you have a legitimate use case for older Swift versions, please let us know
|
||||
[on our Issue Tracke](https://github.com/pmd/pmd/issues).
|
||||
|
||||
#### New Rules
|
||||
|
||||
* The new Java rule [`InsecureCryptoIv`](pmd_rules_java_security.html#insecurecryptoiv) (`java-security`)
|
||||
detects hard coded initialization vectors used in cryptographic operations. It is recommended to use
|
||||
a randomly generated IV.
|
||||
|
||||
#### Modified Rules
|
||||
|
||||
* The Java rule `UnnecessaryConstructor` (`java-codestyle`) has been rewritten as a Java rule (previously it was
|
||||
a XPath-based rule). It supports a new property `ignoredAnnotations` and ignores by default empty constructors,
|
||||
* The Java rule [`UnnecessaryConstructor`](pmd_rules_java_codestyle.html#unnecessaryconstructor) (`java-codestyle`)
|
||||
has been rewritten as a Java rule (previously it was a XPath-based rule). It supports a new property
|
||||
`ignoredAnnotations` and ignores by default empty constructors,
|
||||
that are annotated with `javax.inject.Inject`. Additionally, it detects now also unnecessary private constructors
|
||||
in enums.
|
||||
|
||||
@ -62,12 +125,17 @@ we have measured up to 10% improvements during Type Resolution, Symbol Table ana
|
||||
### Fixed Issues
|
||||
|
||||
* all
|
||||
* [#695](https://github.com/pmd/pmd/issues/695): \[core] Extend comment-based suppression to all JavaCC languages
|
||||
* [#988](https://github.com/pmd/pmd/issues/988): \[core] FileNotFoundException for missing classes directory with analysis cache enabled
|
||||
* [#1036](https://github.com/pmd/pmd/issues/1036): \[core] Non-XML output breaks XML-based CLI integrations
|
||||
* apex-errorprone
|
||||
* [#776](https://github.com/pmd/pmd/issues/776): \[apex] AvoidHardcodingId false positives
|
||||
* documentation
|
||||
* [#994](https://github.com/pmd/pmd/issues/994): \[doc] Delete duplicate page contributing.md on the website
|
||||
* java
|
||||
* [#894](https://github.com/pmd/pmd/issues/894): \[java] Maven PMD plugin fails to process some files without any explanation
|
||||
* [#899](https://github.com/pmd/pmd/issues/899): \[java] JavaTypeDefinitionSimple.toString can cause NPEs
|
||||
* [#1020](https://github.com/pmd/pmd/issues/1020): \[java] The CyclomaticComplexity rule runs forever in 6.2.0
|
||||
* [#1030](https://github.com/pmd/pmd/pull/1030): \[java] NoClassDefFoundError when analyzing PMD with PMD
|
||||
* java-bestpractices
|
||||
* [#370](https://github.com/pmd/pmd/issues/370): \[java] GuardLogStatementJavaUtil not considering lambdas
|
||||
@ -77,8 +145,13 @@ we have measured up to 10% improvements during Type Resolution, Symbol Table ana
|
||||
* java-codestyle
|
||||
* [#1003](https://github.com/pmd/pmd/issues/1003): \[java] UnnecessaryConstructor triggered on required empty constructor (Dagger @Inject)
|
||||
* [#1023](https://github.com/pmd/pmd/issues/1023): \[java] False positive for useless parenthesis
|
||||
* java-errorprone
|
||||
* [#629](https://github.com/pmd/pmd/issues/629): \[java] NullAssignment false positive
|
||||
* [#816](https://github.com/pmd/pmd/issues/816): \[java] SingleMethodSingleton false positives with inner classes
|
||||
* java-performance
|
||||
* [#586](https://github.com/pmd/pmd/issues/586): \[java] AvoidUsingShortType erroneously triggered on overrides of 3rd party methods
|
||||
* swift
|
||||
* [#678](https://github.com/pmd/pmd/issues/678): \[swift][cpd] Exception when running for Swift 4 code (KeyPath)
|
||||
|
||||
### API Changes
|
||||
|
||||
@ -90,9 +163,14 @@ we have measured up to 10% improvements during Type Resolution, Symbol Table ana
|
||||
|
||||
### External Contributions
|
||||
|
||||
* [#778](https://github.com/pmd/pmd/pull/778): \[swift] Support Swift 4 grammar - [kenji21](https://github.com/kenji21)
|
||||
* [#1002](https://github.com/pmd/pmd/pull/1002): \[doc] Delete duplicate page contributing.md on the website - [Ishan Srivastava](https://github.com/ishanSrt)
|
||||
* [#1008](https://github.com/pmd/pmd/pull/1008): \[core] DOC: fix closing tag for <pmdVersion> - [stonio](https://github.com/stonio)
|
||||
* [#1010](https://github.com/pmd/pmd/pull/1010): \[java] UnnecessaryConstructor triggered on required empty constructor (Dagger @Inject) - [BBG](https://github.com/djydewang)
|
||||
* [#1012](https://github.com/pmd/pmd/pull/1012): \[java] JUnitAssertionsShouldIncludeMessage - False positive with assertEquals and JUnit5 - [BBG](https://github.com/djydewang)
|
||||
* [#1024](https://github.com/pmd/pmd/pull/1024): \[java]Issue 558: Properlogger for enums - [Utku Cuhadaroglu](https://github.com/utkuc)
|
||||
* [#1024](https://github.com/pmd/pmd/pull/1024): \[java] Issue 558: Properlogger for enums - [Utku Cuhadaroglu](https://github.com/utkuc)
|
||||
* [#1041](https://github.com/pmd/pmd/pull/1041): \[java] Make BasicProjectMemoizer thread safe. - [bergander](https://github.com/bergander)
|
||||
* [#1042](https://github.com/pmd/pmd/pull/1042): \[java] New security rule: report usage of hard coded IV in crypto operations - [Sergey Gorbaty](https://github.com/sgorbaty)
|
||||
* [#1044](https://github.com/pmd/pmd/pull/1044): \[java] Fix for issue #816 - [Akshat Bahety](https://github.com/akshatbahety)
|
||||
* [#1048](https://github.com/pmd/pmd/pull/1048): \[core] Make MultiThreadProcessor more space efficient - [Gonzalo Exequiel Ibars Ingman](https://github.com/gibarsin)
|
||||
|
||||
|
@ -4,18 +4,35 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.apex.rule.errorprone;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.sourceforge.pmd.lang.apex.ast.ASTLiteralExpression;
|
||||
import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule;
|
||||
|
||||
public class AvoidHardcodingIdRule extends AbstractApexRule {
|
||||
private static final Pattern PATTERN = Pattern.compile("^[a-zA-Z0-9]{5}[0][a-zA-Z0-9]{9,12}$", Pattern.CASE_INSENSITIVE);
|
||||
private static final Pattern PATTERN = Pattern.compile("^[a-zA-Z0-9]{5}0[a-zA-Z0-9]{9}([a-zA-Z0-5]{3})?$");
|
||||
private static final Map<String, Character> CHECKSUM_LOOKUP;
|
||||
|
||||
static {
|
||||
final Map<String, Character> lookup = new HashMap<>();
|
||||
final char[] chartable = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345".toCharArray();
|
||||
|
||||
for (int i = 0; i < chartable.length; i++) {
|
||||
lookup.put(String.format("%5s", Integer.toBinaryString(i)).replace(' ', '0'), chartable[i]);
|
||||
}
|
||||
|
||||
CHECKSUM_LOOKUP = Collections.unmodifiableMap(lookup);
|
||||
}
|
||||
|
||||
public AvoidHardcodingIdRule() {
|
||||
setProperty(CODECLIMATE_CATEGORIES, "Style");
|
||||
setProperty(CODECLIMATE_REMEDIATION_MULTIPLIER, 100);
|
||||
setProperty(CODECLIMATE_BLOCK_HIGHLIGHTING, false);
|
||||
|
||||
addRuleChainVisit(ASTLiteralExpression.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -24,9 +41,39 @@ public class AvoidHardcodingIdRule extends AbstractApexRule {
|
||||
if (o instanceof String) {
|
||||
String literal = (String) o;
|
||||
if (PATTERN.matcher(literal).matches()) {
|
||||
// 18-digit ids are just 15 digit ids + checksums, validate it or it's not an id
|
||||
if (literal.length() == 18 && !validateChecksum(literal)) {
|
||||
return data;
|
||||
}
|
||||
addViolation(data, node);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
* ID validation - sources:
|
||||
* https://stackoverflow.com/questions/9742913/validating-a-salesforce-id#answer-29299786
|
||||
* https://gist.github.com/jeriley/36b29f7c46527af4532aaf092c90dd56
|
||||
*/
|
||||
private boolean validateChecksum(String literal) {
|
||||
final String part1 = literal.substring(0, 5);
|
||||
final String part2 = literal.substring(5, 10);
|
||||
final String part3 = literal.substring(10, 15);
|
||||
|
||||
final char checksum1 = checksum(part1);
|
||||
final char checksum2 = checksum(part2);
|
||||
final char checksum3 = checksum(part3);
|
||||
|
||||
return literal.charAt(15) == checksum1 && literal.charAt(16) == checksum2
|
||||
&& literal.charAt(17) == checksum3;
|
||||
}
|
||||
|
||||
private char checksum(String part) {
|
||||
final StringBuilder sb = new StringBuilder(5);
|
||||
for (int i = 4; i >= 0; i--) {
|
||||
sb.append(Character.isUpperCase(part.charAt(i)) ? '1' : '0');
|
||||
}
|
||||
return CHECKSUM_LOOKUP.get(sb.toString());
|
||||
}
|
||||
}
|
||||
|
@ -50,4 +50,45 @@ public class Foo {
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>Test for random string combinations - more than 15, less than 18 digits</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
public class Foo {
|
||||
void foo() {
|
||||
return 'jatua0tzbtazi1243';
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>Test for random string combinations - checksum doesn't match</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
public class Foo {
|
||||
void foo() {
|
||||
return '001A0000006Vm9uIAE';
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>[apex] AvoidHardcodingId false positives #776</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
public class Foo {
|
||||
void foo() {
|
||||
// this is a false positive, we can't say, whether it's a salesforce id or not
|
||||
@SuppressWarnings('PMD.AvoidHardcodingId')
|
||||
String IMEI__c = '359040082913024';
|
||||
|
||||
// now the 6th character is non-0, definitive not a salesforce id
|
||||
String IMEI2__c = '359041082913024';
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
</test-data>
|
||||
|
@ -15,7 +15,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.ConsoleHandler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@ -45,7 +45,6 @@ import net.sourceforge.pmd.util.database.DBURI;
|
||||
import net.sourceforge.pmd.util.database.SourceObject;
|
||||
import net.sourceforge.pmd.util.datasource.DataSource;
|
||||
import net.sourceforge.pmd.util.datasource.ReaderDataSource;
|
||||
import net.sourceforge.pmd.util.log.ConsoleLogHandler;
|
||||
import net.sourceforge.pmd.util.log.ScopedLogHandlersManager;
|
||||
|
||||
/**
|
||||
@ -450,8 +449,7 @@ public class PMD {
|
||||
final PMDConfiguration configuration = params.toConfiguration();
|
||||
|
||||
final Level logLevel = params.isDebug() ? Level.FINER : Level.INFO;
|
||||
final Handler logHandler = new ConsoleLogHandler();
|
||||
final ScopedLogHandlersManager logHandlerManager = new ScopedLogHandlersManager(logLevel, logHandler);
|
||||
final ScopedLogHandlersManager logHandlerManager = new ScopedLogHandlersManager(logLevel, new ConsoleHandler());
|
||||
final Level oldLogLevel = LOG.getLevel();
|
||||
// Need to do this, since the static logger has already been initialized
|
||||
// at this point
|
||||
|
@ -351,7 +351,7 @@ public class CPDConfiguration extends AbstractConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
FilenameFilter filter = new FilenameFilter() {
|
||||
return new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
File f = new File(dir, name);
|
||||
@ -362,7 +362,6 @@ public class CPDConfiguration extends AbstractConfiguration {
|
||||
return languageFilter.accept(dir, name);
|
||||
}
|
||||
};
|
||||
return filter;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -720,7 +720,7 @@ public class GUI implements CPDListener {
|
||||
|
||||
final long start = System.currentTimeMillis();
|
||||
|
||||
Timer t = new Timer(1000, new ActionListener() {
|
||||
return new Timer(1000, new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
long now = System.currentTimeMillis();
|
||||
@ -731,7 +731,6 @@ public class GUI implements CPDListener {
|
||||
timeField.setText(formatTime(minutes, seconds));
|
||||
}
|
||||
});
|
||||
return t;
|
||||
}
|
||||
|
||||
private static String formatTime(long minutes, long seconds) {
|
||||
@ -762,7 +761,7 @@ public class GUI implements CPDListener {
|
||||
|
||||
private TableModel tableModelFrom(final List<Match> items) {
|
||||
|
||||
TableModel model = new SortingTableModel<Match>() {
|
||||
return new SortingTableModel<Match>() {
|
||||
|
||||
private int sortColumn;
|
||||
private boolean sortDescending;
|
||||
@ -837,8 +836,6 @@ public class GUI implements CPDListener {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
private void sortOnColumn(int columnIndex) {
|
||||
|
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.cpd.token;
|
||||
|
||||
import net.sourceforge.pmd.lang.TokenManager;
|
||||
import net.sourceforge.pmd.lang.ast.GenericToken;
|
||||
|
||||
/**
|
||||
* A generic filter for JavaCC-based token managers that allows to use comments
|
||||
* to enable / disable analysis of parts of the stream
|
||||
*/
|
||||
public class JavaCCTokenFilter implements TokenFilter {
|
||||
|
||||
private final TokenManager tokenManager;
|
||||
private boolean discardingSuppressing;
|
||||
|
||||
/**
|
||||
* Creates a new JavaCCTokenFilter
|
||||
* @param tokenManager The token manager from which to retrieve tokens to be filtered
|
||||
*/
|
||||
public JavaCCTokenFilter(final TokenManager tokenManager) {
|
||||
this.tokenManager = tokenManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final GenericToken getNextToken() {
|
||||
GenericToken currentToken = (GenericToken) tokenManager.getNextToken();
|
||||
while (!currentToken.getImage().isEmpty()) {
|
||||
analyzeToken(currentToken);
|
||||
processCPDSuppression(currentToken);
|
||||
|
||||
if (!isDiscarding()) {
|
||||
return currentToken;
|
||||
}
|
||||
|
||||
currentToken = (GenericToken) tokenManager.getNextToken();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isDiscarding() {
|
||||
return discardingSuppressing || isLanguageSpecificDiscarding();
|
||||
}
|
||||
|
||||
private void processCPDSuppression(final GenericToken currentToken) {
|
||||
// Check if a comment is altering the suppression state
|
||||
GenericToken comment = currentToken.getPreviousComment();
|
||||
while (comment != null) {
|
||||
if (comment.getImage().contains("CPD-OFF")) {
|
||||
discardingSuppressing = true;
|
||||
break;
|
||||
}
|
||||
if (comment.getImage().contains("CPD-ON")) {
|
||||
discardingSuppressing = false;
|
||||
break;
|
||||
}
|
||||
comment = comment.getPreviousComment();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension point for subclasses to indicate tokens are to be filtered.
|
||||
*
|
||||
* @return True if tokens should be filtered, false otherwise
|
||||
*/
|
||||
protected boolean isLanguageSpecificDiscarding() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension point for subclasses to analyze all tokens (before filtering)
|
||||
* and update internal status to decide on custom discard rules.
|
||||
*
|
||||
* @param currentToken The token to be analyzed
|
||||
* @see #isLanguageSpecificDiscarding()
|
||||
*/
|
||||
protected void analyzeToken(final GenericToken currentToken) {
|
||||
// noop
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.cpd.token;
|
||||
|
||||
import net.sourceforge.pmd.lang.ast.GenericToken;
|
||||
|
||||
/**
|
||||
* Defines filter to be applied to the token stream during CPD analysis
|
||||
*/
|
||||
public interface TokenFilter {
|
||||
|
||||
/**
|
||||
* Retrieves the next token to pass the filter
|
||||
* @return The next token to pass the filter, or null if the end of the stream was reached
|
||||
*/
|
||||
GenericToken getNextToken();
|
||||
}
|
@ -8,6 +8,7 @@ package net.sourceforge.pmd.lang;
|
||||
* Common interface for interacting with parser Token Managers.
|
||||
*/
|
||||
public interface TokenManager {
|
||||
// TODO : Change the return to GenericToken in 7.0.0 - maybe even use generics TokenManager<T extends GenericToken>
|
||||
Object getNextToken();
|
||||
|
||||
void setFileName(String fileName);
|
||||
|
@ -28,6 +28,8 @@ public abstract class BasicProjectMemoizer<T extends QualifiableNode, O extends
|
||||
private Map<QualifiedName, MetricMemoizer<T>> classes = new WeakHashMap<>();
|
||||
private Map<QualifiedName, MetricMemoizer<O>> operations = new WeakHashMap<>();
|
||||
|
||||
private final Object classesSynchronizer = new Object();
|
||||
private final Object operationsSynchronizer = new Object();
|
||||
|
||||
/** Clears all memoizers. Used for tests. */
|
||||
public void reset() {
|
||||
@ -38,8 +40,10 @@ public abstract class BasicProjectMemoizer<T extends QualifiableNode, O extends
|
||||
|
||||
@Override
|
||||
public MetricMemoizer<O> getOperationMemoizer(QualifiedName qname) {
|
||||
if (!operations.containsKey(qname)) {
|
||||
operations.put(qname, new BasicMetricMemoizer<O>());
|
||||
synchronized (operationsSynchronizer) {
|
||||
if (!operations.containsKey(qname)) {
|
||||
operations.put(qname, new BasicMetricMemoizer<O>());
|
||||
}
|
||||
}
|
||||
|
||||
return operations.get(qname);
|
||||
@ -48,8 +52,10 @@ public abstract class BasicProjectMemoizer<T extends QualifiableNode, O extends
|
||||
|
||||
@Override
|
||||
public MetricMemoizer<T> getClassMemoizer(QualifiedName qname) {
|
||||
if (!classes.containsKey(qname)) {
|
||||
classes.put(qname, new BasicMetricMemoizer<T>());
|
||||
synchronized (classesSynchronizer) {
|
||||
if (!classes.containsKey(qname)) {
|
||||
classes.put(qname, new BasicMetricMemoizer<T>());
|
||||
}
|
||||
}
|
||||
|
||||
return classes.get(qname);
|
||||
|
@ -4,28 +4,26 @@
|
||||
|
||||
package net.sourceforge.pmd.processor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletionService;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorCompletionService;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import net.sourceforge.pmd.PMDConfiguration;
|
||||
import net.sourceforge.pmd.Report;
|
||||
import net.sourceforge.pmd.renderers.Renderer;
|
||||
|
||||
|
||||
/**
|
||||
* @author Romain Pelisse <belaran@gmail.com>
|
||||
*
|
||||
*/
|
||||
public class MultiThreadProcessor extends AbstractPMDProcessor {
|
||||
private final ExecutorService executor;
|
||||
private final CompletionService<Report> completionService;
|
||||
|
||||
private ExecutorService executor;
|
||||
private CompletionService<Report> completionService;
|
||||
private List<Future<Report>> tasks = new ArrayList<>();
|
||||
private long submittedTasks = 0L;
|
||||
|
||||
public MultiThreadProcessor(final PMDConfiguration configuration) {
|
||||
super(configuration);
|
||||
@ -36,22 +34,21 @@ public class MultiThreadProcessor extends AbstractPMDProcessor {
|
||||
|
||||
@Override
|
||||
protected void runAnalysis(PmdRunnable runnable) {
|
||||
// multi-threaded execution, dispatch analysis to worker threads
|
||||
tasks.add(completionService.submit(runnable));
|
||||
completionService.submit(runnable);
|
||||
submittedTasks++;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void collectReports(List<Renderer> renderers) {
|
||||
// Collect result analysis, waiting for termination if needed
|
||||
try {
|
||||
for (int i = 0; i < tasks.size(); i++) {
|
||||
for (int i = 0; i < submittedTasks; i++) {
|
||||
final Report report = completionService.take().get();
|
||||
super.renderReports(renderers, report);
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
} catch (final InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (ExecutionException ee) {
|
||||
Throwable t = ee.getCause();
|
||||
} catch (final ExecutionException ee) {
|
||||
final Throwable t = ee.getCause();
|
||||
if (t instanceof RuntimeException) {
|
||||
throw (RuntimeException) t;
|
||||
} else if (t instanceof Error) {
|
||||
|
@ -312,7 +312,7 @@ public class Designer implements ClipboardOwner {
|
||||
|
||||
@Override
|
||||
public Enumeration<TreeNode> children() {
|
||||
Enumeration<TreeNode> e = new Enumeration<TreeNode>() {
|
||||
return new Enumeration<TreeNode>() {
|
||||
int i = 0;
|
||||
|
||||
@Override
|
||||
@ -325,7 +325,6 @@ public class Designer implements ClipboardOwner {
|
||||
return kids[i++];
|
||||
}
|
||||
};
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -395,7 +394,7 @@ public class Designer implements ClipboardOwner {
|
||||
getChildAt(0); // force it to build kids
|
||||
}
|
||||
|
||||
Enumeration<TreeNode> e = new Enumeration<TreeNode>() {
|
||||
return new Enumeration<TreeNode>() {
|
||||
int i = 0;
|
||||
|
||||
@Override
|
||||
@ -408,7 +407,6 @@ public class Designer implements ClipboardOwner {
|
||||
return kids[i++];
|
||||
}
|
||||
};
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -14,7 +14,9 @@ import java.util.logging.LogRecord;
|
||||
* Log to the console using a basic formatter.
|
||||
*
|
||||
* @author Wouter Zelle
|
||||
* @deprecated This class will be complety removed in 7.0.0
|
||||
*/
|
||||
@Deprecated
|
||||
public class ConsoleLogHandler extends Handler {
|
||||
|
||||
private static final Formatter FORMATTER = new PmdLogFormatter();
|
||||
|
@ -36,7 +36,9 @@ public class ScopedLogHandlersManager {
|
||||
}
|
||||
for (Handler handler : newHandlers) {
|
||||
logger.addHandler(handler);
|
||||
handler.setLevel(level);
|
||||
}
|
||||
logger.setUseParentHandlers(false);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
@ -47,5 +49,6 @@ public class ScopedLogHandlersManager {
|
||||
logger.addHandler(handler);
|
||||
}
|
||||
logger.setLevel(oldLogLevel);
|
||||
logger.setUseParentHandlers(true);
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,6 @@
|
||||
<rule ref="category/java/errorprone.xml" />
|
||||
<rule ref="category/java/multithreading.xml" />
|
||||
<rule ref="category/java/performance.xml" />
|
||||
<!-- <rule ref="category/java/security.xml" /> -->
|
||||
<rule ref="category/java/security.xml" />
|
||||
|
||||
</ruleset>
|
||||
|
13
pmd-core/src/main/resources/rulesets/releases/630.xml
Normal file
13
pmd-core/src/main/resources/rulesets/releases/630.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<ruleset name="630"
|
||||
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
|
||||
<description>
|
||||
This ruleset contains links to rules that are new in PMD v6.3.0
|
||||
</description>
|
||||
|
||||
<rule ref="category/java/security.xml/InsecureCryptoIv"/>
|
||||
</ruleset>
|
||||
|
@ -129,10 +129,9 @@ public final class CppParser {
|
||||
return sym.IsCtor(GetFullyScopedName());
|
||||
}
|
||||
}
|
||||
|
||||
PARSER_END(CppParser)
|
||||
|
||||
SKIP :
|
||||
SKIP:
|
||||
{
|
||||
" "
|
||||
|
|
||||
@ -143,38 +142,28 @@ SKIP :
|
||||
"\r\n"
|
||||
|
|
||||
"\n"
|
||||
|
|
||||
"//" : IN_LINE_COMMENT
|
||||
|
|
||||
"/*" : IN_COMMENT
|
||||
|
|
||||
"#" : PREPROCESSOR_OUTPUT
|
||||
}
|
||||
|
||||
<IN_LINE_COMMENT> SKIP:
|
||||
{
|
||||
"\n" : DEFAULT
|
||||
}
|
||||
<DEFAULT,PREPROCESSOR_OUTPUT> SPECIAL_TOKEN:
|
||||
{ <SINGLE_LINE_COMMENT: "//"(~["\n","\r"])* ("\n"|"\r"|"\r\n")?> }
|
||||
|
||||
<IN_LINE_COMMENT> MORE:
|
||||
{
|
||||
< ~[] >
|
||||
}
|
||||
MORE:
|
||||
{ "/*" : IN_MULTI_LINE_COMMENT }
|
||||
|
||||
<IN_COMMENT> SKIP:
|
||||
{ "*/" : DEFAULT }
|
||||
<IN_MULTI_LINE_COMMENT> SPECIAL_TOKEN:
|
||||
{ <MULTI_LINE_COMMENT: "*/">: DEFAULT }
|
||||
|
||||
<IN_COMMENT,IN_PREPROCESSOR_OUTPUT_COMMENT> MORE:
|
||||
<IN_MULTI_LINE_COMMENT,IN_PREPROCESSOR_OUTPUT_COMMENT> MORE:
|
||||
{ < ~[] > }
|
||||
|
||||
<IN_PREPROCESSOR_OUTPUT_COMMENT> SKIP:
|
||||
{ "*/" : PREPROCESSOR_OUTPUT }
|
||||
<IN_PREPROCESSOR_OUTPUT_COMMENT> SPECIAL_TOKEN:
|
||||
{ <PREPROCESSOR_OUTPUT_COMMENT: "*/">: PREPROCESSOR_OUTPUT }
|
||||
|
||||
<PREPROCESSOR_OUTPUT> SKIP:
|
||||
{
|
||||
"\n" : DEFAULT
|
||||
| "/*" : IN_PREPROCESSOR_OUTPUT_COMMENT
|
||||
| "//" : IN_LINE_COMMENT
|
||||
}
|
||||
|
||||
<PREPROCESSOR_OUTPUT> MORE:
|
||||
@ -183,6 +172,8 @@ SKIP :
|
||||
|
|
||||
"\\\r\n"
|
||||
|
|
||||
"/*": IN_PREPROCESSOR_OUTPUT_COMMENT
|
||||
|
|
||||
< ~[] >
|
||||
}
|
||||
|
||||
|
@ -13,12 +13,13 @@ import java.util.Properties;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import net.sourceforge.pmd.PMD;
|
||||
import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter;
|
||||
import net.sourceforge.pmd.cpd.token.TokenFilter;
|
||||
import net.sourceforge.pmd.lang.LanguageRegistry;
|
||||
import net.sourceforge.pmd.lang.LanguageVersionHandler;
|
||||
import net.sourceforge.pmd.lang.TokenManager;
|
||||
import net.sourceforge.pmd.lang.ast.GenericToken;
|
||||
import net.sourceforge.pmd.lang.ast.TokenMgrError;
|
||||
import net.sourceforge.pmd.lang.cpp.CppLanguageModule;
|
||||
import net.sourceforge.pmd.lang.cpp.ast.Token;
|
||||
import net.sourceforge.pmd.util.IOUtil;
|
||||
|
||||
/**
|
||||
@ -61,13 +62,14 @@ public class CPPTokenizer implements Tokenizer {
|
||||
.getDefaultVersion().getLanguageVersionHandler();
|
||||
reader = new StringReader(maybeSkipBlocks(buffer.toString()));
|
||||
reader = IOUtil.skipBOM(reader);
|
||||
TokenManager tokenManager = languageVersionHandler
|
||||
.getParser(languageVersionHandler.getDefaultParserOptions())
|
||||
.getTokenManager(sourceCode.getFileName(), reader);
|
||||
Token currentToken = (Token) tokenManager.getNextToken();
|
||||
while (currentToken.image.length() > 0) {
|
||||
tokenEntries.add(new TokenEntry(currentToken.image, sourceCode.getFileName(), currentToken.beginLine));
|
||||
currentToken = (Token) tokenManager.getNextToken();
|
||||
final TokenFilter tokenFilter = new JavaCCTokenFilter(
|
||||
languageVersionHandler.getParser(languageVersionHandler.getDefaultParserOptions())
|
||||
.getTokenManager(sourceCode.getFileName(), reader));
|
||||
|
||||
GenericToken currentToken = tokenFilter.getNextToken();
|
||||
while (currentToken != null) {
|
||||
tokenEntries.add(new TokenEntry(currentToken.getImage(), sourceCode.getFileName(), currentToken.getBeginLine()));
|
||||
currentToken = tokenFilter.getNextToken();
|
||||
}
|
||||
tokenEntries.add(TokenEntry.getEOF());
|
||||
System.err.println("Added " + sourceCode.getFileName());
|
||||
|
@ -5,6 +5,7 @@
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Properties;
|
||||
@ -19,7 +20,7 @@ public class CPPTokenizerTest {
|
||||
@Test
|
||||
public void testUTFwithBOM() {
|
||||
Tokens tokens = parse("\ufeffint start()\n{ int ret = 1;\nreturn ret;\n}\n");
|
||||
assertTrue(TokenEntry.getEOF() != tokens.getTokens().get(0));
|
||||
assertNotSame(TokenEntry.getEOF(), tokens.getTokens().get(0));
|
||||
assertEquals(15, tokens.size());
|
||||
}
|
||||
|
||||
@ -29,9 +30,19 @@ public class CPPTokenizerTest {
|
||||
+ "int main()\n" + "{\n" + " std::string text(\"ąęćśźńó\");\n" + " std::cout << text;\n"
|
||||
+ " return 0;\n" + "}\n";
|
||||
Tokens tokens = parse(code);
|
||||
assertTrue(TokenEntry.getEOF() != tokens.getTokens().get(0));
|
||||
assertNotSame(TokenEntry.getEOF(), tokens.getTokens().get(0));
|
||||
assertEquals(24, tokens.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIgnoreBetweenSpecialComments() {
|
||||
String code = "#include <iostream>\n" + "#include <string>\n" + "\n" + "// CPD-OFF\n"
|
||||
+ "int main()\n" + "{\n" + " std::string text(\"ąęćśźńó\");\n" + " std::cout << text;\n"
|
||||
+ " return 0;\n" + "// CPD-ON\n" + "}\n";
|
||||
Tokens tokens = parse(code);
|
||||
assertNotSame(TokenEntry.getEOF(), tokens.getTokens().get(0));
|
||||
assertEquals(2, tokens.size()); // "}" + EOF
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiLineMacros() {
|
||||
|
@ -9,9 +9,11 @@ import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter;
|
||||
import net.sourceforge.pmd.lang.LanguageRegistry;
|
||||
import net.sourceforge.pmd.lang.LanguageVersionHandler;
|
||||
import net.sourceforge.pmd.lang.TokenManager;
|
||||
import net.sourceforge.pmd.lang.ast.GenericToken;
|
||||
import net.sourceforge.pmd.lang.java.JavaLanguageModule;
|
||||
import net.sourceforge.pmd.lang.java.ast.JavaParserConstants;
|
||||
import net.sourceforge.pmd.lang.java.ast.Token;
|
||||
@ -31,34 +33,30 @@ public class JavaTokenizer implements Tokenizer {
|
||||
ignoreIdentifiers = Boolean.parseBoolean(properties.getProperty(IGNORE_IDENTIFIERS, "false"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tokenize(SourceCode sourceCode, Tokens tokenEntries) {
|
||||
StringBuilder stringBuilder = sourceCode.getCodeBuffer();
|
||||
|
||||
// Note that Java version is irrelevant for tokenizing
|
||||
LanguageVersionHandler languageVersionHandler = LanguageRegistry.getLanguage(JavaLanguageModule.NAME)
|
||||
.getVersion("1.4").getLanguageVersionHandler();
|
||||
String fileName = sourceCode.getFileName();
|
||||
TokenManager tokenMgr = languageVersionHandler.getParser(languageVersionHandler.getDefaultParserOptions())
|
||||
.getTokenManager(fileName, new StringReader(stringBuilder.toString()));
|
||||
Token currentToken = (Token) tokenMgr.getNextToken();
|
||||
|
||||
TokenDiscarder discarder = new TokenDiscarder(ignoreAnnotations);
|
||||
ConstructorDetector constructorDetector = new ConstructorDetector(ignoreIdentifiers);
|
||||
|
||||
while (currentToken.image.length() > 0) {
|
||||
discarder.updateState(currentToken);
|
||||
|
||||
if (discarder.isDiscarding()) {
|
||||
currentToken = (Token) tokenMgr.getNextToken();
|
||||
continue;
|
||||
}
|
||||
final String fileName = sourceCode.getFileName();
|
||||
final JavaTokenFilter tokenFilter = createTokenFilter(sourceCode);
|
||||
final ConstructorDetector constructorDetector = new ConstructorDetector(ignoreIdentifiers);
|
||||
|
||||
Token currentToken = (Token) tokenFilter.getNextToken();
|
||||
while (currentToken != null) {
|
||||
processToken(tokenEntries, fileName, currentToken, constructorDetector);
|
||||
currentToken = (Token) tokenMgr.getNextToken();
|
||||
currentToken = (Token) tokenFilter.getNextToken();
|
||||
}
|
||||
tokenEntries.add(TokenEntry.getEOF());
|
||||
}
|
||||
|
||||
private JavaTokenFilter createTokenFilter(final SourceCode sourceCode) {
|
||||
final StringBuilder stringBuilder = sourceCode.getCodeBuffer();
|
||||
// Note that Java version is irrelevant for tokenizing
|
||||
final LanguageVersionHandler languageVersionHandler = LanguageRegistry.getLanguage(JavaLanguageModule.NAME)
|
||||
.getVersion("1.4").getLanguageVersionHandler();
|
||||
final TokenManager tokenMgr = languageVersionHandler.getParser(languageVersionHandler.getDefaultParserOptions())
|
||||
.getTokenManager(sourceCode.getFileName(), new StringReader(stringBuilder.toString()));
|
||||
return new JavaTokenFilter(tokenMgr, ignoreAnnotations);
|
||||
}
|
||||
|
||||
private void processToken(Tokens tokenEntries, String fileName, Token currentToken,
|
||||
ConstructorDetector constructorDetector) {
|
||||
String image = currentToken.image;
|
||||
@ -93,15 +91,14 @@ public class JavaTokenizer implements Tokenizer {
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link TokenDiscarder} consumes token by token and maintains state.
|
||||
* It can detect, whether the current token belongs to an annotation and
|
||||
* whether the current token should be discarded by CPD.
|
||||
* The {@link JavaTokenFilter} extends the {@link JavaCCTokenFilter} to discard
|
||||
* Java-specific tokens.
|
||||
* <p>
|
||||
* By default, it discards semicolons, package and import statements, and
|
||||
* enables CPD suppression. Optionally, all annotations can be ignored, too.
|
||||
* enables annotation-based CPD suppression. Optionally, all annotations can be ignored, too.
|
||||
* </p>
|
||||
*/
|
||||
private static class TokenDiscarder {
|
||||
private static class JavaTokenFilter extends JavaCCTokenFilter {
|
||||
private boolean isAnnotation = false;
|
||||
private boolean nextTokenEndsAnnotation = false;
|
||||
private int annotationStack = 0;
|
||||
@ -112,22 +109,24 @@ public class JavaTokenizer implements Tokenizer {
|
||||
private boolean discardingAnnotations = false;
|
||||
private boolean ignoreAnnotations = false;
|
||||
|
||||
TokenDiscarder(boolean ignoreAnnotations) {
|
||||
JavaTokenFilter(final TokenManager tokenManager, final boolean ignoreAnnotations) {
|
||||
super(tokenManager);
|
||||
this.ignoreAnnotations = ignoreAnnotations;
|
||||
}
|
||||
|
||||
public void updateState(Token currentToken) {
|
||||
detectAnnotations(currentToken);
|
||||
@Override
|
||||
protected void analyzeToken(final GenericToken currentToken) {
|
||||
detectAnnotations((Token) currentToken);
|
||||
|
||||
skipSemicolon(currentToken);
|
||||
skipPackageAndImport(currentToken);
|
||||
skipCPDSuppression(currentToken);
|
||||
skipSemicolon((Token) currentToken);
|
||||
skipPackageAndImport((Token) currentToken);
|
||||
skipAnnotationSuppression((Token) currentToken);
|
||||
if (ignoreAnnotations) {
|
||||
skipAnnotations();
|
||||
}
|
||||
}
|
||||
|
||||
private void skipPackageAndImport(Token currentToken) {
|
||||
private void skipPackageAndImport(final Token currentToken) {
|
||||
if (currentToken.kind == JavaParserConstants.PACKAGE || currentToken.kind == JavaParserConstants.IMPORT) {
|
||||
discardingKeywords = true;
|
||||
} else if (discardingKeywords && currentToken.kind == JavaParserConstants.SEMICOLON) {
|
||||
@ -135,7 +134,7 @@ public class JavaTokenizer implements Tokenizer {
|
||||
}
|
||||
}
|
||||
|
||||
private void skipSemicolon(Token currentToken) {
|
||||
private void skipSemicolon(final Token currentToken) {
|
||||
if (currentToken.kind == JavaParserConstants.SEMICOLON) {
|
||||
discardingSemicolon = true;
|
||||
} else if (discardingSemicolon && currentToken.kind != JavaParserConstants.SEMICOLON) {
|
||||
@ -143,21 +142,7 @@ public class JavaTokenizer implements Tokenizer {
|
||||
}
|
||||
}
|
||||
|
||||
private void skipCPDSuppression(Token currentToken) {
|
||||
// Check if a comment is altering the suppression state
|
||||
Token st = currentToken.specialToken;
|
||||
while (st != null) {
|
||||
if (st.image.contains("CPD-OFF")) {
|
||||
discardingSuppressing = true;
|
||||
break;
|
||||
}
|
||||
if (st.image.contains("CPD-ON")) {
|
||||
discardingSuppressing = false;
|
||||
break;
|
||||
}
|
||||
st = st.specialToken;
|
||||
}
|
||||
|
||||
private void skipAnnotationSuppression(final Token currentToken) {
|
||||
// if processing an annotation, look for a CPD-START or CPD-END
|
||||
if (isAnnotation) {
|
||||
if (!discardingSuppressing && currentToken.kind == JavaParserConstants.STRING_LITERAL
|
||||
@ -178,7 +163,8 @@ public class JavaTokenizer implements Tokenizer {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDiscarding() {
|
||||
@Override
|
||||
protected boolean isLanguageSpecificDiscarding() {
|
||||
return discardingSemicolon || discardingKeywords || discardingAnnotations
|
||||
|| discardingSuppressing;
|
||||
}
|
||||
|
@ -5,21 +5,26 @@
|
||||
package net.sourceforge.pmd.lang.java.rule.errorprone;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAssignmentOperator;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTConditionalExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTLambdaExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTName;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTVariableInitializer;
|
||||
import net.sourceforge.pmd.lang.java.ast.AccessNode;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
|
||||
import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
|
||||
|
||||
// TODO - should check that this is not the first assignment. e.g., this is OK:
|
||||
// Object x;
|
||||
// x = null;
|
||||
public class NullAssignmentRule extends AbstractJavaRule {
|
||||
|
||||
public NullAssignmentRule() {
|
||||
addRuleChainVisit(ASTNullLiteral.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTNullLiteral node, Object data) {
|
||||
|
||||
@ -55,7 +60,19 @@ public class NullAssignmentRule extends AbstractJavaRule {
|
||||
&& ((AccessNode) ((VariableNameDeclaration) name.getNameDeclaration()).getAccessNodeParent()).isFinal();
|
||||
}
|
||||
|
||||
private boolean isBadTernary(ASTConditionalExpression n) {
|
||||
return n.isTernary() && !(n.jjtGetChild(0) instanceof ASTEqualityExpression);
|
||||
private boolean isBadTernary(ASTConditionalExpression ternary) {
|
||||
boolean isInitializer = false;
|
||||
|
||||
ASTVariableInitializer variableInitializer = ternary.getFirstParentOfType(ASTVariableInitializer.class);
|
||||
if (variableInitializer != null) {
|
||||
ASTBlockStatement statement = ternary.getFirstParentOfType(ASTBlockStatement.class);
|
||||
isInitializer = statement == variableInitializer.getFirstParentOfType(ASTBlockStatement.class);
|
||||
}
|
||||
|
||||
return ternary.isTernary()
|
||||
&& !(ternary.jjtGetChild(0) instanceof ASTEqualityExpression)
|
||||
&& !isInitializer
|
||||
&& !(ternary.getNthParent(2) instanceof ASTReturnStatement)
|
||||
&& !(ternary.getNthParent(2) instanceof ASTLambdaExpression);
|
||||
}
|
||||
}
|
||||
|
@ -4,35 +4,49 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.rule.errorprone;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
|
||||
|
||||
/**
|
||||
* Returns Checks if the singleton rule is used properly.
|
||||
*/
|
||||
public class SingleMethodSingletonRule extends AbstractJavaRule {
|
||||
|
||||
private Set<String> methodset = new HashSet<String>();
|
||||
/**
|
||||
* Checks for getInstance method usage in the same class.
|
||||
* @param node of ASTCLass
|
||||
* @param data of Object
|
||||
* @return Object
|
||||
*
|
||||
*/
|
||||
|
||||
@Override
|
||||
public Object visit(ASTCompilationUnit node, Object data) {
|
||||
methodset.clear();
|
||||
return super.visit(node, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTMethodDeclaration node, Object data) {
|
||||
if (node.getResultType().isVoid()) {
|
||||
return super.visit(node, data);
|
||||
}
|
||||
public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
|
||||
|
||||
if ("getInstance".equals(node.getMethodName())) {
|
||||
if (!methodset.add(node.getMethodName())) {
|
||||
addViolation(data, node);
|
||||
|
||||
List<ASTMethodDeclaration> methods = node.findDescendantsOfType(ASTMethodDeclaration.class); // Find the name of methods in it
|
||||
|
||||
int count = 0;
|
||||
for (ASTMethodDeclaration method : methods) {
|
||||
|
||||
if (method.getName().equals("getInstance")) {
|
||||
count++;
|
||||
if (count > 1) {
|
||||
addViolation(data, node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
return super.visit(node, data);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,132 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.java.rule.security;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTArrayInitializer;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTName;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTReferenceType;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTVariableInitializer;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
|
||||
import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
|
||||
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
|
||||
|
||||
/**
|
||||
* Finds hardcoded static Initialization Vectors vectors used with cryptographic
|
||||
* operations.
|
||||
*
|
||||
* //bad: byte[] ivBytes = new byte[] {32, 87, -14, 25, 78, -104, 98, 40};
|
||||
* //bad: byte[] ivBytes = "hardcoded".getBytes(); //bad: byte[] ivBytes =
|
||||
* someString.getBytes();
|
||||
*
|
||||
* javax.crypto.spec.IvParameterSpec must not be created from a static sources
|
||||
*
|
||||
* @author sergeygorbaty
|
||||
* @since 6.3.0
|
||||
*
|
||||
*/
|
||||
public class InsecureCryptoIvRule extends AbstractJavaRule {
|
||||
|
||||
public InsecureCryptoIvRule() {
|
||||
addRuleChainVisit(ASTClassOrInterfaceBodyDeclaration.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTClassOrInterfaceBodyDeclaration node, Object data) {
|
||||
Set<ASTLocalVariableDeclaration> foundLocalVars = new HashSet<>();
|
||||
Set<String> passedInIvVarNames = new HashSet<>();
|
||||
|
||||
// find new javax.crypto.spec.IvParameterSpec(...)
|
||||
List<ASTAllocationExpression> allocations = node.findDescendantsOfType(ASTAllocationExpression.class);
|
||||
for (ASTAllocationExpression allocation : allocations) {
|
||||
|
||||
ASTClassOrInterfaceType declClassName = allocation.getFirstDescendantOfType(ASTClassOrInterfaceType.class);
|
||||
if (declClassName != null) {
|
||||
Class<?> foundClass = declClassName.getType();
|
||||
if (foundClass != null && javax.crypto.spec.IvParameterSpec.class.isAssignableFrom(foundClass)) {
|
||||
ASTPrimaryExpression init = allocation.getFirstDescendantOfType(ASTPrimaryExpression.class);
|
||||
if (init != null) {
|
||||
ASTName name = init.getFirstDescendantOfType(ASTName.class);
|
||||
if (name != null) {
|
||||
passedInIvVarNames.add(name.getImage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<ASTLocalVariableDeclaration> localVars = node.findDescendantsOfType(ASTLocalVariableDeclaration.class);
|
||||
for (ASTLocalVariableDeclaration localVar : localVars) {
|
||||
foundLocalVars.addAll(extractPrimitiveTypes(localVar));
|
||||
}
|
||||
|
||||
Map<VariableNameDeclaration, List<NameOccurrence>> globalDecls = node.getScope()
|
||||
.getDeclarations(VariableNameDeclaration.class);
|
||||
|
||||
for (VariableNameDeclaration fieldVar : globalDecls.keySet()) {
|
||||
if (passedInIvVarNames.contains(fieldVar.getNode().getImage())) {
|
||||
ASTVariableDeclarator var = fieldVar.getNode().getFirstParentOfType(ASTVariableDeclarator.class);
|
||||
if (var != null) {
|
||||
validateProperIv(data, var.getFirstDescendantOfType(ASTVariableInitializer.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ASTLocalVariableDeclaration foundLocalVar : foundLocalVars) {
|
||||
if (passedInIvVarNames.contains(foundLocalVar.getVariableName())) {
|
||||
validateProperIv(data, foundLocalVar.getFirstDescendantOfType(ASTVariableInitializer.class));
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private Set<ASTLocalVariableDeclaration> extractPrimitiveTypes(ASTLocalVariableDeclaration localVar) {
|
||||
List<ASTPrimitiveType> types = localVar.findDescendantsOfType(ASTPrimitiveType.class);
|
||||
Set<ASTLocalVariableDeclaration> retVal = new HashSet<>();
|
||||
extractPrimitiveTypesInner(retVal, localVar, types);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private <T> void extractPrimitiveTypesInner(Set<T> retVal, T field, List<ASTPrimitiveType> types) {
|
||||
for (ASTPrimitiveType type : types) {
|
||||
if (type.hasImageEqualTo("byte")) {
|
||||
ASTReferenceType parent = type.getFirstParentOfType(ASTReferenceType.class);
|
||||
if (parent != null) {
|
||||
retVal.add(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateProperIv(Object data, ASTVariableInitializer varInit) {
|
||||
// hard coded array
|
||||
ASTArrayInitializer arrayInit = varInit.getFirstDescendantOfType(ASTArrayInitializer.class);
|
||||
if (arrayInit != null) {
|
||||
addViolation(data, varInit);
|
||||
}
|
||||
|
||||
// string literal
|
||||
ASTLiteral literal = varInit.getFirstDescendantOfType(ASTLiteral.class);
|
||||
if (literal != null && literal.isStringLiteral()) {
|
||||
addViolation(data, varInit);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -9,7 +9,5 @@ rulesets.filenames=\
|
||||
category/java/documentation.xml,\
|
||||
category/java/errorprone.xml,\
|
||||
category/java/multithreading.xml,\
|
||||
category/java/performance.xml
|
||||
|
||||
# security doesn't contain any rules yet
|
||||
# category/java/security.xml
|
||||
category/java/performance.xml,\
|
||||
category/java/security.xml
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user