* Примечание: мнения, выраженные в этой записи блога, являются моими и не обязательно отражают точку зрения моего работодателя. *
Теперь, когда уведомление работодателя не соответствует моему способу, я действительно могу начать говорить о том, о чем я хочу поговорить. На работе у нас есть большая инициатива, чтобы поместить CFQUERYPARAM в наши операторы SQL, у которых его нет. Поскольку этот код довольно старый, большая его часть не использует этот удобный тег, так что, как вы можете себе представить, довольно сложно просмотреть более 3000 файлов и проверить, что CFQUERYPARAM помещен везде, где это необходимо.
После первоначальных попыток мой коллега создал серию регулярных выражений, чтобы помочь подтвердить усилия и указать на те области, где мы могли выполнить работу неправильно. И давайте будем честными, глядя на экран в течение 8 часов и ища CFQUERY и переменные в знаках фунта, могут начать довольно быстро размываться. Учитывая эти полезные регулярные выражения, было бы неплохо иметь простой ANT-скрипт, который запрашивал бы каталог или имя файла и запускал эти регулярные выражения для этого ресурса и создавал отчет о любых потенциальных проблемных областях в электронной таблице Excel.
Чтобы продемонстрировать, давайте посмотрим, как выглядит сам скрипт ANT.
<!--?xml version="1.0"?--> <project name="CFQUERYPARAM Tester" default="main" basedir="."> <taskdef name="queryParamChecker" classname="com.apihealthcare.opt.QueryParamChecker"> <target name="main"> <input message="Please provide a directory path or file path to scan:" addproperty="path" defaultvalue="${basedir}"> <queryparamchecker path="${path}" outputfile="C:\\anttasktestresults.xlsx"> </queryparamchecker></target> </taskdef></project>
Как видите, первое, что нужно сделать, это «импортировать» пользовательскую задачу с именем
queryParamChecker . Это пользовательская задача ANT, которую я написал, которая сканирует ресурс на наличие потенциальных проблем на основе набора регулярных выражений. Затем мы используем
задачу ввода , чтобы запросить у пользователя имя ресурса, каталог или путь к файлу, а затем передаем его в задачу проверки параметров запроса.
Так как же написать пользовательскую задачу ANT? Для этого я использовал Groovy, поэтому я начал с нового проекта Groovy в Eclipse. Затем я добавил следующие JAR-файлы в мой classpath:
- подпапке
- Обще-каротаж 1.1.jar
- dom4j-1.6.1.jar
- пои-3,7-beta3-20100924.jar
- пои-OOXML-3,7-beta3-20100924.jar
- пои-OOXML-схемы-3,7-beta3-20100924.jar
- XMLBeans-2.3.0.jar
Эти JAR-файлы дают нам проект Apache POI для создания документов Excel, а также необходимые классы ANT для создания пользовательской задачи ANT. Затем я создал новый пакет в своем недавно созданном проекте и назвал его ** com.apihealthcare.opt **. В этом пакете я создал новый класс Groovy с именем
QueryParamChecker . Первое, что необходимо для создания пользовательской задачи ANT, это импортировать классы Apache ANT, а затем расширить класс Task. Ваш новый класс должен переопределить метод
execute () и предоставить
сеттеры для каждого свойства, которое будет поддерживать ваша новая задача.
package com.apihealthcare.opt import org.apache.tools.ant.* class QueryParamChecker extends Task { private String path private String outputFile @Override public void execute() throws BuildException { } public void setPath(String path) { this.path = path } public void setOutputFile(String outputFile) { this.outputFile = outputFile } }
Это скелет для пользовательской задачи ANT. Но я явно хотел больше, чем скелет. Мне нужно, чтобы проверить ресурсы на наличие ошибок в CFQUERYPARAM. Есть три вида информации, которые я использовал для этого. Первый — это массив объектов регулярных выражений, которые представляют собой то, что мы пытаемся сделать здесь. Следующим является массив расширений файлов, которые мы можем проверить, поэтому он содержит «.cfm» и «.cfc». И, наконец, у меня есть массив регулярных выражений, которые используются для фильтрации любых нежелательных файлов или папок, поскольку в этом приложении есть ряд старых файлов, которые больше не используются. Вот эти массивы.
/* * An array of regular expressions to check files against. * Modify this list to change the rules yo. */ private def checks = [ ~/(?i)in\s*\(\s*<cfqueryparam((?!list).)*>/, //~/(?i)in\s*\(\s*<cfqueryparam((?!cfsqltype).)*>/, ~/(?i)(#\s*cfsqltype=|#\s*maxlength=|#\s*list=|#\s*value=)/, ~/(?i)\sin(\s|\()[^>]*(value="#listqualify|value="#replace|value="#preservesinglequotes)/, ~/(?i)[^<]cfqueryparam/, ~/(?i)<cfqueryparam[^<>]*"\s*\/[^>]/, ~/(?i)<cfqueryparam\s*value=#/, ~/(?i)value="#dateadd/, ~/(?i)<cfqueryparams/, ~/(?i)cfsqltype=""/, ~/(?i)(#"list|#"value|#"cfsqltype|#"maxlength)/, ~/(?i)order\s*by\s*<cfqueryparam/, ~/(?i)(cfqueryparamvalue|cfqueryparamcfsqltype|cfqueryparammaxlength|cfqueryparamlist)/, ~/(?i)<cfqueryparam[^>]*(?=\/\s*"\s*>)/, ~/(?i)charindex\([^\)]*<cfqueryparam/, ~/(?i)[^<!-|<!]--[^>|-].*<cfqueryparam/, ~/(?i)session\.(?!(hasPermission|get|usersession|set).*)/ ] /* * An array of extensions that we care about. Ignore all else. */ private def validExtensions = [ ".cfm", ".cfc" ] /* * File name regex patterns to ignore. */ private def ignores = [ ~/(?i)(.*?)unused_(.*)/ ]
Отсюда я создал две функции. Первый проверит один файл на наличие ошибок в регулярных выражениях, а второй — рекурсивную структуру каталогов. Они оба очень похожи и, вероятно, могли бы быть написаны более многократно, но пока они делают свое дело. По сути, эти функции будут читать текст из файла и сравнивать его с каждым регулярным выражением в массиве ** проверок **. Если есть совпадения, они сохраняются в структуре и помещаются в массив ** badCodeResults **.
После этого вызывается метод ** _ writeOutputFile () **, чтобы взять элементы в ** badCodeResults ** и поместить их в электронную таблицу Excel. Для этого вы сначала создаете объект ** XSSFWorkbook **, ** XSSFCreationHelper ** и ** XSSFSheet **. Лист создается из рабочей книги; по сути, он создает новый лист в книге в Excel. Я зацикливаюсь на ** badCodeResults ** и создаю ячейки для пути к файлу, поврежденного текста, а также начального и конечного местоположений поврежденного текстового местоположения. В довершение я записываю файл на диск.
Ниже приведена задача в полном объеме. Вы также можете
скачать полный исходный код . Удачного кодирования!
package com.apihealthcare.opt import org.apache.tools.ant.* import groovy.io.FileType import org.apache.poi.poifs.filesystem.* import org.apache.poi.xssf.extractor.* import org.apache.poi.xssf.usermodel.* class QueryParamChecker extends Task { private String path private String outputFile /* * An array of regular expressions to check files against. * Modify this list to change the rules yo. */ private def checks = [ ~/(?i)in\s*\(\s*<cfqueryparam((?!list).)*>/, //~/(?i)in\s*\(\s*<cfqueryparam((?!cfsqltype).)*>/, ~/(?i)(#\s*cfsqltype=|#\s*maxlength=|#\s*list=|#\s*value=)/, ~/(?i)\sin(\s|\()[^>]*(value="#listqualify|value="#replace|value="#preservesinglequotes)/, ~/(?i)[^<]cfqueryparam/, ~/(?i)<cfqueryparam[^<>]*"\s*\/[^>]/, ~/(?i)<cfqueryparam\s*value=#/, ~/(?i)value="#dateadd/, ~/(?i)<cfqueryparams/, ~/(?i)cfsqltype=""/, ~/(?i)(#"list|#"value|#"cfsqltype|#"maxlength)/, ~/(?i)order\s*by\s*<cfqueryparam/, ~/(?i)(cfqueryparamvalue|cfqueryparamcfsqltype|cfqueryparammaxlength|cfqueryparamlist)/, ~/(?i)<cfqueryparam[^>]*(?=\/\s*"\s*>)/, ~/(?i)charindex\([^\)]*|-].*<cfqueryparam/, ~/(?i)session\.(?!(hasPermission|get|usersession|set).*)/ ] /* * An array of extensions that we care about. Ignore all else. */ private def validExtensions = [ ".cfm", ".cfc" ] /* * File name regex patterns to ignore. */ private def ignores = [ ~/(?i)(.*?)unused_(.*)/ ] @Override public void execute() throws BuildException { def fileCheck = new File(this.path) def result = [] if (fileCheck.isFile()) { result = _doFile() } else if (fileCheck.isDirectory()) { result = _doDirectory() } else throw new Exception("The path passed in doesn't seem to be a file or a directory!") _writeOutputFile(result) } public void setPath(String path) { this.path = path } public void setOutputFile(String outputFile) { this.outputFile = outputFile } private def _doFile() { def badCodeResults = [] /* * The directory we are searching goes here! */ def f = new File(this.path) assert f.isFile() def filesProcessed = 0 def badFiles = 0 def validFile = false def printed = false /* * Do we care about this particular file? If not set the * validFile flag to false. */ validExtensions.each { if (f.name.endsWith(it)) validFile = true } ignores.each { def ignoreMe = f.name ==~ it if (validFile != false && ignoreMe) validFile = false } /* * Enter here if we care. */ if (validFile) { filesProcessed++ /* * Start looping over each regex we wish to run against this file. */ checks.each { regex -> def matcher = f.text =~ regex def index = 0 /* * Loop over any matches in the file. */ while (matcher.find()) { /* * We have a bad code match! Put it into our results array. */ if (matcher.group(0) != null && matcher.group(0) != "") { if (!printed) { println "File: ${f.name}..." badFiles++ } printed = true badCodeResults << [ filePath: f.getAbsolutePath(), offendingText: matcher.group(0), start: matcher.start(), end: matcher.end() ] } } } } println "Processed ${filesProcessed} file(s)" println "${badFiles} bad file(s) found" badCodeResults } private def _doDirectory() { def badCodeResults = [] /* * The directory we are searching goes here! */ def rootPath = this.path def codeBase = new File(rootPath) assert codeBase.isDirectory() def filesProcessed = 0 def badFiles = 0 /* * Iterate over all files in our source directory. */ codeBase.eachFileRecurse FileType.FILES, { f -> def validFile = false def printed = false /* * Do we care about this particular file? If not set the * validFile flag to false. */ validExtensions.each { if (f.name.endsWith(it)) validFile = true } ignores.each { def ignoreMe = f.name ==~ it if (validFile != false && ignoreMe) validFile = false } /* * Enter here if we care. */ if (validFile) { filesProcessed++ /* * Start looping over each regex we wish to run against this file. */ checks.each { regex -> def matcher = f.text =~ regex def index = 0 /* * Loop over any matches in the file. */ while (matcher.find()) { /* * We have a bad code match! Put it into our results array. */ if (matcher.group(0) != null && matcher.group(0) != "") { if (!printed) { println "File: ${f.name}..." badFiles++ } printed = true badCodeResults << [ filePath: f.getAbsolutePath() - rootPath, offendingText: matcher.group(0), start: matcher.start(), end: matcher.end() ] } } } } println "Processed ${filesProcessed} file(s)" println "${badFiles} bad file(s) found" badCodeResults } private def _writeOutputFile(badCodeResults) { /* * Create a workbook and worksheet. */ XSSFWorkbook wb = new XSSFWorkbook() XSSFCreationHelper helper = wb.getCreationHelper() XSSFSheet sheet = wb.createSheet("Search Results") def rowIndex = 0 /* * Loop over all our bad code results and write them to * rows in the Excel sheet. */ badCodeResults.each { XSSFRow row = sheet.createRow(rowIndex++) row.createCell(0).setCellValue(helper.createRichTextString(it.filePath)) row.createCell(1).setCellValue(helper.createRichTextString(it.offendingText)) row.createCell(2).setCellValue(it.start) row.createCell(3).setCellValue(it.end) } /* * Write out the results file. Note the path. */ FileOutputStream out = new FileOutputStream(this.outputFile) wb.write(out) out.close() println "Output results written to ${this.outputFile}" } }