diff --git a/README.md b/README.md
index b2ff396ea5..4ffb7c745d 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ unnecessary object creation, and so forth. It supports Java, JavaScript, Salesfo
 XML, XSL.
 
 Additionally it includes **CPD**, the copy-paste-detector. CPD finds duplicated code in
-C/C++, C#, Dart, Fortran, Go, Groovy, Java, JavaScript, JSP, Kotlin, Matlab,
+C/C++, C#, Dart, Fortran, Go, Groovy, Java, JavaScript, JSP, Kotlin, Lua, Matlab,
 Objective-C, Perl, PHP, PLSQL, Python, Ruby, Salesforce.com Apex, Scala, Swift and Visualforce.
 
 ## Source and Documentation
diff --git a/docs/pages/pmd/projectdocs/trivia/news.md b/docs/pages/pmd/projectdocs/trivia/news.md
index ec415ca8f4..b9a2112c1b 100644
--- a/docs/pages/pmd/projectdocs/trivia/news.md
+++ b/docs/pages/pmd/projectdocs/trivia/news.md
@@ -45,7 +45,7 @@ author: Tom Copeland <tom@infoether.org>
     PMD/CPD analyses of Azureus and Columba.
 
 *   April 2006 - John Ferguson Smart's article "PMD Squashes Code Bugs" on
-    [DevX](http://www.devx.com/Java/Article/31286) discusses PMD and the Eclipse plugin. Lots of screenshots!
+    [DevX](https://web.archive.org/web/20140214143838/http://www.devx.com/Java/Article/31286) discusses PMD and the Eclipse plugin. Lots of screenshots!
 
 *   November 2005 - Mike Clark's article "Staying Out of Code Debt" on
     [StickyMinds](http://www.stickyminds.com/sitewide.asp?Function=edetail&amp;ObjectType=ART&amp;ObjectId=9860&amp;tth=DYN&amp;tt=siteemail&amp;iDyn=2)
@@ -63,7 +63,7 @@ author: Tom Copeland <tom@infoether.org>
     [Doctor Dobb's Journal](http://www.drdobbs.com/benefits-of-the-build/184415286) mentions PMD as a way
     to automate code reviews
 
-*   February 2005 - [Java Is Well-Suited for Open-Source Projects](http://www.eweek.com/c/a/Application-Development/Java-Is-WellSuited-for-OpenSource-Projects/) -
+*   February 2005 - [Java Is Well-Suited for Open-Source Projects](https://webcache.googleusercontent.com/search?q=cache:aEL-9Ncx2RgJ:https://www.eweek.com/development/java-is-well-suited-for-open-source-projects) -
     Peter Coffee's eWeek article on open source, Java, and PMD
 
 *   January 2005 - [Zap bugs with PMD](http://www.ibm.com/developerworks/java/library/j-pmd/) - Elliotte Rusty
diff --git a/docs/pages/pmd/rules/xml.md b/docs/pages/pmd/rules/xml.md
index 0b10fd891b..fca26968bb 100644
--- a/docs/pages/pmd/rules/xml.md
+++ b/docs/pages/pmd/rules/xml.md
@@ -11,7 +11,7 @@ folder: pmd/rules
 
 {% include callout.html content="Rules to detect constructs that are either broken, extremely confusing or prone to runtime errors." %}
 
-*   [MistypedCDATASection](pmd_rules_xml_errorprone.html#mistypedcdatasection): An XML CDATA section begins with a &lt;!CDATA[ marker, which has only one [, and ends with a ]]&gt; mar...
+*   [MistypedCDATASection](pmd_rules_xml_errorprone.html#mistypedcdatasection): An XML CDATA section begins with a &lt;![CDATA[ marker, which has only one [, and ends with a ]]&gt; ma...
 
 ## Additional rulesets
 
diff --git a/docs/pages/pmd/rules/xml/errorprone.md b/docs/pages/pmd/rules/xml/errorprone.md
index 2ddd87c822..1c612d8467 100644
--- a/docs/pages/pmd/rules/xml/errorprone.md
+++ b/docs/pages/pmd/rules/xml/errorprone.md
@@ -15,7 +15,7 @@ language: XML
 
 **Priority:** Medium (3)
 
-An XML CDATA section begins with a &lt;!CDATA[ marker, which has only one [, and ends with a ]]&gt; marker, which has only two ].
+An XML CDATA section begins with a &lt;![CDATA[ marker, which has only one [, and ends with a ]]&gt; marker, which has two ].
 
 **This rule is defined by the following XPath expression:**
 ``` xpath
@@ -25,7 +25,18 @@ An XML CDATA section begins with a &lt;!CDATA[ marker, which has only one [, and
 **Example(s):**
 
 ``` xml
-An extra [ looks like &lt;!CDATA[[]]&gt;, and an extra ] looks like &lt;!CDATA[]]]&gt;.
+<root>
+    <child>
+        <![CDATA[[ character data ]]> - this cdata section is valid, but it contains an
+        additional square bracket at the beginning.
+        It should probably be just <![CDATA[ character data ]]>.
+    </child>
+    <child>
+        <![CDATA[ character data ]]]> - this cdata section is valid, but it contains an
+        additional square bracket in the end.
+        It should probably be just <![CDATA[ character data ]]>.
+    </child>
+</root>
 ```
 
 **Use this rule by referencing it:**
diff --git a/docs/pages/pmd/userdocs/cpd.md b/docs/pages/pmd/userdocs/cpd.md
index fbc1b267f7..1afc993ae1 100644
--- a/docs/pages/pmd/userdocs/cpd.md
+++ b/docs/pages/pmd/userdocs/cpd.md
@@ -218,6 +218,7 @@ This behavior has been introduced to ease CPD integration into scripts or hooks,
 * Java
 * Jsp
 * Kotlin
+* Lua
 * Matlab
 * Objective-C
 * Perl
@@ -366,7 +367,7 @@ 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**, **C/C++**, **Dart**, **Go**, **Javascript**,
-**Kotlin**, **Matlab**, **Objective-C**, **PL/SQL**, **Python** and **Swift** by including the keywords `CPD-OFF` and `CPD-ON`.
+**Kotlin**, **Lua**, **Matlab**, **Objective-C**, **PL/SQL**, **Python** and **Swift** by including the keywords `CPD-OFF` and `CPD-ON`.
 
 ```java
     public Object someParameterizedFactoryMethod(int x) throws Exception {
diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md
index 43128b8b43..fe3d2aa4c7 100644
--- a/docs/pages/release_notes.md
+++ b/docs/pages/release_notes.md
@@ -14,17 +14,30 @@ This is a {{ site.pmd.release_type }} release.
 
 ### New and noteworthy
 
+#### Lua support
+
+Thanks to the contribution from [Maikel Steneker](https://github.com/maikelsteneker), and built on top of the ongoing efforts to fully support Antlr-based languages,
+PMD now has CPD support for [Lua](https://www.lua.org/).
+
+Being based on a proper Antlr grammar, CPD can:
+*   ignore comments
+*   honor [comment-based suppressions](pmd_userdocs_cpd.html#suppression)
+
 ### Fixed Issues
 
 *   doc
     *   [#1896](https://github.com/pmd/pmd/issues/1896): \[doc] Error in changelog 6.16.0 due to not properly closed rule tag
     *   [#1906](https://github.com/pmd/pmd/issues/1906): \[doc] Broken link for adding own CPD languages
     *   [#1909](https://github.com/pmd/pmd/issues/1909): \[doc] Sample usage example refers to deprecated ruleset "basic.xml" instead of "quickstart.xml"
+*   xml
+    *   [#1666](https://github.com/pmd/pmd/issues/1666): \[xml] wrong cdata rule description and examples
 
 ### API Changes
 
 ### External Contributions
 
+*   [#1869](https://github.com/pmd/pmd/pull/1869): \[xml] fix #1666 wrong cdata rule description and examples - [Artem](https://github.com/KroArtem)
+*   [#1892](https://github.com/pmd/pmd/pull/1892): \[lua] \[cpd] Added CPD support for Lua - [Maikel Steneker](https://github.com/maikelsteneker)
 *   [#1908](https://github.com/pmd/pmd/pull/1908): \[doc] Update ruleset filename from deprecated basic.xml to quickstart.xml - [crunsk](https://github.com/crunsk)
 
 {% endtocmaker %}
diff --git a/pmd-dist/pom.xml b/pmd-dist/pom.xml
index 1540caebfc..5538c8e402 100644
--- a/pmd-dist/pom.xml
+++ b/pmd-dist/pom.xml
@@ -127,6 +127,11 @@
             <artifactId>pmd-groovy</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>net.sourceforge.pmd</groupId>
+            <artifactId>pmd-lua</artifactId>
+            <version>${project.version}</version>
+        </dependency>
         <dependency>
             <groupId>net.sourceforge.pmd</groupId>
             <artifactId>pmd-java</artifactId>
diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java
index 5da0b5f5a1..8caab02442 100644
--- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java
+++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java
@@ -112,7 +112,7 @@ public class BinaryDistributionIT {
 
         result = CpdExecutor.runCpd(tempDir, "-h");
 
-        result.assertExecutionResult(1, "Supported languages: [apex, cpp, cs, dart, ecmascript, fortran, go, groovy, java, jsp, kotlin, matlab, objectivec, perl, php, plsql, python, ruby, scala, swift, vf]");
+        result.assertExecutionResult(1, "Supported languages: [apex, cpp, cs, dart, ecmascript, fortran, go, groovy, java, jsp, kotlin, lua, matlab, objectivec, perl, php, plsql, python, ruby, scala, swift, vf]");
 
         result = CpdExecutor.runCpd(tempDir, "--minimum-tokens", "10", "--format", "text", "--files", srcDir);
         result.assertExecutionResult(4, "Found a 10 line (55 tokens) duplication in the following files:");
diff --git a/pmd-lua/pom.xml b/pmd-lua/pom.xml
new file mode 100644
index 0000000000..a67b0556e5
--- /dev/null
+++ b/pmd-lua/pom.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>pmd-lua</artifactId>
+    <name>PMD Lua</name>
+
+    <parent>
+        <groupId>net.sourceforge.pmd</groupId>
+        <artifactId>pmd</artifactId>
+        <version>6.17.0-SNAPSHOT</version>
+    </parent>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.antlr</groupId>
+                <artifactId>antlr4-maven-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <artifactId>maven-resources-plugin</artifactId>
+                <configuration>
+                    <useDefaultDelimiters>false</useDefaultDelimiters>
+                    <delimiters>
+                        <delimiter>${*}</delimiter>
+                    </delimiters>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.antlr</groupId>
+            <artifactId>antlr4-runtime</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>net.sourceforge.pmd</groupId>
+            <artifactId>pmd-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.sourceforge.pmd</groupId>
+            <artifactId>pmd-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/pmd-lua/src/main/antlr4/net/sourceforge/pmd/lang/lua/antlr4/Lua.g4 b/pmd-lua/src/main/antlr4/net/sourceforge/pmd/lang/lua/antlr4/Lua.g4
new file mode 100644
index 0000000000..497f11060e
--- /dev/null
+++ b/pmd-lua/src/main/antlr4/net/sourceforge/pmd/lang/lua/antlr4/Lua.g4
@@ -0,0 +1,335 @@
+/*
+BSD License
+
+Copyright (c) 2013, Kazunori Sakamoto
+Copyright (c) 2016, Alexander Alexeev
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the NAME of Rainer Schuster nor the NAMEs of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+This grammar file derived from:
+
+    Lua 5.3 Reference Manual
+    http://www.lua.org/manual/5.3/manual.html
+
+    Lua 5.2 Reference Manual
+    http://www.lua.org/manual/5.2/manual.html
+
+    Lua 5.1 grammar written by Nicolai Mainiero
+    http://www.antlr3.org/grammar/1178608849736/Lua.g
+
+Tested by Kazunori Sakamoto with Test suite for Lua 5.2 (http://www.lua.org/tests/5.2/)
+
+Tested by Alexander Alexeev with Test suite for Lua 5.3 http://www.lua.org/tests/lua-5.3.2-tests.tar.gz
+*/
+
+grammar Lua;
+
+chunk
+    : block EOF
+    ;
+
+block
+    : stat* retstat?
+    ;
+
+stat
+    : ';'
+    | varlist '=' explist
+    | functioncall
+    | label
+    | 'break'
+    | 'goto' NAME
+    | 'do' block 'end'
+    | 'while' exp 'do' block 'end'
+    | 'repeat' block 'until' exp
+    | 'if' exp 'then' block ('elseif' exp 'then' block)* ('else' block)? 'end'
+    | 'for' NAME '=' exp ',' exp (',' exp)? 'do' block 'end'
+    | 'for' namelist 'in' explist 'do' block 'end'
+    | 'function' funcname funcbody
+    | 'local' 'function' NAME funcbody
+    | 'local' namelist ('=' explist)?
+    ;
+
+retstat
+    : 'return' explist? ';'?
+    ;
+
+label
+    : '::' NAME '::'
+    ;
+
+funcname
+    : NAME ('.' NAME)* (':' NAME)?
+    ;
+
+varlist
+    : var (',' var)*
+    ;
+
+namelist
+    : NAME (',' NAME)*
+    ;
+
+explist
+    : exp (',' exp)*
+    ;
+
+exp
+    : 'nil' | 'false' | 'true'
+    | number
+    | string
+    | '...'
+    | functiondef
+    | prefixexp
+    | tableconstructor
+    | <assoc=right> exp operatorPower exp
+    | operatorUnary exp
+    | exp operatorMulDivMod exp
+    | exp operatorAddSub exp
+    | <assoc=right> exp operatorStrcat exp
+    | exp operatorComparison exp
+    | exp operatorAnd exp
+    | exp operatorOr exp
+    | exp operatorBitwise exp
+    ;
+
+prefixexp
+    : varOrExp nameAndArgs*
+    ;
+
+functioncall
+    : varOrExp nameAndArgs+
+    ;
+
+varOrExp
+    : var | '(' exp ')'
+    ;
+
+var
+    : (NAME | '(' exp ')' varSuffix) varSuffix*
+    ;
+
+varSuffix
+    : nameAndArgs* ('[' exp ']' | '.' NAME)
+    ;
+
+nameAndArgs
+    : (':' NAME)? args
+    ;
+
+/*
+var
+    : NAME | prefixexp '[' exp ']' | prefixexp '.' NAME
+    ;
+
+prefixexp
+    : var | functioncall | '(' exp ')'
+    ;
+
+functioncall
+    : prefixexp args | prefixexp ':' NAME args
+    ;
+*/
+
+args
+    : '(' explist? ')' | tableconstructor | string
+    ;
+
+functiondef
+    : 'function' funcbody
+    ;
+
+funcbody
+    : '(' parlist? ')' block 'end'
+    ;
+
+parlist
+    : namelist (',' '...')? | '...'
+    ;
+
+tableconstructor
+    : '{' fieldlist? '}'
+    ;
+
+fieldlist
+    : field (fieldsep field)* fieldsep?
+    ;
+
+field
+    : '[' exp ']' '=' exp | NAME '=' exp | exp
+    ;
+
+fieldsep
+    : ',' | ';'
+    ;
+
+operatorOr
+	: 'or';
+
+operatorAnd
+	: 'and';
+
+operatorComparison
+	: '<' | '>' | '<=' | '>=' | '~=' | '==';
+
+operatorStrcat
+	: '..';
+
+operatorAddSub
+	: '+' | '-';
+
+operatorMulDivMod
+	: '*' | '/' | '%' | '//';
+
+operatorBitwise
+	: '&' | '|' | '~' | '<<' | '>>';
+
+operatorUnary
+    : 'not' | '#' | '-' | '~';
+
+operatorPower
+    : '^';
+
+number
+    : INT | HEX | FLOAT | HEX_FLOAT
+    ;
+
+string
+    : NORMALSTRING | CHARSTRING | LONGSTRING
+    ;
+
+// LEXER
+
+NAME
+    : [a-zA-Z_][a-zA-Z_0-9]*
+    ;
+
+NORMALSTRING
+    : '"' ( EscapeSequence | ~('\\'|'"') )* '"'
+    ;
+
+CHARSTRING
+    : '\'' ( EscapeSequence | ~('\''|'\\') )* '\''
+    ;
+
+LONGSTRING
+    : '[' NESTED_STR ']'
+    ;
+
+fragment
+NESTED_STR
+    : '=' NESTED_STR '='
+    | '[' .*? ']'
+    ;
+
+INT
+    : Digit+
+    ;
+
+HEX
+    : '0' [xX] HexDigit+
+    ;
+
+FLOAT
+    : Digit+ '.' Digit* ExponentPart?
+    | '.' Digit+ ExponentPart?
+    | Digit+ ExponentPart
+    ;
+
+HEX_FLOAT
+    : '0' [xX] HexDigit+ '.' HexDigit* HexExponentPart?
+    | '0' [xX] '.' HexDigit+ HexExponentPart?
+    | '0' [xX] HexDigit+ HexExponentPart
+    ;
+
+fragment
+ExponentPart
+    : [eE] [+-]? Digit+
+    ;
+
+fragment
+HexExponentPart
+    : [pP] [+-]? Digit+
+    ;
+
+fragment
+EscapeSequence
+    : '\\' [abfnrtvz"'\\]
+    | '\\' '\r'? '\n'
+    | DecimalEscape
+    | HexEscape
+    | UtfEscape
+    ;
+
+fragment
+DecimalEscape
+    : '\\' Digit
+    | '\\' Digit Digit
+    | '\\' [0-2] Digit Digit
+    ;
+
+fragment
+HexEscape
+    : '\\' 'x' HexDigit HexDigit
+    ;
+
+fragment
+UtfEscape
+    : '\\' 'u{' HexDigit+ '}'
+    ;
+
+fragment
+Digit
+    : [0-9]
+    ;
+
+fragment
+HexDigit
+    : [0-9a-fA-F]
+    ;
+
+COMMENT
+    : '--[' NESTED_STR ']' -> channel(HIDDEN)
+    ;
+
+LINE_COMMENT
+    : '--'
+    (                                               // --
+    | '[' '='*                                      // --[==
+    | '[' '='* ~('='|'['|'\r'|'\n') ~('\r'|'\n')*   // --[==AA
+    | ~('['|'\r'|'\n') ~('\r'|'\n')*                // --AAA
+    ) ('\r\n'|'\r'|'\n'|EOF)
+    -> channel(HIDDEN)
+    ;
+
+WS
+    : [ \t\u000C\r\n]+ -> skip
+    ;
+
+SHEBANG
+    : '#' '!' ~('\n'|'\r')* -> channel(HIDDEN)
+    ;
diff --git a/pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaLanguage.java b/pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaLanguage.java
new file mode 100644
index 0000000000..e2a87ec878
--- /dev/null
+++ b/pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaLanguage.java
@@ -0,0 +1,18 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.cpd;
+
+/**
+ * Language implementation for Lua
+ */
+public class LuaLanguage extends AbstractLanguage {
+
+    /**
+     * Creates a new Lua Language instance.
+     */
+    public LuaLanguage() {
+        super("Lua", "lua", new LuaTokenizer(), ".lua");
+    }
+}
diff --git a/pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaTokenizer.java b/pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaTokenizer.java
new file mode 100644
index 0000000000..23c292dbe7
--- /dev/null
+++ b/pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaTokenizer.java
@@ -0,0 +1,28 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.cpd;
+
+import org.antlr.v4.runtime.CharStream;
+
+import net.sourceforge.pmd.cpd.token.AntlrTokenFilter;
+import net.sourceforge.pmd.lang.antlr.AntlrTokenManager;
+import net.sourceforge.pmd.lang.lua.antlr4.LuaLexer;
+
+/**
+ * The Lua Tokenizer
+ */
+public class LuaTokenizer extends AntlrTokenizer {
+
+    @Override
+    protected AntlrTokenManager getLexerForSource(SourceCode sourceCode) {
+        CharStream charStream = AntlrTokenizer.getCharStreamFromSourceCode(sourceCode);
+        return new AntlrTokenManager(new LuaLexer(charStream), sourceCode.getFileName());
+    }
+
+    @Override
+    protected AntlrTokenFilter getTokenFilter(final AntlrTokenManager tokenManager) {
+        return new AntlrTokenFilter(tokenManager);
+    }
+}
diff --git a/pmd-lua/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-lua/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language
new file mode 100644
index 0000000000..ff792867ee
--- /dev/null
+++ b/pmd-lua/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language
@@ -0,0 +1 @@
+net.sourceforge.pmd.cpd.LuaLanguage
diff --git a/pmd-lua/src/test/java/net/sourceforge/pmd/cpd/LuaTokenizerTest.java b/pmd-lua/src/test/java/net/sourceforge/pmd/cpd/LuaTokenizerTest.java
new file mode 100644
index 0000000000..5328e71823
--- /dev/null
+++ b/pmd-lua/src/test/java/net/sourceforge/pmd/cpd/LuaTokenizerTest.java
@@ -0,0 +1,56 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.cpd;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import net.sourceforge.pmd.testframework.AbstractTokenizerTest;
+
+@RunWith(Parameterized.class)
+public class LuaTokenizerTest extends AbstractTokenizerTest {
+
+    private final String filename;
+    private final int nExpectedTokens;
+
+    public LuaTokenizerTest(String filename, int nExpectedTokens) {
+        this.filename = filename;
+        this.nExpectedTokens = nExpectedTokens;
+    }
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> data() {
+        return Arrays.asList(
+                new Object[] { "factorial.lua", 44 },
+                new Object[] { "helloworld.lua", 5 }
+        );
+    }
+
+    @Before
+    @Override
+    public void buildTokenizer() throws IOException {
+        this.tokenizer = new LuaTokenizer();
+        this.sourceCode = new SourceCode(new SourceCode.StringCodeLoader(this.getSampleCode(), this.filename));
+    }
+
+    @Override
+    public String getSampleCode() throws IOException {
+        return IOUtils.toString(LuaTokenizer.class.getResourceAsStream(this.filename), StandardCharsets.UTF_8);
+    }
+
+    @Test
+    public void tokenizeTest() throws IOException {
+        this.expectedTokenCount = nExpectedTokens;
+        super.tokenizeTest();
+    }
+}
diff --git a/pmd-lua/src/test/resources/net/sourceforge/pmd/cpd/factorial.lua b/pmd-lua/src/test/resources/net/sourceforge/pmd/cpd/factorial.lua
new file mode 100644
index 0000000000..f2f2d82661
--- /dev/null
+++ b/pmd-lua/src/test/resources/net/sourceforge/pmd/cpd/factorial.lua
@@ -0,0 +1,13 @@
+-- defines a factorial function
+    function fact (n)
+      if n == 0 then
+        return 1
+      else
+        return n * fact(n-1)
+      end
+    end
+    
+    print("enter a number:")
+    a = io.read("*number")        -- read a number
+    print(fact(a))
+
diff --git a/pmd-lua/src/test/resources/net/sourceforge/pmd/cpd/helloworld.lua b/pmd-lua/src/test/resources/net/sourceforge/pmd/cpd/helloworld.lua
new file mode 100644
index 0000000000..45d4840a9f
--- /dev/null
+++ b/pmd-lua/src/test/resources/net/sourceforge/pmd/cpd/helloworld.lua
@@ -0,0 +1,2 @@
+ print("Hello World")
+
diff --git a/pmd-xml/src/main/resources/category/xml/errorprone.xml b/pmd-xml/src/main/resources/category/xml/errorprone.xml
index 18c01924d6..46f598d8e4 100644
--- a/pmd-xml/src/main/resources/category/xml/errorprone.xml
+++ b/pmd-xml/src/main/resources/category/xml/errorprone.xml
@@ -12,11 +12,11 @@ Rules to detect constructs that are either broken, extremely confusing or prone
     <rule name="MistypedCDATASection"
           language="xml"
           since="5.0"
-          message="Potentialy mistyped CDATA section with extra [ at beginning or ] at the end."
+          message="Potentially mistyped CDATA section with extra [ at beginning or ] at the end."
           class="net.sourceforge.pmd.lang.rule.XPathRule"
           externalInfoUrl="${pmd.website.baseurl}/pmd_rules_xml_errorprone.html#mistypedcdatasection">
         <description>
-An XML CDATA section begins with a &lt;!CDATA[ marker, which has only one [, and ends with a ]]&gt; marker, which has only two ]. 
+An XML CDATA section begins with a &lt;![CDATA[ marker, which has only one [, and ends with a ]]&gt; marker, which has two ].
         </description>
         <priority>3</priority>
         <properties>
@@ -30,7 +30,18 @@ An XML CDATA section begins with a &lt;!CDATA[ marker, which has only one [, and
         </properties>
         <example>
 <![CDATA[
-An extra [ looks like &lt;!CDATA[[]]&gt;, and an extra ] looks like &lt;!CDATA[]]]&gt;.
+<root>
+    <child>
+        <![CDATA[[ character data ]]><![CDATA[]]]]><![CDATA[> - this cdata section is valid, but it contains an
+        additional square bracket at the beginning.
+        It should probably be just <![CDATA[ character data ]]><![CDATA[]]]]><![CDATA[>.
+    </child>
+    <child>
+        <![CDATA[ character data ]]]><![CDATA[]]]]><![CDATA[> - this cdata section is valid, but it contains an
+        additional square bracket in the end.
+        It should probably be just <![CDATA[ character data ]]><![CDATA[]]]]><![CDATA[>.
+    </child>
+</root>
 ]]>
         </example>
     </rule>
diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java
index 6d560cc89c..15955b0565 100644
--- a/pmd-xml/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java
+++ b/pmd-xml/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java
@@ -15,6 +15,6 @@ public class PMDTaskTest extends AbstractAntTestHelper {
     @Test
     public void testXML() {
         executeTarget("testXML");
-        assertOutputContaining("Potentialy mistyped CDATA section with extra [ at beginning or ] at the end.");
+        assertOutputContaining("Potentially mistyped CDATA section with extra [ at beginning or ] at the end.");
     }
 }
diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/rule/errorprone/xml/MistypedCDATASection.xml b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/rule/errorprone/xml/MistypedCDATASection.xml
index 45388522ab..c748e2e5e4 100644
--- a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/rule/errorprone/xml/MistypedCDATASection.xml
+++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/rule/errorprone/xml/MistypedCDATASection.xml
@@ -70,4 +70,34 @@
      ]]></code>
 		<source-type>xml</source-type>
 	</test-code>
+    <test-code>
+        <description>Example code</description>
+        <expected-problems>2</expected-problems>
+        <expected-linenumbers>3,8</expected-linenumbers>
+        <code><![CDATA[
+<root>
+    <child>
+        <![CDATA[ character data ]]]><![CDATA[]]]]><![CDATA[> - this cdata section is valid, but it contains an
+        additional square bracket in the end.
+        It should probably be just <![CDATA[ character data ]]><![CDATA[]]]]><![CDATA[>.
+    </child>
+    <child>
+        <![CDATA[[ character data ]]><![CDATA[]]]]><![CDATA[> - this cdata section is valid, but it contains an
+        additional square bracket at the beginning.
+        It should probably be just <![CDATA[ character data ]]><![CDATA[]]]]><![CDATA[>.
+    </child>
+</root>
+        ]]></code>
+    </test-code>
+    <test-code>
+        <description>Square bracket at end separate by space is ok</description>
+        <expected-problems>0</expected-problems>
+        <code><![CDATA[
+<root>
+    <child>
+        <![CDATA[ character data[] ]]><![CDATA[]]]]><![CDATA[>
+    </child>
+</root>
+        ]]></code>
+    </test-code>
 </test-data>
diff --git a/pom.xml b/pom.xml
index 098b6abf37..4ffaa8910d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1138,6 +1138,7 @@ Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code
         <module>pmd-fortran</module>
         <module>pmd-go</module>
         <module>pmd-groovy</module>
+        <module>pmd-lua</module>
         <module>pmd-java</module>
         <module>pmd-javascript</module>
         <module>pmd-jsp</module>