| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100 |
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/main/java/com/itextpdf/rups/model/XfaFile.java\n### 代码内容:\npackage com.itextpdf.rups.model;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\n\nimport org.dom4j.Document;\nimport org.dom4j.DocumentException;\nimport org.dom4j.io.OutputFormat;\nimport org.dom4j.io.SAXReader;\nimport org.dom4j.io.XMLWriter;\n\nimport com.itextpdf.rups.io.OutputStreamResource;\n\n/** Class that deals with the XFA file that can be inside a PDF file. */\npublic class XfaFile implements OutputStreamResource {\n\n\t/** The X4J Document object (XML). */\n\tprotected Document xfaDocument;\n\t\n\t/**\n\t * Constructs an XFA file from an OutputStreamResource.\n\t * This resource can be an XML file or a node in a RUPS application.\n\t * @param\tresource\tthe XFA resource\n\t * @throws IOException \n\t * @throws DocumentException \n\t */\n\tpublic XfaFile(OutputStreamResource resource) throws IOException, DocumentException {\n\t\t// Is there a way to avoid loading everything in memory?\n\t\t// Can we somehow get the XML from the PDF as an InputSource, Reader or InputStream?\n\t\tByteArrayOutputStream baos = new ByteArrayOutputStream();\n\t\tresource.writeTo(baos);\n\t\tByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());\n\t\tSAXReader reader = new SAXReader();\n\t\txfaDocument = reader.read(bais);\n\t}\n\n\t/**\n\t * Getter for the XFA Document object.\n\t * @return\ta Document object (X4J)\n\t */\n\tpublic Document getXfaDocument() {\n\t\treturn xfaDocument;\n\t}\n\n\t/**\n\t * Writes a formatted XML file to the OutputStream.\n\t * @see com.itextpdf.rups.io.OutputStreamResource#writeTo(java.io.OutputStream)\n\t */\n\tpublic void writeTo(OutputStream os) throws IOException {\n\t\tif (xfaDocument == null)\n\t\t\treturn;\n\t\tOutputFormat format = new OutputFormat(\" \", true);\n XMLWriter writer = new XMLWriter(os, format);\n writer.write(xfaDocument);\n\t}\n}\n\n", "input": "", "output": "【漏洞类型】XML外部实体(XXE)注入漏洞\n【严重性】High\n【漏洞摘要】XXE是一种安全漏洞,允许攻击者通过在XML文档中包含恶意实体来攻击XML解析器。在原始代码中,`SAXReader`在解析XML时未禁用外部实体加载,这可能导致攻击者利用外部实体注入恶意XML数据,从而泄露服务器上的敏感信息或执行拒绝服务攻击。\n【关键代码模式】`SAXReader reader = new SAXReader();` 和 `reader.read(bais);`\n【修复方法说明】通过设置一个空的`EntityResolver`来禁用外部实体加载,从而防止XXE攻击。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nrequest/util.js\n### 代码内容:\ndefine([\n\t'exports',\n\t'../errors/RequestError',\n\t'../errors/CancelError',\n\t'../Deferred',\n\t'../io-query',\n\t'../_base/array',\n\t'../_base/lang',\n\t'../promise/Promise',\n\t'../has'\n], function(exports, RequestError, CancelError, Deferred, ioQuery, array, lang, Promise, has){\n\n\tfunction isArrayBuffer(value) {\n\t\treturn has('native-arraybuffer') && value instanceof ArrayBuffer\n\t}\n\n\tfunction isBlob(value) {\n\t\treturn has('native-blob') && value instanceof Blob\n\t}\n\t\n\tfunction isElement(value) {\n\t\tif(typeof Element !== 'undefined') { //all other\n\t\t\treturn value instanceof Element;\n\t\t}\n\n\t\t//IE<=7\n\t\treturn value.nodeType === 1;\n\t}\n\n\tfunction isFormData(value) {\n\t\treturn has('native-formdata') && value instanceof FormData;\n\t}\n\n\tfunction shouldDeepCopy(value) {\n\t\treturn value &&\n\t\t\ttypeof value === 'object' &&\n\t\t\t!isFormData(value) &&\n\t\t\t!isElement(value) &&\n\t\t\t!isBlob(value) &&\n\t\t\t!isArrayBuffer(value)\n\t}\n\n\texports.deepCopy = function(target, source) {\n\t\tfor (var name in source) {\n\t\t\tvar tval = target[name],\n \t\t\t sval = source[name];\n\t\t\tif (tval !== sval) {\n\t\t\t\tif (shouldDeepCopy(sval)) {\n\t\t\t\t\tif (Object.prototype.toString.call(sval) === '[object Date]') { // use this date test to handle crossing frame boundaries\n\t\t\t\t\t\ttarget[name] = new Date(sval);\n\t\t\t\t\t} else if (lang.isArray(sval)) {\n \t\t\t\t\t\t target[name] = exports.deepCopyArray(sval);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (tval && typeof tval === 'object') {\n\t\t\t\t\t\t\texports.deepCopy(tval, sval);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttarget[name] = exports.deepCopy({}, sval);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\ttarget[name] = sval;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn target;\n\t};\n\n\texports.deepCopyArray = function(source) {\n\t\tvar clonedArray = [];\n\t\tfor (var i = 0, l = source.length; i < l; i++) {\n\t\t\tvar svalItem = source[i];\n\t\t\tif (typeof svalItem === 'object') {\n\t\t\t\tclonedArray.push(exports.deepCopy({}, svalItem));\n\t\t\t} else {\n\t\t\t\tclonedArray.push(svalItem);\n\t\t\t}\n\t\t}\n\n\t\treturn clonedArray;\n\t};\n\n\texports.deepCreate = function deepCreate(source, properties){\n\t\tproperties = properties || {};\n\t\tvar target = lang.delegate(source),\n\t\t\tname, value;\n\n\t\tfor(name in source){\n\t\t\tvalue = source[name];\n\n\t\t\tif(value && typeof value === 'object'){\n\t\t\t\ttarget[name] = exports.deepCreate(value, properties[name]);\n\t\t\t}\n\t\t}\n\t\treturn exports.deepCopy(target, properties);\n\t};\n\n\tvar freeze = Object.freeze || function(obj){ return obj; };\n\tfunction okHandler(response){\n\t\treturn freeze(response);\n\t}\n\tfunction dataHandler (response) {\n\t\treturn response.data !== undefined ? response.data : response.text;\n\t}\n\n\texports.deferred = function deferred(response, cancel, isValid, isReady, handleResponse, last){\n\t\tvar def = new Deferred(function(reason){\n\t\t\tcancel && cancel(def, response);\n\n\t\t\tif(!reason || !(reason instanceof RequestError) && !(reason instanceof CancelError)){\n\t\t\t\treturn new CancelError('Request canceled', response);\n\t\t\t}\n\t\t\treturn reason;\n\t\t});\n\n\t\tdef.response = response;\n\t\tdef.isValid = isValid;\n\t\tdef.isReady = isReady;\n\t\tdef.handleResponse = handleResponse;\n\n\t\tfunction errHandler(error){\n\t\t\terror.response = response;\n\t\t\tthrow error;\n\t\t}\n\t\tvar responsePromise = def.then(okHandler).otherwise(errHandler);\n\n\t\tif(exports.notify){\n\t\t\tresponsePromise.then(\n\t\t\t\tlang.hitch(exports.notify, 'emit', 'load'),\n\t\t\t\tlang.hitch(exports.notify, 'emit', 'error')\n\t\t\t);\n\t\t}\n\n\t\tvar dataPromise = responsePromise.then(dataHandler);\n\n\t\t// http://bugs.dojotoolkit.org/ticket/16794\n\t\t// The following works around a leak in IE9 through the\n\t\t// prototype using lang.delegate on dataPromise and\n\t\t// assigning the result a property with a reference to\n\t\t// responsePromise.\n\t\tvar promise = new Promise();\n\t\tfor (var prop in dataPromise) {\n\t\t\tif (dataPromise.hasOwnProperty(prop)) {\n\t\t\t\tpromise[prop] = dataPromise[prop];\n\t\t\t}\n\t\t}\n\t\tpromise.response = responsePromise;\n\t\tfreeze(promise);\n\t\t// End leak fix\n\n\t\tif(last){\n\t\t\tdef.then(function(response){\n\t\t\t\tlast.call(def, response);\n\t\t\t}, function(error){\n\t\t\t\tlast.call(def, response, error);\n\t\t\t});\n\t\t}\n\n\t\tdef.promise = promise;\n\t\tdef.then = promise.then;\n\n\t\treturn def;\n\t};\n\n\texports.addCommonMethods = function addCommonMethods(provider, methods){\n\t\tarray.forEach(methods||['GET', 'POST', 'PUT', 'DELETE'], function(method){\n\t\t\tprovider[(method === 'DELETE' ? 'DEL' : method).toLowerCase()] = function(url, options){\n\t\t\t\toptions = lang.delegate(options||{});\n\t\t\t\toptions.method = method;\n\t\t\t\treturn provider(url, options);\n\t\t\t};\n\t\t});\n\t};\n\n\texports.parseArgs = function parseArgs(url, options, skipData){\n\t\tvar data = options.data,\n\t\t\tquery = options.query;\n\n\t\tif(data && !skipData){\n\t\t\tif(typeof data === 'object' && (!(has('native-xhr2')) || !(isArrayBuffer(data) || isBlob(data) ))){\n\t\t\t\toptions.data = ioQuery.objectToQuery(data);\n\t\t\t}\n\t\t}\n\n\t\tif(query){\n\t\t\tif(typeof query === 'object'){\n\t\t\t\tquery = ioQuery.objectToQuery(query);\n\t\t\t}\n\t\t\tif(options.preventCache){\n\t\t\t\tquery += (query ? '&' : '') + 'request.preventCache=' + (+(new Date));\n\t\t\t}\n\t\t}else if(options.preventCache){\n\t\t\tquery = 'request.preventCache=' + (+(new Date));\n\t\t}\n\n\t\tif(url && query){\n\t\t\turl += (~url.indexOf('?') ? '&' : '?') + query;\n\t\t}\n\n\t\treturn {\n\t\t\turl: url,\n\t\t\toptions: options,\n\t\t\tgetHeader: function(headerName){ return null; }\n\t\t};\n\t};\n\n\texports.checkStatus = function(stat){\n\t\tstat = stat || 0;\n\t\treturn (stat >= 200 && stat < 300) || // allow any 2XX response code\n\t\t\tstat === 304 || // or, get it out of the cache\n\t\t\tstat === 1223 || // or, Internet Explorer mangled the status code\n\t\t\t!stat; // or, we're Titanium/browser chrome/chrome extension requesting a local file\n\t};\n});\n\n", "input": "", "output": "【漏洞类型】原型污染漏洞\n【严重性】High\n【漏洞摘要】这是一个原型污染漏洞。在原始代码的第47行,`exports.deepCopy`函数中,循环遍历`source`对象的所有属性时,没有检查属性名是否为`__proto__`。这可能导致攻击者通过构造恶意输入来修改目标对象的原型,从而影响所有继承自该原型的对象。\n【关键代码模式】与原型污染漏洞相关的关键代码模式包括:\n- 使用`for...in`循环遍历对象属性。\n- 没有对属性名进行安全检查,特别是`__proto__`。\n- 递归地复制对象属性,可能导致深层原型污染。\n【修复方法说明】修复方法通过在第47行添加`if (name !== '__proto__')`的检查,确保不会将`__proto__`属性复制到目标对象中,从而防止原型污染。这遵循了最佳实践,即在处理用户输入或不受信任的对象时,应该对属性名进行验证。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nbbb-common-web/build.sbt\n### 代码内容:\nimport org.bigbluebutton.build._\n\nversion := \"0.0.3-SNAPSHOT\"\n\nval compileSettings = Seq(\n organization := \"org.bigbluebutton\",\n\n scalacOptions ++= List(\n \"-unchecked\",\n \"-deprecation\",\n \"-Xlint\",\n \"-Ywarn-dead-code\",\n \"-language:_\",\n \"-target:11\",\n \"-encoding\", \"UTF-8\"\n ),\n javacOptions ++= List(\n \"-Xlint:unchecked\",\n \"-Xlint:deprecation\"\n )\n)\n\n// We want to have our jar files in lib_managed dir.\n// This way we'll have the right path when we import\n// into eclipse.\nretrieveManaged := true\n\ntestOptions in Test += Tests.Argument(TestFrameworks.Specs2, \"html\", \"console\", \"junitxml\")\n\ntestOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, \"-h\", \"target/scalatest-reports\")\n\nlibraryDependencies += \"org.scalatest\" %% \"scalatest\" % \"3.0.8\" % \"test\"\nlibraryDependencies += \"org.scala-lang.modules\" %% \"scala-xml\" % \"2.0.0\"\n\nSeq(Revolver.settings: _*)\n\nlazy val commonWeb = (project in file(\".\")).settings(name := \"bbb-common-web\", libraryDependencies ++= Dependencies.runtime).settings(compileSettings)\n\n// See https://github.com/scala-ide/scalariform\n// Config file is in ./.scalariform.conf\nscalariformAutoformat := true\n\nscalaVersion := \"2.13.9\"\n//-----------\n// Packaging\n//\n// Reference:\n// http://xerial.org/blog/2014/03/24/sbt/\n// http://www.scala-sbt.org/sbt-pgp/usage.html\n// http://www.scala-sbt.org/0.13/docs/Using-Sonatype.html\n// http://central.sonatype.org/pages/requirements.html\n// http://central.sonatype.org/pages/releasing-the-deployment.html\n//-----------\n\n// Build pure Java lib (i.e. without scala)\n// Do not append Scala versions to the generated artifacts\ncrossPaths := false\n\n// This forbids including Scala related libraries into the dependency\nautoScalaLibrary := false\n\n/** *************************\n * When developing, change the version above to x.x.x-SNAPSHOT then use the file resolver to\n * publish to the local maven repo using \"sbt publish\"\n */\n// Uncomment this to publish to local maven repo while commenting out the nexus repo\npublishTo := Some(Resolver.file(\"file\", new File(Path.userHome.absolutePath + \"/.m2/repository\")))\n\n// Comment this out when publishing to local maven repo using SNAPSHOT version.\n// To push to sonatype \"sbt publishSigned\"\n//publishTo := {\n// val nexus = \"https://oss.sonatype.org/\"\n// if (isSnapshot.value)\n// Some(\"snapshots\" at nexus + \"content/repositories/snapshots\")\n// else\n// Some(\"releases\" at nexus + \"service/local/staging/deploy/maven2\")\n//}\n\n// Enables publishing to maven repo\npublishMavenStyle := true\n\npublishArtifact in Test := false\n\npomIncludeRepository := { _ => false }\n\npomExtra := (\n <scm>\n <url>git@github.com:bigbluebutton/bigbluebutton.git</url>\n <connection>scm:git:git@github.com:bigbluebutton/bigbluebutton.git</connection>\n </scm>\n <developers>\n <developer>\n <id>ritzalam</id>\n <name>Richard Alam</name>\n <url>http://www.bigbluebutton.org</url>\n </developer>\n </developers>)\n\nlicenses := Seq(\"LGPL-3.0\" -> url(\"http://opensource.org/licenses/LGPL-3.0\"))\n\nhomepage := Some(url(\"http://www.bigbluebutton.org\"))\n\nlibraryDependencies ++= Seq(\n \"javax.validation\" % \"validation-api\" % \"2.0.1.Final\",\n \"org.springframework.boot\" % \"spring-boot-starter-validation\" % \"2.7.12\",\n \"org.springframework.data\" % \"spring-data-commons\" % \"2.7.6\",\n \"org.apache.httpcomponents\" % \"httpclient\" % \"4.5.13\",\n \"commons-validator\" % \"commons-validator\" % \"1.7\"\n)\n\n## 内容2\n### 文件路径:\nbbb-common-web/src/main/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java\n### 代码内容:\npackage org.bigbluebutton.presentation;\n\nimport java.io.File;\nimport java.io.FilenameFilter;\nimport java.io.IOException;\nimport java.net.*;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ScheduledFuture;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Stream;\n\nimport org.apache.commons.codec.digest.DigestUtils;\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.io.FilenameUtils;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.HttpStatus;\nimport org.apache.http.client.ClientProtocolException;\nimport org.apache.http.entity.ContentType;\nimport org.apache.http.impl.nio.client.CloseableHttpAsyncClient;\nimport org.apache.http.impl.nio.client.HttpAsyncClients;\nimport org.apache.http.nio.client.methods.HttpAsyncMethods;\nimport org.apache.http.nio.client.methods.ZeroCopyConsumer;\nimport org.apache.commons.validator.routines.InetAddressValidator;\nimport org.bigbluebutton.api.Util;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class PresentationUrlDownloadService {\n private static Logger log = LoggerFactory\n .getLogger(PresentationUrlDownloadService.class);\n\n private static final int MAX_REDIRECTS = 5;\n private PageExtractor pageExtractor;\n private DocumentConversionService documentConversionService;\n private String presentationBaseURL;\n private String presentationDir;\n private String BLANK_PRESENTATION;\n private List<String> insertDocumentSupportedProtocols;\n private List<String> insertDocumentBlockedHosts;\n\n private ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);\n\n public void stop() {\n scheduledThreadPool.shutdownNow();\n }\n\n public void processUploadedPresentation(final UploadedPresentation uploadedPres) {\n /**\n * We delay processing of the presentation to make sure that the meeting has already been created.\n * Otherwise, the meeting won't get the conversion events.\n */\n ScheduledFuture scheduledFuture =\n scheduledThreadPool.schedule(new Runnable() {\n public void run() {\n documentConversionService.processDocument(uploadedPres);\n }\n }, 5, TimeUnit.SECONDS);\n\n }\n\n public void processUploadedFile(String podId, String meetingId, String presId,\n String filename, File presFile, Boolean current, String authzToken,\n Boolean uploadFailed, ArrayList<String> uploadFailReasons) {\n // TODO add podId\n UploadedPresentation uploadedPres = new UploadedPresentation(\n podId,\n meetingId,\n presId,\n filename,\n presentationBaseURL,\n current,\n authzToken,\n uploadFailed,\n uploadFailReasons);\n uploadedPres.setUploadedFile(presFile);\n processUploadedPresentation(uploadedPres);\n }\n\n public void extractPresentationPage(final String sourceMeetingId, final String presentationId,\n final Integer presentationSlide, final String destinationMeetingId) {\n /**\n * We delay processing of the presentation to make sure that the meeting has already been created.\n * Otherwise, the meeting won't get the conversion events.\n */\n ScheduledFuture scheduledFuture =\n scheduledThreadPool.schedule(new Runnable() {\n public void run() {\n extractPage(sourceMeetingId, presentationId, presentationSlide, destinationMeetingId) ;\n }\n }, 5, TimeUnit.SECONDS);\n }\n\n private void extractPage(final String sourceMeetingId, final String presentationId,\n final Integer presentationSlide, final String destinationMeetingId) {\n\n Boolean uploadFailed = false;\n ArrayList<String> uploadFailedReasons = new ArrayList<String>();\n\n // Build the source meeting path\n File sourceMeetingPath = new File(presentationDir + File.separatorChar\n + sourceMeetingId + File.separatorChar + sourceMeetingId\n + File.separatorChar + presentationId);\n\n // Find the source meeting presentation file\n final String presentationFilter = presentationId;\n FilenameFilter pdfFilter = new FilenameFilter() {\n public boolean accept(File dir, String name) {\n return name.startsWith(presentationFilter)\n && name.toLowerCase().endsWith(\"pdf\");\n }\n };\n\n File[] matches = sourceMeetingPath.listFiles(pdfFilter);\n if (matches != null && matches.length != 1) {\n // No PDF presentation was found, we look for an image presentation\n FilenameFilter imgFlter = new FilenameFilter() {\n public boolean accept(File dir, String name) {\n return name.startsWith(presentationFilter);\n }\n };\n\n matches = sourceMeetingPath.listFiles(imgFlter);\n }\n File sourcePresentationFile;\n if (matches == null || matches.length != 1) {\n log.warn(\n \"Not matching PDF file with prefix {} found at {}. Using the default blank PDF\",\n sourceMeetingId, sourceMeetingPath);\n sourcePresentationFile = new File(BLANK_PRESENTATION);\n } else {\n sourcePresentationFile = matches[0];\n }\n\n // Build the target meeting path\n String filenameExt = FilenameUtils.getExtension(sourcePresentationFile.getName());\n String presId = Util.generatePresentationId(presentationId);\n String newFilename = Util.createNewFilename(presId, filenameExt);\n\n File uploadDir = Util.createPresentationDir(destinationMeetingId,\n presentationDir, presId);\n String newFilePath = uploadDir.getAbsolutePath() + File.separatorChar\n + newFilename;\n File newPresentation = new File(newFilePath);\n\n if (sourcePresentationFile.getName().toLowerCase().endsWith(\"pdf\")) {\n pageExtractor.extractPage(sourcePresentationFile, new File(\n newFilePath), presentationSlide);\n } else {\n try {\n FileUtils.copyFile(sourcePresentationFile, newPresentation);\n } catch (IOException e) {\n log.error(\"Could not copy presentation {} to {}\", sourcePresentationFile.getAbsolutePath(),\n newPresentation.getAbsolutePath(), e);\n }\n }\n\n // Hardcode pre-uploaded presentation for breakout room to the default presentation window\n processUploadedFile(\"DEFAULT_PRESENTATION_POD\",\n destinationMeetingId,\n presId,\n \"default-\" + presentationSlide.toString() + \".\" + filenameExt,\n newPresentation,\n true,\n \"breakout-authz-token\",\n uploadFailed,\n uploadFailedReasons);\n }\n\n private String followRedirect(String meetingId, String redirectUrl,\n int redirectCount, String origUrl) {\n\n if (redirectCount > MAX_REDIRECTS) {\n log.error(\"Max redirect reached for meeting=[{}] with url=[{}]\",\n meetingId, origUrl);\n return null;\n }\n\n if(!isValidRedirectUrl(redirectUrl)) return null;\n\n URL presUrl;\n try {\n presUrl = new URL(redirectUrl);\n } catch (MalformedURLException e) {\n log.error(\"Malformed url=[{}] for meeting=[{}]\", redirectUrl, meetingId, e);\n return null;\n }\n\n HttpURLConnection conn;\n try {\n conn = (HttpURLConnection) presUrl.openConnection();\n conn.setReadTimeout(60000);\n conn.addRequestProperty(\"Accept-Language\", \"en-US,en;q=0.8\");\n conn.addRequestProperty(\"User-Agent\", \"Mozilla\");\n\n // normally, 3xx is redirect\n int status = conn.getResponseCode();\n if (status != HttpURLConnection.HTTP_OK) {\n if (status == HttpURLConnection.HTTP_MOVED_TEMP\n || status == HttpURLConnection.HTTP_MOVED_PERM\n || status == HttpURLConnection.HTTP_SEE_OTHER) {\n String newUrl = conn.getHeaderField(\"Location\");\n return followRedirect(meetingId, newUrl, redirectCount + 1,\n origUrl);\n } else {\n log.error(\n \"Invalid HTTP response=[{}] for url=[{}] with meeting[{}]\",\n status, redirectUrl, meetingId);\n return null;\n }\n } else {\n return redirectUrl;\n }\n } catch (IOException e) {\n log.error(\"IOException for url=[{}] with meeting[{}]\", redirectUrl, meetingId, e);\n return null;\n }\n }\n\n private boolean isValidRedirectUrl(String redirectUrl) {\n URL url;\n\n try {\n url = new URL(redirectUrl);\n String protocol = url.getProtocol();\n String host = url.getHost();\n\n if(insertDocumentSupportedProtocols.stream().noneMatch(p -> p.equalsIgnoreCase(protocol))) {\n if(insertDocumentSupportedProtocols.size() == 1 && insertDocumentSupportedProtocols.get(0).equalsIgnoreCase(\"all\")) {\n log.warn(\"Warning: All protocols are supported for presentation download. It is recommended to only allow HTTPS.\");\n } else {\n log.error(\"Invalid protocol [{}]\", protocol);\n return false;\n }\n }\n\n if(insertDocumentBlockedHosts.stream().anyMatch(h -> h.equalsIgnoreCase(host))) {\n log.error(\"Attempted to download from blocked host [{}]\", host);\n return false;\n }\n } catch(MalformedURLException e) {\n log.error(\"Malformed URL [{}]\", redirectUrl);\n return false;\n }\n\n try {\n InetAddress[] addresses = InetAddress.getAllByName(url.getHost());\n InetAddressValidator validator = InetAddressValidator.getInstance();\n\n boolean localhostBlocked = insertDocumentBlockedHosts.stream().anyMatch(h -> h.equalsIgnoreCase(\"localhost\"));\n\n for(InetAddress address: addresses) {\n if(!validator.isValid(address.getHostAddress())) {\n log.error(\"Invalid address [{}]\", address.getHostAddress());\n return false;\n }\n\n if(localhostBlocked) {\n if(address.isAnyLocalAddress()) {\n log.error(\"Address [{}] is a local address\", address.getHostAddress());\n return false;\n }\n\n if(address.isLoopbackAddress()) {\n log.error(\"Address [{}] is a loopback address\", address.getHostAddress());\n return false;\n }\n }\n }\n } catch(UnknownHostException e) {\n log.error(\"Unknown host [{}]\", url.getHost());\n return false;\n }\n\n return true;\n }\n\n public boolean savePresentation(final String meetingId,\n final String filename, final String urlString) {\n\n String finalUrl = followRedirect(meetingId, urlString, 0, urlString);\n\n if (finalUrl == null) return false;\n\n boolean success = false;\n\n CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();\n try {\n httpclient.start();\n File download = new File(filename);\n ZeroCopyConsumer<File> consumer = new ZeroCopyConsumer<File>(download) {\n @Override\n protected File process(\n final HttpResponse response,\n final File file,\n final ContentType contentType) throws Exception {\n if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {\n throw new ClientProtocolException(\"Upload failed: \" + response.getStatusLine());\n }\n return file;\n }\n\n };\n Future<File> future = httpclient.execute(HttpAsyncMethods.createGet(finalUrl), consumer, null);\n File result = future.get();\n success = result.exists();\n } catch (java.lang.InterruptedException ex) {\n log.error(\"InterruptedException while saving presentation\", meetingId, ex);\n } catch (java.util.concurrent.ExecutionException ex) {\n log.error(\"ExecutionException while saving presentation\", meetingId, ex);\n } catch (java.io.FileNotFoundException ex) {\n log.error(\"FileNotFoundException while saving presentation\", meetingId, ex);\n } finally {\n try {\n httpclient.close();\n } catch (java.io.IOException ex) {\n log.error(\"IOException while saving presentation\", meetingId, ex);\n }\n }\n\n return success;\n }\n\n public void setPageExtractor(PageExtractor extractor) {\n this.pageExtractor = extractor;\n }\n\n public void setPresentationDir(String presDir) {\n presentationDir = presDir;\n }\n\n public void setPresentationBaseURL(String presentationBaseUrl) {\n presentationBaseURL = presentationBaseUrl;\n }\n\n public void setDocumentConversionService(\n DocumentConversionService documentConversionService) {\n this.documentConversionService = documentConversionService;\n }\n\n public void setBlankPresentation(String blankPresentation) {\n this.BLANK_PRESENTATION = blankPresentation;\n }\n\n public void setInsertDocumentSupportedProtocols(String insertDocumentSupportedProtocols) {\n this.insertDocumentSupportedProtocols = new ArrayList<>(Arrays.asList(insertDocumentSupportedProtocols.split(\",\")));\n }\n\n public void setInsertDocumentBlockedHosts(String insertDocumentBlockedHosts) {\n this.insertDocumentBlockedHosts = new ArrayList<>(Arrays.asList(insertDocumentBlockedHosts.split(\",\")));\n }\n\n}\n\n## 内容3\n### 文件路径:\nbbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/CallbackUrlService.java\n### 代码内容:\npackage org.bigbluebutton.web.services.callback;\n\nimport java.io.IOException;\nimport java.net.HttpURLConnection;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.*;\n\nimport org.apache.http.HttpResponse;\nimport org.apache.http.client.methods.HttpGet;\nimport org.apache.http.impl.nio.client.CloseableHttpAsyncClient;\nimport org.apache.http.impl.nio.client.HttpAsyncClients;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.Gson;\n\npublic class CallbackUrlService {\n\tprivate static Logger log = LoggerFactory.getLogger(CallbackUrlService.class);\n\n\tprivate BlockingQueue<DelayCallback> receivedMessages = new DelayQueue<DelayCallback>();\n\n\tprivate volatile boolean processMessage = false;\n\tprivate static final int MAX_REDIRECTS = 5;\n\n\tprivate final Executor msgProcessorExec = Executors.newSingleThreadExecutor();\n\tprivate final Executor runExec = Executors.newSingleThreadExecutor();\n\n\tpublic void stop() {\n\t\tlog.info(\"Stopping callback url service.\");\n\t\tprocessMessage = false;\n\t}\n\n\tpublic void start() {\n\t\tlog.info(\"Starting callback url service.\");\n\n\t\ttry {\n\t\t\tprocessMessage = true;\n\n\t\t\tRunnable messageProcessor = new Runnable() {\n\t\t\t\tpublic void run() {\n\t\t\t\t\twhile (processMessage) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tDelayCallback msg = receivedMessages.take();\n\t\t\t\t\t\t\tprocessMessage(msg);\n\t\t\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t\t\tlog.warn(\"Error while taking received message from queue.\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\t\tmsgProcessorExec.execute(messageProcessor);\n\t\t} catch (Exception e) {\n\t\t\tlog.error(\"Error subscribing to channels: {}\", e);\n\t\t}\n\t}\n\n\tprivate void processMessage(final DelayCallback msg) {\n\t\tRunnable task = new Runnable() {\n\t\t\tpublic void run() {\n\t\t\t\tMeetingEndedEvent event = (MeetingEndedEvent) msg.callbackEvent;\n\t\t\t\tif (fetchCallbackUrl(msg.callbackEvent.getCallbackUrl())) {\n\t\t\t\t\tMap<String, Object> logData = new HashMap<>();\n\t\t\t\t\tlogData.put(\"meetingId\", event.meetingid);\n\t\t\t\t\tlogData.put(\"externalMeetingId\", event.extMeetingid);\n\t\t\t\t\tlogData.put(\"name\",event.name);\n\t\t\t\t\tlogData.put(\"callback\", event.getCallbackUrl());\n\t\t\t\t\tlogData.put(\"attempts\", msg.numAttempts);\n\t\t\t\t\tlogData.put(\"logCode\", \"callback_success\");\n\t\t\t\t\tlogData.put(\"description\", \"Callback successful.\");\n\n\t\t\t\t\tGson gson = new Gson();\n\t\t\t\t\tString logStr = gson.toJson(logData);\n\n\t\t\t\t\tlog.info(\" --analytics-- data={}\", logStr);\n\t\t\t\t} else {\n\t\t\t\t\tschedRetryCallback(msg);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\trunExec.execute(task);\n\t}\n\n\tprivate void schedCallback(final DelayCallback msg, long delayInMillis, int numAttempt) {\n\t\tMeetingEndedEvent event = (MeetingEndedEvent) msg.callbackEvent;\n\t\tMap<String, Object> logData = new HashMap<>();\n\t\tlogData.put(\"meetingId\", event.meetingid);\n\t\tlogData.put(\"externalMeetingId\", event.extMeetingid);\n\t\tlogData.put(\"name\",event.name);\n\t\tlogData.put(\"callback\", event.getCallbackUrl());\n\t\tlogData.put(\"attempts\", msg.numAttempts);\n\t\tlogData.put(\"retryInMs\", delayInMillis);\n\t\tlogData.put(\"logCode\", \"callback_failed_retry\");\n\t\tlogData.put(\"description\", \"Callback failed but retrying.\");\n\n\t\tGson gson = new Gson();\n\t\tString logStr = gson.toJson(logData);\n\n\t\tlog.info(\" --analytics-- data={}\", logStr);\n\n\t\tDelayCallback dc = new DelayCallback(event, delayInMillis, numAttempt);\n\t\treceivedMessages.add(dc);\n\t}\n\n\tprivate void giveupCallback(final DelayCallback msg) {\n\t\tMeetingEndedEvent event = (MeetingEndedEvent) msg.callbackEvent;\n\t\tMap<String, Object> logData = new HashMap<>();\n\t\tlogData.put(\"meetingId\", event.meetingid);\n\t\tlogData.put(\"externalMeetingId\", event.extMeetingid);\n\t\tlogData.put(\"name\",event.name);\n\t\tlogData.put(\"callback\", event.getCallbackUrl());\n\t\tlogData.put(\"attempts\", msg.numAttempts);\n\t\tlogData.put(\"logCode\", \"callback_failed_give_up\");\n\t\tlogData.put(\"description\", \"Callback failed and giving up.\");\n\n\t\tGson gson = new Gson();\n\t\tString logStr = gson.toJson(logData);\n\n\t\tlog.info(\" --analytics-- data={}\", logStr);\n\t}\n\tprivate void schedRetryCallback(final DelayCallback msg) {\n\t\tMeetingEndedEvent event = (MeetingEndedEvent) msg.callbackEvent;\n\n\t\tswitch (msg.numAttempts) {\n\t\t\tcase 1:\n\t\t\t\tschedCallback(msg, 30_000 /** 30sec **/, 2);\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tschedCallback(msg, 60_000 /** 1min **/, 3);\n\t\t\t\tbreak;\n\t\t\tcase 3:\n\t\t\t\tschedCallback(msg, 120_000 /** 2min **/, 4);\n\t\t\t\tbreak;\n\t\t\tcase 4:\n\t\t\t\tschedCallback(msg, 300_000 /** 5min **/, 5);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tgiveupCallback(msg);\n\t\t\t}\n\t}\n\n\tpublic void handleMessage(ICallbackEvent message) {\n\t\tlong delayInMillis = -1000 /**Send right away **/;\n\t\tint numAttempt = 1;\n\t\tDelayCallback dc = new DelayCallback(message, delayInMillis, numAttempt);\n\t\treceivedMessages.add(dc);\n\t}\n\n\tprivate boolean fetchCallbackUrl(final String callbackUrl) {\n\n\t\tboolean success = false;\n\n\t\tCloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();\n\t\ttry {\n\t\t\thttpclient.start();\n\n\t\t\tHttpGet request = new HttpGet(callbackUrl);\n\n\t\t\tFuture<HttpResponse> future = httpclient.execute(request, null);\n\t\t\tHttpResponse response = future.get();\n\t\t\t// Consider 2xx response code as success.\n\t\t\tsuccess = (response.getStatusLine().getStatusCode() >= 200 && response.getStatusLine().getStatusCode() < 300);\n\t\t} catch (java.lang.InterruptedException ex) {\n\t\t\tlog.error(\"Interrupted exception while calling url {}\", callbackUrl);\n\t\t} catch (java.util.concurrent.ExecutionException ex) {\n\t\t\tlog.error(\"ExecutionException exception while calling url {}\", callbackUrl);\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\thttpclient.close();\n\t\t\t} catch (java.io.IOException ex) {\n\t\t\t\tlog.error(\"IOException exception while closing http client for url {}\", callbackUrl);\n\t\t\t}\n\t\t}\n\n\t\treturn success;\n\t}\n}\n\n## 内容4\n### 文件路径:\nbigbluebutton-web/grails-app/conf/bigbluebutton.properties\n### 代码内容:\n#\n# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/\n#\n# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).\n#\n# This program is free software; you can redistribute it and/or modify it under the\n# terms of the GNU Lesser General Public License as published by the Free Software\n# Foundation; either version 3.0 of the License, or (at your option) any later\n# version.\n#\n# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY\n# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A\n# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.\n#\n# You should have received a copy of the GNU Lesser General Public License along\n# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.\n#\n\n#\n# These are the default properites for BigBlueButton Web application\n\n# Default loglevel.\nappLogLevel=DEBUG\n\n#----------------------------------------------------\n# Directory where BigBlueButton stores uploaded slides\npresentationDir=/var/bigbluebutton\n\n#----------------------------------------------------\n# Directory where SWFTOOLS (pdf2swf, jpeg2swf, png2swf) are located\nswfToolsDir=/usr/bin\n\n#----------------------------------------------------\n# Directory where ImageMagick's convert executable is located\nimageMagickDir=/usr/bin\n\n#----------------------------------------------------\n# Fonts directory passed into PDF2SWF to support highlighting of texts\n# in the SWF slides.\nfontsDir=/usr/share/fonts\n\n#----------------------------------------------------\n# Executable for presentation checker\npresCheckExec=/usr/share/prescheck/prescheck.sh\n\n#----------------------------------------------------\n# Skip Office doc conversion pre-check. Attempt to convert\n# Office doc to PDF right away.\nskipOfficePrecheck=true\n\n#----------------------------------------------------\n# Executable for presentation office conversion\npresOfficeConversionExec=/usr/share/bbb-libreoffice-conversion/convert.sh\n\n#----------------------------------------------------\n# These will be copied in cases where the conversion process\n# fails to generate a slide from the uploaded presentation\nBLANK_SLIDE=/usr/share/bigbluebutton/blank/blank-slide.swf\nBLANK_PRESENTATION=/usr/share/bigbluebutton/blank/blank-presentation.pdf\nBLANK_THUMBNAIL=/usr/share/bigbluebutton/blank/blank-thumb.png\nBLANK_PNG=/usr/share/bigbluebutton/blank/blank-png.png\nBLANK_SVG=/usr/share/bigbluebutton/blank/blank-svg.svg\n\n#----------------------------------------------------\n# Number of minutes the conversion should take. If it takes\n# more than this time, cancel the conversion process.\nmaxConversionTime=5\n\n#----------------------------------------------------\n# Maximum number of pages allowed for an uploaded presentation (default 100).\nmaxNumPages=200\n\n#----------------------------------------------------\n# Maximum file size for an uploaded presentation (default 30MB).\nmaxFileSizeUpload=30000000\n\n#----------------------------------------------------\n# Maximum swf file size for load to the client (default 500000).\nMAX_SWF_FILE_SIZE=500000\n\n#----------------------------------------------------\n# Maximum allowed number of place object tags in the converted SWF, if exceeded the conversion will fallback to full BMP (default 800)\nplacementsThreshold=800\n\n# Maximum allowed number of bitmap images in the converted SWF, if exceeded the conversion will fallback to full BMP (default 800)\nimageTagThreshold=800\n\n# Maximum allowed number of define text tags in the converted SWF, if exceeded the conversion will fallback to full BMP (default 200)\ndefineTextThreshold=2000\n\n#------------------------------------\n# Number of threads in the pool to do the presentation conversion.\n#------------------------------------\nnumConversionThreads=5\n\n#------------------------------------\n# Number of threads to process file uploads\n#------------------------------------\nnumFileProcessorThreads=2\n\n#------------------------------------\n# Timeout(secs) to wait for pdf to svg conversion (timeout for each tool called during the process)\n#------------------------------------\nsvgConversionTimeout=60\n\n#------------------------------------\n# pdfFonts is used to detect whether PDF contains text with font Type 3\n# it is also used to check if the pdf has some problem to be opened (timeout) and abort the conversion if so\n# Configuration for pdfFonts Timeout(secs) and Max number of attempts\n#------------------------------------\npdfFontsTimeout=3\nmaxNumberOfAttemptsForPdfFonts=3\n\n#------------------------------------\n# Presentation resolution, in PPI (will be set to generated svg)\n#------------------------------------\nsvgPresentationResolutionPpi=300\n\n#------------------------------------\n# Force conversion of slides to PNG before converting to SVG\n## Experimental - this option might be removed in next releases\n## This will solve problems like reported in issue #8835\n## Disabled by default as it can affect the quality in zoom\n#------------------------------------\nforceRasterizeSlides=false\n\n#------------------------------------\n# Presentation will be resized to this width (in pixels) when rasterizing (converting to PNG)\n## Applied in these situations:\n## a) the source can't be converted directly to SVG ;\n## b) option \"forceRasterizeSlides\" is defined as true ;\n## To disable this constraint (and keep source resolution) define this property as 0.\n#------------------------------------\npngWidthRasterizedSlides=2048\n\n#------------------------------------\n# Timeout(secs) to wait for conversion script execution\n#------------------------------------\nofficeToPdfConversionTimeout=60\n\n#------------------------------------\n# Max concurrent of conversion script execution\n#------------------------------------\nofficeToPdfMaxConcurrentConversions=4\n\n#----------------------------------------------------\n# Conversion of the presentation slides to SWF to be\n# used in the Flash client\nswfSlidesRequired=false\n\n#----------------------------------------------------\n# Additional conversion of the presentation slides to SVG\n# to be used in the HTML5 client\nsvgImagesRequired=true\n\n#----------------------------------------------------\n# Additional conversion of the presentation slides to PNG\n# to be used in the IOS mobile client\ngeneratePngs=false\npngSlideWidth=1200\n\n# Default number of digits for voice conference users joining through the PSTN.\ndefaultNumDigitsForTelVoice=5\n\n#----------------------------------------------------\n# Configuration for large images, 2 MB by default, if bigger it will down-scaled\nmaxImageSize=2000000\n\n#----------------------------------------------------\n# Configuration for large PDF, 14 MB by default, if bigger it will be analysed during the conversion process\nbigPdfSize=14000000\n\n# The maximum allowed page size for PDF files exceeding the 'pdfCheckSize' value, 2 MB by default\nmaxBigPdfPageSize=2000000\n\n#----------------------------------------------------\n# Default dial access number\ndefaultDialAccessNumber=613-555-1234\n\n# Default Guest Policy\n# Valid values are ALWAYS_ACCEPT, ALWAYS_DENY, ASK_MODERATOR\n#\ndefaultGuestPolicy=ALWAYS_ACCEPT\n\n# Enables or disables authenticated guest\nauthenticatedGuest=true\n\n#---------------------------------------------------\n# Default Meeting Layout\n# Valid values are CUSTOM_LAYOUT, SMART_LAYOUT, PRESENTATION_FOCUS, VIDEO_FOCUS\ndefaultMeetingLayout=CUSTOM_LAYOUT\n\n#\n#----------------------------------------------------\n# Default welcome message to display when the participant joins the web\n# conference. This is only used for the old scheduling which will be\n# removed in the future. Use the API to create a conference.\n#\n# If the message contains characters not in ISO-8859-1 character sets\n# they must be properly escaped to unicode characters. An easy way to\n# do this is running the native2ascii command setting UTF8 encoding and\n# passing this file's path as input and output parameters, e.g.:\n#\n# native2ascii -encoding UTF8 bigbluebutton.properties bigbluebutton.properties\n#\ndefaultWelcomeMessage=Welcome to <b>%%CONFNAME%%</b>!<br><br>For help on using BigBlueButton see these (short) <a href=\"https://bigbluebutton.org/teachers/tutorials/\"><u>tutorial videos</u></a>.<br><br>To join the audio bridge click the speaker button. Use a headset to avoid causing background noise for others.\ndefaultWelcomeMessageFooter=This server is running <a href=\"https://bigbluebutton.org/\" target=\"_blank\"><u>BigBlueButton</u></a>.\n\n# Default maximum number of users a meeting can have.\n# Current default is 0 (meeting doesn't have a user limit).\ndefaultMaxUsers=0\n\n# Default duration of the meeting in minutes.\n# Current default is 0 (meeting doesn't end).\ndefaultMeetingDuration=0\n\n# Number of minutes to logout client if user\n# isn't responsive\nclientLogoutTimerInMinutes=0\n\n# End meeting if no user joined within\n# a period of time after meeting created.\nmeetingExpireIfNoUserJoinedInMinutes=5\n\n# Number of minutes to end meeting when\n# the last user left.\nmeetingExpireWhenLastUserLeftInMinutes=1\n\n# User inactivity audit timer interval.\nuserInactivityInspectTimerInMinutes=0\n\n# Number of minutes to consider a user inactive.\n# iSend warning message to client to check if really inactive.\nuserInactivityThresholdInMinutes=30\n\n# Number of minutes for user to respond to inactivity\n# warning before being logged out.\nuserActivitySignResponseDelayInMinutes=5\n\n# Disable recording by default.\n# true - don't record even if record param in the api call is set to record\n# false - when record param is passed from api, override this default\ndisableRecordingDefault=false\n\n# Start recording when first user joins the meeting.\n# For backward compatibility with 0.81 where whole meeting\n# is recorded.\nautoStartRecording=false\n\n# Allow the user to start/stop recording.\nallowStartStopRecording=true\n\n# Number of minutes that Learning Dashboard will be available after the end of the meeting\n# if 0, the Learning Dashboard will keep available permanently\n# this is the default value, can be customized using the create API\nlearningDashboardCleanupDelayInMinutes=2\n\n# Allow webcams streaming reception only to and from moderators\nwebcamsOnlyForModerator=false\n\n# Per meeting camera share limit\n# if 0, there's no limit\nmeetingCameraCap=0\n\n# Per user camera share limit\n# if 0, there's no limit\nuserCameraCap=3\n\n# Mute the meeting on start\nmuteOnStart=false\n\n# Unmute users\n# Gives moderators permisson to unmute other users\nallowModsToUnmuteUsers=false\n\n# Eject user webcams\n# Gives moderators permisson to close other users' webcams\nallowModsToEjectCameras=false\n\n# Saves meeting events even if the meeting is not recorded\ndefaultKeepEvents=false\n\n# Timeout (millis) to remove a joined user after her/his left event without a rejoin\n# e.g. regular user left event\n# Default 60s\nusersTimeout=60000\n\n# Timeout (millis) to remove guest users that stopped fetching for her/his status\n# e.g. guest that closed the waiting page before being approved\n# Default 30s\nwaitingGuestUsersTimeout=30000\n\n# Timeout (millis) to remove users that called the enter API but did not join\n# e.g. user's client hanged between the enter call and join event\n# Default 45s\nenteredUsersTimeout=45000\n\n#----------------------------------------------------\n# This URL is where the BBB client is accessible. When a user sucessfully\n# enters a name and password, she is redirected here to load the client.\n# Do not commit changes to this field.\nbigbluebutton.web.serverURL=http://bigbluebutton.example.com\n\n#----------------------------------------------------\n# Assign URL where the logged-out participant will be redirected after sign-out.\n# If \"default\", it returns to bigbluebutton.web.serverURL\nbigbluebutton.web.logoutURL=default\n\n# The url of the BigBlueButton HTML5 client. Users will be redirected here when\n# successfully joining the meeting.\ndefaultHTML5ClientUrl=${bigbluebutton.web.serverURL}/html5client/join\n\nuseDefaultLogo=false\ndefaultLogoURL=${bigbluebutton.web.serverURL}/images/logo.png\n\n# Allow requests without JSESSIONID to be handled (default = false)\nallowRequestsWithoutSession=false\n\n# The url for where the guest will poll if approved to join or not.\ndefaultGuestWaitURL=${bigbluebutton.web.serverURL}/html5client/guestWait\n\n# The default avatar image to display.\nuseDefaultAvatar=false\ndefaultAvatarURL=${bigbluebutton.web.serverURL}/html5client/resources/images/avatar.png\n\napiVersion=2.0\n\n# Salt which is used by 3rd-party apps to authenticate api calls\nsecuritySalt=330a8b08c3b4c61533e1d0c5ce1ac88f\n\n# List of supported hash algorithms for validating checksums (comma-separated)\n# Available options: sha1, sha256, sha384, sha512\nsupportedChecksumAlgorithms=sha1,sha256,sha384,sha512\n\n# Directory where we drop the <meeting-id-recorded>.done file\nrecordStatusDir=/var/bigbluebutton/recording/status/recorded\n\nredisHost=127.0.0.1\nredisPort=6379\nredisPassword=\nredisKeyExpiry=1209600\n\n# The directory where the published/unpublised recordings are located. This is for\n# the get recording* api calls\npublishedDir=/var/bigbluebutton/published\nunpublishedDir=/var/bigbluebutton/unpublished\ncaptionsDir=/var/bigbluebutton/captions\n\n# The directory where the pre-built configs are stored\nconfigDir=/var/bigbluebutton/configs\n\n# The directory to export Json with Meeting activities (used in Learning Dashboard)\nlearningDashboardFilesDir=/var/bigbluebutton/learning-dashboard\n\n# If the API is enabled.\nserviceEnabled = true\n\n# Test voiceBridge number\ntestVoiceBridge=99999\ntestConferenceMock=conference-mock-default\n\n#------------------------------------------------------\n# These properties are used to test the conversion process.\n# Conference name folder in ${presentationDir} (see above)\nbeans.presentationService.testConferenceMock=${testConferenceMock}\n\n# Conference room folder in ${presentationDir}/${testConferenceMock}\nbeans.presentationService.testRoomMock=conference-mock-default\n# Uploaded presentation name\nbeans.presentationService.testPresentationName=appkonference\n# Uploaded presentation file\nbeans.presentationService.testUploadedPresentation=appkonference.txt\n# Default Uploaded presentation file\nbeans.presentationService.defaultUploadedPresentation=${bigbluebutton.web.serverURL}/default.pdf\n# Discard default presentation (default.pdf) when Pre-upload Slides are sent within the create call (default true)\nbeans.presentationService.preUploadedPresentationOverrideDefault=true\n\npresentationBaseURL=${bigbluebutton.web.serverURL}/bigbluebutton/presentation\n\n# Comma separated list of supported protocols for hosts to download presentations from\n# By default only HTTPS is supported. To enable support for all protocols (NOT RECOMMENDED) use 'all' instead\ninsertDocumentSupportedProtocols=https\n\n# Comma separated list of hosts that presentations CANNOT be downloaded from\n# By default localhost is blocked (i.e. all IPs related to localhost will be blocked and cannot be used for presentation download)\ninsertDocumentBlockedHosts=localhost\n\n#----------------------------------------------------\n# The URL where the presentations will be loaded from.\n#----------------------------------------------------\nbeans.presentationService.presentationBaseUrl=${presentationBaseURL}\n#----------------------------------------------------\n# Inject values into grails service beans\nbeans.presentationService.presentationDir=${presentationDir}\n\n#----------------------------------------------------\n# Specify which IPs can do cross domain requests\naccessControlAllowOrigin=${bigbluebutton.web.serverURL}\n\n#----------------------------------------------------\n# The lapsus of seconds for polling the BBB Server in order to check if it's down.\n# After 5 tries if there isn't response, it will be declared down\ncheckBBBServerEvery=10\n\n# Default settings for breakout rooms\nbreakoutRoomsRecord=false\nbreakoutRoomsPrivateChatEnabled=true\n\n# Default Lock Settings\nlockSettingsDisableCam=false\nlockSettingsDisableMic=false\nlockSettingsDisablePrivateChat=false\nlockSettingsDisablePublicChat=false\nlockSettingsDisableNotes=false\nlockSettingsHideUserList=false\nlockSettingsLockedLayout=false\nlockSettingsLockOnJoin=true\nlockSettingsLockOnJoinConfigurable=false\nlockSettingsHideViewersCursor=false\n\nallowDuplicateExtUserid=true\n\ndefaultTextTrackUrl=${bigbluebutton.web.serverURL}/bigbluebutton\n\n# Param to end the meeting when there are no moderators after a certain period of time.\n# Needed for classes where teacher gets disconnected and can't get back in. Prevents\n# students from running amok.\nendWhenNoModerator=false\n\n# Number of minutes to wait for moderator rejoin before end meeting (if `endWhenNoModerator` enabled)\nendWhenNoModeratorDelayInMinutes=1\n\n# List of features to disable (comma-separated)\n# Available options: breakoutRooms, captions, chat, externalVideos, layouts, learningDashboard, polls, screenshare, sharedNotes, virtualBackgrounds\n# disabledFeatures=\n\n# Allow endpoint with current BigBlueButton version\nallowRevealOfBBBVersion=false\n\nlearningDashboardEnabled=true\nbreakoutRoomsEnabled=true\n\n\n## 内容5\n### 文件路径:\nbigbluebutton-web/grails-app/conf/spring/resources.xml\n### 代码内容:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n\nBigBlueButton open source conferencing system - http://www.bigbluebutton.org/\n\nThis program is free software; you can redistribute it and/or modify it under the\nterms of the GNU Lesser General Public License as published by the Free Software\nFoundation; either version 3.0 of the License, or (at your option) any later\nversion.\n\nBigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY\nWARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A\nPARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.\n\nYou should have received a copy of the GNU Lesser General Public License along\nwith BigBlueButton; if not, see <http://www.gnu.org/licenses/>.\n\n-->\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xmlns:util=\"http://www.springframework.org/schema/util\"\n xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\n http://www.springframework.org/schema/util\n http://www.springframework.org/schema/util/spring-util-2.0.xsd\">\n\n <bean id=\"validator\" class=\"org.springframework.validation.beanvalidation.LocalValidatorFactoryBean\"/>\n\n <bean id=\"characterEncodingFilter\" class=\"org.springframework.web.filter.CharacterEncodingFilter\">\n <property name=\"encoding\">\n <value>utf-8</value>\n </property>\n </bean>\n\n <bean id=\"waitingGuestCleanupTimerTask\" class=\"org.bigbluebutton.web.services.WaitingGuestCleanupTimerTask\"/>\n <bean id=\"userCleanupTimerTask\" class=\"org.bigbluebutton.web.services.UserCleanupTimerTask\"/>\n <bean id=\"enteredUserCleanupTimerTask\" class=\"org.bigbluebutton.web.services.EnteredUserCleanupTimerTask\"/>\n\n <bean id=\"keepAliveService\" class=\"org.bigbluebutton.web.services.KeepAliveService\"\n init-method=\"start\" destroy-method=\"stop\">\n <property name=\"runEvery\" value=\"${checkBBBServerEvery}\"/>\n <property name=\"gw\" ref=\"bbbWebApiGWApp\"/>\n </bean>\n\n <bean id=\"meetingService\" class=\"org.bigbluebutton.api.MeetingService\" init-method=\"start\" destroy-method=\"stop\">\n <property name=\"redisStorageService\" ref=\"redisStorageService\"/>\n <property name=\"learningDashboardService\" ref=\"learningDashboardService\"/>\n <property name=\"recordingService\" ref=\"recordingService\"/>\n <property name=\"presDownloadService\" ref=\"presDownloadService\"/>\n <property name=\"paramsProcessorUtil\" ref=\"paramsProcessorUtil\"/>\n <property name=\"stunTurnService\" ref=\"stunTurnService\"/>\n <property name=\"waitingGuestCleanupTimerTask\" ref=\"waitingGuestCleanupTimerTask\"/>\n <property name=\"userCleanupTimerTask\" ref=\"userCleanupTimerTask\"/>\n <property name=\"enteredUserCleanupTimerTask\" ref=\"enteredUserCleanupTimerTask\"/>\n <property name=\"gw\" ref=\"bbbWebApiGWApp\"/>\n <property name=\"callbackUrlService\" ref=\"callbackUrlService\"/>\n <property name=\"usersTimeout\" value=\"${usersTimeout}\"/>\n <property name=\"waitingGuestUsersTimeout\" value=\"${waitingGuestUsersTimeout}\"/>\n <property name=\"enteredUsersTimeout\" value=\"${enteredUsersTimeout}\"/>\n <property name=\"swfSlidesGenerationProgressNotifier\" ref=\"swfSlidesGenerationProgressNotifier\"/>\n </bean>\n\n <bean id=\"oldMessageReceivedGW\" class=\"org.bigbluebutton.api2.bus.OldMessageReceivedGW\">\n <constructor-arg index=\"0\" ref=\"redisMessageHandler\"/>\n </bean>\n\n <bean id=\"callbackUrlService\" class=\"org.bigbluebutton.web.services.callback.CallbackUrlService\"\n init-method=\"start\" destroy-method=\"stop\">\n </bean>\n\n <bean id=\"bbbWebApiGWApp\" class=\"org.bigbluebutton.api2.BbbWebApiGWApp\">\n <constructor-arg index=\"0\" ref=\"oldMessageReceivedGW\"/>\n <constructor-arg index=\"1\" value=\"${redisHost}\"/>\n <constructor-arg index=\"2\" value=\"${redisPort}\"/>\n <constructor-arg index=\"3\" value=\"${redisPassword}\"/>\n <constructor-arg index=\"4\" value=\"${redisKeyExpiry}\"/>\n </bean>\n\n <bean id=\"recordingServiceHelper\" class=\"org.bigbluebutton.api.util.RecordingMetadataReaderHelper\">\n <property name=\"recordingServiceGW\" ref=\"recordingServiceGW\"/>\n </bean>\n\n <bean id=\"recordingServiceGW\" class=\"org.bigbluebutton.api2.util.RecMetaXmlHelper\">\n <constructor-arg index=\"0\" ref=\"bbbWebApiGWApp\"/>\n </bean>\n\n <bean id=\"presDownloadService\" class=\"org.bigbluebutton.presentation.PresentationUrlDownloadService\"\n destroy-method=\"stop\">\n <property name=\"presentationDir\" value=\"${presentationDir}\"/>\n <property name=\"presentationBaseURL\" value=\"${presentationBaseURL}\"/>\n <property name=\"pageExtractor\" ref=\"pageExtractor\"/>\n <property name=\"documentConversionService\" ref=\"documentConversionService\"/>\n <property name=\"blankPresentation\" value=\"${BLANK_PRESENTATION}\"/>\n <property name=\"insertDocumentSupportedProtocols\" value=\"${insertDocumentSupportedProtocols}\" />\n <property name=\"insertDocumentBlockedHosts\" value=\"${insertDocumentBlockedHosts}\" />\n </bean>\n\n <bean id=\"recordingService\" class=\"org.bigbluebutton.api.RecordingService\">\n <property name=\"recordingStatusDir\" value=\"${recordStatusDir}\"/>\n <property name=\"publishedDir\" value=\"${publishedDir}\"/>\n <property name=\"unpublishedDir\" value=\"${unpublishedDir}\"/>\n <property name=\"captionsDir\" value=\"${captionsDir}\"/>\n <property name=\"recordingServiceHelper\" ref=\"recordingServiceHelper\"/>\n <property name=\"presentationBaseDir\" value=\"${presentationDir}\"/>\n <property name=\"defaultServerUrl\" value=\"${bigbluebutton.web.serverURL}\"/>\n <property name=\"defaultTextTrackUrl\" value=\"${defaultTextTrackUrl}\"/>\n </bean>\n\n <bean id=\"learningDashboardService\" class=\"org.bigbluebutton.api.LearningDashboardService\">\n <property name=\"learningDashboardFilesDir\" value=\"${learningDashboardFilesDir}\"/>\n </bean>\n\n <bean id=\"html5LoadBalancingService\" class=\"org.bigbluebutton.api.HTML5LoadBalancingService\" init-method=\"init\" />\n\n <bean id=\"configServiceHelper\" class=\"org.bigbluebutton.api.ClientConfigServiceHelperImp\"/>\n\n <bean id=\"configService\" class=\"org.bigbluebutton.api.ClientConfigService\" init-method=\"init\">\n <property name=\"configDir\" value=\"${configDir}\"/>\n <property name=\"clientConfigServiceHelper\" ref=\"configServiceHelper\"/>\n </bean>\n\n <bean id=\"validationService\" class=\"org.bigbluebutton.api.service.ValidationService\">\n <property name=\"securitySalt\" value=\"${securitySalt}\"/>\n <property name=\"supportedChecksumAlgorithms\" value=\"${supportedChecksumAlgorithms}\"/>\n <property name=\"allowRequestsWithoutSession\" value=\"${allowRequestsWithoutSession}\"/>\n </bean>\n\n <bean id=\"serviceUtils\" class=\"org.bigbluebutton.api.service.ServiceUtils\">\n <property name=\"meetingService\" ref=\"meetingService\" />\n <property name=\"validationService\" ref=\"validationService\" />\n </bean>\n\n <bean id=\"paramsProcessorUtil\" class=\"org.bigbluebutton.api.ParamsProcessorUtil\">\n <property name=\"apiVersion\" value=\"${apiVersion}\"/>\n <property name=\"serviceEnabled\" value=\"${serviceEnabled}\"/>\n <property name=\"securitySalt\" value=\"${securitySalt}\"/>\n <property name=\"supportedChecksumAlgorithms\" value=\"${supportedChecksumAlgorithms}\"/>\n <property name=\"defaultMaxUsers\" value=\"${defaultMaxUsers}\"/>\n <property name=\"defaultWelcomeMessage\" value=\"${defaultWelcomeMessage}\"/>\n <property name=\"defaultWelcomeMessageFooter\" value=\"${defaultWelcomeMessageFooter}\"/>\n <property name=\"defaultDialAccessNumber\" value=\"${defaultDialAccessNumber}\"/>\n <property name=\"testVoiceBridge\" value=\"${testVoiceBridge}\"/>\n <property name=\"testConferenceMock\" value=\"${testConferenceMock}\"/>\n <property name=\"defaultLogoutUrl\" value=\"${bigbluebutton.web.logoutURL}\"/>\n <property name=\"defaultServerUrl\" value=\"${bigbluebutton.web.serverURL}\"/>\n <property name=\"defaultNumDigitsForTelVoice\" value=\"${defaultNumDigitsForTelVoice}\"/>\n <property name=\"defaultHTML5ClientUrl\" value=\"${defaultHTML5ClientUrl}\"/>\n <property name=\"useDefaultLogo\" value=\"${useDefaultLogo}\"/>\n <property name=\"defaultLogoURL\" value=\"${defaultLogoURL}\"/>\n <property name=\"defaultGuestWaitURL\" value=\"${defaultGuestWaitURL}\"/>\n <property name=\"allowRequestsWithoutSession\" value=\"${allowRequestsWithoutSession}\"/>\n <property name=\"defaultMeetingDuration\" value=\"${defaultMeetingDuration}\"/>\n <property name=\"disableRecordingDefault\" value=\"${disableRecordingDefault}\"/>\n <property name=\"autoStartRecording\" value=\"${autoStartRecording}\"/>\n <property name=\"allowStartStopRecording\" value=\"${allowStartStopRecording}\"/>\n <property name=\"learningDashboardEnabled\" value=\"${learningDashboardEnabled}\"/>\n <property name=\"learningDashboardCleanupDelayInMinutes\" value=\"${learningDashboardCleanupDelayInMinutes}\"/>\n <property name=\"webcamsOnlyForModerator\" value=\"${webcamsOnlyForModerator}\"/>\n <property name=\"defaultMeetingCameraCap\" value=\"${meetingCameraCap}\"/>\n <property name=\"defaultUserCameraCap\" value=\"${userCameraCap}\"/>\n <property name=\"useDefaultAvatar\" value=\"${useDefaultAvatar}\"/>\n <property name=\"defaultAvatarURL\" value=\"${defaultAvatarURL}\"/>\n <property name=\"defaultGuestPolicy\" value=\"${defaultGuestPolicy}\"/>\n <property name=\"authenticatedGuest\" value=\"${authenticatedGuest}\"/>\n <property name=\"defaultMeetingLayout\" value=\"${defaultMeetingLayout}\"/>\n <property name=\"meetingExpireIfNoUserJoinedInMinutes\" value=\"${meetingExpireIfNoUserJoinedInMinutes}\"/>\n <property name=\"meetingExpireWhenLastUserLeftInMinutes\" value=\"${meetingExpireWhenLastUserLeftInMinutes}\"/>\n <property name=\"userInactivityInspectTimerInMinutes\" value=\"${userInactivityInspectTimerInMinutes}\"/>\n <property name=\"userInactivityThresholdInMinutes\" value=\"${userInactivityThresholdInMinutes}\"/>\n <property name=\"userActivitySignResponseDelayInMinutes\" value=\"${userActivitySignResponseDelayInMinutes}\"/>\n <property name=\"maxPresentationFileUpload\" value=\"${maxFileSizeUpload}\"/>\n <property name=\"clientLogoutTimerInMinutes\" value=\"${clientLogoutTimerInMinutes}\"/>\n <property name=\"muteOnStart\" value=\"${muteOnStart}\"/>\n <property name=\"allowModsToUnmuteUsers\" value=\"${allowModsToUnmuteUsers}\"/>\n <property name=\"allowModsToEjectCameras\" value=\"${allowModsToEjectCameras}\"/>\n <property name=\"breakoutRoomsEnabled\" value=\"${breakoutRoomsEnabled}\"/>\n <property name=\"breakoutRoomsRecord\" value=\"${breakoutRoomsRecord}\"/>\n <property name=\"breakoutRoomsPrivateChatEnabled\" value=\"${breakoutRoomsPrivateChatEnabled}\"/>\n <property name=\"lockSettingsDisableCam\" value=\"${lockSettingsDisableCam}\"/>\n <property name=\"lockSettingsDisableMic\" value=\"${lockSettingsDisableMic}\"/>\n <property name=\"lockSettingsDisablePrivateChat\" value=\"${lockSettingsDisablePrivateChat}\"/>\n <property name=\"lockSettingsDisablePublicChat\" value=\"${lockSettingsDisablePublicChat}\"/>\n <property name=\"lockSettingsDisableNotes\" value=\"${lockSettingsDisableNotes}\"/>\n <property name=\"lockSettingsHideUserList\" value=\"${lockSettingsHideUserList}\"/>\n <property name=\"lockSettingsLockedLayout\" value=\"${lockSettingsLockedLayout}\"/>\n <property name=\"lockSettingsLockOnJoin\" value=\"${lockSettingsLockOnJoin}\"/>\n <property name=\"lockSettingsLockOnJoinConfigurable\" value=\"${lockSettingsLockOnJoinConfigurable}\"/>\n <property name=\"lockSettingsHideViewersCursor\" value=\"${lockSettingsHideViewersCursor}\"/>\n <property name=\"allowDuplicateExtUserid\" value=\"${allowDuplicateExtUserid}\"/>\n <property name=\"endWhenNoModerator\" value=\"${endWhenNoModerator}\"/>\n <property name=\"endWhenNoModeratorDelayInMinutes\" value=\"${endWhenNoModeratorDelayInMinutes}\"/>\n <property name=\"disabledFeatures\" value=\"${disabledFeatures}\"/>\n <property name=\"defaultKeepEvents\" value=\"${defaultKeepEvents}\"/>\n <property name=\"allowRevealOfBBBVersion\" value=\"${allowRevealOfBBBVersion}\"/>\n </bean>\n\n <bean id=\"presentationService\" class=\"org.bigbluebutton.web.services.PresentationService\">\n <property name=\"presentationDir\" value=\"${presentationDir}\"/>\n <property name=\"testConferenceMock\" value=\"${testConferenceMock}\"/>\n <property name=\"testRoomMock\" value=\"${beans.presentationService.testRoomMock}\"/>\n <property name=\"testPresentationName\" value=\"${beans.presentationService.testPresentationName}\"/>\n <property name=\"testUploadedPresentation\" value=\"${beans.presentationService.testUploadedPresentation}\"/>\n <property name=\"defaultUploadedPresentation\" value=\"${beans.presentationService.defaultUploadedPresentation}\"/>\n <property name=\"presentationBaseUrl\" value=\"${presentationBaseURL}\"/>\n <property name=\"preUploadedPresentationOverrideDefault\" value=\"${beans.presentationService.preUploadedPresentationOverrideDefault}\"/>\n </bean>\n\n <import resource=\"doc-conversion.xml\"/>\n <import resource=\"bbb-redis-messaging.xml\"/>\n <!-- moved the following import statement for turn-stun-servers.xml to resources.groovy to be able to use overlay config for turn/stun servers -->\n <!--\t <import resource=\"turn-stun-servers.xml\"/>-->\n</beans>\n\n", "input": "", "output": "【漏洞类型】服务器端请求伪造(SSRF)\n【严重性】High\n【漏洞摘要】代码中存在服务器端请求伪造(SSRF)漏洞。具体表现为`PresentationUrlDownloadService`类中的`savePresentation`方法和`CallbackUrlService`类中的`fetchCallbackUrl`方法,它们使用用户提供的URL进行HTTP请求,而未进行适当的输入验证或主机名验证。这可能导致攻击者通过构造恶意URL访问内部网络资源或执行其他未经授权的操作。\n【关键代码模式】\n- `followRedirect`函数,用于处理重定向URL。\n- `fetchCallbackUrl`函数,用于处理回调URL。\n- `HttpURLConnection`和`CloseableHttpAsyncClient`的使用,进行HTTP请求。\n- `URL`类的`openConnection`方法,用于打开连接。\n- `insertDocumentSupportedProtocols`和`insertDocumentBlockedHosts`的设置和验证。\n【修复方法说明】通过在`followRedirect`函数中添加`isValidRedirectUrl`方法,验证重定向URL的协议和主机名,修复了SSRF漏洞。同时,在`fetchCallbackUrl`函数中移除了重定向处理,以确保回调URL不被重定向到其他主机。这一修复通过限制HTTP请求的来源,防止了未经授权的内部网络访问。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsm-shop/src/main/java/com/salesmanager/shop/store/controller/shoppingCart/facade/ShoppingCartFacadeImpl.java\n### 代码内容:\n/**\n *\n */\npackage com.salesmanager.shop.store.controller.shoppingCart.facade;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.UUID;\n\nimport javax.inject.Inject;\nimport javax.persistence.NoResultException;\n\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.commons.lang3.Validate;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\n\nimport com.salesmanager.core.business.exception.ServiceException;\nimport com.salesmanager.core.business.services.catalog.product.PricingService;\nimport com.salesmanager.core.business.services.catalog.product.ProductService;\nimport com.salesmanager.core.business.services.catalog.product.attribute.ProductAttributeService;\nimport com.salesmanager.core.business.services.shoppingcart.ShoppingCartCalculationService;\nimport com.salesmanager.core.business.services.shoppingcart.ShoppingCartService;\nimport com.salesmanager.core.business.utils.ProductPriceUtils;\nimport com.salesmanager.core.model.catalog.product.Product;\nimport com.salesmanager.core.model.catalog.product.attribute.ProductAttribute;\nimport com.salesmanager.core.model.catalog.product.availability.ProductAvailability;\nimport com.salesmanager.core.model.catalog.product.price.FinalPrice;\nimport com.salesmanager.core.model.customer.Customer;\nimport com.salesmanager.core.model.merchant.MerchantStore;\nimport com.salesmanager.core.model.reference.language.Language;\nimport com.salesmanager.core.model.shoppingcart.ShoppingCart;\nimport com.salesmanager.shop.constants.Constants;\nimport com.salesmanager.shop.model.shoppingcart.CartModificationException;\nimport com.salesmanager.shop.model.shoppingcart.PersistableShoppingCartItem;\nimport com.salesmanager.shop.model.shoppingcart.ReadableShoppingCart;\nimport com.salesmanager.shop.model.shoppingcart.ShoppingCartAttribute;\nimport com.salesmanager.shop.model.shoppingcart.ShoppingCartData;\nimport com.salesmanager.shop.model.shoppingcart.ShoppingCartItem;\nimport com.salesmanager.shop.populator.shoppingCart.ReadableShoppingCartPopulator;\nimport com.salesmanager.shop.populator.shoppingCart.ShoppingCartDataPopulator;\nimport com.salesmanager.shop.store.api.exception.ResourceNotFoundException;\nimport com.salesmanager.shop.utils.DateUtil;\nimport com.salesmanager.shop.utils.ImageFilePath;\n\n/**\n * @author Umesh Awasthi\n * @version 1.0\n * @since 1.0\n */\n@Service( value = \"shoppingCartFacade\" )\npublic class ShoppingCartFacadeImpl\n implements ShoppingCartFacade\n{\n\n \n private static final Logger LOG = LoggerFactory.getLogger(ShoppingCartFacadeImpl.class);\n\n @Inject\n private ShoppingCartService shoppingCartService;\n\n @Inject\n ShoppingCartCalculationService shoppingCartCalculationService;\n\n @Inject\n private ProductPriceUtils productPriceUtils;\n\n @Inject\n private ProductService productService;\n\n @Inject\n private PricingService pricingService;\n\n @Inject\n private ProductAttributeService productAttributeService;\n\n\t@Inject\n\t@Qualifier(\"img\")\n\tprivate ImageFilePath imageUtils;\n\n public void deleteShoppingCart(final Long id, final MerchantStore store) throws Exception {\n \tShoppingCart cart = shoppingCartService.getById(id, store);\n \tif(cart!=null) {\n \t\tshoppingCartService.deleteCart(cart);\n \t}\n }\n \n @Override\n public void deleteShoppingCart(final String code, final MerchantStore store) throws Exception {\n \tShoppingCart cart = shoppingCartService.getByCode(code, store);\n \tif(cart!=null) {\n \t\tshoppingCartService.deleteCart(cart);\n \t}\n }\n\n @Override\n public ShoppingCartData addItemsToShoppingCart( final ShoppingCartData shoppingCartData,\n final ShoppingCartItem item, final MerchantStore store, final Language language,final Customer customer )\n throws Exception\n {\n\n ShoppingCart cartModel = null;\n if(item.getQuantity() < 1) item.setQuantity(1);\n /**\n * Sometimes a user logs in and a shopping cart is present in db (shoppingCartData\n * but ui has no cookie with shopping cart code so the cart code will have\n * to be added to the item in order to process add to cart normally\n */\n if(shoppingCartData != null && StringUtils.isBlank(item.getCode())) {\n \titem.setCode(shoppingCartData.getCode());\n }\n \n \n if ( !StringUtils.isBlank( item.getCode() ) )\n {\n // get it from the db\n cartModel = getShoppingCartModel( item.getCode(), store );\n if ( cartModel == null )\n {\n cartModel = createCartModel( shoppingCartData.getCode(), store,customer );\n }\n\n }\n\n if ( cartModel == null )\n {\n\n final String shoppingCartCode =\n StringUtils.isNotBlank( shoppingCartData.getCode() ) ? shoppingCartData.getCode() : null;\n cartModel = createCartModel( shoppingCartCode, store,customer );\n\n }\n com.salesmanager.core.model.shoppingcart.ShoppingCartItem shoppingCartItem =\n createCartItem( cartModel, item, store );\n \n \n boolean duplicateFound = false;\n if(CollectionUtils.isEmpty(item.getShoppingCartAttributes())) {//increment quantity\n \t//get duplicate item from the cart\n \tSet<com.salesmanager.core.model.shoppingcart.ShoppingCartItem> cartModelItems = cartModel.getLineItems();\n \tfor(com.salesmanager.core.model.shoppingcart.ShoppingCartItem cartItem : cartModelItems) {\n \t\tif(cartItem.getProduct().getId().longValue()==shoppingCartItem.getProduct().getId().longValue()) {\n \t\t\tif(CollectionUtils.isEmpty(cartItem.getAttributes())) {\n \t\t\t\tif(!duplicateFound) {\n \t\t\t\t\tif(!shoppingCartItem.isProductVirtual()) {\n\t \t\t\t\t\tcartItem.setQuantity(cartItem.getQuantity() + shoppingCartItem.getQuantity());\n \t\t\t\t\t}\n \t\t\t\t\tduplicateFound = true;\n \t\t\t\t\tbreak;\n \t\t\t\t}\n \t\t\t}\n \t\t}\n \t}\n }\n \n if(!duplicateFound) {\n \t//shoppingCartItem.getAttributes().stream().forEach(a -> {a.setProductAttributeId(productAttributeId);});\n \tcartModel.getLineItems().add( shoppingCartItem );\n }\n \n /** Update cart in database with line items **/\n shoppingCartService.saveOrUpdate( cartModel );\n\n //refresh cart\n cartModel = shoppingCartService.getById(cartModel.getId(), store);\n\n shoppingCartCalculationService.calculate( cartModel, store, language );\n\n ShoppingCartDataPopulator shoppingCartDataPopulator = new ShoppingCartDataPopulator();\n shoppingCartDataPopulator.setShoppingCartCalculationService( shoppingCartCalculationService );\n shoppingCartDataPopulator.setPricingService( pricingService );\n shoppingCartDataPopulator.setimageUtils(imageUtils);\n\n return shoppingCartDataPopulator.populate( cartModel, store, language );\n }\n\n private com.salesmanager.core.model.shoppingcart.ShoppingCartItem createCartItem( final ShoppingCart cartModel,\n final ShoppingCartItem shoppingCartItem,\n final MerchantStore store )\n throws Exception\n {\n\n Product product = productService.getById( shoppingCartItem.getProductId() );\n\n if ( product == null )\n {\n throw new Exception( \"Item with id \" + shoppingCartItem.getProductId() + \" does not exist\" );\n }\n\n if ( product.getMerchantStore().getId().intValue() != store.getId().intValue() )\n {\n throw new Exception( \"Item with id \" + shoppingCartItem.getProductId() + \" does not belong to merchant \"\n + store.getId() );\n }\n \n\t\t/**\n\t\t * Check if product quantity is 0\n\t\t * Check if product is available\n\t\t * Check if date available <= now\n\t\t */\n \n Set<ProductAvailability> availabilities = product.getAvailabilities();\n if(availabilities == null) {\n \t\n \tthrow new Exception( \"Item with id \" + product.getId() + \" is not properly configured\" );\n \t\n }\n \t\n for(ProductAvailability availability : availabilities) {\n \tif(availability.getProductQuantity() == null || availability.getProductQuantity().intValue() <= 0) {\n throw new Exception( \"Item with id \" + product.getId() + \" is not available\");\n \t}\n }\n \n if(!product.isAvailable()) {\n \tthrow new Exception( \"Item with id \" + product.getId() + \" is not available\");\n }\n \n if(!DateUtil.dateBeforeEqualsDate(product.getDateAvailable(), new Date())) {\n \tthrow new Exception( \"Item with id \" + product.getId() + \" is not available\");\n }\n\n com.salesmanager.core.model.shoppingcart.ShoppingCartItem item =\n shoppingCartService.populateShoppingCartItem( product );\n\n item.setQuantity( shoppingCartItem.getQuantity() );\n item.setShoppingCart( cartModel );\n\n // attributes\n List<ShoppingCartAttribute> cartAttributes = shoppingCartItem.getShoppingCartAttributes();\n if ( !CollectionUtils.isEmpty( cartAttributes ) )\n {\n for ( ShoppingCartAttribute attribute : cartAttributes )\n {\n ProductAttribute productAttribute = productAttributeService.getById( attribute.getAttributeId() );\n if ( productAttribute != null\n && productAttribute.getProduct().getId().longValue() == product.getId().longValue() )\n {\n com.salesmanager.core.model.shoppingcart.ShoppingCartAttributeItem attributeItem =\n new com.salesmanager.core.model.shoppingcart.ShoppingCartAttributeItem( item,\n productAttribute );\n\n item.addAttributes( attributeItem );\n }\n }\n }\n return item;\n\n }\n\n \n //used for api\n\tprivate com.salesmanager.core.model.shoppingcart.ShoppingCartItem createCartItem(ShoppingCart cartModel,\n\t\t\t PersistableShoppingCartItem shoppingCartItem, MerchantStore store) throws Exception {\n\n\t\tProduct product = productService.getById(shoppingCartItem.getProduct());\n\n\t\tif (product == null) {\n\t\t\tthrow new ResourceNotFoundException(\"Item with id \" + shoppingCartItem.getProduct() + \" does not exist\");\n\t\t}\n\n\t\tif (product.getMerchantStore().getId().intValue() != store.getId().intValue()) {\n\t\t\tthrow new ResourceNotFoundException(\"Item with id \" + shoppingCartItem.getProduct() + \" does not belong to merchant \"\n\t\t\t\t\t+ store.getId());\n\t\t}\n\t\t\n\t\t/**\n\t\t * Check if product quantity is 0\n\t\t * Check if product is available\n\t\t * Check if date available <= now\n\t\t */\n \n Set<ProductAvailability> availabilities = product.getAvailabilities();\n if(availabilities == null) {\n \t\n \tthrow new Exception( \"Item with id \" + product.getId() + \" is not properly configured\" );\n \t\n }\n \t\n for(ProductAvailability availability : availabilities) {\n \tif(availability.getProductQuantity() == null || availability.getProductQuantity().intValue() <= 0) {\n throw new Exception( \"Item with id \" + product.getId() + \" is not available\");\n \t}\n }\n \n if(!product.isAvailable()) {\n \tthrow new Exception( \"Item with id \" + product.getId() + \" is not available\");\n }\n \n if(!DateUtil.dateBeforeEqualsDate(product.getDateAvailable(), new Date())) {\n \tthrow new Exception( \"Item with id \" + product.getId() + \" is not available\");\n }\n\t\t\n\n\t\tcom.salesmanager.core.model.shoppingcart.ShoppingCartItem item = shoppingCartService\n\t\t\t\t.populateShoppingCartItem(product);\n\n\t\titem.setQuantity(shoppingCartItem.getQuantity());\n\t\titem.setShoppingCart(cartModel);\n\t\t\n\t\t//set attributes\n\t\tList<com.salesmanager.shop.model.catalog.product.attribute.ProductAttribute> attributes = shoppingCartItem.getAttributes();\n\t\tif (!CollectionUtils.isEmpty(attributes)) {\n\t\t\tfor(com.salesmanager.shop.model.catalog.product.attribute.ProductAttribute attribute : attributes) {\n\t\t\t\t\n\t\t\t\tProductAttribute productAttribute = productAttributeService.getById(attribute.getId());\n\t\t\t\t\n\t\t\t\tif (productAttribute != null\n\t\t\t\t\t\t&& productAttribute.getProduct().getId().longValue() == product.getId().longValue()) {\n\t\t\t\t\t\n\t\t\t\t\tcom.salesmanager.core.model.shoppingcart.ShoppingCartAttributeItem attributeItem = new com.salesmanager.core.model.shoppingcart.ShoppingCartAttributeItem(\n\t\t\t\t\t\t\titem, productAttribute);\n\n\t\t\t\t\titem.addAttributes(attributeItem);\n\t\t\t\t}\t\t\t\t\n\t\t\t}\n\t\t}\n\n\t\treturn item;\n\n\t} \n \n\n @Override\n public ShoppingCart createCartModel( final String shoppingCartCode, final MerchantStore store,final Customer customer )\n throws Exception\n {\n final Long CustomerId = customer != null ? customer.getId() : null;\n ShoppingCart cartModel = new ShoppingCart();\n if ( StringUtils.isNotBlank( shoppingCartCode ) )\n {\n cartModel.setShoppingCartCode( shoppingCartCode );\n }\n else\n {\n cartModel.setShoppingCartCode( uniqueShoppingCartCode() );\n }\n\n cartModel.setMerchantStore( store );\n if ( CustomerId != null )\n {\n cartModel.setCustomerId( CustomerId );\n }\n shoppingCartService.create( cartModel );\n return cartModel;\n }\n\n private com.salesmanager.core.model.shoppingcart.ShoppingCartItem getEntryToUpdate( final long entryId,\n final ShoppingCart cartModel )\n {\n if ( CollectionUtils.isNotEmpty( cartModel.getLineItems() ) )\n {\n for ( com.salesmanager.core.model.shoppingcart.ShoppingCartItem shoppingCartItem : cartModel.getLineItems() )\n {\n if ( shoppingCartItem.getId().longValue() == entryId )\n {\n LOG.info( \"Found line item for given entry id: \" + entryId );\n return shoppingCartItem;\n\n }\n }\n }\n LOG.info( \"Unable to find any entry for given Id: \" + entryId );\n return null;\n }\n\n private Object getKeyValue( final String key )\n {\n ServletRequestAttributes reqAttr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();\n return reqAttr.getRequest().getAttribute( key );\n }\n\n @Override\n public ShoppingCartData getShoppingCartData( final Customer customer, final MerchantStore store,\n final String shoppingCartId, Language language)\n throws Exception\n {\n\n ShoppingCart cart = null;\n try\n {\n if ( customer != null )\n {\n LOG.info( \"Reteriving customer shopping cart...\" );\n\n cart = shoppingCartService.getShoppingCart( customer );\n\n }\n\n else\n {\n if ( StringUtils.isNotBlank( shoppingCartId ) && cart == null )\n {\n cart = shoppingCartService.getByCode( shoppingCartId, store );\n }\n\n }\n }\n catch ( ServiceException ex )\n {\n LOG.error( \"Error while retriving cart from customer\", ex );\n }\n catch( NoResultException nre) {\n \t//nothing\n }\n\n if ( cart == null )\n {\n return null;\n }\n\n LOG.info( \"Cart model found.\" );\n\n ShoppingCartDataPopulator shoppingCartDataPopulator = new ShoppingCartDataPopulator();\n shoppingCartDataPopulator.setShoppingCartCalculationService( shoppingCartCalculationService );\n shoppingCartDataPopulator.setPricingService( pricingService );\n shoppingCartDataPopulator.setimageUtils(imageUtils);\n\n //Language language = (Language) getKeyValue( Constants.LANGUAGE );\n MerchantStore merchantStore = (MerchantStore) getKeyValue( Constants.MERCHANT_STORE );\n \n ShoppingCartData shoppingCartData = shoppingCartDataPopulator.populate( cart, merchantStore, language );\n \n/* List<ShoppingCartItem> unavailables = new ArrayList<ShoppingCartItem>();\n List<ShoppingCartItem> availables = new ArrayList<ShoppingCartItem>();\n //Take out items no more available\n List<ShoppingCartItem> items = shoppingCartData.getShoppingCartItems();\n for(ShoppingCartItem item : items) {\n \tString code = item.getProductCode();\n \tProduct p =productService.getByCode(code, language);\n \tif(!p.isAvailable()) {\n \t\tunavailables.add(item);\n \t} else {\n \t\tavailables.add(item);\n \t}\n \t\n }\n shoppingCartData.setShoppingCartItems(availables);\n shoppingCartData.setUnavailables(unavailables);*/\n \n return shoppingCartData;\n\n }\n\n //@Override\n public ShoppingCartData getShoppingCartData( final ShoppingCart shoppingCartModel, Language language)\n throws Exception\n {\n\n ShoppingCartDataPopulator shoppingCartDataPopulator = new ShoppingCartDataPopulator();\n shoppingCartDataPopulator.setShoppingCartCalculationService( shoppingCartCalculationService );\n shoppingCartDataPopulator.setPricingService( pricingService );\n shoppingCartDataPopulator.setimageUtils(imageUtils);\n //Language language = (Language) getKeyValue( Constants.LANGUAGE );\n MerchantStore merchantStore = (MerchantStore) getKeyValue( Constants.MERCHANT_STORE );\n return shoppingCartDataPopulator.populate( shoppingCartModel, merchantStore, language );\n }\n\n\t@Override\n public ShoppingCartData removeCartItem( final Long itemID, final String cartId ,final MerchantStore store,final Language language )\n throws Exception\n {\n if ( StringUtils.isNotBlank( cartId ) )\n {\n\n ShoppingCart cartModel = getCartModel( cartId,store );\n if ( cartModel != null )\n {\n if ( CollectionUtils.isNotEmpty( cartModel.getLineItems() ) )\n {\n Set<com.salesmanager.core.model.shoppingcart.ShoppingCartItem> shoppingCartItemSet =\n new HashSet<com.salesmanager.core.model.shoppingcart.ShoppingCartItem>();\n for ( com.salesmanager.core.model.shoppingcart.ShoppingCartItem shoppingCartItem : cartModel.getLineItems() )\n {\n if(shoppingCartItem.getId().longValue() == itemID.longValue() )\n {\n \t\tshoppingCartService.deleteShoppingCartItem(itemID);\n } else {\n shoppingCartItemSet.add(shoppingCartItem);\n }\n }\n \n cartModel.setLineItems(shoppingCartItemSet);\n\n ShoppingCartDataPopulator shoppingCartDataPopulator = new ShoppingCartDataPopulator();\n shoppingCartDataPopulator.setShoppingCartCalculationService( shoppingCartCalculationService );\n shoppingCartDataPopulator.setPricingService( pricingService );\n shoppingCartDataPopulator.setimageUtils(imageUtils);\n return shoppingCartDataPopulator.populate( cartModel, store, language );\n \n\n }\n }\n }\n return null;\n }\n\n @Override\n public ShoppingCartData updateCartItem( final Long itemID, final String cartId, final long newQuantity,final MerchantStore store, final Language language )\n throws Exception\n {\n if ( newQuantity < 1 )\n {\n throw new CartModificationException( \"Quantity must not be less than one\" );\n }\n if ( StringUtils.isNotBlank( cartId ) )\n {\n ShoppingCart cartModel = getCartModel( cartId,store );\n if ( cartModel != null )\n {\n com.salesmanager.core.model.shoppingcart.ShoppingCartItem entryToUpdate =\n getEntryToUpdate( itemID.longValue(), cartModel );\n\n if ( entryToUpdate == null )\n {\n throw new CartModificationException( \"Unknown entry number.\" );\n }\n\n entryToUpdate.getProduct();\n\n LOG.info( \"Updating cart entry quantity to\" + newQuantity );\n entryToUpdate.setQuantity( (int) newQuantity );\n List<ProductAttribute> productAttributes = new ArrayList<ProductAttribute>();\n productAttributes.addAll( entryToUpdate.getProduct().getAttributes() );\n final FinalPrice finalPrice =\n productPriceUtils.getFinalProductPrice( entryToUpdate.getProduct(), productAttributes );\n entryToUpdate.setItemPrice( finalPrice.getFinalPrice() );\n shoppingCartService.saveOrUpdate( cartModel );\n\n LOG.info( \"Cart entry updated with desired quantity\" );\n ShoppingCartDataPopulator shoppingCartDataPopulator = new ShoppingCartDataPopulator();\n shoppingCartDataPopulator.setShoppingCartCalculationService( shoppingCartCalculationService );\n shoppingCartDataPopulator.setPricingService( pricingService );\n shoppingCartDataPopulator.setimageUtils(imageUtils);\n return shoppingCartDataPopulator.populate( cartModel, store, language );\n\n }\n }\n return null;\n }\n \n @Override\n public ShoppingCartData updateCartItems( final List<ShoppingCartItem> shoppingCartItems, final MerchantStore store, final Language language )\n throws Exception\n {\n \t\n \t\tValidate.notEmpty(shoppingCartItems,\"shoppingCartItems null or empty\");\n \t\tShoppingCart cartModel = null;\n \t\tSet<com.salesmanager.core.model.shoppingcart.ShoppingCartItem> cartItems = new HashSet<com.salesmanager.core.model.shoppingcart.ShoppingCartItem>();\n \t\tfor(ShoppingCartItem item : shoppingCartItems) {\n \t\t\t\n \t\t\tif(item.getQuantity()<1) {\n \t\t\t\tthrow new CartModificationException( \"Quantity must not be less than one\" );\n \t\t\t}\n \t\t\t\n \t\t\tif(cartModel==null) {\n \t\t\t\tcartModel = getCartModel( item.getCode(), store );\n \t\t\t}\n \t\t\t\n com.salesmanager.core.model.shoppingcart.ShoppingCartItem entryToUpdate =\n getEntryToUpdate( item.getId(), cartModel );\n\n if ( entryToUpdate == null ) {\n throw new CartModificationException( \"Unknown entry number.\" );\n }\n\n entryToUpdate.getProduct();\n\n LOG.info( \"Updating cart entry quantity to\" + item.getQuantity() );\n entryToUpdate.setQuantity( (int) item.getQuantity() );\n \n List<ProductAttribute> productAttributes = new ArrayList<ProductAttribute>();\n productAttributes.addAll( entryToUpdate.getProduct().getAttributes() );\n \n final FinalPrice finalPrice =\n productPriceUtils.getFinalProductPrice( entryToUpdate.getProduct(), productAttributes );\n entryToUpdate.setItemPrice( finalPrice.getFinalPrice() );\n \n\n cartItems.add(entryToUpdate);\n \t\t\t\n \t\t\t\n \t\t\t\n \t\t\t\n \t\t}\n \t\t\n \t\tcartModel.setLineItems(cartItems);\n \t\tshoppingCartService.saveOrUpdate( cartModel );\n\n \t\t\n LOG.info( \"Cart entry updated with desired quantity\" );\n ShoppingCartDataPopulator shoppingCartDataPopulator = new ShoppingCartDataPopulator();\n shoppingCartDataPopulator.setShoppingCartCalculationService( shoppingCartCalculationService );\n shoppingCartDataPopulator.setPricingService( pricingService );\n shoppingCartDataPopulator.setimageUtils(imageUtils);\n return shoppingCartDataPopulator.populate( cartModel, store, language );\n\n }\n\n private ShoppingCart getCartModel( final String cartId,final MerchantStore store )\n {\n if ( StringUtils.isNotBlank( cartId ) )\n {\n try\n {\n return shoppingCartService.getByCode( cartId, store );\n }\n catch ( ServiceException e )\n {\n LOG.error( \"unable to find any cart asscoiated with this Id: \" + cartId );\n LOG.error( \"error while fetching cart model...\", e );\n return null;\n }\n catch( NoResultException nre) {\n \t//nothing\n }\n\n }\n return null;\n }\n\n\t@Override\n\tpublic ShoppingCartData getShoppingCartData(String code, MerchantStore store, Language language) {\n\t\ttry {\n\t\t\tShoppingCart cartModel = shoppingCartService.getByCode( code, store );\n\t\t\tif(cartModel!=null) {\n\t\t\t\tShoppingCartData cart = getShoppingCartData(cartModel, language);\n\t\t\t\treturn cart;\n\t\t\t}\n\t\t} catch( NoResultException nre) {\n\t \t//nothing\n\n\t\t} catch(Exception e) {\n\t\t\tLOG.error(\"Cannot retrieve cart code \" + code,e);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic ShoppingCart getShoppingCartModel(String shoppingCartCode,\n\t\t\tMerchantStore store) throws Exception {\n\t\treturn shoppingCartService.getByCode( shoppingCartCode, store );\n\t}\n\n\t@Override\n\tpublic ShoppingCart getShoppingCartModel(Customer customer,\n\t\t\tMerchantStore store) throws Exception {\n\t\treturn shoppingCartService.getByCustomer(customer);\n\t}\n\n\t@Override\n\tpublic void saveOrUpdateShoppingCart(ShoppingCart cart) throws Exception {\n\t\tshoppingCartService.saveOrUpdate(cart);\n\t\t\n\t}\n\n\t@Override\n\tpublic ReadableShoppingCart getCart(Customer customer, MerchantStore store, Language language) throws Exception {\n\t\t\n\t\tValidate.notNull(customer,\"Customer cannot be null\");\n\t\tValidate.notNull(customer.getId(),\"Customer.id cannot be null or empty\");\n\t\t\n\t\t//Check if customer has an existing shopping cart\n\t\tShoppingCart cartModel = shoppingCartService.getByCustomer(customer);\n\t\t\n\t\tif(cartModel == null) {\n\t\t\treturn null;\n\t\t}\n\t\t\n shoppingCartCalculationService.calculate( cartModel, store, language );\n \n ReadableShoppingCartPopulator readableShoppingCart = new ReadableShoppingCartPopulator();\n \n readableShoppingCart.setImageUtils(imageUtils);\n readableShoppingCart.setPricingService(pricingService);\n readableShoppingCart.setProductAttributeService(productAttributeService);\n readableShoppingCart.setShoppingCartCalculationService(shoppingCartCalculationService);\n \n ReadableShoppingCart readableCart = new ReadableShoppingCart();\n \n readableShoppingCart.populate(cartModel, readableCart, store, language);\n\n\t\t\n\t\treturn readableCart;\n\t}\n\t\n\t@Override\n\tpublic ReadableShoppingCart addToCart(PersistableShoppingCartItem item, MerchantStore store,\n\t\t\tLanguage language) throws Exception {\n\t\t\n\t\tValidate.notNull(item,\"PersistableShoppingCartItem cannot be null\");\n\t\t\n\t\t//if cart does not exist create a new one\n\n\t\tShoppingCart cartModel = new ShoppingCart();\n\t\tcartModel.setMerchantStore(store);\n\t\tcartModel.setShoppingCartCode(uniqueShoppingCartCode());\n\n\t\treturn readableShoppingCart(cartModel,item,store,language);\n\t}\n\t\n\n\t@Override\n\tpublic void removeShoppingCartItem(String cartCode, Long productId,\n\t MerchantStore merchant, Language language) throws Exception {\n\t Validate.notNull(cartCode, \"Shopping cart code must not be null\");\n\t Validate.notNull(productId, \"product id must not be null\");\n\t Validate.notNull(merchant, \"MerchantStore must not be null\");\n\t \n\t \n\t //get cart\n\t ShoppingCart cart = getCartModel(cartCode, merchant);\n\t \n\t if(cart == null) {\n\t throw new ResourceNotFoundException(\"Cart code [ \" + cartCode + \" ] not found\");\n\t }\n\t \n\t Set<com.salesmanager.core.model.shoppingcart.ShoppingCartItem> items = new HashSet<com.salesmanager.core.model.shoppingcart.ShoppingCartItem>();\n\t com.salesmanager.core.model.shoppingcart.ShoppingCartItem itemToDelete = null;\n\t for ( com.salesmanager.core.model.shoppingcart.ShoppingCartItem shoppingCartItem : cart.getLineItems() )\n {\n if ( shoppingCartItem.getProduct().getId().longValue() == productId.longValue() )\n {\n //get cart item\n itemToDelete =\n getEntryToUpdate( shoppingCartItem.getId(), cart );\n \n \n //break;\n\n } else {\n items.add(shoppingCartItem);\n }\n }\n\t //delete item\n\t if(itemToDelete!=null) {\n\t shoppingCartService.deleteShoppingCartItem(itemToDelete.getId());\n\t }\n \n //remaining items\n\t if(items.size()>0) {\n\t \tcart.setLineItems(items);\n\t } else {\n\t \tcart.getLineItems().clear();\n\t }\n\n //if(items.size()>0) {\n shoppingCartService.saveOrUpdate(cart);//update cart with remaining items\n //ReadableShoppingCart readableShoppingCart = this.getByCode(cartCode, merchant, language);\n //}\n\n\t}\n\t\n\tprivate ReadableShoppingCart readableShoppingCart(ShoppingCart cartModel, PersistableShoppingCartItem item, MerchantStore store,\n\t\t\tLanguage language) throws Exception {\n\t\t\n\t\t\n\t\tcom.salesmanager.core.model.shoppingcart.ShoppingCartItem itemModel = createCartItem(cartModel, item, store);\n\t\t\n\t\t//need to check if the item is already in the cart\n boolean duplicateFound = false;\n //only if item has no attributes\n if(CollectionUtils.isEmpty(item.getAttributes())) {//increment quantity\n \t//get duplicate item from the cart\n \tSet<com.salesmanager.core.model.shoppingcart.ShoppingCartItem> cartModelItems = cartModel.getLineItems();\n \tfor(com.salesmanager.core.model.shoppingcart.ShoppingCartItem cartItem : cartModelItems) {\n \t\tif(cartItem.getProduct().getId().longValue()==item.getProduct().longValue()) {\n \t\t\tif(CollectionUtils.isEmpty(cartItem.getAttributes())) {\n \t\t\t\tif(!duplicateFound) {\n \t\t\t\t\tif(!itemModel.isProductVirtual()) {\n\t \t\t\t\t\tcartItem.setQuantity(cartItem.getQuantity() + item.getQuantity());\n \t\t\t\t\t}\n \t\t\t\t\tduplicateFound = true;\n \t\t\t\t\tbreak;\n \t\t\t\t}\n \t\t\t}\n \t\t}\n \t}\n } \n \n if(!duplicateFound) {\n \tcartModel.getLineItems().add( itemModel );\n }\n \n saveShoppingCart( cartModel );\n\n //refresh cart\n cartModel = shoppingCartService.getById(cartModel.getId(), store);\n\n shoppingCartCalculationService.calculate( cartModel, store, language );\n \n ReadableShoppingCartPopulator readableShoppingCart = new ReadableShoppingCartPopulator();\n \n readableShoppingCart.setImageUtils(imageUtils);\n readableShoppingCart.setPricingService(pricingService);\n readableShoppingCart.setProductAttributeService(productAttributeService);\n readableShoppingCart.setShoppingCartCalculationService(shoppingCartCalculationService);\n \n ReadableShoppingCart readableCart = new ReadableShoppingCart();\n \n readableShoppingCart.populate(cartModel, readableCart, store, language);\n\n\t\t\n\t\treturn readableCart;\n\t\t\n\t}\n\n\tprivate ReadableShoppingCart modifyCart(ShoppingCart cartModel, PersistableShoppingCartItem item, MerchantStore store,\n\t\t\tLanguage language) throws Exception {\n\t\t\n\t\t\n\t\tcom.salesmanager.core.model.shoppingcart.ShoppingCartItem itemModel = createCartItem(cartModel, item, store);\n\n boolean itemModified = false;\n //check if existing product\n \tSet<com.salesmanager.core.model.shoppingcart.ShoppingCartItem> items = cartModel.getLineItems();\n \t//com.salesmanager.core.model.shoppingcart.ShoppingCartItem affectedItem = null;\n \tif(!CollectionUtils.isEmpty(items)) {\n \t\tSet<com.salesmanager.core.model.shoppingcart.ShoppingCartItem> newItems = new HashSet<com.salesmanager.core.model.shoppingcart.ShoppingCartItem>();\n \t\tSet<com.salesmanager.core.model.shoppingcart.ShoppingCartItem> removeItems = new HashSet<com.salesmanager.core.model.shoppingcart.ShoppingCartItem>();\n\t \tfor(com.salesmanager.core.model.shoppingcart.ShoppingCartItem anItem : items) {//take care of existing product\n\t \t\tif(itemModel.getProduct().getId().longValue() == anItem.getProduct().getId()) {\n\t \t\t\tif(item.getQuantity()==0) {//left aside item to be removed\n\t \t\t\t\t//don't add it to new list of item\n\t \t\t\t\tremoveItems.add(anItem);\n\t \t\t\t} else {\n\t \t\t\t\t//new quantity\n\t \t\t\t\tanItem.setQuantity(item.getQuantity());\n\t \t\t\t\tnewItems.add(anItem);\n\t \t\t\t}\n\t \t\t\titemModified = true;\n\t \t\t} else {\n\t \t\t\tnewItems.add(anItem);\n\t \t\t}\n\t \t}\n\t \t\n\t \tif(!removeItems.isEmpty()) {\n\t \t\tfor(com.salesmanager.core.model.shoppingcart.ShoppingCartItem emptyItem : removeItems) {\n\t \t\t\tshoppingCartService.deleteShoppingCartItem(emptyItem.getId());\n\t \t\t}\n\t \t\t\n\t \t}\n\t \t\n\t \tif(!itemModified) {\n\t \t newItems.add(itemModel);\n\t \t}\n\t \t\n\t \tif(newItems.isEmpty()) {\n\t \t\tnewItems = null;\n\t \t}\n\t \t\n\t \tcartModel.setLineItems(newItems);\n \t} else {\n \t//new item\n if(item.getQuantity() > 0) {\n cartModel.getLineItems().add( itemModel );\n }\n \t}\n\n \t//if cart items are null just return cart with no items\n\n saveShoppingCart( cartModel );\n\n //refresh cart\n cartModel = shoppingCartService.getById(cartModel.getId(), store);\n \n if(cartModel==null) {\n \treturn null;\n }\n\n shoppingCartCalculationService.calculate( cartModel, store, language );\n \n ReadableShoppingCartPopulator readableShoppingCart = new ReadableShoppingCartPopulator();\n \n readableShoppingCart.setImageUtils(imageUtils);\n readableShoppingCart.setPricingService(pricingService);\n readableShoppingCart.setProductAttributeService(productAttributeService);\n readableShoppingCart.setShoppingCartCalculationService(shoppingCartCalculationService);\n \n ReadableShoppingCart readableCart = new ReadableShoppingCart();\n \n readableShoppingCart.populate(cartModel, readableCart, store, language);\n\n\t\t\n\t\treturn readableCart;\n\t\t\n\t}\n\n\t@Override\n\tpublic ReadableShoppingCart addToCart(Customer customer, PersistableShoppingCartItem item, MerchantStore store,\n\t\t\tLanguage language) throws Exception {\n\t\t\n\t\tValidate.notNull(customer,\"Customer cannot be null\");\n\t\tValidate.notNull(customer.getId(),\"Customer.id cannot be null or empty\");\n\t\tif(item.getQuantity() < 1) item.setQuantity(1);\n\t\t//Check if customer has an existing shopping cart\n\t\tShoppingCart cartModel = shoppingCartService.getByCustomer(customer);\n\t\t\n\t\t//if cart does not exist create a new one\n\t\tif(cartModel==null) {\n\t\t\tcartModel = new ShoppingCart();\n\t\t\tcartModel.setCustomerId(customer.getId());\n\t\t\tcartModel.setMerchantStore(store);\n\t\t\tcartModel.setShoppingCartCode(uniqueShoppingCartCode());\n\t\t}\n\t\t\n\t\treturn readableShoppingCart(cartModel,item,store,language);\n\t}\n\t\n\t@Override\n\tpublic ReadableShoppingCart modifyCart(String cartCode, PersistableShoppingCartItem item, MerchantStore store,\n\t\t\tLanguage language) throws Exception {\n\n\t\tValidate.notNull(cartCode,\"PString cart code cannot be null\");\n\t\tValidate.notNull(item,\"PersistableShoppingCartItem cannot be null\");\n\t\t\n\t\tShoppingCart cartModel = this.getCartModel(cartCode, store);\n\n\t\treturn modifyCart(cartModel,item, store, language);\n\t\t\n\t\t\n\t}\n\t\n\tprivate void saveShoppingCart(ShoppingCart shoppingCart) throws Exception {\n\t\tshoppingCartService.save(shoppingCart);\n\t}\n\t\n\tprivate String uniqueShoppingCartCode() {\n\t\treturn UUID.randomUUID().toString().replaceAll( \"-\", \"\" );\n\t}\n\n\t@Override\n\tpublic ReadableShoppingCart getById(Long shoppingCartId, MerchantStore store, Language language) throws Exception {\n\n\t\tShoppingCart cart = shoppingCartService.getById(shoppingCartId);\n\t\t\n\t\tReadableShoppingCart readableCart = null;\n\t\t\n\t\tif(cart != null) {\n\t\t\t\n\t ReadableShoppingCartPopulator readableShoppingCart = new ReadableShoppingCartPopulator();\n\t \n\t readableShoppingCart.setImageUtils(imageUtils);\n\t readableShoppingCart.setPricingService(pricingService);\n\t readableShoppingCart.setProductAttributeService(productAttributeService);\n\t readableShoppingCart.setShoppingCartCalculationService(shoppingCartCalculationService);\n\n\t readableShoppingCart.populate(cart, readableCart, store, language);\n\t\t\t\n\t\t\t\n\t\t}\n\t\t\n\t\treturn readableCart;\n\t}\n\n\t@Override\n\tpublic ShoppingCart getShoppingCartModel(Long id, MerchantStore store) throws Exception {\n\t\treturn shoppingCartService.getById(id);\n\t}\n\n\t@Override\n\tpublic ReadableShoppingCart getByCode(String code, MerchantStore store, Language language) throws Exception {\n\t\t\n\t\tShoppingCart cart = shoppingCartService.getByCode(code, store);\n\t\t\n\t\tReadableShoppingCart readableCart = null;\n\t\t\n\t\tif(cart != null) {\n\t\t\t\n\t ReadableShoppingCartPopulator readableShoppingCart = new ReadableShoppingCartPopulator();\n\t \n\t readableShoppingCart.setImageUtils(imageUtils);\n\t readableShoppingCart.setPricingService(pricingService);\n\t readableShoppingCart.setProductAttributeService(productAttributeService);\n\t readableShoppingCart.setShoppingCartCalculationService(shoppingCartCalculationService);\n\n\t readableCart = readableShoppingCart.populate(cart, null, store, language);\n\t\t\t\n\t\t\t\n\t\t}\n\t\t\n\t\treturn readableCart;\n\t\t\n\t}\n\n}\n\n", "input": "", "output": "【漏洞类型】潜在的Negative Charge漏洞\n【严重性】Medium\n【漏洞摘要】此代码存在潜在的Negative Charge漏洞。在`addItemsToShoppingCart`和`createCartItem`方法中,代码没有验证`ShoppingCartItem`的`Quantity`属性是否为正数,允许用户在购物车中添加负数或零的物品数量。例如,在`addItemsToShoppingCart`方法中,代码仅在`shoppingCartItems`为空时将`item`的`Quantity`设置为1,但在其他情况下,`item`的`Quantity`可能为负数或零,这可能导致在计算购物车总价时出现错误。\n【关键代码模式】`ShoppingCartItem`的`Quantity`属性未被验证为正数。\n【修复方法说明】修复方法通过在`addItemsToShoppingCart`和`createCartItem`方法中添加对`Quantity`属性的验证,确保其始终为正数。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/libopensc/pkcs15-oberthur.c\n### 代码内容:\n#ifdef HAVE_CONFIG_H\n#include <config.h>\n#endif\n\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n#include \"../common/compat_strlcpy.h\"\n\n#include \"pkcs15.h\"\n#include \"log.h\"\n#include \"asn1.h\"\n#include \"internal.h\"\n\n#ifdef ENABLE_OPENSSL\n#include <openssl/bio.h>\n#include <openssl/crypto.h>\n#include <openssl/x509.h>\n#include <openssl/x509v3.h>\n#endif\n\n#define OBERTHUR_ATTR_MODIFIABLE\t0x0001\n#define OBERTHUR_ATTR_TRUSTED\t\t0x0002\n#define OBERTHUR_ATTR_LOCAL\t\t0x0004\n#define OBERTHUR_ATTR_ENCRYPT\t\t0x0008\n#define OBERTHUR_ATTR_DECRYPT\t\t0x0010\n#define OBERTHUR_ATTR_SIGN\t\t0x0020\n#define OBERTHUR_ATTR_VERIFY\t\t0x0040\n#define OBERTHUR_ATTR_RSIGN\t\t0x0080\n#define OBERTHUR_ATTR_RVERIFY\t\t0x0100\n#define OBERTHUR_ATTR_WRAP\t\t0x0200\n#define OBERTHUR_ATTR_UNWRAP\t\t0x0400\n#define OBERTHUR_ATTR_DERIVE\t\t0x0800\n\n#define USAGE_PRV_ENC\t(SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_DECRYPT |\\\n\t\t\t SC_PKCS15_PRKEY_USAGE_WRAP | SC_PKCS15_PRKEY_USAGE_UNWRAP)\n#define USAGE_PRV_AUT\t SC_PKCS15_PRKEY_USAGE_SIGN\n#define USAGE_PRV_SIGN\t(SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION)\n#define USAGE_PUB_ENC\t(SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP)\n#define USAGE_PUB_AUT\t SC_PKCS15_PRKEY_USAGE_VERIFY\n#define USAGE_PUB_SIGN\t(SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER)\n\n#define PIN_DOMAIN_LABEL\t\"SCM\"\nconst unsigned char PinDomainID[3] = {0x53, 0x43, 0x4D};\n\n#define AWP_PIN_DF\t\t\"3F005011\"\n#define AWP_TOKEN_INFO\t\t\"3F0050111000\"\n#define AWP_PUK_FILE\t\t\"3F0050112000\"\n#define AWP_CONTAINERS_MS\t\"3F0050113000\"\n#define AWP_OBJECTS_LIST_PUB\t\"3F0050114000\"\n#define AWP_OBJECTS_LIST_PRV\t\"3F0050115000\"\n#define AWP_OBJECTS_DF_PUB\t\"3F0050119001\"\n#define AWP_OBJECTS_DF_PRV\t\"3F0050119002\"\n#define AWP_BASE_RSA_PRV\t\"3F00501190023000\"\n#define AWP_BASE_RSA_PUB\t\"3F00501190011000\"\n#define AWP_BASE_CERTIFICATE\t\"3F00501190012000\"\n\n#define BASE_ID_PUB_RSA 0x10\n#define BASE_ID_CERT\t 0x20\n#define BASE_ID_PRV_RSA 0x30\n#define BASE_ID_PRV_DES 0x40\n#define BASE_ID_PUB_DATA 0x50\n#define BASE_ID_PRV_DATA 0x60\n#define BASE_ID_PUB_DES 0x70\n\nstatic int sc_pkcs15emu_oberthur_add_prvkey(struct sc_pkcs15_card *, unsigned, unsigned);\nstatic int sc_pkcs15emu_oberthur_add_pubkey(struct sc_pkcs15_card *, unsigned, unsigned);\nstatic int sc_pkcs15emu_oberthur_add_cert(struct sc_pkcs15_card *, unsigned);\nstatic int sc_pkcs15emu_oberthur_add_data(struct sc_pkcs15_card *, unsigned, unsigned, int);\n\nstatic int sc_oberthur_parse_tokeninfo (struct sc_pkcs15_card *, unsigned char *, size_t, int);\nstatic int sc_oberthur_parse_containers (struct sc_pkcs15_card *, unsigned char *, size_t, int);\nstatic int sc_oberthur_parse_publicinfo (struct sc_pkcs15_card *, unsigned char *, size_t, int);\nstatic int sc_oberthur_parse_privateinfo (struct sc_pkcs15_card *, unsigned char *, size_t, int);\n\nstatic int sc_awp_parse_df(struct sc_pkcs15_card *, struct sc_pkcs15_df *);\nstatic void sc_awp_clear(struct sc_pkcs15_card *);\n\nstruct crypto_container {\n\tunsigned id_pub;\n\tunsigned id_prv;\n\tunsigned id_cert;\n};\n\nstruct container {\n\tchar uuid[37];\n\tstruct crypto_container exchange;\n\tstruct crypto_container sign;\n\n\tstruct container *next;\n\tstruct container *prev;\n};\n\nstruct container *Containers = NULL;\n\nstatic struct {\n\tconst char *name;\n\tconst char *path;\n\tunsigned char *content;\n\tsize_t len;\n\tint (*parser)(struct sc_pkcs15_card *, unsigned char *, size_t, int);\n\tint postpone_allowed;\n} oberthur_infos[] = {\n\t/* Never change the following order */\n\t{ \"Token info\",\t\t\tAWP_TOKEN_INFO, \tNULL, 0, sc_oberthur_parse_tokeninfo, \t0},\n\t{ \"Containers MS\",\t\tAWP_CONTAINERS_MS, \tNULL, 0, sc_oberthur_parse_containers, \t0},\n\t{ \"Public objects list\",\tAWP_OBJECTS_LIST_PUB, \tNULL, 0, sc_oberthur_parse_publicinfo, \t0},\n\t{ \"Private objects list\",\tAWP_OBJECTS_LIST_PRV,\tNULL, 0, sc_oberthur_parse_privateinfo, 1},\n\t{ NULL, NULL, NULL, 0, NULL, 0}\n};\n\nstatic unsigned\nsc_oberthur_decode_usage(unsigned flags)\n{\n\tunsigned ret = 0;\n\n\tif (flags & OBERTHUR_ATTR_ENCRYPT)\n\t\tret |= SC_PKCS15_PRKEY_USAGE_ENCRYPT;\n\tif (flags & OBERTHUR_ATTR_DECRYPT)\n\t\tret |= SC_PKCS15_PRKEY_USAGE_DECRYPT;\n\tif (flags & OBERTHUR_ATTR_SIGN)\n\t\tret |= SC_PKCS15_PRKEY_USAGE_SIGN;\n\tif (flags & OBERTHUR_ATTR_RSIGN)\n\t\tret |= SC_PKCS15_PRKEY_USAGE_SIGNRECOVER;\n\tif (flags & OBERTHUR_ATTR_WRAP)\n\t\tret |= SC_PKCS15_PRKEY_USAGE_WRAP;\n\tif (flags & OBERTHUR_ATTR_UNWRAP)\n\t\tret |= SC_PKCS15_PRKEY_USAGE_UNWRAP;\n\tif (flags & OBERTHUR_ATTR_VERIFY)\n\t\tret |= SC_PKCS15_PRKEY_USAGE_VERIFY;\n\tif (flags & OBERTHUR_ATTR_RVERIFY)\n\t\tret |= SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER;\n\tif (flags & OBERTHUR_ATTR_DERIVE)\n\t\tret |= SC_PKCS15_PRKEY_USAGE_DERIVE;\n\treturn ret;\n}\n\nstatic int\nsc_oberthur_get_friends (unsigned int id, struct crypto_container *ccont)\n{\n\tstruct container *cont;\n\n\tfor (cont = Containers; cont; cont = cont->next) {\n\t\tif (cont->exchange.id_pub == id || cont->exchange.id_prv == id || cont->exchange.id_cert == id) {\n\t\t\tif (ccont)\n\t\t\t\tmemcpy(ccont, &cont->exchange, sizeof(struct crypto_container));\n\t\t\tbreak;\n\t\t}\n\n\t\tif (cont->sign.id_pub == id || cont->sign.id_prv == id || cont->sign.id_cert == id) {\n\t\t\tif (ccont)\n\t\t\t\tmemcpy(ccont, &cont->sign, sizeof(struct crypto_container));\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn cont ? 0 : SC_ERROR_TEMPLATE_NOT_FOUND;\n}\n\nstatic int\nsc_oberthur_get_certificate_authority(struct sc_pkcs15_der *der, int *out_authority)\n{\n#ifdef ENABLE_OPENSSL\n\tX509\t*x;\n\tBUF_MEM buf_mem;\n\tBIO *bio = NULL;\n\tBASIC_CONSTRAINTS *bs = NULL;\n\n\tif (!der)\n\t\treturn SC_ERROR_INVALID_ARGUMENTS;\n\n\tbuf_mem.data = malloc(der->len);\n\tif (!buf_mem.data)\n\t\treturn SC_ERROR_OUT_OF_MEMORY;\n\n\tmemcpy(buf_mem.data, der->value, der->len);\n\tbuf_mem.max = buf_mem.length = der->len;\n\n\tbio = BIO_new(BIO_s_mem());\n\tif (!bio) {\n\t\tfree(buf_mem.data);\n\t\treturn SC_ERROR_OUT_OF_MEMORY;\n\t}\n\n\tBIO_set_mem_buf(bio, &buf_mem, BIO_NOCLOSE);\n\tx = d2i_X509_bio(bio, 0);\n\tBIO_free(bio);\n\tif (!x)\n\t\treturn SC_ERROR_INVALID_DATA;\n\n\tbs = (BASIC_CONSTRAINTS *)X509_get_ext_d2i(x, NID_basic_constraints, NULL, NULL);\n\tif (out_authority)\n\t\t*out_authority = (bs && bs->ca);\n\n\tX509_free(x);\n\n\treturn SC_SUCCESS;\n#else\n\treturn SC_ERROR_NOT_SUPPORTED;\n#endif\n}\n\nstatic int\nsc_oberthur_read_file(struct sc_pkcs15_card *p15card, const char *in_path,\n\t\tunsigned char **out, size_t *out_len,\n\t\tint verify_pin)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tstruct sc_card *card = p15card->card;\n\tstruct sc_file *file = NULL;\n\tstruct sc_path path;\n\tsize_t sz;\n\tint rv;\n\n\tLOG_FUNC_CALLED(ctx);\n\tif (!in_path || !out || !out_len)\n\t\tLOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, \"Cannot read oberthur file\");\n\n\tsc_log(ctx, \"read file '%s'; verify_pin:%i\", in_path, verify_pin);\n\n\t*out = NULL;\n\t*out_len = 0;\n\n\tsc_format_path(in_path, &path);\n\trv = sc_select_file(card, &path, &file);\n\tif (rv != SC_SUCCESS) {\n\t\tsc_file_free(file);\n\t\tLOG_TEST_RET(ctx, rv, \"Cannot select oberthur file to read\");\n\t}\n\n\tif (file->ef_structure == SC_FILE_EF_TRANSPARENT)\n\t\tsz = file->size;\n\telse\n\t\tsz = (file->record_length + 2) * file->record_count;\n\n\t*out = calloc(sz, 1);\n\tif (*out == NULL) {\n\t\tsc_file_free(file);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, \"Cannot read oberthur file\");\n\t}\n\n\tif (file->ef_structure == SC_FILE_EF_TRANSPARENT) {\n\t\trv = sc_read_binary(card, 0, *out, sz, 0);\n\t}\n\telse\t{\n\t\tsize_t rec;\n\t\tsize_t offs = 0;\n\t\tsize_t rec_len = file->record_length;\n\n\t\tfor (rec = 1; ; rec++) {\n\t\t\tif (rec > file->record_count) {\n\t\t\t\trv = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\trv = sc_read_record(card, rec, *out + offs + 2, rec_len, SC_RECORD_BY_REC_NR);\n\t\t\tif (rv == SC_ERROR_RECORD_NOT_FOUND) {\n\t\t\t\trv = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (rv < 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\trec_len = rv;\n\n\t\t\t*(*out + offs) = 'R';\n\t\t\t*(*out + offs + 1) = rv;\n\n\t\t\toffs += rv + 2;\n\t\t}\n\n\t\tsz = offs;\n\t}\n\n\tsc_log(ctx, \"read oberthur file result %i\", rv);\n\tif (verify_pin && rv == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) {\n\t\tstruct sc_pkcs15_object *objs[0x10], *pin_obj = NULL;\n\t\tconst struct sc_acl_entry *acl = sc_file_get_acl_entry(file, SC_AC_OP_READ);\n\t\tint ii, nobjs;\n\n\t\tif (acl == NULL) {\n\t\t\tsc_file_free(file);\n\t\t\tfree(*out);\n\t\t\t*out = NULL;\n\t\t\tLOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA);\n\t\t}\n\n\t\tnobjs = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, 0x10);\n\t\tif (nobjs < 1) {\n\t\t\tsc_file_free(file);\n\t\t\tfree(*out);\n\t\t\t*out = NULL;\n\t\t\tLOG_TEST_RET(ctx, SC_ERROR_DATA_OBJECT_NOT_FOUND,\n\t\t\t\t\"Cannot read oberthur file: get AUTH objects error\");\n\t\t}\n\n\t\tfor (ii = 0; ii < nobjs; ii++) {\n\t\t\tstruct sc_pkcs15_auth_info *auth_info = (struct sc_pkcs15_auth_info *) objs[ii]->data;\n\t\t\tsc_log(ctx, \"compare PIN/ACL refs:%i/%i, method:%i/%i\",\n\t\t\t\tauth_info->attrs.pin.reference, acl->key_ref, auth_info->auth_method, acl->method);\n\t\t\tif (auth_info->attrs.pin.reference == (int)acl->key_ref && auth_info->auth_method == (unsigned)acl->method) {\n\t\t\t\tpin_obj = objs[ii];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!pin_obj || !pin_obj->content.value) {\n\t\t\trv = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED;\n\t\t}\n\t\telse {\n\t\t\trv = sc_pkcs15_verify_pin(p15card, pin_obj, pin_obj->content.value, pin_obj->content.len);\n\t\t\tif (!rv)\n\t\t\t\trv = sc_oberthur_read_file(p15card, in_path, out, out_len, 0);\n\t\t}\n\t}\n\n\tsc_file_free(file);\n\n\tif (rv < 0) {\n\t\tfree(*out);\n\t\t*out = NULL;\n\t\t*out_len = 0;\n\t}\n\n\t*out_len = sz;\n\n\tLOG_FUNC_RETURN(ctx, rv);\n}\n\nstatic int\nsc_oberthur_parse_tokeninfo (struct sc_pkcs15_card *p15card,\n\t\tunsigned char *buff, size_t len, int postpone_allowed)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tchar label[0x21];\n\tunsigned flags;\n\tint ii;\n\n\tLOG_FUNC_CALLED(ctx);\n\tif (!buff || len < 0x24)\n\t\tLOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, \"Cannot parse token info\");\n\n\tmemset(label, 0, sizeof(label));\n\n\tmemcpy(label, buff, 0x20);\n\tii = 0x20;\n\twhile (*(label + --ii)==' ' && ii)\n\t\t;\n\t*(label + ii + 1) = '\\0';\n\n\tflags = *(buff + 0x22) * 0x100 + *(buff + 0x23);\n\n\tset_string(&p15card->tokeninfo->label, label);\n\tset_string(&p15card->tokeninfo->manufacturer_id, \"Oberthur/OpenSC\");\n\n\tif (flags & 0x01)\n\t\tp15card->tokeninfo->flags |= SC_PKCS15_TOKEN_PRN_GENERATION;\n\n\tsc_log(ctx, \"label %s\", p15card->tokeninfo->label);\n\tsc_log(ctx, \"manufacturer_id %s\", p15card->tokeninfo->manufacturer_id);\n\n\tLOG_FUNC_RETURN(ctx, SC_SUCCESS);\n}\n\nstatic int\nsc_oberthur_parse_containers (struct sc_pkcs15_card *p15card,\n\t\tunsigned char *buff, size_t len, int postpone_allowed)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tsize_t offs;\n\n\tLOG_FUNC_CALLED(ctx);\n\n\twhile (Containers) {\n\t\tstruct container *next = Containers->next;\n\n\t\tfree (Containers);\n\t\tContainers = next;\n\t}\n\n\tfor (offs=0; offs + 2 + 2+2+2 + 2+2+2 + 2+36 <= len;) {\n\t\tstruct container *cont;\n\t\tunsigned char *ptr = buff + offs + 2;\n\n\t\tsc_log(ctx,\n\t\t \"parse contaniers offs:%\"SC_FORMAT_LEN_SIZE_T\"u, len:%\"SC_FORMAT_LEN_SIZE_T\"u\",\n\t\t offs, len);\n\t\tif (*(buff + offs) != 'R')\n\t\t\treturn SC_ERROR_INVALID_DATA;\n\n\t\tcont = (struct container *)calloc(sizeof(struct container), 1);\n\t\tif (!cont)\n\t\t\treturn SC_ERROR_OUT_OF_MEMORY;\n\n\t\tcont->exchange.id_pub = *ptr * 0x100 + *(ptr + 1); ptr += 2;\n\t\tcont->exchange.id_prv = *ptr * 0x100 + *(ptr + 1); ptr += 2;\n\t\tcont->exchange.id_cert = *ptr * 0x100 + *(ptr + 1); ptr += 2;\n\n\t\tcont->sign.id_pub = *ptr * 0x100 + *(ptr + 1); ptr += 2;\n\t\tcont->sign.id_prv = *ptr * 0x100 + *(ptr + 1); ptr += 2;\n\t\tcont->sign.id_cert = *ptr * 0x100 + *(ptr + 1); ptr += 2;\n\n\t\tmemcpy(cont->uuid, ptr + 2, 36);\n\t\tsc_log(ctx, \"UUID: %s; 0x%X, 0x%X, 0x%X\", cont->uuid,\n\t\t\t\tcont->exchange.id_pub, cont->exchange.id_prv, cont->exchange.id_cert);\n\n\t\tif (!Containers) {\n\t\t\tContainers = cont;\n\t\t}\n\t\telse {\n\t\t\tcont->next = Containers;\n\t\t\tContainers->prev = (void *)cont;\n\t\t\tContainers = cont;\n\t\t}\n\n\t\toffs += *(buff + offs + 1) + 2;\n\t}\n\n\tLOG_FUNC_RETURN(ctx, SC_SUCCESS);\n}\n\nstatic int\nsc_oberthur_parse_publicinfo (struct sc_pkcs15_card *p15card,\n\t\tunsigned char *buff, size_t len, int postpone_allowed)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tsize_t ii;\n\tint rv;\n\n\tLOG_FUNC_CALLED(ctx);\n\tfor (ii=0; ii+5<=len; ii+=5) {\n\t\tunsigned int file_id, size;\n\n\t\tif(*(buff+ii) != 0xFF)\n\t\t\tcontinue;\n\n\t\tfile_id = 0x100 * *(buff+ii + 1) + *(buff+ii + 2);\n\t\tsize = 0x100 * *(buff+ii + 3) + *(buff+ii + 4);\n\t\tsc_log(ctx, \"add public object(file-id:%04X,size:%X)\", file_id, size);\n\n\t\tswitch (*(buff+ii + 1)) {\n\t\tcase BASE_ID_PUB_RSA :\n\t\t\trv = sc_pkcs15emu_oberthur_add_pubkey(p15card, file_id, size);\n\t\t\tLOG_TEST_RET(ctx, rv, \"Cannot parse public key info\");\n\t\t\tbreak;\n\t\tcase BASE_ID_CERT :\n\t\t\trv = sc_pkcs15emu_oberthur_add_cert(p15card, file_id);\n\t\t\tLOG_TEST_RET(ctx, rv, \"Cannot parse certificate info\");\n\t\t\tbreak;\n\t\tcase BASE_ID_PUB_DES :\n\t\t\tbreak;\n\t\tcase BASE_ID_PUB_DATA :\n\t\t\trv = sc_pkcs15emu_oberthur_add_data(p15card, file_id, size, 0);\n\t\t\tLOG_TEST_RET(ctx, rv, \"Cannot parse data info\");\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Public object parse error\");\n\t\t}\n\t}\n\n\tLOG_FUNC_RETURN(ctx, SC_SUCCESS);\n}\n\nstatic int\nsc_oberthur_parse_privateinfo (struct sc_pkcs15_card *p15card,\n\t\tunsigned char *buff, size_t len, int postpone_allowed)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tsize_t ii;\n\tint rv;\n\tint no_more_private_keys = 0, no_more_private_data = 0;\n\n\tLOG_FUNC_CALLED(ctx);\n\n\tfor (ii=0; ii+5<=len; ii+=5) {\n\t\tunsigned int file_id, size;\n\n\t\tif(*(buff+ii) != 0xFF)\n\t\t\tcontinue;\n\n\t\tfile_id = 0x100 * *(buff+ii + 1) + *(buff+ii + 2);\n\t\tsize = 0x100 * *(buff+ii + 3) + *(buff+ii + 4);\n\t\tsc_log(ctx, \"add private object (file-id:%04X, size:%X)\", file_id, size);\n\n\t\tswitch (*(buff+ii + 1)) {\n\t\tcase BASE_ID_PRV_RSA :\n\t\t\tif (no_more_private_keys)\n\t\t\t\tbreak;\n\n\t\t\trv = sc_pkcs15emu_oberthur_add_prvkey(p15card, file_id, size);\n\t\t\tif (rv == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED && postpone_allowed) {\n\t\t\t\tstruct sc_path path;\n\n\t\t\t\tsc_log(ctx, \"postpone adding of the private keys\");\n\t\t\t\tsc_format_path(\"5011A5A5\", &path);\n\t\t\t\trv = sc_pkcs15_add_df(p15card, SC_PKCS15_PRKDF, &path);\n\t\t\t\tLOG_TEST_RET(ctx, rv, \"Add PrkDF error\");\n\t\t\t\tno_more_private_keys = 1;\n\t\t\t}\n\t\t\tLOG_TEST_RET(ctx, rv, \"Cannot parse private key info\");\n\t\t\tbreak;\n\t\tcase BASE_ID_PRV_DES :\n\t\t\tbreak;\n\t\tcase BASE_ID_PRV_DATA :\n\t\t\tsc_log(ctx, \"*(buff+ii + 1):%X\", *(buff+ii + 1));\n\t\t\tif (no_more_private_data)\n\t\t\t\tbreak;\n\n\t\t\trv = sc_pkcs15emu_oberthur_add_data(p15card, file_id, size, 1);\n\t\t\tif (rv == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED && postpone_allowed) {\n\t\t\t\tstruct sc_path path;\n\n\t\t\t\tsc_log(ctx, \"postpone adding of the private data\");\n\t\t\t\tsc_format_path(\"5011A6A6\", &path);\n\t\t\t\trv = sc_pkcs15_add_df(p15card, SC_PKCS15_DODF, &path);\n\t\t\t\tLOG_TEST_RET(ctx, rv, \"Add DODF error\");\n\t\t\t\tno_more_private_data = 1;\n\t\t\t}\n\t\t\tLOG_TEST_RET(ctx, rv, \"Cannot parse private data info\");\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Private object parse error\");\n\t\t}\n\t}\n\n\tLOG_FUNC_RETURN(ctx, SC_SUCCESS);\n}\n\n/* Public key info:\n * \tflags:2,\n * \tCN(len:2,value:<variable length>),\n * \tID(len:2,value:(SHA1 value)),\n * \tStartDate(Ascii:8)\n * \tEndDate(Ascii:8)\n * \t??(0x00:2)\n */\nstatic int\nsc_pkcs15emu_oberthur_add_pubkey(struct sc_pkcs15_card *p15card,\n\t\tunsigned int file_id, unsigned int size)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tstruct sc_pkcs15_pubkey_info key_info;\n\tstruct sc_pkcs15_object key_obj;\n\tchar ch_tmp[0x100];\n\tunsigned char *info_blob = NULL;\n\tsize_t len, info_len, offs;\n\tunsigned flags;\n\tint rv;\n\n\tLOG_FUNC_CALLED(ctx);\n\tsc_log(ctx, \"public key(file-id:%04X,size:%X)\", file_id, size);\n\n\tmemset(&key_info, 0, sizeof(key_info));\n\tmemset(&key_obj, 0, sizeof(key_obj));\n\n\tsnprintf(ch_tmp, sizeof(ch_tmp), \"%s%04X\", AWP_OBJECTS_DF_PUB, file_id | 0x100);\n\trv = sc_oberthur_read_file(p15card, ch_tmp, &info_blob, &info_len, 1);\n\tLOG_TEST_RET(ctx, rv, \"Failed to add public key: read oberthur file error\");\n\n\t/* Flags */\n\toffs = 2;\n\tif (offs > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add public key: no 'tag'\");\n\t}\n\tflags = *(info_blob + 0) * 0x100 + *(info_blob + 1);\n\tkey_info.usage = sc_oberthur_decode_usage(flags);\n\tif (flags & OBERTHUR_ATTR_MODIFIABLE)\n\t\tkey_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE;\n\tsc_log(ctx, \"Public key key-usage:%04X\", key_info.usage);\n\n\t/* Label */\n\tif (offs + 2 > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add public key: no 'Label'\");\n\t}\n\tlen = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100;\n\tif (len) {\n\t\tif (len > sizeof(key_obj.label) - 1)\n\t\t\tlen = sizeof(key_obj.label) - 1;\n\t\tmemcpy(key_obj.label, info_blob + offs + 2, len);\n\t}\n\toffs += 2 + len;\n\n\t/* ID */\n\tif (offs > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add public key: no 'ID'\");\n\t}\n\tlen = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100;\n\tif (!len || len > sizeof(key_info.id.value)) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, \"Failed to add public key: invalid 'ID' length\");\n\t}\n\tmemcpy(key_info.id.value, info_blob + offs + 2, len);\n\tkey_info.id.len = len;\n\n\tfree(info_blob);\n\n\t/* Ignore Start/End dates */\n\n\tsnprintf(ch_tmp, sizeof(ch_tmp), \"%s%04X\", AWP_OBJECTS_DF_PUB, file_id);\n\tsc_format_path(ch_tmp, &key_info.path);\n\n\tkey_info.native = 1;\n\tkey_info.key_reference = file_id & 0xFF;\n\tkey_info.modulus_length = size;\n\n\trv = sc_pkcs15emu_add_rsa_pubkey(p15card, &key_obj, &key_info);\n\n\tLOG_FUNC_RETURN(ctx, rv);\n}\n\n/* Certificate info:\n * \tflags:2,\n * \tLabel(len:2,value:),\n * \tID(len:2,value:(SHA1 value)),\n * \tSubject in ASN.1(len:2,value:)\n * \tIssuer in ASN.1(len:2,value:)\n * \tSerial encoded in LV or ASN.1\tFIXME\n */\nstatic int\nsc_pkcs15emu_oberthur_add_cert(struct sc_pkcs15_card *p15card, unsigned int file_id)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tstruct sc_pkcs15_cert_info cinfo;\n\tstruct sc_pkcs15_object cobj;\n\tunsigned char *info_blob, *cert_blob;\n\tsize_t info_len, cert_len, len, offs;\n\tunsigned flags;\n\tint rv;\n\tchar ch_tmp[0x20];\n\n\tLOG_FUNC_CALLED(ctx);\n\tsc_log(ctx, \"add certificate(file-id:%04X)\", file_id);\n\n\tmemset(&cinfo, 0, sizeof(cinfo));\n\tmemset(&cobj, 0, sizeof(cobj));\n\n\tsnprintf(ch_tmp, sizeof(ch_tmp), \"%s%04X\", AWP_OBJECTS_DF_PUB, file_id | 0x100);\n\trv = sc_oberthur_read_file(p15card, ch_tmp, &info_blob, &info_len, 1);\n\tLOG_TEST_RET(ctx, rv, \"Failed to add certificate: read oberthur file error\");\n\n\tif (info_len < 2)\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add certificate: no 'tag'\");\n\tflags = *(info_blob + 0) * 0x100 + *(info_blob + 1);\n\toffs = 2;\n\n\t/* Label */\n\tif (offs + 2 > info_len)\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add certificate: no 'CN'\");\n\tlen = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100;\n\tif (len) {\n\t\tif (len > sizeof(cobj.label) - 1)\n\t\t\tlen = sizeof(cobj.label) - 1;\n\t\tmemcpy(cobj.label, info_blob + offs + 2, len);\n\t}\n\toffs += 2 + len;\n\n\t/* ID */\n\tif (offs > info_len)\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add certificate: no 'ID'\");\n\tlen = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100;\n\tif (len > sizeof(cinfo.id.value))\n\t\tLOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, \"Failed to add certificate: invalid 'ID' length\");\n\tmemcpy(cinfo.id.value, info_blob + offs + 2, len);\n\tcinfo.id.len = len;\n\n\t/* Ignore subject, issuer and serial */\n\n\tsnprintf(ch_tmp, sizeof(ch_tmp), \"%s%04X\", AWP_OBJECTS_DF_PUB, file_id);\n\tsc_format_path(ch_tmp, &cinfo.path);\n\trv = sc_oberthur_read_file(p15card, ch_tmp, &cert_blob, &cert_len, 1);\n\tLOG_TEST_RET(ctx, rv, \"Failed to add certificate: read certificate error\");\n\n\tcinfo.value.value = cert_blob;\n\tcinfo.value.len = cert_len;\n\n\trv = sc_oberthur_get_certificate_authority(&cinfo.value, &cinfo.authority);\n\tLOG_TEST_RET(ctx, rv, \"Failed to add certificate: get certificate attributes error\");\n\n\tif (flags & OBERTHUR_ATTR_MODIFIABLE)\n\t\tcobj.flags |= SC_PKCS15_CO_FLAG_MODIFIABLE;\n\n\trv = sc_pkcs15emu_add_x509_cert(p15card, &cobj, &cinfo);\n\n\tLOG_FUNC_RETURN(p15card->card->ctx, rv);\n}\n\n/* Private key info:\n * \tflags:2,\n * \tCN(len:2,value:),\n * \tID(len:2,value:(SHA1 value)),\n * \tStartDate(Ascii:8)\n * \tEndDate(Ascii:8)\n * \tSubject in ASN.1(len:2,value:)\n * \tmodulus(value:)\n *\texponent(length:1, value:3)\n */\nstatic int\nsc_pkcs15emu_oberthur_add_prvkey(struct sc_pkcs15_card *p15card,\n\t\tunsigned int file_id, unsigned int size)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tstruct sc_pkcs15_prkey_info kinfo;\n\tstruct sc_pkcs15_object kobj;\n\tstruct crypto_container ccont;\n\tunsigned char *info_blob = NULL;\n\tsize_t info_len = 0;\n\tunsigned flags;\n\tsize_t offs, len;\n\tchar ch_tmp[0x100];\n\tint rv;\n\n\tLOG_FUNC_CALLED(ctx);\n\tsc_log(ctx, \"add private key(file-id:%04X,size:%04X)\", file_id, size);\n\n\tmemset(&kinfo, 0, sizeof(kinfo));\n\tmemset(&kobj, 0, sizeof(kobj));\n\tmemset(&ccont, 0, sizeof(ccont));\n\n\trv = sc_oberthur_get_friends (file_id, &ccont);\n\tLOG_TEST_RET(ctx, rv, \"Failed to add private key: get friends error\");\n\n\tif (ccont.id_cert) {\n\t\tstruct sc_pkcs15_object *objs[32];\n\t\tint ii;\n\n\t\tsc_log(ctx, \"friend certificate %04X\", ccont.id_cert);\n\t\trv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_CERT_X509, objs, 32);\n\t\tLOG_TEST_RET(ctx, rv, \"Failed to add private key: get certificates error\");\n\n\t\tfor (ii=0; ii<rv; ii++) {\n\t\t\tstruct sc_pkcs15_cert_info *cert = (struct sc_pkcs15_cert_info *)objs[ii]->data;\n\t\t\tstruct sc_path path = cert->path;\n\t\t\tunsigned int id = path.value[path.len - 2] * 0x100 + path.value[path.len - 1];\n\n\t\t\tif (id == ccont.id_cert) {\n\t\t\t\tstrlcpy(kobj.label, objs[ii]->label, sizeof(kobj.label));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (ii == rv)\n\t\t\tLOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_PROFILE, \"Failed to add private key: friend not found\");\n\t}\n\n\tsnprintf(ch_tmp, sizeof(ch_tmp), \"%s%04X\", AWP_OBJECTS_DF_PRV, file_id | 0x100);\n\trv = sc_oberthur_read_file(p15card, ch_tmp, &info_blob, &info_len, 1);\n\tLOG_TEST_RET(ctx, rv, \"Failed to add private key: read oberthur file error\");\n\n\tif (info_len < 2)\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add private key: no 'tag'\");\n\tflags = *(info_blob + 0) * 0x100 + *(info_blob + 1);\n\toffs = 2;\n\n\t/* CN */\n\tif (offs > info_len)\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add private key: no 'CN'\");\n\tlen = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100;\n\tif (len && !strlen(kobj.label)) {\n\t\tif (len > sizeof(kobj.label) - 1)\n\t\t\tlen = sizeof(kobj.label) - 1;\n\t\tstrncpy(kobj.label, (char *)(info_blob + offs + 2), len);\n\t}\n\toffs += 2 + len;\n\n\t/* ID */\n\tif (offs > info_len)\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add private key: no 'ID'\");\n\tlen = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100;\n\tif (!len)\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add private key: zero length ID\");\n\telse if (len > sizeof(kinfo.id.value))\n\t\tLOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, \"Failed to add private key: invalid ID length\");\n\tmemcpy(kinfo.id.value, info_blob + offs + 2, len);\n\tkinfo.id.len = len;\n\toffs += 2 + len;\n\n\t/* Ignore Start/End dates */\n\toffs += 16;\n\n\t/* Subject encoded in ASN1 */\n\tif (offs > info_len)\n\t\treturn SC_ERROR_UNKNOWN_DATA_RECEIVED;\n\tlen = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100;\n\tif (len) {\n\t\tkinfo.subject.value = malloc(len);\n\t\tif (!kinfo.subject.value)\n\t\t\tLOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, \"Failed to add private key: memory allocation error\");\n\t\tkinfo.subject.len = len;\n\t\tmemcpy(kinfo.subject.value, info_blob + offs + 2, len);\n\t}\n\n\t/* Modulus and exponent are ignored */\n\n\tsnprintf(ch_tmp, sizeof(ch_tmp), \"%s%04X\", AWP_OBJECTS_DF_PRV, file_id);\n\tsc_format_path(ch_tmp, &kinfo.path);\n\tsc_log(ctx, \"Private key info path %s\", ch_tmp);\n\n\tkinfo.modulus_length\t= size;\n\tkinfo.native\t\t= 1;\n\tkinfo.key_reference\t = file_id & 0xFF;\n\n\tkinfo.usage = sc_oberthur_decode_usage(flags);\n\tkobj.flags = SC_PKCS15_CO_FLAG_PRIVATE;\n\tif (flags & OBERTHUR_ATTR_MODIFIABLE)\n\t\tkobj.flags |= SC_PKCS15_CO_FLAG_MODIFIABLE;\n\n\tkobj.auth_id.len = sizeof(PinDomainID) > sizeof(kobj.auth_id.value)\n\t\t\t? sizeof(kobj.auth_id.value) : sizeof(PinDomainID);\n\tmemcpy(kobj.auth_id.value, PinDomainID, kobj.auth_id.len);\n\n\tsc_log(ctx, \"Parsed private key(reference:%i,usage:%X,flags:%X)\", kinfo.key_reference, kinfo.usage, kobj.flags);\n\n\trv = sc_pkcs15emu_add_rsa_prkey(p15card, &kobj, &kinfo);\n\tLOG_FUNC_RETURN(ctx, rv);\n}\n\nstatic int\nsc_pkcs15emu_oberthur_add_data(struct sc_pkcs15_card *p15card,\n\t\tunsigned int file_id, unsigned int size, int private)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tstruct sc_pkcs15_data_info dinfo;\n\tstruct sc_pkcs15_object dobj;\n\tunsigned flags;\n\tunsigned char *info_blob = NULL, *label = NULL, *app = NULL, *oid = NULL;\n\tsize_t info_len, label_len, app_len, oid_len, offs;\n\tchar ch_tmp[0x100];\n\tint rv;\n\n\tSC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE);\n\tsc_log(ctx, \"Add data(file-id:%04X,size:%i,is-private:%i)\", file_id, size, private);\n\tmemset(&dinfo, 0, sizeof(dinfo));\n\tmemset(&dobj, 0, sizeof(dobj));\n\n\tsnprintf(ch_tmp, sizeof(ch_tmp), \"%s%04X\", private ? AWP_OBJECTS_DF_PRV : AWP_OBJECTS_DF_PUB, file_id | 0x100);\n\n\trv = sc_oberthur_read_file(p15card, ch_tmp, &info_blob, &info_len, 1);\n\tLOG_TEST_RET(ctx, rv, \"Failed to add data: read oberthur file error\");\n\n\tif (info_len < 2) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add certificate: no 'tag'\");\n\t}\n\tflags = *(info_blob + 0) * 0x100 + *(info_blob + 1);\n\toffs = 2;\n\n\t/* Label */\n\tif (offs + 2 > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add data: no 'label'\");\n\t}\n\tlabel = info_blob + offs + 2;\n\tlabel_len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100;\n\tif (offs + 2 + label_len > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Invalid length of 'label' received\");\n\t}\n\tif (label_len > sizeof(dobj.label) - 1)\n\t\tlabel_len = sizeof(dobj.label) - 1;\n\toffs += 2 + *(info_blob + offs + 1);\n\n\t/* Application */\n\tif (offs > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add data: no 'application'\");\n\t}\n\tapp = info_blob + offs + 2;\n\tapp_len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100;\n\tif (app_len > sizeof(dinfo.app_label) - 1)\n\t\tapp_len = sizeof(dinfo.app_label) - 1;\n\toffs += 2 + app_len;\n\n\t/* OID encode like DER(ASN.1(oid)) */\n\tif (offs + 1 > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add data: no 'OID'\");\n\t}\n\toid_len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100;\n\tif (oid_len) {\n\t\toid = info_blob + offs + 2;\n\t\tif (*oid != 0x06 || (*(oid + 1) != oid_len - 2)) {\n\t\t\tfree(info_blob);\n\t\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add data: invalid 'OID' format\");\n\t\t}\n\t\toid += 2;\n\t\toid_len -= 2;\n\t}\n\n\tsnprintf(ch_tmp, sizeof(ch_tmp), \"%s%04X\", private ? AWP_OBJECTS_DF_PRV : AWP_OBJECTS_DF_PUB, file_id);\n\n\tsc_format_path(ch_tmp, &dinfo.path);\n\n\tmemcpy(dobj.label, label, label_len);\n\tmemcpy(dinfo.app_label, app, app_len);\n\tif (oid_len)\n\t\tsc_asn1_decode_object_id(oid, oid_len, &dinfo.app_oid);\n\n\tif (flags & OBERTHUR_ATTR_MODIFIABLE)\n\t\tdobj.flags |= SC_PKCS15_CO_FLAG_MODIFIABLE;\n\n\tif (private) {\n\t\tdobj.auth_id.len = sizeof(PinDomainID) > sizeof(dobj.auth_id.value)\n\t\t\t\t? sizeof(dobj.auth_id.value) : sizeof(PinDomainID);\n\t\tmemcpy(dobj.auth_id.value, PinDomainID, dobj.auth_id.len);\n\n\t\tdobj.flags |= SC_PKCS15_CO_FLAG_PRIVATE;\n\t}\n\n\trv = sc_pkcs15emu_add_data_object(p15card, &dobj, &dinfo);\n\n\tfree(info_blob);\n\tLOG_FUNC_RETURN(p15card->card->ctx, rv);\n}\n\nstatic int\nsc_pkcs15emu_oberthur_init(struct sc_pkcs15_card * p15card)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tstruct sc_pkcs15_auth_info auth_info;\n\tstruct sc_pkcs15_object obj;\n\tstruct sc_card *card = p15card->card;\n\tstruct sc_path path;\n\tint rv, ii, tries_left;\n\tchar serial[0x10];\n\tunsigned char sopin_reference = 0x04;\n\n\tSC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);\n\tsc_bin_to_hex(card->serialnr.value, card->serialnr.len, serial, sizeof(serial), 0);\n\tset_string(&p15card->tokeninfo->serial_number, serial);\n\n\tp15card->ops.parse_df = sc_awp_parse_df;\n\tp15card->ops.clear = sc_awp_clear;\n\n\tsc_log(ctx, \"Oberthur init: serial %s\", p15card->tokeninfo->serial_number);\n\n\tsc_format_path(AWP_PIN_DF, &path);\n\trv = sc_select_file(card, &path, NULL);\n\tLOG_TEST_RET(ctx, rv, \"Oberthur init failed: cannot select PIN dir\");\n\n\ttries_left = -1;\n\trv = sc_verify(card, SC_AC_CHV, sopin_reference, (unsigned char *)\"\", 0, &tries_left);\n\tif (rv && rv != SC_ERROR_PIN_CODE_INCORRECT) {\n\t\tsopin_reference = 0x84;\n\t\trv = sc_verify(card, SC_AC_CHV, sopin_reference, (unsigned char *)\"\", 0, &tries_left);\n\t}\n\tif (rv && rv != SC_ERROR_PIN_CODE_INCORRECT)\n\t\tLOG_TEST_RET(ctx, rv, \"Invalid state of SO-PIN\");\n\n\t/* add PIN */\n\tmemset(&auth_info, 0, sizeof(auth_info));\n\tmemset(&obj, 0, sizeof(obj));\n\n\tauth_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN;\n\tauth_info.auth_method\t= SC_AC_CHV;\n\tauth_info.auth_id.len = 1;\n\tauth_info.auth_id.value[0] = 0xFF;\n\tauth_info.attrs.pin.min_length\t\t= 4;\n\tauth_info.attrs.pin.max_length\t\t= 64;\n\tauth_info.attrs.pin.stored_length\t= 64;\n\tauth_info.attrs.pin.type\t\t= SC_PKCS15_PIN_TYPE_ASCII_NUMERIC;\n\tauth_info.attrs.pin.reference\t\t= sopin_reference;\n\tauth_info.attrs.pin.pad_char\t\t= 0xFF;\n\tauth_info.attrs.pin.flags\t\t= SC_PKCS15_PIN_FLAG_CASE_SENSITIVE\n\t\t\t\t| SC_PKCS15_PIN_FLAG_INITIALIZED\n\t\t\t\t| SC_PKCS15_PIN_FLAG_NEEDS_PADDING\n\t\t\t\t| SC_PKCS15_PIN_FLAG_SO_PIN;\n\tauth_info.tries_left\t\t= tries_left;\n\tauth_info.logged_in = SC_PIN_STATE_UNKNOWN;\n\n\tstrncpy(obj.label, \"SO PIN\", SC_PKCS15_MAX_LABEL_SIZE-1);\n\tobj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE;\n\n\tsc_log(ctx, \"Add PIN(%s,auth_id:%s,reference:%i)\", obj.label,\n\t\t\tsc_pkcs15_print_id(&auth_info.auth_id), auth_info.attrs.pin.reference);\n\trv = sc_pkcs15emu_add_pin_obj(p15card, &obj, &auth_info);\n\tLOG_TEST_RET(ctx, rv, \"Oberthur init failed: cannot add PIN object\");\n\n\ttries_left = -1;\n\trv = sc_verify(card, SC_AC_CHV, 0x81, (unsigned char *)\"\", 0, &tries_left);\n\tif (rv == SC_ERROR_PIN_CODE_INCORRECT) {\n\t\t/* add PIN */\n\t\tmemset(&auth_info, 0, sizeof(auth_info));\n\t\tmemset(&obj, 0, sizeof(obj));\n\n\t\tauth_info.auth_id.len = sizeof(PinDomainID) > sizeof(auth_info.auth_id.value)\n\t\t\t\t? sizeof(auth_info.auth_id.value) : sizeof(PinDomainID);\n\t\tmemcpy(auth_info.auth_id.value, PinDomainID, auth_info.auth_id.len);\n\t\tauth_info.auth_method\t= SC_AC_CHV;\n\n\t\tauth_info.attrs.pin.min_length\t\t= 4;\n\t\tauth_info.attrs.pin.max_length\t\t= 64;\n\t\tauth_info.attrs.pin.stored_length\t= 64;\n\t\tauth_info.attrs.pin.type\t\t= SC_PKCS15_PIN_TYPE_ASCII_NUMERIC;\n\t\tauth_info.attrs.pin.reference\t\t= 0x81;\n\t\tauth_info.attrs.pin.pad_char\t\t= 0xFF;\n\t\tauth_info.attrs.pin.flags\t\t= SC_PKCS15_PIN_FLAG_CASE_SENSITIVE\n\t\t\t\t\t| SC_PKCS15_PIN_FLAG_INITIALIZED\n\t\t\t\t\t| SC_PKCS15_PIN_FLAG_NEEDS_PADDING\n\t\t\t\t\t| SC_PKCS15_PIN_FLAG_LOCAL;\n\t\tauth_info.tries_left\t\t= tries_left;\n\n\t\tstrncpy(obj.label, PIN_DOMAIN_LABEL, SC_PKCS15_MAX_LABEL_SIZE-1);\n\t\tobj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE;\n\t\tif (sopin_reference == 0x84) {\n\t\t\t/*\n\t\t\t * auth_pin_reset_oberthur_style() in card-oberthur.c\n\t\t\t * always uses PUK with reference 0x84 for\n\t\t\t * unblocking of User PIN\n\t\t\t */\n\t\t\tobj.auth_id.len = 1;\n\t\t\tobj.auth_id.value[0] = 0xFF;\n\t\t}\n\n\t\tsc_format_path(AWP_PIN_DF, &auth_info.path);\n\t\tauth_info.path.type = SC_PATH_TYPE_PATH;\n\n\t\tsc_log(ctx, \"Add PIN(%s,auth_id:%s,reference:%i)\", obj.label,\n\t\t\t\tsc_pkcs15_print_id(&auth_info.auth_id), auth_info.attrs.pin.reference);\n\t\trv = sc_pkcs15emu_add_pin_obj(p15card, &obj, &auth_info);\n\t\tLOG_TEST_RET(ctx, rv, \"Oberthur init failed: cannot add PIN object\");\n\t}\n\telse if (rv != SC_ERROR_DATA_OBJECT_NOT_FOUND) {\n\t\tLOG_TEST_RET(ctx, rv, \"Oberthur init failed: cannot verify PIN\");\n\t}\n\n\tfor (ii=0; oberthur_infos[ii].name; ii++) {\n\t\tsc_log(ctx, \"Oberthur init: read %s file\", oberthur_infos[ii].name);\n\t\tfree(oberthur_infos[ii].content);\n\t\trv = sc_oberthur_read_file(p15card, oberthur_infos[ii].path,\n\t\t\t\t&oberthur_infos[ii].content, &oberthur_infos[ii].len, 1);\n\t\tLOG_TEST_RET(ctx, rv, \"Oberthur init failed: read oberthur file error\");\n\n\t\tsc_log(ctx,\n\t\t \"Oberthur init: parse %s file, content length %\"SC_FORMAT_LEN_SIZE_T\"u\",\n\t\t oberthur_infos[ii].name, oberthur_infos[ii].len);\n\t\trv = oberthur_infos[ii].parser(p15card, oberthur_infos[ii].content, oberthur_infos[ii].len,\n\t\t\t\toberthur_infos[ii].postpone_allowed);\n\t\tLOG_TEST_RET(ctx, rv, \"Oberthur init failed: parse error\");\n\t}\n\n\tLOG_FUNC_RETURN(ctx, SC_SUCCESS);\n}\n\nstatic int\noberthur_detect_card(struct sc_pkcs15_card * p15card)\n{\n\tstruct sc_card *card = p15card->card;\n\n\tSC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);\n\tif (p15card->card->type != SC_CARD_TYPE_OBERTHUR_64K)\n\t\tLOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_WRONG_CARD);\n\tLOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS);\n}\n\nint\nsc_pkcs15emu_oberthur_init_ex(struct sc_pkcs15_card * p15card, struct sc_aid *aid)\n{\n\tint rv;\n\n\tLOG_FUNC_CALLED(p15card->card->ctx);\n\trv = oberthur_detect_card(p15card);\n\tif (!rv)\n\t\trv = sc_pkcs15emu_oberthur_init(p15card);\n\n\tLOG_FUNC_RETURN(p15card->card->ctx, rv);\n}\n\nstatic int\nsc_awp_parse_df(struct sc_pkcs15_card *p15card, struct sc_pkcs15_df *df)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tunsigned char *buf = NULL;\n\tsize_t buf_len;\n\tint rv;\n\n\tLOG_FUNC_CALLED(ctx);\n\tif (df->type != SC_PKCS15_PRKDF && df->type != SC_PKCS15_DODF)\n\t\tLOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED);\n\n\tif (df->enumerated)\n\t\tLOG_FUNC_RETURN(ctx, SC_SUCCESS);\n\n\trv = sc_oberthur_read_file(p15card, AWP_OBJECTS_LIST_PRV, &buf, &buf_len, 1);\n\tLOG_TEST_RET(ctx, rv, \"Parse DF: read private objects info failed\");\n\n\trv = sc_oberthur_parse_privateinfo(p15card, buf, buf_len, 0);\n\n\tif (buf)\n\t\tfree(buf);\n\n\tif (rv == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED)\n\t\tLOG_FUNC_RETURN(ctx, SC_SUCCESS);\n\n\tLOG_TEST_RET(ctx, rv, \"Parse DF: private info parse error\");\n\tdf->enumerated = 1;\n\n\tLOG_FUNC_RETURN(ctx, rv);\n}\n\nstatic void\nsc_awp_clear(struct sc_pkcs15_card *p15card)\n{\n\tLOG_FUNC_CALLED(p15card->card->ctx);\n}\n\n", "input": "", "output": "【漏洞类型】内存管理漏洞\n【严重性】High\n【漏洞摘要】原始代码在处理文件读取和解析过程中存在内存管理问题。具体表现为在某些情况下,`info_blob` 和 `cert_blob` 在它们对应的解析函数中可能未被正确释放,导致内存泄漏。例如,在 `sc_pkcs15emu_oberthur_add_cert` 函数中,如果 `info_len < 2` 或 `len > sizeof(cinfo.id.value)`,`info_blob` 可能未被释放。此外,`sc_pkcs15emu_oberthur_add_prvkey` 和 `sc_pkcs15emu_oberthur_add_data` 函数中也存在类似问题。根本原因在于未在所有可能的错误分支中释放分配的内存。\n【关键代码模式】`sc_oberthur_read_file` 函数,`free(info_blob)`,`info_len`,`len`,错误处理分支。\n【修复方法说明】修复方法通过在所有可能的错误分支中添加 `free(info_blob)` 和 `free(cert_blob)` 来确保分配的内存在不再需要时被正确释放,解决了内存泄漏的问题。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nxwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/api/Document.java\n### 代码内容:\n/*\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n */\npackage com.xpn.xwiki.api;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Vector;\n\nimport org.apache.commons.fileupload.FileItem;\nimport org.apache.commons.lang3.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.suigeneris.jrcs.diff.DifferentiationFailedException;\nimport org.suigeneris.jrcs.diff.delta.Delta;\nimport org.suigeneris.jrcs.rcs.Version;\nimport org.xwiki.component.util.DefaultParameterizedType;\nimport org.xwiki.configuration.ConfigurationSource;\nimport org.xwiki.context.Execution;\nimport org.xwiki.context.ExecutionContext;\nimport org.xwiki.display.internal.DocumentDisplayerParameters;\nimport org.xwiki.model.document.DocumentAuthors;\nimport org.xwiki.model.reference.DocumentReference;\nimport org.xwiki.model.reference.DocumentReferenceResolver;\nimport org.xwiki.model.reference.EntityReferenceSerializer;\nimport org.xwiki.model.reference.ObjectReference;\nimport org.xwiki.model.reference.PageReference;\nimport org.xwiki.rendering.block.XDOM;\nimport org.xwiki.rendering.parser.ParseException;\nimport org.xwiki.rendering.syntax.Syntax;\nimport org.xwiki.security.authorization.Right;\nimport org.xwiki.stability.Unstable;\nimport org.xwiki.user.CurrentUserReference;\nimport org.xwiki.user.UserReference;\nimport org.xwiki.user.UserReferenceResolver;\n\nimport com.xpn.xwiki.XWiki;\nimport com.xpn.xwiki.XWikiConstant;\nimport com.xpn.xwiki.XWikiContext;\nimport com.xpn.xwiki.XWikiException;\nimport com.xpn.xwiki.criteria.impl.Period;\nimport com.xpn.xwiki.criteria.impl.PeriodFactory;\nimport com.xpn.xwiki.criteria.impl.Range;\nimport com.xpn.xwiki.criteria.impl.RangeFactory;\nimport com.xpn.xwiki.criteria.impl.RevisionCriteria;\nimport com.xpn.xwiki.criteria.impl.Scope;\nimport com.xpn.xwiki.criteria.impl.ScopeFactory;\nimport com.xpn.xwiki.doc.DocumentRevisionProvider;\nimport com.xpn.xwiki.doc.MetaDataDiff;\nimport com.xpn.xwiki.doc.XWikiAttachment;\nimport com.xpn.xwiki.doc.XWikiDocument;\nimport com.xpn.xwiki.doc.XWikiDocumentArchive;\nimport com.xpn.xwiki.doc.XWikiLink;\nimport com.xpn.xwiki.doc.XWikiLock;\nimport com.xpn.xwiki.objects.BaseObject;\nimport com.xpn.xwiki.objects.BaseProperty;\nimport com.xpn.xwiki.objects.ObjectDiff;\nimport com.xpn.xwiki.objects.classes.BaseClass;\nimport com.xpn.xwiki.plugin.fileupload.FileUploadPlugin;\nimport com.xpn.xwiki.stats.api.XWikiStatsService;\nimport com.xpn.xwiki.stats.impl.DocumentStats;\nimport com.xpn.xwiki.stats.impl.RefererStats;\nimport com.xpn.xwiki.util.TOCGenerator;\nimport com.xpn.xwiki.util.Util;\nimport com.xpn.xwiki.web.Utils;\n\n/**\n * This class represents a document or page in the wiki. This is a security and usability wrapper which wraps\n * {@link com.xpn.xwiki.doc.XWikiDocument} In scripting, an object representing the document in which the script resides\n * will be bound to a variable called doc.\n *\n * @version $Id$\n */\npublic class Document extends Api\n{\n /** Logging helper object. */\n private static final Logger LOGGER = LoggerFactory.getLogger(Document.class);\n\n /**\n * The XWikiDocument object wrapped by this API.\n */\n protected XWikiDocument initialDoc;\n\n /**\n * The XWikiDocument object wrapped by this API.\n */\n protected XWikiDocument doc;\n\n /**\n * Convenience object used by object related methods.\n */\n protected Object currentObj;\n\n /**\n * Used to resolve a string into a proper Document Reference using the current document's reference to fill the\n * blanks, except for the page name for which the default page name is used instead and for the wiki name for which\n * the current wiki is used instead of the current document reference's wiki.\n */\n private DocumentReferenceResolver<String> currentMixedDocumentReferenceResolver;\n\n /**\n * Used to convert a proper Document Reference to string (standard form).\n */\n private EntityReferenceSerializer<String> defaultEntityReferenceSerializer;\n\n /**\n * Used to convert a proper Document Reference to a string but without the wiki name.\n */\n private EntityReferenceSerializer<String> localEntityReferenceSerializer;\n\n /**\n * Used to convert user references to string.\n */\n private EntityReferenceSerializer<String> compactWikiEntityReferenceSerializer;\n\n private DocumentRevisionProvider documentRevisionProvider;\n\n private ConfigurationSource configuration;\n\n private DocumentReferenceResolver<String> getCurrentMixedDocumentReferenceResolver()\n {\n if (this.currentMixedDocumentReferenceResolver == null) {\n this.currentMixedDocumentReferenceResolver =\n Utils.getComponent(DocumentReferenceResolver.TYPE_STRING, \"currentmixed\");\n }\n\n return this.currentMixedDocumentReferenceResolver;\n }\n\n private EntityReferenceSerializer<String> getDefaultEntityReferenceSerializer()\n {\n if (this.defaultEntityReferenceSerializer == null) {\n this.defaultEntityReferenceSerializer = Utils.getComponent(EntityReferenceSerializer.TYPE_STRING);\n }\n\n return this.defaultEntityReferenceSerializer;\n }\n\n private EntityReferenceSerializer<String> getLocalEntityReferenceSerializer()\n {\n if (this.localEntityReferenceSerializer == null) {\n this.localEntityReferenceSerializer = Utils.getComponent(EntityReferenceSerializer.TYPE_STRING, \"local\");\n }\n\n return this.localEntityReferenceSerializer;\n }\n\n private EntityReferenceSerializer<String> getCompactWikiEntityReferenceSerializer()\n {\n if (this.compactWikiEntityReferenceSerializer == null) {\n this.compactWikiEntityReferenceSerializer =\n Utils.getComponent(EntityReferenceSerializer.TYPE_STRING, \"compactwiki\");\n }\n\n return this.compactWikiEntityReferenceSerializer;\n }\n\n private DocumentRevisionProvider getDocumentRevisionProvider()\n {\n if (this.documentRevisionProvider == null) {\n this.documentRevisionProvider = Utils.getComponent(DocumentRevisionProvider.class);\n }\n\n return this.documentRevisionProvider;\n }\n\n private ConfigurationSource getConfiguration()\n {\n if (this.configuration == null) {\n this.configuration = Utils.getComponent(ConfigurationSource.class);\n }\n\n return this.configuration;\n }\n\n /**\n * Document constructor.\n *\n * @param doc The XWikiDocument object to wrap.\n * @param context The current request context.\n */\n public Document(XWikiDocument doc, XWikiContext context)\n {\n super(context);\n\n this.initialDoc = doc;\n this.doc = this.initialDoc;\n }\n\n /**\n * Get the XWikiDocument wrapped by this API. This function is accessible only if you have the programming rights\n * give access to the priviledged API of the Document.\n *\n * @return The XWikiDocument wrapped by this API.\n */\n public XWikiDocument getDocument()\n {\n if (hasProgrammingRights()) {\n return this.doc;\n } else {\n return null;\n }\n }\n\n /**\n * Get a clone of the XWikiDocument wrapped by this API.\n *\n * @return A clone of the XWikiDocument wrapped by this API.\n */\n protected XWikiDocument getDoc()\n {\n if (this.initialDoc == this.doc) {\n this.doc = this.initialDoc.clone();\n }\n\n return this.doc;\n }\n\n /**\n * return the ID of the document. this ID is unique across the wiki.\n *\n * @return the id of the document.\n */\n public long getId()\n {\n return this.doc.getId();\n }\n\n /**\n * returns the DocumentReference for the current document\n *\n * @return the DocumentReference of the current document\n * @since 2.3M1\n */\n public DocumentReference getDocumentReference()\n {\n return this.doc.getDocumentReference();\n }\n\n /**\n * @return the {@link DocumentReference} of the document also containing the document {@link Locale}\n * @since 9.3RC1\n */\n public DocumentReference getDocumentReferenceWithLocale()\n {\n return this.doc.getDocumentReferenceWithLocale();\n }\n\n /**\n * @return the reference of the document as {@link PageReference} without the {@link Locale}\n * @since 10.6RC1\n */\n public PageReference getPageReference()\n {\n return this.doc.getPageReference();\n }\n\n /**\n * @return the reference of the document as {@link PageReference} including the {@link Locale}\n * @since 10.6RC1\n */\n public PageReference getPageReferenceWithLocale()\n {\n return this.doc.getPageReferenceWithLocale();\n }\n\n /**\n * @return the last part of the document's reference. For example if the reference of a document is\n * {@code MySpace.Mydoc}, the returned name is {@code MyDoc}. For a nested document, the last part of the\n * reference is always {@code WebHome} and thus the returned name is {@code Webhome}. It's better to use\n * {@link #getPageReference()} or {@link #getDocumentReference()}, e.g. with\n * {@code getPageReference().getName()} or {@code getDocumentReference().getName()}. To get the space name\n * of the nested document you can use {@code getPageReference().getName()} or\n * {@code getDocumentReference().getParent().getName()}.\n * @see #getPageReference()\n * @see #getDocumentReference()\n * @deprecated since 11.0, use {@link #getDocumentReference()} instead\n */\n @Deprecated\n public String getName()\n{\n return this.doc.getDocumentReference().getName();\n}\n\n /**\n * Return the full local space reference of the Document. For example a document located in sub-space\n * <code>space11</code> of space <code>space1</code> will return <code>space1.space11</code>.\n *\n * @return the name of the spaces of the document\n */\n public String getSpace()\n {\n return this.doc.getSpace();\n }\n\n /**\n * Get the name wiki where the document is stored.\n *\n * @return The name of the wiki where this document is stored.\n * @since 1.1.2\n * @since 1.2M2\n */\n public String getWiki()\n {\n return this.doc.getDocumentReference().getWikiReference().getName();\n }\n\n /**\n * Get the fullName of the document. If a document is named \"MyDoc\" in space \"MySpace\", the fullname is\n * \"MySpace.MyDoc\". In a wiki, all the documents have a different fullName.\n *\n * @return fullName of the document.\n */\n public String getFullName()\n {\n return getLocalEntityReferenceSerializer().serialize(this.doc.getDocumentReference());\n }\n\n /**\n * Get the complete fullName of the document. The real full name of the document containing the name of the wiki\n * where the document is stored. For a document stored in the wiki \"xwiki\", in space \"MySpace\", named \"MyDoc\", its\n * complete full name is \"xwiki:MySpace.MyDoc\".\n *\n * @return The complete fullName of the document.\n * @since 1.1.2\n * @since 1.2M2\n */\n public String getPrefixedFullName()\n {\n return getDefaultEntityReferenceSerializer().serialize(this.doc.getDocumentReference());\n }\n\n /**\n * Get a Version object representing the current version of the document.\n *\n * @return A Version object representing the current version of the document\n */\n public Version getRCSVersion()\n {\n return this.doc.getRCSVersion();\n }\n\n /**\n * Get a string representing the current version of the document.\n *\n * @return A string representing the current version of the document.\n */\n public String getVersion()\n {\n return this.doc.getVersion();\n }\n\n /**\n * Get a string representing the previous version of the document.\n *\n * @return A string representing the previous version of the document. If this is the first version then it returns\n * null.\n */\n public String getPreviousVersion()\n {\n return this.doc.getPreviousVersion();\n }\n\n /**\n * Get the value of the title field of the document.\n *\n * @return The value of the title field of the document.\n */\n public String getTitle()\n {\n return this.doc.getTitle();\n }\n\n /**\n * Get document title. If a title has not been provided through the title field, it looks for a section title in the\n * document's content and if not found return the page name. The returned title is also interpreted which means it's\n * allowed to use Velocity, Groovy, etc syntax within a title.\n *\n * @return The document title as XHTML\n */\n public String getDisplayTitle()\n {\n return this.doc.getRenderedTitle(getXWikiContext());\n }\n\n /**\n * Returns the document title as plain text\n *\n * @return the document title as plain text (all markup removed)\n * @since 3.0M1\n */\n public String getPlainTitle()\n {\n return this.doc.getRenderedTitle(Syntax.PLAIN_1_0, getXWikiContext());\n }\n\n /**\n * Returns the title of the document rendered through wiki syntax and velocity\n *\n * @return the title rendered through wiki syntax and velocity\n * @see XWikiDocument#getRenderedTitle(Syntax, XWikiContext)\n */\n public String getRenderedTitle(String syntaxId) throws XWikiException\n {\n try {\n return this.doc.getRenderedTitle(Syntax.valueOf(syntaxId), getXWikiContext());\n } catch (ParseException e) {\n LOGGER.error(\"Failed to parse provided syntax identifier [\" + syntaxId + \"]\", e);\n\n throw new XWikiException(XWikiException.MODULE_XWIKI_RENDERING, XWikiException.ERROR_XWIKI_UNKNOWN,\n \"Failed to parse syntax identifier [\" + syntaxId + \"]\", e);\n } catch (Exception e) {\n LOGGER.error(\"Failed to render document [\" + getPrefixedFullName() + \"] title content\", e);\n\n throw new XWikiException(XWikiException.MODULE_XWIKI_RENDERING, XWikiException.ERROR_XWIKI_UNKNOWN,\n \"Failed to render document [\" + getPrefixedFullName() + \"] content title\", e);\n }\n }\n\n /**\n * TODO document this or mark it deprecated\n */\n public String getFormat()\n {\n return this.doc.getFormat();\n }\n\n /**\n * Get fullName of the profile document of the author of the current version of the document. Example: XWiki.Admin.\n *\n * @return The fullName of the profile document of the author of the current version of the document.\n */\n public String getAuthor()\n {\n return this.doc.getAuthor();\n }\n\n /**\n * @return the document author reference\n * @since 6.4RC1\n */\n public DocumentReference getAuthorReference()\n {\n return this.doc.getAuthorReference();\n }\n\n /**\n * Get fullName of the profile document of the author of the content modification of this document version. Example:\n * XWiki.Admin.\n *\n * @return The fullName of the profile document of the author of the content modification in this document version.\n */\n public String getContentAuthor()\n {\n return this.doc.getContentAuthor();\n }\n\n /**\n * @return the document content author reference\n * @since 6.4RC1\n */\n public DocumentReference getContentAuthorReference()\n {\n return this.doc.getContentAuthorReference();\n }\n\n /**\n * @return The date when this document version has been modified.\n */\n public Date getDate()\n {\n return this.doc.getDate();\n }\n\n /**\n * Get the date when the content modification has been done on this document version. A content update excludes\n * modifications to meta data fields or comments of the document.\n *\n * @return The date where the content modification has been done on this document version.\n */\n public Date getContentUpdateDate()\n {\n return this.doc.getContentUpdateDate();\n }\n\n /**\n * @return the original creation date of the document.\n */\n public Date getCreationDate()\n {\n return this.doc.getCreationDate();\n }\n\n /**\n * Get the name of the parent of this document.\n *\n * @return The name of the parent of this document.\n */\n public String getParent()\n {\n return this.doc.getParent();\n }\n\n /**\n * @return the parent reference or null if the parent is not set\n * @since 7.3M1\n */\n public DocumentReference getParentReference()\n {\n return this.doc.getParentReference();\n }\n\n /**\n * Get fullName of the profile document of the document creator.\n *\n * @return The fullName of the profile document of the document creator.\n */\n public String getCreator()\n {\n return this.doc.getCreator();\n }\n\n /**\n * @return the document creator reference\n * @since 6.4RC1\n */\n public DocumentReference getCreatorReference()\n {\n return this.doc.getCreatorReference();\n }\n\n /**\n * Get raw content of the document, i.e. the content that is visible through the wiki editor.\n *\n * @return The raw content of the document.\n */\n public String getContent()\n {\n return this.doc.getContent();\n }\n\n /**\n * NOTE: This method caches the XDOM and returns a clone that can be safely modified.\n *\n * @return the XDOM corresponding to the document's string content\n * @since 7.0RC1\n */\n public XDOM getXDOM()\n {\n return this.doc.getXDOM();\n }\n\n /**\n * @return The syntax representing the syntax used for the document's content\n * @since 2.3M1\n */\n public Syntax getSyntax()\n {\n return this.doc.getSyntax();\n }\n\n /**\n * Get the Syntax id representing the syntax used for the document. For example \"xwiki/1.0\" represents the first\n * version XWiki syntax while \"xwiki/2.0\" represents version 2.0 of the XWiki Syntax.\n *\n * @return The syntax id representing the syntax used for the document.\n * @deprecated since 2.3M1 use {@link #getSyntax()} instead\n */\n @Deprecated\n public String getSyntaxId()\n {\n return this.doc.getSyntax().toIdString();\n }\n\n /**\n * Same as {@link #getLocale()} but as String.\n *\n * @return the locale of the document.\n * @deprecated since 5.4M1 use {@link #getLocale()} instead\n */\n @Deprecated\n public String getLanguage()\n {\n return this.doc.getLanguage();\n }\n\n /**\n * Get the locale of the document. If the document is a translation it returns the locale set for it, otherwise, it\n * returns the root locale.\n *\n * @return the locale of the document\n * @since 5.4M1\n */\n public Locale getLocale()\n {\n return this.doc.getLocale();\n }\n\n /**\n * TODO document this or mark it deprecated\n */\n public String getTemplate()\n {\n String templateReferenceAsString = \"\";\n DocumentReference templateDocumentReference = this.doc.getTemplateDocumentReference();\n if (templateDocumentReference != null) {\n templateReferenceAsString = getLocalEntityReferenceSerializer().serialize(templateDocumentReference);\n }\n return templateReferenceAsString;\n }\n\n /**\n * Same as {@link #getRealLocale()} but as String.\n *\n * @return the real locale\n * @deprecated since 8.0M1, use {@link #getRealLocale()} instead\n */\n @Deprecated\n public String getRealLanguage() throws XWikiException\n {\n return this.doc.getRealLanguage(getXWikiContext());\n }\n\n /**\n * Gets the real locale of the document. The real locale is either the default locale field when the locale field is\n * empty (when the document is the default document) or the locale field otherwise when the document is a\n * translation document\n * \n * @return the actual locale of the document\n * @since 8.0M1\n */\n public Locale getRealLocale()\n {\n return this.doc.getRealLocale();\n }\n\n /**\n * Same as {@link #getDefaultLocale()} but as String.\n * \n * @return the locale of the default document\n * @deprecated since 8.0M1, use {@link #getDefaultLocale()} instead\n */\n @Deprecated\n public String getDefaultLanguage()\n {\n return this.doc.getDefaultLanguage();\n }\n\n /**\n * @return the Locale of the default version of the document (usually {@link Locale#ROOT} or {@link Locale#ENGLISH})\n * @since 8.0M1\n */\n public Locale getDefaultLocale()\n {\n return this.doc.getDefaultLocale();\n }\n\n /**\n * @param defaultLocale the locale content in the default document version\n * @since 11.9RC1\n */\n public void setDefaultLocale(Locale defaultLocale)\n {\n this.doc.setDefaultLocale(defaultLocale);\n }\n\n /**\n * TODO document this or mark it deprecated\n */\n public String getDefaultTemplate()\n {\n return this.doc.getDefaultTemplate();\n }\n\n /**\n * @return the comment of of the document version\n */\n public String getComment()\n {\n return this.doc.getComment();\n }\n\n /**\n * @return true if the this document version was a minor edit.\n */\n public boolean isMinorEdit()\n {\n return this.doc.isMinorEdit();\n }\n\n /**\n * @return the list of locales for which this document has a translation; the original (default) locale is not\n * included\n * @throws XWikiException if retrieving the document translations from the database fails\n * @since 12.4RC1\n */\n public List<Locale> getTranslationLocales() throws XWikiException\n {\n return this.doc.getTranslationLocales(getXWikiContext());\n }\n\n /**\n * @return the translated document's content if the wiki is multilingual, the locale is first checked in the URL,\n * the cookie, the user profile and finally the wiki configuration if not, the locale is the one on the wiki\n * configuration.\n */\n public String getTranslatedContent() throws XWikiException\n {\n return this.doc.getTranslatedContent(getXWikiContext());\n }\n\n /**\n * @return the translated content in the given locale\n */\n public String getTranslatedContent(String locale) throws XWikiException\n {\n return this.doc.getTranslatedContent(locale, getXWikiContext());\n }\n\n /**\n * @return the translated document in the given locale\n */\n public Document getTranslatedDocument(String locale) throws XWikiException\n {\n return this.doc.getTranslatedDocument(locale, getXWikiContext()).newDocument(getXWikiContext());\n }\n\n /**\n * @return the tranlated Document if the wiki is multilingual, the locale is first checked in the URL, the cookie,\n * the user profile and finally the wiki configuration if not, the locale is the one on the wiki\n * configuration.\n */\n public Document getTranslatedDocument() throws XWikiException\n {\n return this.doc.getTranslatedDocument(getXWikiContext()).newDocument(getXWikiContext());\n }\n\n /**\n * @param targetSyntax the syntax in which to render the document content\n * @return the content of the current document rendered.\n * @since 11.3RC1\n */\n public String displayDocument(Syntax targetSyntax) throws XWikiException\n {\n return this.doc.displayDocument(targetSyntax, getXWikiContext());\n }\n\n /**\n * @param targetSyntax the syntax in which to render the document content\n * @param restricted see {@link DocumentDisplayerParameters#isTransformationContextRestricted}.\n * @return the content of the current document rendered.\n * @since 11.5RC1\n */\n public String displayDocument(Syntax targetSyntax, boolean restricted) throws XWikiException\n {\n return this.doc.displayDocument(targetSyntax, restricted, getXWikiContext());\n }\n\n /**\n * @return the content of the current document rendered.\n * @since 11.3RC1\n */\n public String displayDocument() throws XWikiException\n {\n return this.doc.displayDocument(getXWikiContext());\n }\n\n /**\n * @return the content of the current document rendered.\n * @param restricted see {@link DocumentDisplayerParameters#isTransformationContextRestricted}.\n * @since 11.5RC1\n */\n public String displayDocument(boolean restricted) throws XWikiException\n {\n return this.doc.displayDocument(restricted, getXWikiContext());\n }\n\n /**\n * @return the content of the document or its translations rendered.\n */\n public String getRenderedContent() throws XWikiException\n {\n return this.doc.getRenderedContent(getXWikiContext());\n }\n\n /**\n * Execute and render the document in the current context.\n * <p>\n * The code is executed with right of this document content author.\n * \n * @param transformationContextIsolated see {@link DocumentDisplayerParameters#isTransformationContextIsolated()}\n * @return the result\n * @throws XWikiException when failing to display the document\n * @since 8.4RC1\n */\n public String getRenderedContent(boolean transformationContextIsolated) throws XWikiException\n {\n return this.doc.getRenderedContent(transformationContextIsolated, getXWikiContext());\n }\n\n /**\n * @param text the text to render\n * @return the given text rendered in the context of this document\n * @deprecated since 1.6M1 use {@link #getRenderedContent(String, String)}\n */\n @Deprecated\n public String getRenderedContent(String text) throws XWikiException\n {\n return getRenderedContent(text, Syntax.XWIKI_1_0.toIdString());\n }\n\n /**\n * @param text the text to render\n * @param syntaxId the id of the Syntax used by the passed text (for example: \"xwiki/1.0\")\n * @return the given text rendered in the context of this document using the passed Syntax\n * @since 1.6M1\n */\n public String getRenderedContent(String text, String syntaxId) throws XWikiException\n {\n return getRenderedContent(text, syntaxId, false);\n }\n\n /**\n * Render a text in a restricted mode, where script macros are completely disabled.\n *\n * @param text the text to render\n * @param syntaxId the id of the Syntax used by the passed text (for example: \"xwiki/1.0\")\n * @return the given text rendered in the context of this document using the passed Syntax\n * @since 4.2M1\n */\n public String getRenderedContentRestricted(String text, String syntaxId) throws XWikiException\n {\n return getRenderedContent(text, syntaxId, true);\n }\n\n /**\n * Render a text in a restricted mode, where script macros are completely disabled.\n *\n * @param text the text to render\n * @param syntaxId the id of the Syntax used by the passed text (for example: \"xwiki/1.0\")\n * @param restricted see {@link DocumentDisplayerParameters#isTransformationContextRestricted}.\n * @return the given text rendered in the context of this document using the passed Syntax\n */\n private String getRenderedContent(String text, String syntaxId, boolean restricted) throws XWikiException\n {\n // Make sure we keep using current author as passed content author\n return this.doc.getRenderedContent(text, syntaxId, restricted, getCallerDocument(getXWikiContext()),\n getXWikiContext());\n }\n\n /**\n * @param text the text to render\n * @param sourceSyntaxId the id of the Syntax used by the passed text (for example: \"xwiki/1.0\")\n * @param targetSyntaxId the id of the syntax in which to render the document content\n * @return the given text rendered in the context of this document using the passed Syntax\n * @since 2.0M3\n */\n public String getRenderedContent(String text, String sourceSyntaxId, String targetSyntaxId) throws XWikiException\n {\n // Make sure we keep using current author as passed content author\n return this.doc.getRenderedContent(text, sourceSyntaxId, targetSyntaxId, false,\n getCallerDocument(getXWikiContext()), getXWikiContext());\n }\n\n private XWikiDocument getCallerDocument(XWikiContext xcontext)\n {\n XWikiDocument sdoc = (XWikiDocument) xcontext.get(\"sdoc\");\n if (sdoc == null) {\n sdoc = xcontext.getDoc();\n }\n\n return sdoc;\n }\n\n /**\n * @param targetSyntax the syntax in which to render the document content\n * @return the rendered content\n * @throws XWikiException error when rendering content\n */\n public String getRenderedContent(Syntax targetSyntax) throws XWikiException\n {\n return this.doc.getRenderedContent(targetSyntax, getXWikiContext());\n }\n\n /**\n * Get the document's content XML-escaped.\n *\n * @return an XML-escaped version of the content of this document.\n */\n public String getEscapedContent() throws XWikiException\n {\n return this.doc.getEscapedContent(getXWikiContext());\n }\n\n /**\n * @return the archive of this document's history in string format\n */\n public String getArchive() throws XWikiException\n {\n return this.doc.getDocumentArchive(getXWikiContext()).getArchive(getXWikiContext());\n }\n\n /**\n * Get the archive of this document's history. This function is accessible only if you have the programming rights.\n *\n * @return the archive of this document's history as an {@link XWikiDocumentArchive}.\n */\n public XWikiDocumentArchive getDocumentArchive() throws XWikiException\n {\n if (hasProgrammingRights()) {\n return this.doc.getDocumentArchive(getXWikiContext());\n }\n return null;\n }\n\n /**\n * @return true if the document is a new one (ie it has never been saved) or false otherwise\n */\n public boolean isNew()\n {\n return this.doc.isNew();\n }\n\n /**\n * Return the relative URL of download for the the given attachment name.\n *\n * @param filename the name of the attachment\n * @return A String with the URL or null if the file name is empty\n */\n public String getAttachmentURL(String filename)\n {\n return this.doc.getAttachmentURL(filename, getXWikiContext());\n }\n\n /**\n * Get the relative URL of the given action for the the given attachment name.\n *\n * @param filename the name of the attachment.\n * @param action what to do to the file for example \"delattachment\", \"download\" or \"downloadrev\".\n * @return a string representation of a URL to do the given operation or null if the file name is empty\n */\n public String getAttachmentURL(String filename, String action)\n {\n return this.doc.getAttachmentURL(filename, action, getXWikiContext());\n }\n\n /**\n * Get the relative URL of an action on an attachment. the given action for the the given attachment name with\n * \"queryString\" parameters\n *\n * @param filename the name of the attachment.\n * @param action what to do to the file for example \"delattachment\", \"download\" or \"downloadrev\"\n * @param queryString parameters added to the URL, the \"rev\" parameter is used to specify a revision if using the\n * \"downloadrev\" action. The query string must not begin with an ? character.\n * @return a string representation of a URL to do the given operation or null if the file name is empty\n */\n public String getAttachmentURL(String filename, String action, String queryString)\n {\n return this.doc.getAttachmentURL(filename, action, queryString, getXWikiContext());\n }\n\n /**\n * Get an old revision of an attachment.\n *\n * @param filename the name of the attachment.\n * @param version a revision number such as \"1.1\" or \"1.2\".\n * @return the URL for accessing to the archive of the attachment \"filename\" at the version \"version\" or null if the\n * file name is empty\n */\n public String getAttachmentRevisionURL(String filename, String version)\n {\n return this.doc.getAttachmentRevisionURL(filename, version, getXWikiContext());\n }\n\n /**\n * Get an old revision of an attachment.\n *\n * @param filename the name of the attachment.\n * @param version a revision number such as \"1.1\" or \"1.2\".\n * @param queryString additional query parameters to pass in the request.\n * @return the URL for accessing to the archive of the attachment \"filename\" at the version \"version\" with the given\n * queryString parameters or null if the file name is empty\n */\n public String getAttachmentRevisionURL(String filename, String version, String queryString)\n {\n return this.doc.getAttachmentRevisionURL(filename, version, queryString, getXWikiContext());\n }\n\n /**\n * Get the URL of this document.\n *\n * @return the URL to view this document, this will be a relative URL for example: /xwiki/bin/view/Main/WebHome\n * @see #getExternalURL() for an absolute URL which can used outside of the site.\n */\n public String getURL()\n {\n return this.doc.getURL(\"view\", getXWikiContext());\n }\n\n /**\n * Get the URL to do a given action on this document.\n *\n * @param action what to do to the document for example \"view\", \"edit\" or \"inline\".\n * @return the URL of this document with the given action.\n * @see #getExternalURL(String) for an absolute URL which can used outside of the site.\n */\n public String getURL(String action)\n {\n return this.doc.getURL(action, getXWikiContext());\n }\n\n /**\n * Get the URL to do a given action on this document.\n *\n * @param action what to do to the document for example \"view\", \"edit\" or \"inline\".\n * @param queryString parameters to pass in the request eg: {@code paramA=value1¶mB=value2}\n * @return the URL of this document with the given action and queryString as parameters.\n * @see #getExternalURL(String, String) for an absolute URL which can used outside of the site.\n */\n public String getURL(String action, String queryString)\n {\n return this.doc.getURL(action, queryString, getXWikiContext());\n }\n\n /**\n * Get the external URL to do a given action on this document.\n *\n * @return the full URL of the document, sutable for use at external websites for example:\n * http://www.xwiki.org/xwiki/bin/view/Main/WebHome\n * @see #getURL() for a relative URL which can only be used inside of the site.\n */\n public String getExternalURL()\n {\n return this.doc.getExternalURL(\"view\", getXWikiContext());\n }\n\n /**\n * Get the external URL to do a given action on this document.\n *\n * @param action what to do to the document for example \"view\", \"edit\" or \"inline\".\n * @return the URL of this document with the given action.\n * @see #getURL() for a relative URL which can only be used inside of the site.\n */\n public String getExternalURL(String action)\n {\n return this.doc.getExternalURL(action, getXWikiContext());\n }\n\n /**\n * Get the URL to do a given action on this document.\n *\n * @param action what to do to the document for example \"view\", \"edit\" or \"inline\".\n * @param queryString parameters to pass in the request eg: {@code paramA=value1¶mB=value2}\n * @return the URL of this document with the given action and queryString as parameters.\n * @see #getURL() for a relative URL which can only be used inside of the site.\n */\n public String getExternalURL(String action, String queryString)\n {\n return this.doc.getExternalURL(action, queryString, getXWikiContext());\n }\n\n /**\n * @return the relative URL of the parent document of this document\n */\n public String getParentURL() throws XWikiException\n {\n return this.doc.getParentURL(getXWikiContext());\n }\n\n /**\n * @return the XClass associated to this document when the document represents an XWiki Class\n */\n public Class getxWikiClass()\n {\n BaseClass bclass = this.getDoc().getXClass();\n if (bclass == null) {\n return null;\n } else {\n return new Class(bclass, getXWikiContext());\n }\n }\n\n /**\n * @return the array of XClasses representing the objects of this document\n */\n public Class[] getxWikiClasses()\n {\n List<BaseClass> list = this.getDoc().getXClasses(getXWikiContext());\n if (list == null) {\n return null;\n }\n Class[] result = new Class[list.size()];\n for (int i = 0; i < list.size(); i++) {\n result[i] = new Class(list.get(i), getXWikiContext());\n }\n return result;\n }\n\n /**\n * Creates a New XWiki Object of the given classname\n *\n * @param classname the classname used\n * @return the number of the object created\n */\n public int createNewObject(String classname) throws XWikiException\n {\n int index = getDoc().createNewObject(classname, getXWikiContext());\n\n updateAuthor();\n\n return index;\n }\n\n /**\n * Creates a New XWiki Object of the given classname\n *\n * @param classname the classname used\n * @return the object created\n */\n public Object newObject(String classname) throws XWikiException\n {\n int nb = createNewObject(classname);\n return getObject(classname, nb);\n }\n\n /**\n * @return true of the document has been loaded from cache\n */\n public boolean isFromCache()\n {\n return this.doc.isFromCache();\n }\n\n /**\n * @param classname the classname used\n * @return the number of objects available for a given classname\n */\n public int getObjectNumbers(String classname)\n {\n return this.doc.getObjectNumbers(classname);\n }\n\n /**\n * Get the list of all objects available in this document organized in a Map by classname\n *\n * @return the map of objects\n */\n public Map<String, Vector<Object>> getxWikiObjects()\n {\n Map<DocumentReference, List<BaseObject>> map = this.getDoc().getXObjects();\n Map<String, Vector<Object>> resultmap = new HashMap<String, Vector<Object>>();\n for (Map.Entry<DocumentReference, List<BaseObject>> entry : map.entrySet()) {\n List<BaseObject> objects = entry.getValue();\n if (objects != null) {\n resultmap.put(getLocalEntityReferenceSerializer().serialize(entry.getKey()), getXObjects(objects));\n }\n }\n return resultmap;\n }\n\n protected Vector<Object> getXObjects(List<BaseObject> objects)\n {\n if (objects == null) {\n return new Vector<Object>(0);\n }\n Vector<Object> result = new Vector<Object>(objects.size());\n for (BaseObject bobj : objects) {\n if (bobj != null) {\n result.add(newObjectApi(bobj, getXWikiContext()));\n }\n }\n return result;\n }\n\n /**\n * Get the list of objects for a given classname classname\n *\n * @return the vector of objects\n */\n public Vector<Object> getObjects(String className)\n {\n List<BaseObject> objects = this.getDoc().getXObjects(this.doc.resolveClassReference(className));\n return getXObjects(objects);\n }\n\n /**\n * Get the first object that contains the given fieldname\n *\n * @param fieldname name of the field to find in the object\n * @return the XWiki Object\n */\n public Object getFirstObject(String fieldname)\n {\n try {\n BaseObject obj = this.getDoc().getFirstObject(fieldname, getXWikiContext());\n if (obj == null) {\n return null;\n } else {\n return newObjectApi(obj, getXWikiContext());\n }\n } catch (Exception e) {\n return null;\n }\n }\n\n /**\n * Get the first object of a given classname that has a field name matching the given value\n *\n * @param classname name of the class of the object to look for\n * @param key name of the field to find in the object\n * @param value value of the field to find in the object\n * @param failover true if the first object will be given when none found\n * @return the XWiki Object\n */\n public Object getObject(String classname, String key, String value, boolean failover)\n {\n try {\n BaseObject obj = this.getDoc().getObject(classname, key, value, failover);\n if (obj == null) {\n return null;\n } else {\n return newObjectApi(obj, getXWikiContext());\n }\n } catch (Exception e) {\n return null;\n }\n }\n\n /**\n * Select a subset of objects from a given class, filtered on a \"key = value\" criteria.\n *\n * @param classname The type of objects to return.\n * @param key The name of the property used for filtering.\n * @param value The required value.\n * @return A Vector of {@link Object objects} matching the criteria. If no objects are found, or if the key is an\n * empty String, then an empty vector is returned.\n */\n public Vector<Object> getObjects(String classname, String key, String value)\n {\n Vector<Object> result = new Vector<Object>();\n if (StringUtils.isBlank(key) || value == null) {\n return getObjects(classname);\n }\n try {\n Vector<BaseObject> allObjects = this.getDoc().getObjects(classname);\n if (allObjects == null || allObjects.size() == 0) {\n return result;\n } else {\n for (BaseObject obj : allObjects) {\n if (obj != null) {\n BaseProperty prop = (BaseProperty) obj.get(key);\n if (prop == null || prop.getValue() == null) {\n continue;\n }\n if (value.equals(prop.getValue().toString())) {\n result.add(newObjectApi(obj, getXWikiContext()));\n }\n }\n }\n }\n } catch (Exception e) {\n }\n return result;\n }\n\n /**\n * Get the first object of a given classname that has a field name matching the given value When none found this\n * method will return null\n *\n * @param classname name of the class of the object to look for\n * @param key name of the field to find in the object\n * @param value value of the field to find in the object\n * @return the XWiki Object\n */\n public Object getObject(String classname, String key, String value)\n {\n try {\n BaseObject obj = this.getDoc().getObject(classname, key, value);\n if (obj == null) {\n return null;\n } else {\n return newObjectApi(obj, getXWikiContext());\n }\n } catch (Exception e) {\n return null;\n }\n }\n\n /**\n * Get the first object matching the given classname\n *\n * @param classname name of the class of the object to look for\n * @return the XWiki Object\n */\n public Object getObject(String classname)\n {\n return getObject(classname, false);\n }\n\n /**\n * get the object of the given className. If there is no object of this className and the create parameter at true,\n * the object is created.\n *\n * @param classname name of the class of the object to look for\n * @param create true of the object should be created when it does not exist\n * @return the XWiki Object\n */\n public Object getObject(String classname, boolean create)\n {\n try {\n BaseObject obj = getDoc().getObject(classname, create, getXWikiContext());\n\n if (obj == null) {\n return null;\n } else {\n return newObjectApi(obj, getXWikiContext());\n }\n } catch (Exception e) {\n return null;\n }\n }\n\n /**\n * Get the object matching the given classname and given object number\n *\n * @param classname name of the class of the object\n * @param nb number of the object\n * @return the XWiki Object\n */\n public Object getObject(String classname, int nb)\n {\n try {\n BaseObject obj = this.getDoc().getObject(classname, nb);\n if (obj == null) {\n return null;\n } else {\n return newObjectApi(obj, getXWikiContext());\n }\n } catch (Exception e) {\n return null;\n }\n }\n\n /**\n * @param objectReference the object reference\n * @return the XWiki object from this document that matches the specified object reference\n * @since 12.3RC1\n */\n public Object getObject(ObjectReference objectReference)\n {\n try {\n BaseObject obj = this.getDoc().getXObject(objectReference);\n return obj == null ? null : newObjectApi(obj, getXWikiContext());\n } catch (Exception e) {\n return null;\n }\n }\n\n /**\n * @param objectReference the object reference\n * @param create if true, the object will be created when missing\n * @return the XWiki object from this document that matches the specified object reference\n * @since 14.0RC1\n */\n @Unstable\n public Object getObject(ObjectReference objectReference, boolean create)\n {\n try {\n BaseObject obj = this.getDoc().getXObject(objectReference, create, getXWikiContext());\n return obj == null ? null : newObjectApi(obj, getXWikiContext());\n } catch (Exception e) {\n return null;\n }\n }\n\n private Object newObjectApi(BaseObject obj, XWikiContext context)\n {\n return obj.newObjectApi(obj, context);\n }\n\n public String getXMLContent() throws XWikiException\n {\n String xml = this.doc.getXMLContent(getXWikiContext());\n return getXWikiContext().getUtil().substitute(\"s/<email>.*?<\\\\/email>/<email>********<\\\\/email>/goi\",\n getXWikiContext().getUtil().substitute(\"s/<password>.*?<\\\\/password>/<password>********<\\\\/password>/goi\",\n xml));\n }\n\n public String toXML() throws XWikiException\n {\n if (hasProgrammingRights()) {\n return this.doc.toXML(getXWikiContext());\n } else {\n return \"\";\n }\n }\n\n public org.dom4j.Document toXMLDocument() throws XWikiException\n {\n if (hasProgrammingRights()) {\n return this.doc.toXMLDocument(getXWikiContext());\n } else {\n return null;\n }\n }\n\n public Version[] getRevisions() throws XWikiException\n {\n return this.doc.getRevisions(getXWikiContext());\n }\n\n public String[] getRecentRevisions() throws XWikiException\n {\n return this.doc.getRecentRevisions(5, getXWikiContext());\n }\n\n public String[] getRecentRevisions(int nb) throws XWikiException\n {\n return this.doc.getRecentRevisions(nb, getXWikiContext());\n }\n\n /**\n * Get document versions matching criterias like author, minimum creation date, etc.\n *\n * @param criteria criteria used to match versions\n * @return a list of matching versions\n */\n public List<String> getRevisions(RevisionCriteria criteria) throws XWikiException\n {\n return this.doc.getRevisions(criteria, this.context);\n }\n\n /**\n * Get information about a document version : author, date, etc.\n *\n * @param version the version you want to get information about\n * @return a new RevisionInfo object\n */\n public RevisionInfo getRevisionInfo(String version) throws XWikiException\n {\n return new RevisionInfo(this.doc.getRevisionInfo(version, getXWikiContext()), getXWikiContext());\n }\n\n public List<Attachment> getAttachmentList()\n {\n List<Attachment> apis = new ArrayList<Attachment>();\n for (XWikiAttachment attachment : this.getDoc().getAttachmentList()) {\n apis.add(new Attachment(this, attachment, getXWikiContext()));\n }\n return apis;\n }\n\n public Vector<Object> getComments()\n {\n return getComments(true);\n }\n\n public Vector<Object> getComments(boolean asc)\n {\n return getXObjects(this.getDoc().getComments(asc));\n }\n\n /**\n * Setting the current object to the given object. Following calls to display() will use this object as the\n * reference for finding properties.\n *\n * @param object Object to use as a reference for further display calls\n */\n public void use(Object object)\n {\n this.currentObj = object;\n }\n\n /**\n * Setting the current object to the first object of the given class name. Following calls to display() will use\n * this object as the reference for finding properties.\n *\n * @param className class used to find the first object to use as the reference for display calls\n */\n public void use(String className)\n {\n this.currentObj = getObject(className);\n }\n\n /**\n * Setting the current object to the object of the given class name and the given number. Following calls to\n * display() will use this object as the reference for finding properties.\n *\n * @param className class used to find the object to use as the reference for display calls\n * @param nb number of the object to use as the reference for display calls\n */\n public void use(String className, int nb)\n {\n this.currentObj = getObject(className, nb);\n }\n\n /**\n * @return the className of the current active object use for display calls.\n */\n public String getActiveClass()\n {\n if (this.currentObj == null) {\n return null;\n } else {\n return this.currentObj.getName();\n }\n }\n\n /**\n * Displays the pretty name of the given field. This function uses the active object or will find the first object\n * that has the given field.\n *\n * @param fieldname fieldname to display the pretty name of\n * @return the pretty name display of the field.\n */\n public String displayPrettyName(String fieldname)\n {\n if (this.currentObj == null) {\n return this.doc.displayPrettyName(fieldname, getXWikiContext());\n } else {\n return this.doc.displayPrettyName(fieldname, this.currentObj.getBaseObject(), getXWikiContext());\n }\n }\n\n /**\n * Displays the pretty name of the given field of the given object.\n *\n * @param fieldname fieldname to display the pretty name of\n * @param obj Object to find the class to display the pretty name of\n * @return the pretty name display of the field.\n */\n public String displayPrettyName(String fieldname, Object obj)\n {\n if (obj == null) {\n return \"\";\n }\n return this.doc.displayPrettyName(fieldname, obj.getBaseObject(), getXWikiContext());\n }\n\n /**\n * Displays the pretty name of the given field. This function uses the active object or will find the first object\n * that has the given field. with the optional addition of a mandatory field.\n *\n * @param fieldname fieldname to display the pretty name of\n * @param showMandatory true to display a mandatory sign\n * @return the pretty name display of the field.\n */\n public String displayPrettyName(String fieldname, boolean showMandatory)\n {\n if (this.currentObj == null) {\n return this.doc.displayPrettyName(fieldname, showMandatory, getXWikiContext());\n } else {\n return this.doc.displayPrettyName(fieldname, showMandatory, this.currentObj.getBaseObject(),\n getXWikiContext());\n }\n }\n\n /**\n * Displays the pretty name of the given field of the given object. with the optional addition of a mandatory field.\n *\n * @param fieldname fieldname to display the pretty name of\n * @param obj Object to find the class to display the pretty name of\n * @param showMandatory true to display a mandatory sign\n * @return the pretty name display of the field.\n */\n public String displayPrettyName(String fieldname, boolean showMandatory, Object obj)\n {\n if (obj == null) {\n return \"\";\n }\n return this.doc.displayPrettyName(fieldname, showMandatory, obj.getBaseObject(), getXWikiContext());\n }\n\n /**\n * Displays the pretty name of the given field. This function uses the active object or will find the first object\n * that has the given field. with the optional addition of a mandatory field before or after the field\n *\n * @param fieldname fieldname to display the pretty name of\n * @param showMandatory true to display a mandatory sign\n * @param before true if the mandatory sign should be before the field\n * @return the pretty name display of the field.\n */\n public String displayPrettyName(String fieldname, boolean showMandatory, boolean before)\n {\n if (this.currentObj == null) {\n return this.doc.displayPrettyName(fieldname, showMandatory, before, getXWikiContext());\n } else {\n return this.doc.displayPrettyName(fieldname, showMandatory, before, this.currentObj.getBaseObject(),\n getXWikiContext());\n }\n }\n\n /**\n * Displays the pretty name of the given field of the given object. with the optional addition of a mandatory field\n * before or after the field\n *\n * @param fieldname fieldname to display the pretty name of\n * @param showMandatory true to display a mandatory sign\n * @param before true if the mandatory sign should be before the field\n * @param obj Object to find the class to display the pretty name of\n * @return the pretty name display of the field.\n */\n public String displayPrettyName(String fieldname, boolean showMandatory, boolean before, Object obj)\n {\n if (obj == null) {\n return \"\";\n }\n return this.doc.displayPrettyName(fieldname, showMandatory, before, obj.getBaseObject(), getXWikiContext());\n }\n\n /**\n * Displays the tooltip of the given field. This function uses the active object or will find the first object that\n * has the given field.\n *\n * @param fieldname fieldname to display the tooltip of\n * @return the tooltip display of the field.\n */\n public String displayTooltip(String fieldname)\n {\n if (this.currentObj == null) {\n return this.doc.displayTooltip(fieldname, getXWikiContext());\n } else {\n return this.doc.displayTooltip(fieldname, this.currentObj.getBaseObject(), getXWikiContext());\n }\n }\n\n /**\n * Displays the tooltip of the given field of the given object.\n *\n * @param fieldname fieldname to display the tooltip of\n * @param obj Object to find the class to display the tooltip of\n * @return the tooltip display of the field.\n */\n public String displayTooltip(String fieldname, Object obj)\n {\n if (obj == null) {\n return \"\";\n }\n return this.doc.displayTooltip(fieldname, obj.getBaseObject(), getXWikiContext());\n }\n\n /**\n * Displays the given field. The display mode will be decided depending on page context (edit or inline context will\n * display in edit, view context in view) This function uses the active object or will find the first object that\n * has the given field. This function can return html inside and html macro\n *\n * @param fieldname fieldname to display\n * @return the display of the field.\n */\n public String display(String fieldname)\n {\n if (this.currentObj == null) {\n return this.doc.display(fieldname, getXWikiContext());\n } else {\n return this.doc.display(fieldname, this.currentObj.getBaseObject(), getXWikiContext());\n }\n }\n\n /**\n * Displays the given field in the given mode. This function uses the active object or will find the first object\n * that has the given field. This function can return html inside and html macro\n *\n * @param fieldname fieldname to display\n * @param mode display mode to use (view, edit, hidden, search)\n * @return the display of the field.\n */\n public String display(String fieldname, String mode)\n {\n if (this.currentObj == null) {\n return this.doc.display(fieldname, mode, getXWikiContext());\n } else {\n return this.doc.display(fieldname, mode, this.currentObj.getBaseObject(), getXWikiContext());\n }\n }\n\n /**\n * Displays the given field in the given mode. This function uses the active object or will find the first object\n * that has the given field. This function can return html inside and html macro A given prefix is added to the\n * field names when these are forms.\n *\n * @param fieldname fieldname to display\n * @param mode display mode to use (view, edit, hidden, search)\n * @param prefix prefix to use for the form names\n * @return the display of the field.\n */\n public String display(String fieldname, String mode, String prefix)\n {\n if (this.currentObj == null) {\n return this.doc.display(fieldname, mode, prefix, getXWikiContext());\n } else {\n return this.doc.display(fieldname, mode, prefix, this.currentObj.getBaseObject(), getSyntaxId(),\n getXWikiContext());\n }\n }\n\n /**\n * Displays the given field of the given object The display mode will be decided depending on page context (edit or\n * inline context will display in edit, view context in view) This function can return html inside and html macro\n *\n * @param fieldname fieldname to display\n * @param obj object from which to take the field\n * @return the display of the field.\n */\n public String display(String fieldname, Object obj)\n {\n if (obj == null) {\n return \"\";\n }\n return this.doc.display(fieldname, obj.getBaseObject(), getXWikiContext());\n }\n\n /**\n * Displays the given field of the given object using the given mode and syntax rendering The display mode will be\n * decided depending on page context (edit or inline context will display in edit, view context in view) This\n * function can return html inside and html macro\n *\n * @param fieldname the name of the field to display\n * @param type the type of the field to display\n * @param obj the object containing the field to display\n * @param wrappingSyntaxId the syntax of the content in which the result will be included. This to take care of some\n * escaping depending of the syntax.\n * @return the rendered field\n */\n public String display(String fieldname, String type, Object obj, String wrappingSyntaxId)\n {\n if (obj == null) {\n return \"\";\n }\n return this.doc.display(fieldname, type, obj.getBaseObject(), wrappingSyntaxId, getXWikiContext());\n }\n\n /**\n * Displays the given field of the given object in the given mode. This function can return html inside and html\n * macro\n *\n * @param fieldname fieldname to display\n * @param mode display mode to use (view, edit, hidden, search)\n * @param obj the object containing the field to display\n * @return the display of the field.\n */\n public String display(String fieldname, String mode, Object obj)\n {\n if (obj == null) {\n return \"\";\n }\n return this.doc.display(fieldname, mode, obj.getBaseObject(), getXWikiContext());\n }\n\n /**\n * Displays the given field of the given object in the given mode. This function can return html inside and html\n * macro A given prefix is added to the field names when these are forms.\n *\n * @param fieldname fieldname to display\n * @param mode display mode to use (view, edit, hidden, search)\n * @param prefix prefix to use for the form names\n * @param obj the object containing the field to display\n * @return the display of the field.\n */\n public String display(String fieldname, String mode, String prefix, Object obj)\n {\n if (obj == null) {\n return \"\";\n }\n return this.doc.display(fieldname, mode, prefix, obj.getBaseObject(), getSyntaxId(), getXWikiContext());\n }\n\n public String displayForm(String className, String header, String format)\n {\n return this.doc.displayForm(className, header, format, getXWikiContext());\n }\n\n public String displayForm(String className, String header, String format, boolean linebreak)\n {\n return this.doc.displayForm(className, header, format, linebreak, getXWikiContext());\n }\n\n public String displayForm(String className)\n {\n return this.doc.displayForm(className, getXWikiContext());\n }\n\n public String displayRendered(com.xpn.xwiki.api.PropertyClass pclass, String prefix, Collection object)\n throws XWikiException\n {\n if ((pclass == null) || (object == null)) {\n return \"\";\n }\n return this.doc.displayRendered(pclass.getBasePropertyClass(), prefix, object.getCollection(),\n getXWikiContext());\n }\n\n public String displayView(com.xpn.xwiki.api.PropertyClass pclass, String prefix, Collection object)\n {\n if ((pclass == null) || (object == null)) {\n return \"\";\n }\n return this.doc.displayView(pclass.getBasePropertyClass(), prefix, object.getCollection(), getXWikiContext());\n }\n\n public String displayEdit(com.xpn.xwiki.api.PropertyClass pclass, String prefix, Collection object)\n {\n if ((pclass == null) || (object == null)) {\n return \"\";\n }\n return this.doc.displayEdit(pclass.getBasePropertyClass(), prefix, object.getCollection(), getXWikiContext());\n }\n\n public String displayHidden(com.xpn.xwiki.api.PropertyClass pclass, String prefix, Collection object)\n {\n if ((pclass == null) || (object == null)) {\n return \"\";\n }\n return this.doc.displayHidden(pclass.getBasePropertyClass(), prefix, object.getCollection(), getXWikiContext());\n }\n\n public List<String> getIncludedPages()\n {\n return this.doc.getIncludedPages(getXWikiContext());\n }\n\n public List<String> getIncludedMacros()\n {\n return this.doc.getIncludedMacros(getXWikiContext());\n }\n\n public List<String> getLinkedPages()\n {\n return new ArrayList<String>(this.doc.getUniqueLinkedPages(getXWikiContext()));\n }\n\n public Attachment getAttachment(String filename)\n {\n XWikiAttachment attach = this.getDoc().getAttachment(filename);\n if (attach == null) {\n return null;\n } else {\n return new Attachment(this, attach, getXWikiContext());\n }\n }\n\n public List<Delta> getContentDiff(Document origdoc, Document newdoc)\n throws XWikiException, DifferentiationFailedException\n {\n try {\n if ((origdoc == null) && (newdoc == null)) {\n return Collections.emptyList();\n }\n if (origdoc == null) {\n return this.doc.getContentDiff(new XWikiDocument(newdoc.getDocumentReference()), newdoc.doc,\n getXWikiContext());\n }\n if (newdoc == null) {\n return this.doc.getContentDiff(origdoc.doc, new XWikiDocument(origdoc.getDocumentReference()),\n getXWikiContext());\n }\n\n return this.doc.getContentDiff(origdoc.doc, newdoc.doc, getXWikiContext());\n } catch (Exception e) {\n java.lang.Object[] args = { origdoc.getFullName(), origdoc.getVersion(), newdoc.getVersion() };\n List list = new ArrayList();\n XWikiException xe =\n new XWikiException(XWikiException.MODULE_XWIKI_DIFF, XWikiException.ERROR_XWIKI_DIFF_CONTENT_ERROR,\n \"Error while making content diff of {0} between version {1} and version {2}\", e, args);\n String errormsg = Util.getHTMLExceptionMessage(xe, getXWikiContext());\n list.add(errormsg);\n return list;\n }\n }\n\n public List<Delta> getXMLDiff(Document origdoc, Document newdoc)\n throws XWikiException, DifferentiationFailedException\n {\n try {\n if ((origdoc == null) && (newdoc == null)) {\n return Collections.emptyList();\n }\n if (origdoc == null) {\n return this.doc.getXMLDiff(new XWikiDocument(newdoc.getDocumentReference()), newdoc.doc,\n getXWikiContext());\n }\n if (newdoc == null) {\n return this.doc.getXMLDiff(origdoc.doc, new XWikiDocument(origdoc.getDocumentReference()),\n getXWikiContext());\n }\n\n return this.doc.getXMLDiff(origdoc.doc, newdoc.doc, getXWikiContext());\n } catch (Exception e) {\n java.lang.Object[] args = { origdoc.getFullName(), origdoc.getVersion(), newdoc.getVersion() };\n List list = new ArrayList();\n XWikiException xe =\n new XWikiException(XWikiException.MODULE_XWIKI_DIFF, XWikiException.ERROR_XWIKI_DIFF_XML_ERROR,\n \"Error while making xml diff of {0} between version {1} and version {2}\", e, args);\n String errormsg = Util.getHTMLExceptionMessage(xe, getXWikiContext());\n list.add(errormsg);\n return list;\n }\n }\n\n public List<Delta> getRenderedContentDiff(Document origdoc, Document newdoc)\n throws XWikiException, DifferentiationFailedException\n {\n try {\n if ((origdoc == null) && (newdoc == null)) {\n return Collections.emptyList();\n }\n if (origdoc == null) {\n return this.doc.getRenderedContentDiff(new XWikiDocument(newdoc.getDocumentReference()), newdoc.doc,\n getXWikiContext());\n }\n if (newdoc == null) {\n return this.doc.getRenderedContentDiff(origdoc.doc, new XWikiDocument(origdoc.getDocumentReference()),\n getXWikiContext());\n }\n\n return this.doc.getRenderedContentDiff(origdoc.doc, newdoc.doc, getXWikiContext());\n } catch (Exception e) {\n java.lang.Object[] args = { origdoc.getFullName(), origdoc.getVersion(), newdoc.getVersion() };\n List list = new ArrayList();\n XWikiException xe =\n new XWikiException(XWikiException.MODULE_XWIKI_DIFF, XWikiException.ERROR_XWIKI_DIFF_RENDERED_ERROR,\n \"Error while making rendered diff of {0} between version {1} and version {2}\", e, args);\n String errormsg = Util.getHTMLExceptionMessage(xe, getXWikiContext());\n list.add(errormsg);\n return list;\n }\n }\n\n public List<MetaDataDiff> getMetaDataDiff(Document origdoc, Document newdoc) throws XWikiException\n {\n try {\n if ((origdoc == null) && (newdoc == null)) {\n return Collections.emptyList();\n }\n if (origdoc == null) {\n return this.doc.getMetaDataDiff(new XWikiDocument(newdoc.getDocumentReference()), newdoc.doc,\n getXWikiContext());\n }\n if (newdoc == null) {\n return this.doc.getMetaDataDiff(origdoc.doc, new XWikiDocument(origdoc.getDocumentReference()),\n getXWikiContext());\n }\n\n return this.doc.getMetaDataDiff(origdoc.doc, newdoc.doc, getXWikiContext());\n } catch (Exception e) {\n java.lang.Object[] args = { origdoc.getFullName(), origdoc.getVersion(), newdoc.getVersion() };\n List list = new ArrayList();\n XWikiException xe =\n new XWikiException(XWikiException.MODULE_XWIKI_DIFF, XWikiException.ERROR_XWIKI_DIFF_METADATA_ERROR,\n \"Error while making meta data diff of {0} between version {1} and version {2}\", e, args);\n String errormsg = Util.getHTMLExceptionMessage(xe, getXWikiContext());\n list.add(errormsg);\n return list;\n }\n }\n\n public List<List<ObjectDiff>> getObjectDiff(Document origdoc, Document newdoc)\n {\n try {\n if ((origdoc == null) && (newdoc == null)) {\n return Collections.emptyList();\n }\n if (origdoc == null) {\n return this.doc.getObjectDiff(new XWikiDocument(newdoc.getDocumentReference()), newdoc.doc,\n getXWikiContext());\n }\n if (newdoc == null) {\n return this.doc.getObjectDiff(origdoc.doc, new XWikiDocument(origdoc.getDocumentReference()),\n getXWikiContext());\n }\n\n return this.doc.getObjectDiff(origdoc.doc, newdoc.doc, getXWikiContext());\n } catch (Exception e) {\n java.lang.Object[] args = { origdoc.getFullName(), origdoc.getVersion(), newdoc.getVersion() };\n List list = new ArrayList();\n XWikiException xe =\n new XWikiException(XWikiException.MODULE_XWIKI_DIFF, XWikiException.ERROR_XWIKI_DIFF_OBJECT_ERROR,\n \"Error while making meta object diff of {0} between version {1} and version {2}\", e, args);\n String errormsg = Util.getHTMLExceptionMessage(xe, getXWikiContext());\n list.add(errormsg);\n return list;\n }\n }\n\n public List<List<ObjectDiff>> getClassDiff(Document origdoc, Document newdoc)\n {\n try {\n if ((origdoc == null) && (newdoc == null)) {\n return Collections.emptyList();\n }\n if (origdoc == null) {\n return this.doc.getClassDiff(new XWikiDocument(newdoc.getDocumentReference()), newdoc.doc,\n getXWikiContext());\n }\n if (newdoc == null) {\n return this.doc.getClassDiff(origdoc.doc, new XWikiDocument(origdoc.getDocumentReference()),\n getXWikiContext());\n }\n\n return this.doc.getClassDiff(origdoc.doc, newdoc.doc, getXWikiContext());\n } catch (Exception e) {\n java.lang.Object[] args = { origdoc.getFullName(), origdoc.getVersion(), newdoc.getVersion() };\n List list = new ArrayList();\n XWikiException xe =\n new XWikiException(XWikiException.MODULE_XWIKI_DIFF, XWikiException.ERROR_XWIKI_DIFF_CLASS_ERROR,\n \"Error while making class diff of {0} between version {1} and version {2}\", e, args);\n String errormsg = Util.getHTMLExceptionMessage(xe, getXWikiContext());\n list.add(errormsg);\n return list;\n }\n }\n\n public List<AttachmentDiff> getAttachmentDiff(Document origdoc, Document newdoc)\n {\n try {\n if ((origdoc == null) && (newdoc == null)) {\n return Collections.emptyList();\n }\n if (origdoc == null) {\n return wrapAttachmentDiff(this.doc.getAttachmentDiff(new XWikiDocument(newdoc.getDocumentReference()),\n newdoc.doc, getXWikiContext()));\n }\n if (newdoc == null) {\n return wrapAttachmentDiff(this.doc.getAttachmentDiff(origdoc.doc,\n new XWikiDocument(origdoc.getDocumentReference()), getXWikiContext()));\n }\n\n return wrapAttachmentDiff(this.doc.getAttachmentDiff(origdoc.doc, newdoc.doc, getXWikiContext()));\n } catch (Exception e) {\n java.lang.Object[] args = { (origdoc != null) ? origdoc.getFullName() : null,\n (origdoc != null) ? origdoc.getVersion() : null, (newdoc != null) ? newdoc.getVersion() : null };\n List list = new ArrayList();\n XWikiException xe =\n new XWikiException(XWikiException.MODULE_XWIKI_DIFF, XWikiException.ERROR_XWIKI_DIFF_ATTACHMENT_ERROR,\n \"Error while making attachment diff of {0} between version {1} and version {2}\", e, args);\n String errormsg = Util.getHTMLExceptionMessage(xe, getXWikiContext());\n list.add(errormsg);\n return list;\n }\n }\n\n private List<AttachmentDiff> wrapAttachmentDiff(List<com.xpn.xwiki.doc.AttachmentDiff> diffs)\n {\n List<AttachmentDiff> safeAttachmentDiffs = new ArrayList<>();\n for (com.xpn.xwiki.doc.AttachmentDiff diff : diffs) {\n safeAttachmentDiffs.add(new AttachmentDiff(diff, getXWikiContext()));\n }\n return safeAttachmentDiffs;\n }\n\n public List<Delta> getLastChanges() throws XWikiException, DifferentiationFailedException\n {\n return this.doc.getLastChanges(getXWikiContext());\n }\n\n /**\n * Get statistics about the number of request for the current page during the current month.\n *\n * @param action the type of request for which to retrieve statistics: view, edit...\n * @return the statistics object holding information for this document and the current month\n */\n public DocumentStats getCurrentMonthPageStats(String action)\n {\n Scope scope = ScopeFactory.createPageScope(this.getFullName());\n Range range = RangeFactory.ALL;\n Period period = PeriodFactory.getCurrentMonth();\n XWikiStatsService statisticsService = getXWikiContext().getWiki().getStatsService(getXWikiContext());\n List<DocumentStats> stats = statisticsService.getDocumentStatistics(action, scope, period, range, this.context);\n if (stats.size() > 0) {\n return stats.get(0);\n }\n return new DocumentStats();\n }\n\n /**\n * Get statistics about the number of request for the current space during the current month.\n *\n * @param action the type of request for which to retrieve statistics: view, edit...\n * @return the statistics object holding information for the document's space and the current month\n */\n public DocumentStats getCurrentMonthSpaceStats(String action)\n {\n Scope scope = ScopeFactory.createSpaceScope(this.doc.getSpace(), false);\n Range range = RangeFactory.ALL;\n Period period = PeriodFactory.getCurrentMonth();\n XWikiStatsService statisticsService = getXWikiContext().getWiki().getStatsService(getXWikiContext());\n List<DocumentStats> stats = statisticsService.getDocumentStatistics(action, scope, period, range, this.context);\n if (stats.size() > 0) {\n return stats.get(0);\n }\n return new DocumentStats();\n }\n\n /**\n * Get referer statistics for the current document during the current month.\n *\n * @return a list of referer statistics for the document's space\n */\n public List<RefererStats> getCurrentMonthRefStats()\n {\n Scope scope = ScopeFactory.createPageScope(this.getFullName());\n Range range = RangeFactory.ALL;\n Period period = PeriodFactory.getCurrentMonth();\n XWikiStatsService statisticsService = getXWikiContext().getWiki().getStatsService(getXWikiContext());\n List<RefererStats> stats = statisticsService.getRefererStatistics(\"\", scope, period, range, this.context);\n return stats;\n }\n\n public boolean checkAccess(String right)\n {\n try {\n return getXWikiContext().getWiki().checkAccess(right, this.doc, getXWikiContext());\n } catch (XWikiException e) {\n return false;\n }\n }\n\n public boolean hasAccessLevel(String level)\n {\n try {\n return getXWikiContext().getWiki().getRightService().hasAccessLevel(level, getXWikiContext().getUser(),\n this.getPrefixedFullName(), getXWikiContext());\n } catch (Exception e) {\n return false;\n }\n }\n\n @Override\n public boolean hasAccessLevel(String level, String user)\n {\n try {\n return getXWikiContext().getWiki().getRightService().hasAccessLevel(level, user, this.getPrefixedFullName(),\n getXWikiContext());\n } catch (Exception e) {\n return false;\n }\n }\n\n /**\n * Verifies if the user identified by {@code userReference} has the access identified by {@code right} on this\n * document.\n * \n * @param right the right to check\n * @param userReference the user to check the right for\n * @return {@code true} if the user has the specified right on this document, {@code false} otherwise\n * @since 10.6RC1\n */\n public boolean hasAccess(Right right, DocumentReference userReference)\n {\n return getAuthorizationManager().hasAccess(right, userReference, getDocumentReference());\n }\n\n public boolean getLocked()\n {\n try {\n XWikiLock lock = this.doc.getLock(getXWikiContext());\n if (lock != null && !getXWikiContext().getUser().equals(lock.getUserName())) {\n return true;\n } else {\n return false;\n }\n } catch (Exception e) {\n return false;\n }\n }\n\n public String getLockingUser()\n {\n try {\n XWikiLock lock = this.doc.getLock(getXWikiContext());\n if (lock != null && !getXWikiContext().getUser().equals(lock.getUserName())) {\n return lock.getUserName();\n } else {\n return \"\";\n }\n } catch (XWikiException e) {\n return \"\";\n }\n }\n\n public Date getLockingDate()\n {\n try {\n XWikiLock lock = this.doc.getLock(getXWikiContext());\n if (lock != null && !getXWikiContext().getUser().equals(lock.getUserName())) {\n return lock.getDate();\n } else {\n return null;\n }\n } catch (XWikiException e) {\n return null;\n }\n }\n\n /**\n * Renders the passed xproperty as HTML. Note that if you need the raw value, you should call \n * {@link #getValue(String)} instead. \n *\n * @param classOrFieldName the xproperty (aka field) name to render or an xclass reference\n * @return the rendered xproperty as HTML if an xobject exists with that xproperty. Otherwise considers that the\n * passed parameter is an xclass reference and return the xobject for it or null if none exist\n * @see #getValue(String) \n * @see #getValue(String, Object) \n */\n public java.lang.Object get(String classOrFieldName)\n {\n if (this.currentObj != null) {\n return this.doc.display(classOrFieldName, this.currentObj.getBaseObject(), getXWikiContext());\n }\n BaseObject object = this.doc.getFirstObject(classOrFieldName, getXWikiContext());\n if (object != null) {\n return this.doc.display(classOrFieldName, object, getXWikiContext());\n }\n return this.getDoc().getObject(classOrFieldName);\n }\n\n /**\n * @param fieldName the xproperty (aka field) name for which to get the value\n * @return the raw value of the passed xproperty found in the current xobject or in the first xobject containing\n * such a field\n * @see #getValue(String, Object) \n */\n public java.lang.Object getValue(String fieldName)\n {\n Object object;\n if (this.currentObj == null) {\n object = new Object(this.getDoc().getFirstObject(fieldName, getXWikiContext()), getXWikiContext());\n } else {\n object = this.currentObj;\n }\n return getValue(fieldName, object);\n }\n\n /**\n * @param fieldName the xproperty (aka field) name for which to get the value\n * @param object the specific xobject from which to get the xproperty value\n * @return the raw value of the passed xproperty\n * @see #getValue(String)\n */\n public java.lang.Object getValue(String fieldName, Object object)\n {\n if (object != null) {\n return object.getValue(fieldName);\n }\n\n return null;\n }\n\n public String getTextArea()\n {\n return com.xpn.xwiki.XWiki.getTextArea(this.doc.getContent(), getXWikiContext());\n }\n\n /**\n * Returns data needed for a generation of Table of Content for this document.\n *\n * @param init an intial level where the TOC generation should start at\n * @param max maximum level TOC is generated for\n * @param numbered if should generate numbering for headings\n * @return a map where an heading (title) ID is the key and value is another map with two keys: text, level and\n * numbering\n */\n public Map<String, Map<String, java.lang.Object>> getTOC(int init, int max, boolean numbered)\n {\n getXWikiContext().put(\"tocNumbered\", new Boolean(numbered));\n return TOCGenerator.generateTOC(getContent(), init, max, numbered, getXWikiContext());\n }\n\n public String getTags()\n {\n return this.doc.getTags(getXWikiContext());\n }\n\n public List<String> getTagList()\n {\n return this.getDoc().getTagsList(getXWikiContext());\n }\n\n public List<String> getTagsPossibleValues()\n {\n return this.doc.getTagsPossibleValues(getXWikiContext());\n }\n\n public void insertText(String text, String marker) throws XWikiException\n {\n if (hasAccessLevel(\"edit\")) {\n getDoc().insertText(text, marker, getXWikiContext());\n\n updateAuthor();\n updateContentAuthor();\n }\n }\n\n @Override\n public boolean equals(java.lang.Object arg0)\n {\n if (!(arg0 instanceof Document)) {\n return false;\n }\n Document d = (Document) arg0;\n return d.getXWikiContext().equals(getXWikiContext()) && this.doc.equals(d.doc);\n }\n\n /**\n * Check if the passed one is the one wrapped by this {@link Document}.\n * \n * @param document the document to compare\n * @return true if passed document is the wrapped one\n * @since 8.3M1\n */\n public boolean same(XWikiDocument document)\n {\n return document == this.doc || document == this.initialDoc;\n }\n\n public List<String> getBacklinks() throws XWikiException\n {\n return this.doc.getBackLinkedPages(getXWikiContext());\n }\n\n /**\n * Retrieve the references of the page containing a link to the current page.\n *\n * @return a list of references of the page containing a link to the current page.\n * @throws XWikiException in case of problem to perform the query.\n * @since 12.5RC1\n */\n public List<DocumentReference> getBackLinkedReferences() throws XWikiException\n {\n return this.doc.getBackLinkedReferences(getXWikiContext());\n }\n\n public List<XWikiLink> getLinks() throws XWikiException\n {\n return new ArrayList<XWikiLink>(this.doc.getUniqueWikiLinkedPages(getXWikiContext()));\n }\n\n /**\n * Get the top sections contained in the document.\n * <p>\n * The section are filtered by xwiki.section.depth property on the maximum depth of the sections to return. This\n * method is usually used to get \"editable\" sections.\n *\n * @return the sections in the current document\n */\n public List<DocumentSection> getSections() throws XWikiException\n {\n return this.doc.getSections();\n }\n\n /**\n * Get document children. Children are documents with the current document as parent.\n *\n * @return The list of children for the current document.\n * @since 1.8 Milestone 2\n */\n public List<String> getChildren() throws XWikiException\n {\n return this.doc.getChildren(getXWikiContext());\n }\n\n /**\n * Get document children references. Children are documents with the current document as parent.\n * @return The list of children for the current document.\n * @throws XWikiException in case of problem to query the children.\n * @since 12.5RC1\n */\n public List<DocumentReference> getChildrenReferences() throws XWikiException\n {\n return this.doc.getChildrenReferences(getXWikiContext());\n }\n\n /**\n * Get document children. Children are documents with the current document as parent. Where a document has a large\n * number of children, one may desire to return a certain number of children (nb) and skip some number (start) of\n * the first results.\n *\n * @param nb The number of results to return.\n * @param start The number of results to skip before we begin returning results.\n * @return The list of children for the current document.\n */\n public List<String> getChildren(int nb, int start) throws XWikiException\n {\n return this.doc.getChildren(nb, start, getXWikiContext());\n }\n\n /**\n * Gets the default edit mode for this document. An edit mode (other than the default \"edit\") can be enforced by\n * creating an {@code XWiki.EditModeClass} object in the current document, with the appropriate value for the\n * defaultEditMode property, or by adding this object in a sheet included by the document. This function also falls\n * back on the old {@code SheetClass}, deprecated since 3.1M2, which can be attached to included documents to\n * specify that the current document should be edited inline.\n *\n * @return the default edit mode for this document (\"edit\" or \"inline\" usually)\n * @throws XWikiException since XWiki 6.3M1 it's not used anymore and \"edit\" is returned in case of error, with an\n * error log\n */\n public String getDefaultEditMode() throws XWikiException\n {\n return this.doc.getDefaultEditMode(getXWikiContext());\n }\n\n public String getDefaultEditURL() throws XWikiException\n {\n return this.doc.getDefaultEditURL(getXWikiContext());\n }\n\n public String getEditURL(String action, String mode) throws XWikiException\n {\n return this.doc.getEditURL(action, mode, getXWikiContext());\n }\n\n public String getEditURL(String action, String mode, String locale)\n {\n return this.doc.getEditURL(action, mode, locale, getXWikiContext());\n }\n\n public boolean isCurrentUserCreator()\n {\n return this.doc.isCurrentUserCreator(getXWikiContext());\n }\n\n public boolean isCurrentUserPage()\n {\n return this.doc.isCurrentUserPage(getXWikiContext());\n }\n\n public boolean isCurrentLocalUserPage()\n {\n return this.doc.isCurrentLocalUserPage(getXWikiContext());\n }\n\n public boolean isCreator(String username)\n {\n return this.doc.isCreator(username);\n }\n\n public void set(String fieldname, java.lang.Object value)\n {\n Object obj;\n if (this.currentObj != null) {\n obj = this.currentObj;\n } else {\n obj = getFirstObject(fieldname);\n }\n set(fieldname, value, obj);\n }\n\n public void set(String fieldname, java.lang.Object value, Object obj)\n {\n if (obj == null) {\n return;\n }\n obj.set(fieldname, value);\n }\n\n public void setTitle(String title)\n {\n getDoc().setTitle(title);\n\n updateAuthor();\n updateContentAuthor();\n }\n\n public void setCustomClass(String customClass)\n {\n getDoc().setCustomClass(customClass);\n\n updateAuthor();\n }\n\n public void setParent(String parent)\n {\n getDoc().setParent(parent);\n\n updateAuthor();\n }\n\n private void updateContentAuthor()\n {\n // Temporary set as content author of the document the current script author (until the document is saved)\n XWikiContext xcontext = getXWikiContext();\n getDoc().setContentAuthorReference(xcontext.getAuthorReference());\n }\n\n private void updateAuthor()\n {\n // Temporary set as author of the document the current script author (until the document is saved)\n XWikiContext xcontext = getXWikiContext();\n getDoc().setAuthorReference(xcontext.getAuthorReference());\n }\n\n public void setContent(String content)\n {\n getDoc().setContent(content);\n\n updateAuthor();\n updateContentAuthor();\n }\n\n /**\n * @param content the content as XDOM\n * @throws XWikiException when failing to convert the XDOM to String content\n * @since 7.0RC1\n */\n public void setContent(XDOM content) throws XWikiException\n {\n getDoc().setContent(content);\n\n updateAuthor();\n updateContentAuthor();\n }\n\n /**\n * @param syntax the Syntax representing the syntax used for the current document's content.\n * @since 2.3M1\n */\n public void setSyntax(Syntax syntax)\n {\n getDoc().setSyntax(syntax);\n\n updateAuthor();\n updateContentAuthor();\n }\n\n /**\n * @param syntaxId the Syntax id representing the syntax used for the current document. For example \"xwiki/1.0\"\n * represents the first version XWiki syntax while \"xwiki/2.0\" represents version 2.0 of the XWiki\n * Syntax.\n */\n public void setSyntaxId(String syntaxId)\n {\n getDoc().setSyntaxId(syntaxId);\n\n updateAuthor();\n updateContentAuthor();\n }\n\n public void setDefaultTemplate(String dtemplate)\n {\n getDoc().setDefaultTemplate(dtemplate);\n\n updateAuthor();\n }\n\n public void setComment(String comment)\n {\n getDoc().setComment(comment);\n }\n\n public void setMinorEdit(boolean isMinor)\n {\n getDoc().setMinorEdit(isMinor);\n }\n\n public void save() throws XWikiException\n {\n save(\"\", false);\n }\n\n public void save(String comment) throws XWikiException\n {\n save(comment, false);\n }\n\n private UserReferenceResolver<CurrentUserReference> getCurrentUserReferenceResolver()\n {\n return Utils.getComponent(new DefaultParameterizedType(null, UserReferenceResolver.class,\n CurrentUserReference.class));\n }\n\n public void save(String comment, boolean minorEdit) throws XWikiException\n {\n if (hasAccessLevel(\"edit\")) {\n\n DocumentAuthors authors = this.getAuthors();\n authors.setOriginalMetadataAuthor(getCurrentUserReferenceResolver().resolve(CurrentUserReference.INSTANCE));\n // If the current author does not have PR don't let it set current user as author of the saved document\n // since it can lead to right escalation\n if (hasProgrammingRights() || !getConfiguration().getProperty(\"security.script.save.checkAuthor\", true)) {\n saveDocument(comment, minorEdit);\n } else {\n saveAsAuthor(comment, minorEdit);\n }\n } else {\n java.lang.Object[] args = {getDefaultEntityReferenceSerializer().serialize(getDocumentReference())};\n throw new XWikiException(XWikiException.MODULE_XWIKI_ACCESS, XWikiException.ERROR_XWIKI_ACCESS_DENIED,\n \"Access denied in edit mode on document {0}\", null, args);\n }\n }\n\n public void saveWithProgrammingRights() throws XWikiException\n {\n saveWithProgrammingRights(\"\", false);\n }\n\n public void saveWithProgrammingRights(String comment) throws XWikiException\n {\n saveWithProgrammingRights(comment, false);\n }\n\n public void saveWithProgrammingRights(String comment, boolean minorEdit) throws XWikiException\n {\n if (hasProgrammingRights()) {\n // The rights check above is generic, but the current method is a save operation, thus it should not be\n // performed if the document's wiki is in read only mode.\n XWikiContext context = getXWikiContext();\n String currentWikiId = context.getWikiId();\n try {\n // Make sure we check the current document's wiki and not the current context's wiki.\n context.setWikiId(getWiki());\n\n if (!context.getWiki().isReadOnly()) {\n saveDocument(comment, minorEdit, false);\n } else {\n java.lang.Object[] args =\n { getDefaultEntityReferenceSerializer().serialize(getDocumentReference()), getWiki() };\n throw new XWikiException(XWikiException.MODULE_XWIKI_ACCESS,\n XWikiException.ERROR_XWIKI_ACCESS_DENIED,\n \"Access denied in edit mode on document [{0}]. The wiki [{1}] is in read only mode.\", null,\n args);\n }\n } finally {\n // Restore the context wiki.\n context.setWikiId(currentWikiId);\n }\n } else {\n java.lang.Object[] args = { this.getFullName() };\n throw new XWikiException(XWikiException.MODULE_XWIKI_ACCESS, XWikiException.ERROR_XWIKI_ACCESS_DENIED,\n \"Access denied with no programming rights document {0}\", null, args);\n }\n }\n\n /**\n * Save the document if the current author of the script calling this method has permission to do so. The author of\n * this document is also set to the said author.\n *\n * @throws XWikiException if script author is not allowed to save the document or if save operation fails.\n * @since 2.3M2\n */\n public void saveAsAuthor() throws XWikiException\n {\n saveAsAuthor(\"\", false);\n }\n\n /**\n * Save the document if the current author of the script calling this method has permission to do so. The author of\n * this document is also set to the said author.\n *\n * @param comment The comment to display in document history (what did you change in the document)\n * @throws XWikiException if script author is not allowed to save the document or if save operation fails.\n * @since 2.3M2\n */\n public void saveAsAuthor(String comment) throws XWikiException\n {\n saveAsAuthor(comment, false);\n }\n\n /**\n * Save the document if the current author of the script calling this method has permission to do so. The author of\n * this document is also set to the said author.\n *\n * @param comment The comment to display in document history (what did you change in the document)\n * @param minorEdit Set true to advance the document version number by 0.1 or false to advance version to the next\n * integer + 0.1 eg: 25.1\n * @throws XWikiException if script author is not allowed to save the document or if save operation fails.\n * @since 2.3M2\n */\n public void saveAsAuthor(String comment, boolean minorEdit) throws XWikiException\n {\n XWikiContext xcontext = getXWikiContext();\n\n getAuthors()\n .setOriginalMetadataAuthor(getCurrentUserReferenceResolver().resolve(CurrentUserReference.INSTANCE));\n DocumentReference author = getEffectiveAuthorReference();\n if (hasAccess(Right.EDIT, author)) {\n DocumentReference currentUser = xcontext.getUserReference();\n try {\n xcontext.setUserReference(author);\n\n saveDocument(comment, minorEdit);\n } finally {\n xcontext.setUserReference(currentUser);\n }\n } else {\n java.lang.Object[] args = { author, xcontext.getDoc(), getFullName() };\n throw new XWikiException(XWikiException.MODULE_XWIKI_ACCESS, XWikiException.ERROR_XWIKI_ACCESS_DENIED,\n \"Access denied; user {0}, acting through script in document {1} cannot save document {2}\", null, args);\n }\n }\n\n protected void saveDocument(String comment, boolean minorEdit) throws XWikiException\n {\n saveDocument(comment, minorEdit, true);\n }\n\n private void saveDocument(String comment, boolean minorEdit, boolean checkSaving) throws XWikiException\n {\n XWikiDocument doc = getDoc();\n\n UserReference currentUserReference = getCurrentUserReferenceResolver().resolve(CurrentUserReference.INSTANCE);\n doc.getAuthors().setEffectiveMetadataAuthor(currentUserReference);\n\n if (doc.isNew()) {\n doc.getAuthors().setCreator(currentUserReference);\n }\n\n if (checkSaving) {\n // Make sure the user is allowed to make this modification\n getXWikiContext().getWiki().checkSavingDocument(doc.getAuthorReference(), doc, comment, minorEdit,\n getXWikiContext());\n }\n\n getXWikiContext().getWiki().saveDocument(doc, comment, minorEdit, getXWikiContext());\n this.initialDoc = this.doc;\n }\n\n public com.xpn.xwiki.api.Object addObjectFromRequest() throws XWikiException\n {\n // Call to getDoc() ensures that we are working on a clone()\n com.xpn.xwiki.api.Object obj =\n new com.xpn.xwiki.api.Object(getDoc().addXObjectFromRequest(getXWikiContext()), getXWikiContext());\n\n updateAuthor();\n\n return obj;\n }\n\n public com.xpn.xwiki.api.Object addObjectFromRequest(String className) throws XWikiException\n {\n com.xpn.xwiki.api.Object obj = new com.xpn.xwiki.api.Object(\n getDoc().addObjectFromRequest(className, getXWikiContext()), getXWikiContext());\n\n updateAuthor();\n\n return obj;\n }\n\n public List<Object> addObjectsFromRequest(String className) throws XWikiException\n {\n return addObjectsFromRequest(className, \"\");\n }\n\n public com.xpn.xwiki.api.Object addObjectFromRequest(String className, String prefix) throws XWikiException\n {\n com.xpn.xwiki.api.Object obj = new com.xpn.xwiki.api.Object(\n getDoc().addObjectFromRequest(className, prefix, getXWikiContext()), getXWikiContext());\n\n updateAuthor();\n\n return obj;\n }\n\n public List<Object> addObjectsFromRequest(String className, String prefix) throws XWikiException\n {\n List<BaseObject> objs = getDoc().addObjectsFromRequest(className, prefix, getXWikiContext());\n List<Object> wrapped = new ArrayList<Object>();\n for (BaseObject object : objs) {\n wrapped.add(new com.xpn.xwiki.api.Object(object, getXWikiContext()));\n }\n\n updateAuthor();\n\n return wrapped;\n }\n\n public com.xpn.xwiki.api.Object updateObjectFromRequest(String className) throws XWikiException\n {\n com.xpn.xwiki.api.Object obj = new com.xpn.xwiki.api.Object(\n getDoc().updateObjectFromRequest(className, getXWikiContext()), getXWikiContext());\n\n updateAuthor();\n\n return obj;\n }\n\n public List<Object> updateObjectsFromRequest(String className) throws XWikiException\n {\n return updateObjectsFromRequest(className, \"\");\n }\n\n public com.xpn.xwiki.api.Object updateObjectFromRequest(String className, String prefix) throws XWikiException\n {\n com.xpn.xwiki.api.Object obj = new com.xpn.xwiki.api.Object(\n getDoc().updateObjectFromRequest(className, prefix, getXWikiContext()), getXWikiContext());\n\n updateAuthor();\n\n return obj;\n }\n\n public List<Object> updateObjectsFromRequest(String className, String prefix) throws XWikiException\n {\n List<BaseObject> objs = getDoc().updateObjectsFromRequest(className, prefix, getXWikiContext());\n List<Object> wrapped = new ArrayList<Object>();\n for (BaseObject object : objs) {\n wrapped.add(new com.xpn.xwiki.api.Object(object, getXWikiContext()));\n }\n\n updateAuthor();\n\n return wrapped;\n }\n\n public boolean isAdvancedContent()\n {\n return this.doc.isAdvancedContent();\n }\n\n public boolean isProgrammaticContent()\n {\n return this.doc.isProgrammaticContent();\n }\n\n /**\n * Remove an XObject from the document. The changes are not persisted until the document is saved.\n *\n * @param object the object to remove\n * @return {@code true} if the object was successfully removed, {@code false} if the object was not found in the\n * current document.\n */\n public boolean removeObject(Object object)\n {\n return getDoc().removeObject(object.getBaseObject());\n }\n\n /**\n * Remove all the objects of a given type (XClass) from the document. The object counter is left unchanged, so that\n * future objects will have new (different) numbers. However, on some storage engines the counter will be reset if\n * the document is removed from the cache and reloaded from the persistent storage.\n *\n * @param className The class name of the objects to be removed.\n * @return {@code true} if the objects were successfully removed, {@code false} if no object from the target class\n * was in the current document.\n */\n public boolean removeObjects(String className)\n {\n return getDoc().removeObjects(className);\n }\n\n /**\n * Remove document from the wiki. Reinit <code>cloned</code>.\n *\n * @throws XWikiException\n */\n protected void deleteDocument() throws XWikiException\n {\n getXWikiContext().getWiki().deleteDocument(this.doc, getXWikiContext());\n this.initialDoc = this.doc;\n }\n\n public void delete() throws XWikiException\n {\n if (hasAccessLevel(\"delete\")) {\n deleteDocument();\n } else {\n java.lang.Object[] args = { this.getFullName() };\n throw new XWikiException(XWikiException.MODULE_XWIKI_ACCESS, XWikiException.ERROR_XWIKI_ACCESS_DENIED,\n \"Access denied in edit mode on document {0}\", null, args);\n }\n }\n\n /**\n * Delete the document if the {@link #getContentAuthor content author} of the script calling this method has\n * permission to do so. The deleter is also set to the said content author.\n *\n * @throws XWikiException if script author is not allowed to delete the document or if save operation fails.\n * @since 2.3M2\n */\n public void deleteAsAuthor() throws XWikiException\n {\n XWikiContext xcontext = getXWikiContext();\n\n DocumentReference author = getEffectiveAuthorReference();\n if (hasAccess(Right.DELETE, author)) {\n DocumentReference currentUser = xcontext.getUserReference();\n try {\n xcontext.setUserReference(author);\n\n deleteDocument();\n } finally {\n xcontext.setUserReference(currentUser);\n }\n } else {\n java.lang.Object[] args = { author, xcontext.getDoc(), this.getFullName() };\n throw new XWikiException(XWikiException.MODULE_XWIKI_ACCESS, XWikiException.ERROR_XWIKI_ACCESS_DENIED,\n \"Access denied; user {0}, acting through script in document {1} cannot delete document {2}\", null,\n args);\n }\n }\n\n public void deleteWithProgrammingRights() throws XWikiException\n {\n if (hasProgrammingRights()) {\n deleteDocument();\n } else {\n java.lang.Object[] args = { this.getFullName() };\n throw new XWikiException(XWikiException.MODULE_XWIKI_ACCESS, XWikiException.ERROR_XWIKI_ACCESS_DENIED,\n \"Access denied with no programming rights document {0}\", null, args);\n }\n }\n\n public String getVersionHashCode()\n {\n return this.doc.getVersionHashCode(getXWikiContext());\n }\n\n public int addAttachments() throws XWikiException\n {\n return addAttachments(null);\n }\n\n public int addAttachments(String fieldName) throws XWikiException\n {\n if (!hasAccessLevel(\"edit\")) {\n java.lang.Object[] args = { this.getFullName() };\n throw new XWikiException(XWikiException.MODULE_XWIKI_ACCESS, XWikiException.ERROR_XWIKI_ACCESS_DENIED,\n \"Access denied in edit mode on document {0}\", null, args);\n }\n XWiki xwiki = getXWikiContext().getWiki();\n FileUploadPlugin fileupload = (FileUploadPlugin) xwiki.getPlugin(\"fileupload\", getXWikiContext());\n List<FileItem> fileuploadlist = fileupload.getFileItems(getXWikiContext());\n List<XWikiAttachment> attachments = new ArrayList<XWikiAttachment>();\n // adding attachment list to context so we find the names\n this.context.put(\"addedAttachments\", attachments);\n int nb = 0;\n\n if (fileuploadlist == null) {\n return 0;\n }\n\n for (FileItem item : fileuploadlist) {\n String name = item.getFieldName();\n if (fieldName != null && !fieldName.equals(name)) {\n continue;\n }\n if (item.isFormField()) {\n continue;\n }\n byte[] data = fileupload.getFileItemData(name, getXWikiContext());\n String filename;\n String fname = fileupload.getFileName(name, getXWikiContext());\n int i = fname.lastIndexOf(\"\\\\\");\n if (i == -1) {\n i = fname.lastIndexOf(\"/\");\n }\n filename = fname.substring(i + 1);\n filename = filename.replaceAll(\"\\\\+\", \" \");\n\n if ((data != null) && (data.length > 0)) {\n XWikiAttachment attachment = this.getDoc().addAttachment(filename, data, getXWikiContext());\n getDoc().saveAttachmentContent(attachment, getXWikiContext());\n // commenting because this was already done by addAttachment\n // getDoc().getAttachmentList().add(attachment);\n attachments.add(attachment);\n nb++;\n }\n }\n\n if (nb > 0) {\n getXWikiContext().getWiki().saveDocument(getDoc(), getXWikiContext());\n this.initialDoc = this.doc;\n }\n\n return nb;\n }\n\n public Attachment addAttachment(String fileName, InputStream iStream)\n {\n try {\n return new Attachment(this, this.getDoc().setAttachment(fileName, iStream, getXWikiContext()),\n getXWikiContext());\n } catch (IOException e) {\n // TODO Log the error and let the user know about it\n } finally {\n updateAuthor();\n }\n\n return null;\n }\n\n public Attachment addAttachment(String fileName, byte[] data)\n {\n try {\n return new Attachment(this, this.getDoc().addAttachment(fileName, data, getXWikiContext()),\n getXWikiContext());\n } catch (XWikiException e) {\n // TODO Log the error and let the user know about it\n } finally {\n updateAuthor();\n }\n\n return null;\n }\n\n public boolean validate() throws XWikiException\n {\n return this.doc.validate(getXWikiContext());\n }\n\n public boolean validate(String[] classNames) throws XWikiException\n {\n return this.doc.validate(classNames, getXWikiContext());\n }\n\n /**\n * Retrieves the validation script associated with this document, a Velocity script that is executed when validating\n * the document data.\n *\n * @return A <code>String</code> representation of the validation script, or an empty string if there is no such\n * script.\n */\n public String getValidationScript()\n {\n return getDoc().getValidationScript();\n }\n\n /**\n * Sets a new validation script for this document, a Velocity script that is executed when validating the document\n * data.\n *\n * @param validationScript The new validation script, which can be an empty string or <code>null</code> if the\n * script should be removed.\n */\n public void setValidationScript(String validationScript)\n {\n getDoc().setValidationScript(validationScript);\n\n updateAuthor();\n }\n\n /**\n * Rename the current document and all the backlinks leading to it. Will also change parent field in all documents\n * which list the document we are renaming as their parent. See\n * {@link #rename(String, java.util.List, java.util.List)} for more details.\n *\n * @param newReference the reference to the new document\n * @throws XWikiException in case of an error\n * @since 2.3M2\n */\n public void rename(DocumentReference newReference) throws XWikiException\n {\n if (hasAccessLevel(\"delete\") && this.context.getWiki().checkAccess(\"edit\",\n this.context.getWiki().getDocument(newReference, this.context), this.context)) {\n this.getDoc().rename(newReference, getXWikiContext());\n }\n }\n\n /**\n * Rename the current document and all the links pointing to it in the list of passed backlink documents. The\n * renaming algorithm takes into account the fact that there are several ways to write a link to a given page and\n * all those forms need to be renamed. For example the following links all point to the same page:\n * <ul>\n * <li>[Page]</li>\n * <li>[Page?param=1]</li>\n * <li>[currentwiki:Page]</li>\n * <li>[currentwiki:CurrentSpace.Page]</li>\n * </ul>\n * <p>\n * Note: links without a space are renamed with the space added and all documents which have the document being\n * renamed as parent have their parent field set to \"currentwiki:CurrentSpace.Page\".\n * </p>\n *\n * @param newDocumentName the new document name. If the space is not specified then defaults to the current space.\n * @param backlinkDocumentNames the list of documents to parse and for which links will be modified to point to the\n * new renamed document.\n * @throws XWikiException in case of an error\n */\n public void rename(String newDocumentName, List<String> backlinkDocumentNames) throws XWikiException\n {\n rename(newDocumentName, backlinkDocumentNames, Collections.emptyList());\n }\n\n /**\n * Same as {@link #rename(String, List)} but the list of documents having the current document as their parent is\n * passed in parameter.\n *\n * @param newDocumentName the new document name. If the space is not specified then defaults to the current space.\n * @param backlinkDocumentNames the list of documents to parse and for which links will be modified to point to the\n * new renamed document.\n * @param childDocumentNames the list of documents whose parent field will be set to the new document name.\n * @throws XWikiException in case of an error\n */\n public void rename(String newDocumentName, List<String> backlinkDocumentNames, List<String> childDocumentNames)\n throws XWikiException\n {\n List<DocumentReference> backlinkDocumentReferences = new ArrayList<DocumentReference>();\n for (String backlinkDocumentName : backlinkDocumentNames) {\n backlinkDocumentReferences.add(getCurrentMixedDocumentReferenceResolver().resolve(backlinkDocumentName));\n }\n\n List<DocumentReference> childDocumentReferences = new ArrayList<DocumentReference>();\n for (String childDocumentName : childDocumentNames) {\n childDocumentReferences.add(getCurrentMixedDocumentReferenceResolver().resolve(childDocumentName));\n }\n\n rename(getCurrentMixedDocumentReferenceResolver().resolve(newDocumentName), backlinkDocumentReferences,\n childDocumentReferences);\n }\n\n /**\n * Same as {@link #rename(String, List)} but the list of documents having the current document as their parent is\n * passed in parameter.\n *\n * @param newReference the reference to the new document\n * @param backlinkDocumentNames the list of reference to documents to parse and for which links will be modified to\n * point to the new renamed document\n * @param childDocumentNames the list of references to documents whose parent field will be set to the new document\n * reference\n * @throws XWikiException in case of an error\n * @since 2.3M2\n */\n public void rename(DocumentReference newReference, List<DocumentReference> backlinkDocumentNames,\n List<DocumentReference> childDocumentNames) throws XWikiException\n {\n if (hasAccessLevel(\"delete\") && this.context.getWiki().checkAccess(\"edit\",\n this.context.getWiki().getDocument(newReference, this.context), this.context)) {\n\n // Every page given in childDocumentNames has it's parent changed whether it needs it or not.\n // Let's make sure the user has edit permission on any page given which is not actually a child.\n // Otherwise it would be embarrassing if a user called:\n // $doc.rename(\"mynewpage\",$doc.getBacklinks(),$xwiki.searchDocuments(\"true\"))\n int counter = childDocumentNames.size();\n List<String> actuallyChildren = getChildren();\n while (counter > 0) {\n counter--;\n if (!actuallyChildren.contains(childDocumentNames.get(counter))\n && !this.context.getWiki().checkAccess(\"edit\",\n this.context.getWiki().getDocument(childDocumentNames.get(counter), this.context),\n this.context)) {\n return;\n }\n }\n\n this.getDoc().rename(newReference, backlinkDocumentNames, childDocumentNames, getXWikiContext());\n }\n }\n\n /**\n * Allow to easily access any revision of a document\n *\n * @param revision the version to access\n * @return the document corresponding to the requested revision or {@code null} if the revision does not exist.\n */\n public Document getDocumentRevision(String revision)\n {\n try {\n XWikiDocument documentRevision = getDocumentRevisionProvider().getRevision(this.doc, revision);\n\n return documentRevision != null ? new Document(documentRevision, this.context) : null;\n } catch (Exception e) {\n LOGGER.error(\"Failed to load revision [{}] of document [{}]\", revision, getDocumentReferenceWithLocale(),\n e);\n\n return null;\n }\n }\n\n /**\n * Allow to easily access the previous revision of a document\n *\n * @return Document\n * @throws XWikiException\n */\n public Document getPreviousDocument() throws XWikiException\n {\n return getDocumentRevision(getPreviousVersion());\n }\n\n /**\n * @return is document most recent. false if and only if there are older versions of this document.\n */\n public boolean isMostRecent()\n {\n return this.doc.isMostRecent();\n }\n\n @Override\n public String toString()\n {\n return this.doc.toString();\n }\n\n /**\n * Convert the current document content from its current syntax to the new syntax passed as parameter.\n *\n * @param targetSyntaxId the syntax to convert to (eg \"xwiki/2.0\", \"xhtml/1.0\", etc)\n * @throws XWikiException if an exception occurred during the conversion process\n */\n public boolean convertSyntax(String targetSyntaxId) throws XWikiException\n {\n try {\n getDoc().convertSyntax(targetSyntaxId, this.context);\n } catch (Exception ex) {\n LOGGER.error(\n \"Failed to convert document [\" + getPrefixedFullName() + \"] to syntax [\" + targetSyntaxId + \"]\", ex);\n\n return false;\n } finally {\n updateAuthor();\n updateContentAuthor();\n }\n\n return true;\n }\n\n /**\n * Indicates whether the document is 'hidden' or not, meaning that it should not be returned in public search\n * results or appear in the User Interface in general.\n *\n * @return <code>true</code> if the document is hidden, <code>false</code> otherwise.\n */\n public boolean isHidden()\n {\n return this.doc.isHidden();\n }\n\n /**\n * Indicates whether the document should be 'hidden' or not, meaning that it should not be returned in public search\n * results or appear in the User Interface in general.\n *\n * @param hidden <code>true</code> if the document should be 'hidden', <code>false</code> otherwise.\n */\n public void setHidden(boolean hidden)\n {\n this.doc.setHidden(hidden);\n }\n\n /**\n * Drop permissions for the remainder of the rendering cycle. After this is called:\n * <ul>\n * <li>1. {@link com.xpn.xwiki.api.Api#hasProgrammingRights()} will always return false.</li>\n * <li>2. {@link com.xpn.xwiki.api.XWiki#getDocumentAsAuthor(org.xwiki.model.reference.DocumentReference)},\n * {@link com.xpn.xwiki.api.XWiki#getDocumentAsAuthor(String)}, {@link com.xpn.xwiki.api.Document#saveAsAuthor()},\n * {@link com.xpn.xwiki.api.Document#saveAsAuthor(String)},\n * {@link com.xpn.xwiki.api.Document#saveAsAuthor(String, boolean)}, and\n * {@link com.xpn.xwiki.api.Document#deleteAsAuthor()} will perform all of their actions as if the document's\n * content author was the guest user (XWiki.XWikiGuest).</li>\n * </ul>\n * <p>\n * This sandboxing will expire at the end of the rendering cycle and can be suspended by beginning a new rendering\n * cycle. A rendering cycle can be begin by calling {@link #getRenderedContent(String)}, {@link #display(String)}\n * (or variations thereof) or by invoking the include macro or using\n * {@link com.xpn.xwiki.api.XWiki#includeTopic(String)}\n * <p>\n * NOTE: Even if you include the same document, permissions will be regained. What this does is sandbox the\n * remainder of the code on the page because although it can temporarily suspend the permissions drop, it cannot get\n * itself to be executed with permissions because if it calls itself, it will hit the drop function first.\n * <p>\n * If you are interested in a more secure sandboxing method where code is guaranteed not to have permissions for the\n * remainder of the request, you should consider {@link com.xpn.xwiki.api.Context#dropPermissions()}.\n * <p>\n *\n * @since 3.2M2\n */\n public void dropPermissions()\n {\n // Set the droppedPermissions key to the context so if the context is cloned and\n // pushed, it will return false until it is popped again.\n final ExecutionContext context = Utils.getComponent(Execution.class).getContext();\n context.setProperty(XWikiConstant.DROPPED_PERMISSIONS, System.identityHashCode(context));\n }\n\n /**\n * @return true if this document is a translation of the main document (i.e. returned by\n * {@link #getTranslatedDocument(String)}); false if this is actually the main document (i.e. returned by\n * {@link com.xpn.xwiki.api.XWiki#getDocument(DocumentReference)}.\n * @since 6.2M2\n */\n public boolean isTranslation()\n {\n return 1 == this.getDoc().getTranslation();\n }\n\n /**\n * @return the maximum authorized length for a document full name (see {@link #getFullName()}).\n * @since 11.4RC1\n */\n public int getLocalReferenceMaxLength()\n {\n return this.doc.getLocalReferenceMaxLength();\n }\n\n /**\n * @return the authors of the document.\n * @since 14.0RC1\n */\n @Unstable\n public DocumentAuthors getAuthors()\n {\n return doc.getAuthors();\n }\n}\n\n", "input": "", "output": "【漏洞类型】安全访问控制漏洞\n【严重性】High\n【漏洞摘要】原始代码中存在安全访问控制漏洞。具体表现为,`getAuthors()`方法在获取文档作者信息时,没有进行任何权限检查,允许任何用户获取文档的所有作者信息,包括对某些敏感操作(如修改作者信息)的访问。这种设计可能导致权限提升,因为攻击者可以利用获取的文档作者信息来伪造修改请求,从而执行未经授权的操作。根本原因在于缺乏对获取和设置文档作者信息的权限控制。\n【关键代码模式】`getAuthors()`方法,`DocumentAuthors`类的使用,没有权限检查。\n【修复方法说明】增加了`hasAccess(Right right)`方法来检查用户是否有编程权限,从而决定是否返回可修改的`DocumentAuthors`对象。未授权用户将返回一个只读的`SafeDocumentAuthors`对象,该对象不允许任何设置操作,防止权限提升。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/main/java/org/mozilla/jss/ssl/javax/JSSEngineReferenceImpl.java\n### 代码内容:\npackage org.mozilla.jss.ssl.javax;\n\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.net.ServerSocket;\nimport java.net.Socket;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.Channels;\nimport java.nio.channels.WritableByteChannel;\nimport java.security.PublicKey;\n\nimport javax.net.ssl.SSLEngineResult;\nimport javax.net.ssl.SSLException;\nimport javax.net.ssl.SSLHandshakeException;\nimport javax.net.ssl.X509ExtendedTrustManager;\nimport javax.net.ssl.X509TrustManager;\n\nimport org.mozilla.jss.nss.BadCertHandler;\nimport org.mozilla.jss.nss.Buffer;\nimport org.mozilla.jss.nss.BufferProxy;\nimport org.mozilla.jss.nss.Cert;\nimport org.mozilla.jss.nss.CertAuthHandler;\nimport org.mozilla.jss.nss.PR;\nimport org.mozilla.jss.nss.PRErrors;\nimport org.mozilla.jss.nss.PRFDProxy;\nimport org.mozilla.jss.nss.SSL;\nimport org.mozilla.jss.nss.SSLErrors;\nimport org.mozilla.jss.nss.SSLFDProxy;\nimport org.mozilla.jss.nss.SSLPreliminaryChannelInfo;\nimport org.mozilla.jss.nss.SecurityStatusResult;\nimport org.mozilla.jss.pkcs11.PK11Cert;\nimport org.mozilla.jss.provider.javax.crypto.JSSNativeTrustManager;\nimport org.mozilla.jss.ssl.SSLAlertDescription;\nimport org.mozilla.jss.ssl.SSLAlertEvent;\nimport org.mozilla.jss.ssl.SSLAlertLevel;\nimport org.mozilla.jss.ssl.SSLCipher;\nimport org.mozilla.jss.ssl.SSLHandshakeCompletedEvent;\nimport org.mozilla.jss.ssl.SSLVersion;\nimport org.mozilla.jss.ssl.SSLVersionRange;\n\n/**\n * The reference JSSEngine implementation.\n *\n * This JSSEngine implementation is a reference for future JSSEngine\n * implementations, providing a pure-Java overview of what should happen at\n * each step of the init, wrap, and unwrap calls.\n *\n * This implementation allows for extended debug logging, but also debug\n * packet logging. The latter writes out packets sent via wrap(...) and\n * received from unwrap(...) to a port on localhost. This allows one to easily\n * attach Wireshark or tcpdump and inspect the TLS packets, even if errors\n * occur during the test suite (where packets aren't sent over the wire by\n * default). This maintains the client/server relationship, and are logged\n * as being from the appropriate side of the TLS connection.\n */\npublic class JSSEngineReferenceImpl extends JSSEngine {\n /**\n * Faked peer information that we pass to the underlying BufferPRFD\n * implementation.\n *\n * This is used by NSS for session resumption. However, because we\n * don't have the exact peer information at the JSSEngine level, at\n * best we can guess.\n */\n private String peer_info;\n\n /**\n * Whether or not the underlying ssl_fd is closed or not.\n *\n * Because the socket isn't open yet, we set it to true, to indicate\n * that no data can be sent or received.\n */\n private boolean closed_fd = true;\n\n /**\n * Data to be read by the NSS SSL implementation; data from the peer.\n */\n private BufferProxy read_buf;\n\n /**\n * Data written by the NSS SSL implementation; data sent to the peer.\n */\n private BufferProxy write_buf;\n\n /**\n * Number of times heuristic has not matched the current state.\n *\n * Because this JSSEngine uses a heuristic for determining when the\n * handshake is completed (or, when we need to switch from WRAP to\n * UNWRAP), and the heuristic is sometimes wrong, we track how many\n * times it is in an unknown state. When we hit some internal\n * threshold, we swap states.\n */\n private int unknown_state_count;\n\n /**\n * Whether or not to step the handshake.\n */\n private boolean step_handshake;\n\n /**\n * Whether or not a FINISHED handshake status has been returned to our\n * caller.\n *\n * Because this JSSEngine implementation re-enters the\n * updateHandshakeState() method potentially multiple times during a\n * single call to wrap() or unwrap(), we need to know whether or not\n * the top-level call has returned a FINISHED result. If it hasn't,\n * we want to keep the state on FINISHED until it has been returned,\n * otherwise we'll skip straight to NOT_HANDSHAKING, confusing our\n * peer.\n */\n private boolean returned_finished;\n\n /**\n * Value of the SSLException we've encountered.\n */\n private SSLException ssl_exception;\n\n /**\n * Whether or not we've seen an ssl exception.\n *\n * Note that, when the exception ultimately gets thrown to the caller,\n * ssl_exception will be NULLed; this tracks whether or not the connection\n * has failed previously for some reason.\n */\n private boolean seen_exception;\n\n // In this reference implementation, we allow logging of (encrypted) data\n // to a Socket for ease of testing. By default, this socket is disabled.\n private int debug_port;\n private ServerSocket ss_socket;\n private Socket s_socket;\n private Socket c_socket;\n private InputStream s_istream;\n private OutputStream s_ostream;\n private InputStream c_istream;\n private OutputStream c_ostream;\n\n /**\n * Internal name for this JSSEngine instance; most commonly used during\n * testing.\n */\n private String name;\n\n /**\n * Automatically generated prefix for debug information.\n */\n private String prefix = \"\";\n\n /**\n * Runnable task; this performs certificate validation against user-provided\n * TrustManager instances, passing the result back to NSS.\n */\n private CertValidationTask task;\n\n public JSSEngineReferenceImpl() {\n super();\n\n // We were given no hints about our peer so we have no information\n // to signal to NSS for session resumption.\n peer_info = null;\n\n debug(\"JSSEngine: constructor()\");\n }\n\n public JSSEngineReferenceImpl(String peerHost, int peerPort) {\n super(peerHost, peerPort);\n\n // Signal host and port for session resumption. Only do it when we've\n // been given valid information.\n if (peerHost != null && peerPort != 0) {\n peer_info = peerHost + \":\" + peerPort;\n }\n\n // Massive hack for compatibility. In particular, Java usually\n // specifies the peer information here. NSS uses SSL_SetURL not only\n // for hostname verification, but also for SNI (!!) on the client.\n // This means that there's no way to indicate (to those servers like\n // google.com which require SNI) hostname for the client WITHOUT\n // also validating the hostname at certificate verification time.\n // Because the certificate hostname explicitly isn't provided (per\n // JCA specification) for validation, this might break other clients\n // which don't provide this information. However, the alternative is\n // that we never add SNI indication, ever.\n //\n // Specifically, this breaks a dead-simple Apache HTTP Components\n // client:\n //\n // CloseableHttpClient client = HttpClients.createDefault();\n // HttpGet request = new HttpGet(\"https://google.com/\");\n // HttpResponse response = client.execute(request);\n // System.out.println(response);\n //\n // Without this, we have no way for the above to work.\n setHostname(peerHost);\n\n debug(\"JSSEngine: constructor(\" + peerHost + \", \" + peerPort + \")\");\n }\n\n public JSSEngineReferenceImpl(String peerHost, int peerPort,\n org.mozilla.jss.crypto.X509Certificate localCert,\n org.mozilla.jss.crypto.PrivateKey localKey) {\n super(peerHost, peerPort, localCert, localKey);\n\n // Signal host and port for session resumption. Only do it when we've\n // been given valid information.\n if (peerHost != null && peerPort != 0) {\n peer_info = peerHost + \":\" + peerPort;\n }\n\n // See above.\n setHostname(peerHost);\n\n prefix = prefix + \"[\" + peer_info + \"] \";\n\n debug(\"JSSEngine: constructor(\" + peerHost + \", \" + peerPort + \", \" + localCert + \", \" + localKey + \")\");\n }\n\n private void debug(String msg) {\n logger.debug(prefix + msg);\n }\n\n private void info(String msg) {\n logger.info(prefix + msg);\n }\n\n private void warn(String msg) {\n logger.warn(prefix + msg);\n }\n\n /**\n * Set the name of this JSSEngine instance, to be printed in logging calls.\n *\n * This helps when debugging output from multiple JSSEngine instances at\n * the same time, such as within the JSS test suite.\n */\n public void setName(String name) {\n this.name = name;\n prefix = \"[\" + this.name + \"] \" + prefix;\n }\n\n private void init() throws SSLException {\n debug(\"JSSEngine: init()\");\n\n // Initialize our JSSEngine when we begin to handshake; otherwise,\n // calls to Set<Option>(...) won't be processed if we initialize it\n // too early; some of these need to be applied at initialization time\n // in order to take affect.\n\n // Ensure we don't leak ssl_fd if we're called multiple times.\n if (ssl_fd != null && !closed_fd) {\n is_inbound_closed = true;\n is_outbound_closed = true;\n cleanup();\n }\n\n ssl_fd = null;\n\n // Create buffers for interacting with NSS.\n createBuffers();\n createBufferFD();\n\n // Initialize the appropriate end of this connection.\n if (as_server) {\n initServer();\n } else {\n initClient();\n }\n\n // Apply the requested cipher suites and protocols.\n applyProtocols();\n applyCiphers();\n applyConfig();\n\n // Apply hostname information (via setURL). Note that this is an\n // extension to SSLEngine for use with NSS; we don't always get this\n // information and so need to work around it sometimes. See\n // initClient() for the workaround.\n applyHosts();\n\n // Apply TrustManager(s) information for validating the peer's\n // certificate.\n applyTrustManagers();\n\n // Finally, set up any debug logging necessary.\n createLoggingSocket();\n }\n\n private void createBuffers() {\n debug(\"JSSEngine: createBuffers()\");\n\n // If the buffers exist, destroy them and then recreate them.\n\n if (read_buf != null) {\n Buffer.Free(read_buf);\n }\n read_buf = Buffer.Create(BUFFER_SIZE);\n\n if (write_buf != null) {\n Buffer.Free(write_buf);\n }\n write_buf = Buffer.Create(BUFFER_SIZE);\n }\n\n private void createBufferFD() throws SSLException {\n debug(\"JSSEngine: createBufferFD()\");\n\n // Create the basis for the ssl_fd from the pair of buffers we created\n // above.\n\n PRFDProxy fd;\n if (peer_info != null && peer_info.length() != 0) {\n // When we have peer information, indicate it via BufferPRFD so\n // that NSS can use it for session resumption.\n fd = PR.NewBufferPRFD(read_buf, write_buf, peer_info.getBytes());\n } else {\n fd = PR.NewBufferPRFD(read_buf, write_buf, null);\n }\n\n if (fd == null) {\n throw new SSLException(\"Error creating buffer-backed PRFileDesc.\");\n }\n\n SSLFDProxy model = null;\n if (as_server) {\n // As a performance improvement, we can copy the server template\n // (containing the desired key and certificate) rather than\n // re-creating it from scratch. This saves a significant amount of\n // time during construction. The implementation lives in JSSEngine,\n // to be shared by all other JSSEngine implementations.\n model = getServerTemplate(cert, key);\n }\n\n // Initialize ssl_fd from the model Buffer-backed PRFileDesc.\n ssl_fd = SSL.ImportFD(model, fd);\n if (ssl_fd == null) {\n PR.Close(fd);\n throw new SSLException(\"Error creating SSL socket on top of buffer-backed PRFileDesc.\");\n }\n\n fd = null;\n closed_fd = false;\n\n // Turn on SSL Alert Logging for the ssl_fd object.\n int ret = SSL.EnableAlertLogging(ssl_fd);\n if (ret == SSL.SECFailure) {\n throw new SSLException(\"Unable to enable SSL Alert Logging on this SSLFDProxy instance.\");\n }\n\n // Turn on notifications of handshake completion. This is the best\n // source of this information, compared to SSL_SecurityStatus().on;\n // the latter can indicate \"on\" before the final FINISHED method has\n // been sent.\n ret = SSL.EnableHandshakeCallback(ssl_fd);\n if (ret == SSL.SECFailure) {\n throw new SSLException(\"Unable to enable SSL Handshake Callback on this SSLFDProxy instance.\");\n }\n\n // Pass this ssl_fd to the session object so that we can use\n // SSL methods to invalidate the session.\n }\n\n private void initClient() throws SSLException {\n debug(\"JSSEngine: initClient()\");\n\n if (cert != null && key != null) {\n // NSS uses a callback to check for the client certificate; we\n // assume we have knowledge of it ahead of time and set it\n // directly on our SSLFDProxy instance.\n //\n // In the future, we could use a KeyManager for inquiring at\n // selection time which certificate to use.\n debug(\"JSSEngine.initClient(): Enabling client auth: \" + cert);\n ssl_fd.SetClientCert(cert);\n if (SSL.AttachClientCertCallback(ssl_fd) != SSL.SECSuccess) {\n throw new SSLException(\"Unable to attach client certificate auth callback.\");\n }\n }\n\n if (hostname == null) {\n // When we're a client with no hostname, assume we're running\n // under standard JDK JCA semantics with no hostname available.\n // Bypass NSS's hostname check by adding a BadCertHandler, which\n // check ONLY for the bad hostname error and allows it. This is\n // safe since this is the LAST check in every (NSS, PKIX, and\n // JSS) certificate validation step. And, under JCA semantics, we\n // can assume the caller checks the hostname for us.\n ssl_fd.badCertHandler = new BypassBadHostname(ssl_fd, 0);\n if (SSL.ConfigSyncBadCertCallback(ssl_fd) != SSL.SECSuccess) {\n throw new SSLException(\"Unable to attach bad cert callback.\");\n }\n }\n }\n\n private void initServer() throws SSLException {\n debug(\"JSSEngine: initServer()\");\n\n // The only time cert and key are strictly required are when we're\n // creating a server SSLEngine.\n if (cert == null || key == null) {\n throw new IllegalArgumentException(\"JSSEngine: must be initialized with server certificate and key!\");\n }\n\n debug(\"JSSEngine.initServer(): \" + cert);\n debug(\"JSSEngine.initServer(): \" + key);\n\n session.setLocalCertificates(new PK11Cert[]{ cert } );\n\n // Create a small server session cache.\n //\n // TODO: Make this configurable.\n initializeSessionCache(1, 100, null);\n\n configureClientAuth();\n }\n\n private void configureClientAuth() throws SSLException {\n debug(\"SSLFileDesc: \" + ssl_fd);\n\n // Only specify these on the server side as they affect what we\n // want from the remote peer in NSS. In the server case, this is\n // client auth, but if we were to set these on the client, it would\n // affect server auth.\n if (SSL.OptionSet(ssl_fd, SSL.REQUEST_CERTIFICATE, want_client_auth || need_client_auth ? 1 : 0) == SSL.SECFailure) {\n throw new SSLException(\"Unable to configure SSL_REQUEST_CERTIFICATE option: \" + errorText(PR.GetError()));\n }\n\n if (SSL.OptionSet(ssl_fd, SSL.REQUIRE_CERTIFICATE, need_client_auth ? SSL.REQUIRE_ALWAYS : 0) == SSL.SECFailure) {\n throw new SSLException(\"Unable to configure SSL_REQUIRE_CERTIFICATE option: \" + errorText(PR.GetError()));\n }\n }\n\n @Override\n protected void reconfigureClientAuth() {\n if (ssl_fd == null || !as_server) {\n return;\n }\n\n // This method is called by JSSEngine's setNeedClientAuth and\n // setWantClientAuth to inform us of a change in value here. When\n // we've already configured ssl_fd and we're a server, we need to\n // inform NSS of this change; this usually indicates Post-Handshake\n // Authentication is required.\n\n try {\n configureClientAuth();\n } catch (SSLException se) {\n // We cannot throw SSLException from this helper because it\n // is called from setNeedClientAuth and setWantClientAuth,\n // both of which don't disclose SSLException.\n throw new RuntimeException(se.getMessage(), se);\n }\n }\n\n private void applyCiphers() throws SSLException {\n debug(\"JSSEngine: applyCiphers()\");\n // Enabled the ciphersuites specified by setEnabledCipherSuites(...).\n // When this isn't called, enabled_ciphers will be null, so we'll just\n // use whatever is enabled by default.\n if (enabled_ciphers == null) {\n return;\n }\n\n // We need to disable the suite if it isn't present in the list of\n // suites above. Be lazy about it for the time being and disable all\n // cipher suites first.\n for (SSLCipher suite : SSLCipher.values()) {\n if (SSL.CipherPrefSet(ssl_fd, suite.getID(), false) == SSL.SECFailure) {\n // warn(\"Unable to set cipher suite preference for \" + suite.name() + \": \" + errorText(PR.GetError()));\n }\n }\n\n // Only enable these particular suites. When a cipher suite can't be\n // enabled it is most likely due to local policy. Log it. Also log\n // which ciphers were successfully enabled for debugging purposes.\n for (SSLCipher suite : enabled_ciphers) {\n if (suite == null) {\n continue;\n }\n\n if (SSL.CipherPrefSet(ssl_fd, suite.getID(), true) == SSL.SECFailure) {\n warn(\"Unable to enable cipher suite \" + suite + \": \" + errorText(PR.GetError()));\n } else {\n debug(\"Enabled cipher suite \" + suite + \": \" + errorText(PR.GetError()));\n }\n }\n }\n\n private void applyProtocols() throws SSLException {\n debug(\"JSSEngine: applyProtocols() min_protocol=\" + min_protocol + \" max_protocol=\" + max_protocol);\n // Enable the protocols only when both a maximum and minimum protocol\n // version are specified.\n if (min_protocol == null || max_protocol == null) {\n debug(\"JSSEngine: applyProtocols() - missing min_protocol or max_protocol; using defaults\");\n return;\n }\n\n // We should bound this range by crypto-policies in the future to\n // match the current behavior. However, Tomcat already bounds\n // what we set in the server.xml config by what the JSSEngine\n // indicates it supports. Because we only indicate we support\n // what is allowed under crypto-policies, it effective does\n // this bounding for us.\n SSLVersionRange vrange = new SSLVersionRange(min_protocol, max_protocol);\n if (SSL.VersionRangeSet(ssl_fd, vrange) == SSL.SECFailure) {\n throw new SSLException(\"Unable to set version range: \" + errorText(PR.GetError()));\n }\n }\n\n private void applyConfig() throws SSLException {\n debug(\"JSSEngine: applyConfig()\");\n for (Integer key : config.keySet()) {\n Integer value = config.get(key);\n\n debug(\"Setting configuration option: \" + key + \"=\" + value);\n if (SSL.OptionSet(ssl_fd, key, value) != SSL.SECSuccess) {\n throw new SSLException(\"Unable to set configuration value: \" + key + \"=\" + value);\n }\n }\n }\n\n private void applyHosts() throws SSLException {\n debug(\"JSSEngine: applyHosts()\");\n\n // This is most useful for the client end of the connection; this\n // specifies what to match the server's certificate against.\n if (hostname != null) {\n if (SSL.SetURL(ssl_fd, hostname) == SSL.SECFailure) {\n throw new SSLException(\"Unable to configure server hostname: \" + errorText(PR.GetError()));\n }\n }\n }\n\n private void applyTrustManagers() throws SSLException {\n debug(\"JSSEngine: applyTrustManagers()\");\n\n // If none have been specified, exit early.\n if (trust_managers == null || trust_managers.length == 0) {\n // Use the default NSS certificate authentication handler. We\n // don't need to do anything to use it.\n debug(\"JSSEngine: no TrustManagers to apply.\");\n return;\n }\n\n // Determine which configuration to use for checking certificates. Our\n // options are a Native trust manager (most performant) or using a set\n // of X509TrustManagers.\n if (trust_managers.length == 1 && trust_managers[0] instanceof JSSNativeTrustManager) {\n // This is a dummy TrustManager. It signifies that we should call\n // SSL.ConfigJSSDefaultCertAuthCallback(...) on this SSL\n // PRFileDesc pointer, letting us utilize the same certificate\n // validation logic that SSLSocket had.\n debug(\"JSSEngine: applyTrustManagers() - adding Native TrustManager\");\n if (SSL.ConfigJSSDefaultCertAuthCallback(ssl_fd) == SSL.SECFailure) {\n throw new SSLException(\"Unable to configure JSSNativeTrustManager on this JSSengine: \" + errorText(PR.GetError()));\n }\n return;\n }\n\n if (as_server) {\n // We need to manually invoke the async cert auth handler. However,\n // SSLFDProxy makes this easy for us: our CertAuthHandler derives\n // from Runnable, so we can reuse it here as well. We can create\n // it ahead of time though. In this case, checkNeedCertValidation()\n // is never called.\n ssl_fd.certAuthHandler = new CertValidationTask(ssl_fd);\n\n if (SSL.ConfigSyncTrustManagerCertAuthCallback(ssl_fd) == SSL.SECFailure) {\n throw new SSLException(\"Unable to configure TrustManager validation on this JSSengine: \" + errorText(PR.GetError()));\n }\n } else {\n // Otherwise, we need a hook from NSS into the SSLFDProxy.\n //\n // This hook executes all TrustManagers and if any exception\n // occurs, we'll turn it into the proper response within NSS.\n if (SSL.ConfigAsyncTrustManagerCertAuthCallback(ssl_fd) == SSL.SECFailure) {\n throw new SSLException(\"Unable to configure TrustManager validation on this JSSengine: \" + errorText(PR.GetError()));\n }\n }\n }\n\n private void createLoggingSocket() throws SSLException {\n if (debug_port == 0) {\n return;\n }\n\n try {\n ss_socket = new ServerSocket(debug_port);\n ss_socket.setReuseAddress(true);\n ss_socket.setReceiveBufferSize(BUFFER_SIZE);\n\n c_socket = new Socket(ss_socket.getInetAddress(), ss_socket.getLocalPort());\n c_socket.setReuseAddress(true);\n c_socket.setReceiveBufferSize(BUFFER_SIZE);\n c_socket.setSendBufferSize(BUFFER_SIZE);\n\n s_socket = ss_socket.accept();\n s_socket.setReuseAddress(true);\n s_socket.setReceiveBufferSize(BUFFER_SIZE);\n s_socket.setSendBufferSize(BUFFER_SIZE);\n\n s_istream = s_socket.getInputStream();\n s_ostream = s_socket.getOutputStream();\n\n c_istream = c_socket.getInputStream();\n c_ostream = c_socket.getOutputStream();\n } catch (Exception e) {\n throw new SSLException(\"Unable to enable debug socket logging! \" + e.getMessage(), e);\n }\n }\n\n private void loggingSocketConsumeAllBytes() {\n try {\n int available = s_istream.available();\n byte[] data = new byte[available];\n s_istream.read(data);\n } catch (Exception e) {}\n\n try {\n int available = c_istream.available();\n byte[] data = new byte[available];\n c_istream.read(data);\n } catch (Exception e) {}\n }\n\n @Override\n public void beginHandshake() throws SSLException {\n debug(\"JSSEngine: beginHandshake()\");\n\n // We assume beginHandshake(...) is the entry point for initializing\n // the buffer. In particular, wrap(...) / unwrap(...) *MUST* call\n // beginHandshake(...) if ssl_fd == null.\n\n // ssl_fd == null <-> we've not initialized anything yet.\n\n // TLS begins with the client sending a CLIENT_HELLO to the server;\n // this means that the server needs to unwrap first and the client\n // needs to wrap first; hence unwrap = as_server. However, if we're\n // trying to renegotiate this changes. See when ssl_fd != null below.\n boolean unwrap = as_server;\n\n if (ssl_fd == null) {\n // Initialize and create ssl_fd. Throws various RuntimeExceptions\n // when creation and configuration fails.\n init();\n assert(ssl_fd != null);\n\n // Reset the handshake status, using the new socket and\n // configuration which was just created. This ensures that\n // we'll attempt to handshake when ForceHandshake is called.\n if (SSL.ResetHandshake(ssl_fd, as_server) == SSL.SECFailure) {\n throw new RuntimeException(\"Unable to begin handshake: \" + errorText(PR.GetError()));\n }\n } else {\n // When ssl_fd exists, we need to re-handshake. Usually, the\n // server initiates the conversation (especially when we want\n // to upgrade to requiring client auth from not requiring it).\n //\n // This means that when we're a (as_server == true), we should\n // now wrap, rather than unwrap. So, negate unwrap.\n unwrap = !as_server;\n\n // TLS v1.3 differs from all previous versions in that it removed\n // the ability to completely rehandshake. This makes the first\n // portion more complicated than the latter.\n if (session.getSSLVersion() == SSLVersion.TLS_1_3) {\n // We only send the certificate request as a server when we\n // need client auth. Otherwise, we'll have to issue a rekey\n // request.\n boolean send_certificate_request = as_server && need_client_auth;\n if (send_certificate_request) {\n if (SSL.SendCertificateRequest(ssl_fd) == SSL.SECFailure) {\n throw new RuntimeException(\"Unable to issue certificate request on TLSv1.3: \" + errorText(PR.GetError()));\n }\n } else {\n // Our best guess at what the user wants is to update\n // their keys. They don't need client authentication but\n // they explicitly called beginHandshake() again.\n if (SSL.KeyUpdate(ssl_fd, false) == SSL.SECFailure) {\n throw new RuntimeException(\"Unable to request a new key on TLSv1.3: \" + errorText(PR.GetError()));\n }\n }\n } else {\n // On older protocol versions, this is easier: just issue a\n // new handshake request. This is different from\n // ResetHandshake as for security reasons, the semantics have\n // to differ.\n if (SSL.ReHandshake(ssl_fd, true) == SSL.SECFailure) {\n throw new RuntimeException(\"Unable to rehandshake: \" + errorText(PR.GetError()));\n }\n }\n }\n\n // Make sure we reset the handshake completion status in order for the\n // callback to work correctly.\n ssl_fd.handshakeComplete = false;\n\n // This leaves setting internal variables for HandshakeStatus and\n // the reporting up from SSLEngine.\n if (unwrap) {\n handshake_state = SSLEngineResult.HandshakeStatus.NEED_UNWRAP;\n } else {\n handshake_state = SSLEngineResult.HandshakeStatus.NEED_WRAP;\n }\n\n // We've begun a new handshake; make sure we step it and reset\n // our unknown state count to zero.\n step_handshake = true;\n unknown_state_count = 0;\n\n // Lastly, each handshake must return a FINISHED individually,\n // reset returned_finished to false.\n returned_finished = false;\n }\n\n @Override\n public void closeInbound() {\n debug(\"JSSEngine: closeInbound()\");\n\n if (!is_inbound_closed && ssl_fd != null && !closed_fd) {\n // Send PR_SHUTDOWN_RCV only once. Additionally, this call\n // crashes when ssl_fd == NULL or when the socket is already\n // closed.\n PR.Shutdown(ssl_fd, PR.SHUTDOWN_RCV);\n }\n\n is_inbound_closed = true;\n }\n\n @Override\n public void closeOutbound() {\n debug(\"JSSEngine: closeOutbound()\");\n\n if (!is_outbound_closed && ssl_fd != null && !closed_fd) {\n // Send PR_SHUTDOWN_SEND only once. Additionally, this call\n // crashes when ssl_fd == NULL or when the socket is already\n // closed.\n PR.Shutdown(ssl_fd, PR.SHUTDOWN_SEND);\n }\n\n is_outbound_closed = true;\n }\n\n public String getHostname() {\n return hostname;\n }\n\n @Override\n public Runnable getDelegatedTask() {\n debug(\"JSSEngine: getDelegatedTask()\");\n\n // task can either contain a task instance or null; task gets\n // populated also during getHandshakeStatus(), wrap(), and\n // unwrap(). Since wrap()/unwrap() populate the task early (if\n // one is needed) -- but can return NEED_TASK later with null\n // task, this could result in a stall if we didn't also check\n // here. Best to do it (it is cheap if one isn't necessary),\n // so that way we always return up-to-date information.\n if (ssl_fd != null) {\n // Return code is a boolean, whether or not we have a task.\n // We can safely ignore it here.\n checkNeedCertValidation();\n }\n\n return task;\n }\n\n private boolean checkNeedCertValidation() {\n debug(\"JSSEngine: checkNeedCertValidation()\");\n if (task != null) {\n if (!task.finished) {\n // Already created runnable task; exit with true status to\n // show it still needs to be run.\n debug(\"JSSEngine: checkNeedCertValidation() - task not done\");\n return true;\n }\n\n debug(\"JSSEngine: checkNeedCertValidation() - task done with code \" + task.result);\n\n // Since the task has finished, we now need to inform NSS about\n // the results of our certificate validation step.\n if (SSL.AuthCertificateComplete(ssl_fd, task.result) != SSL.SECSuccess) {\n String msg = \"Got unexpected failure finishing cert \";\n msg += \"authentication in NSS. Returned code \";\n msg += task.result;\n throw new RuntimeException(msg);\n }\n\n // After checking certificates, our best guess will be that we\n // need to run wrap again. This is because we either need to\n // inform the caller of an error that occurred, or continue the\n // handshake. Worst case, we'll call updateHandshakeState() and\n // it'll correct our mistake eventually.\n\n debug(\"JSSEngine: checkNeedCertValidation() - task done, removing\");\n\n task = null;\n handshake_state = SSLEngineResult.HandshakeStatus.NEED_WRAP;\n ssl_fd.needCertValidation = false;\n\n return false;\n }\n\n if (ssl_fd == null) {\n // If we don't have a SSLFDProxy instance, nothing we can do but\n // skip checking if the task exists. Return false to show that\n // we don't yet have a runnable task.\n debug(\"JSSEngine: checkNeedCertValidation() - no ssl_fd\");\n return false;\n }\n\n if (!ssl_fd.needCertValidation) {\n // We don't yet need certificate validation. Don't create a\n // runnable task for now.\n debug(\"JSSEngine: checkNeedCertValidation() - no need for cert validation\");\n return false;\n }\n\n debug(\"JSSEngine: checkNeedCertValidation() - creating task\");\n\n // OK, time to create our runnable task.\n task = new CertValidationTask(ssl_fd);\n\n // Update our handshake state so we know what to do next.\n handshake_state = SSLEngineResult.HandshakeStatus.NEED_TASK;\n\n return true;\n }\n\n @Override\n public SSLEngineResult.HandshakeStatus getHandshakeStatus() {\n debug(\"JSSEngine: getHandshakeStatus()\");\n\n // If task is NULL, we need to update the state to check if the\n // task has been \"run\". Even if it isn't, it would be good to\n // update the status here as well. However, we DO NOT want to\n // call updateHandshakeState() in the event we have a task to\n // run: we need to run it still!\n if (checkNeedCertValidation()) {\n return handshake_state;\n }\n\n // Always update the handshake state; this ensures that we catch\n // looping due to missing data and flip our expected direction.\n updateHandshakeState();\n\n return handshake_state;\n }\n\n @Override\n public SecurityStatusResult getStatus() {\n if (ssl_fd == null) {\n return null;\n }\n\n return SSL.SecurityStatus(ssl_fd);\n }\n\n /**\n * Enable writing of encrypted TLS traffic to the specified port in a\n * client-server relationship (mirroring the actual role of this\n * SSLEngine) to enable debugging with Wireshark.\n */\n public void enableSafeDebugLogging(int port) {\n debug_port = port;\n }\n\n private int computeSize(ByteBuffer[] buffers, int offset, int length) throws IllegalArgumentException {\n debug(\"JSSEngine: computeSize()\");\n int result = 0;\n\n if (buffers == null || buffers.length == 0) {\n debug(\"JSSEngine.compueSize(): no buffers - result=\" + result);\n return result;\n }\n\n // Semantics of arguments:\n //\n // - buffers is where we're reading/writing application data.\n // - offset is the index of the first buffer we read/write to.\n // - length is the total number of buffers we read/write to.\n //\n // We use a relative index and an absolute index to handle these\n // constraints.\n for (int rel_index = 0; rel_index < length; rel_index++) {\n int index = offset + rel_index;\n if (index >= buffers.length) {\n throw new IllegalArgumentException(\"offset (\" + offset + \") or length (\" + length + \") exceeds contract based on number of buffers (\" + buffers.length + \")\");\n }\n\n if (rel_index == 0 && buffers[index] == null) {\n // If our first buffer is null, assume the rest are and skip\n // everything else. This commonly happens when null is passed\n // as the src parameter to wrap or when null is passed as the\n // dst parameter to unwrap.\n debug(\"JSSEngine.computeSize(): null first buffer - result=\" + result);\n return result;\n }\n\n if (buffers[index] == null) {\n throw new IllegalArgumentException(\"Buffer at index \" + index + \" is null.\");\n }\n\n result += buffers[index].remaining();\n }\n\n debug(\"JSSEngine.computeSize(): result=\" + result);\n\n return result;\n }\n\n private int putData(byte[] data, ByteBuffer[] buffers, int offset, int length) {\n debug(\"JSSEngine: putData()\");\n // Handle the rather unreasonable task of moving data into the buffers.\n // We assume the buffer parameters have already been checked by\n // computeSize(...); that is, offset/length contracts hold and that\n // each buffer in the range is non-null.\n //\n // We also assume that data.length does not exceed the total number\n // of bytes the buffers can hold; this is what computeSize(...)'s\n // return value should ensure. This directly means that the inner\n // while loop won't exceed the bounds of offset+length.\n\n int buffer_index = offset;\n int data_index = 0;\n\n if (data == null || buffers == null) {\n return data_index;\n }\n\n for (data_index = 0; data_index < data.length;) {\n // Ensure we have have a buffer with capacity.\n while ((buffers[buffer_index] == null || buffers[buffer_index].remaining() <= 0) &&\n (buffer_index < offset + length)) {\n buffer_index += 1;\n }\n if (buffer_index >= offset + length) {\n break;\n }\n\n // Compute the size of the put: it is the minimum of the space\n // remaining in this buffer and the bytes remaining in the data\n // array.\n int put_size = buffers[buffer_index].remaining();\n if (put_size > (data.length - data_index)) {\n put_size = data.length - data_index;\n }\n\n buffers[buffer_index].put(data, data_index, put_size);\n data_index += put_size;\n }\n\n return data_index;\n }\n\n private SSLException checkSSLAlerts() {\n debug(\"JSSEngine: Checking inbound and outbound SSL Alerts. Have \" + ssl_fd.inboundAlerts.size() + \" inbound and \" + ssl_fd.outboundAlerts.size() + \" outbound alerts.\");\n\n // Prefer inbound alerts to outbound alerts.\n while (ssl_fd.inboundOffset < ssl_fd.inboundAlerts.size()) {\n SSLAlertEvent event = ssl_fd.inboundAlerts.get(ssl_fd.inboundOffset);\n ssl_fd.inboundOffset += 1;\n\t event.setEngine(this);\n\n if (event.getLevelEnum() == SSLAlertLevel.WARNING && event.getDescriptionEnum() == SSLAlertDescription.CLOSE_NOTIFY) {\n debug(\"Got inbound CLOSE_NOTIFY alert\");\n closeInbound();\n }\n\n debug(\"JSSEngine: Got inbound alert: \" + event);\n\n // Fire inbound alert prior to raising any exception.\n fireAlertReceived(event);\n\n // Not every SSL Alert is fatal; toException() only returns a\n // SSLException on fatal instances. We shouldn't return NULL\n // early without checking all alerts.\n SSLException exception = event.toException();\n if (exception != null) {\n return exception;\n }\n }\n\n while (ssl_fd.outboundOffset < ssl_fd.outboundAlerts.size()) {\n SSLAlertEvent event = ssl_fd.outboundAlerts.get(ssl_fd.outboundOffset);\n ssl_fd.outboundOffset += 1;\n event.setEngine(this);\n\n if (event.getLevelEnum() == SSLAlertLevel.WARNING && event.getDescriptionEnum() == SSLAlertDescription.CLOSE_NOTIFY) {\n debug(\"Sent outbound CLOSE_NOTIFY alert.\");\n closeOutbound();\n }\n\n debug(\"JSSEngine: Got outbound alert: \" + event);\n\n // Fire outbound alert prior to raising any exception. Note that\n // this still triggers after this alert is written to the output\n // wire buffer.\n fireAlertSent(event);\n\n SSLException exception = event.toException();\n if (exception != null) {\n return exception;\n }\n }\n\n return null;\n }\n\n private void updateHandshakeState() {\n debug(\"JSSEngine: updateHandshakeState()\");\n\n // If we've previously seen an exception, we should just return\n // here; there's already an alert on the wire, so there's no point\n // in checking for new ones and/or stepping the handshake: it has\n // already failed.\n if (seen_exception) {\n return;\n }\n\n // If we're already done, we should check for SSL ALerts.\n if (!step_handshake && handshake_state == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {\n debug(\"JSSEngine.updateHandshakeState() - not handshaking\");\n unknown_state_count = 0;\n\n ssl_exception = checkSSLAlerts();\n seen_exception = (ssl_exception != null);\n return;\n }\n\n // If we've previously finished handshaking, then move to\n // NOT_HANDSHAKING. Now is also a good time to check for any\n // alerts.\n if (!step_handshake && handshake_state == SSLEngineResult.HandshakeStatus.FINISHED) {\n debug(\"JSSEngine.updateHandshakeState() - FINISHED to NOT_HANDSHAKING\");\n\n // Because updateHandshakeState() gets called multiple times within\n // a single wrap/unwrap invocation, we need to wait for the FINISHED\n // message to be returned (from wrap/unwrap) before moving it to\n // NOT_HANDSHAKING. Otherwise, callers will miss that the handshake\n // has completed. We aren't in an unknown state though and we don't\n // need to call SSL.ForceHandshake().\n if (returned_finished) {\n handshake_state = SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;\n }\n\n unknown_state_count = 0;\n\n ssl_exception = checkSSLAlerts();\n seen_exception = (ssl_exception != null);\n return;\n }\n\n // Since we're not obviously done handshaking, and the last time we\n // were called, we were still handshaking, step the handshake.\n debug(\"JSSEngine.updateHandshakeState() - forcing handshake\");\n if (SSL.ForceHandshake(ssl_fd) == SSL.SECFailure) {\n int error_value = PR.GetError();\n\n if (error_value != PRErrors.WOULD_BLOCK_ERROR) {\n debug(\"JSSEngine.updateHandshakeState() - FATAL \" + getStatus());\n\n ssl_exception = new SSLHandshakeException(\"Error duing SSL.ForceHandshake() :: \" + errorText(error_value));\n seen_exception = true;\n\n handshake_state = SSLEngineResult.HandshakeStatus.NEED_WRAP;\n return;\n }\n }\n\n // Check if we've just finished handshaking.\n debug(\"JSSEngine.updateHandshakeState() - read_buf.read=\" + Buffer.ReadCapacity(read_buf) + \" read_buf.write=\" + Buffer.WriteCapacity(read_buf) + \" write_buf.read=\" + Buffer.ReadCapacity(write_buf) + \" write_buf.write=\" + Buffer.WriteCapacity(write_buf));\n\n // Set NEED_WRAP when we have data to send to the client.\n if (Buffer.ReadCapacity(write_buf) > 0 && handshake_state != SSLEngineResult.HandshakeStatus.NEED_WRAP) {\n // Can't write; to read, we need to call wrap to provide more\n // data to write.\n debug(\"JSSEngine.updateHandshakeState() - can write \" + Buffer.ReadCapacity(write_buf) + \" bytes, NEED_WRAP to process\");\n handshake_state = SSLEngineResult.HandshakeStatus.NEED_WRAP;\n unknown_state_count = 0;\n return;\n }\n\n // Delay the check to see if the handshake finished until after we\n // send the CLIENT FINISHED message and recieved the SERVER FINISHED\n // message if we're a client. Otherwise, wait to send SERVER FINISHED\n // message. This is because NSS thinks the handshake has finished\n // (according to SecurityStatusResult since it has sent the massage)\n // but we haven't yet gotten around to doing so if we're in a WRAP()\n // call.\n if (ssl_fd.handshakeComplete && Buffer.ReadCapacity(write_buf) == 0) {\n debug(\"JSSEngine.updateHandshakeState() - handshakeComplete is \" + ssl_fd.handshakeComplete + \", so we've just finished handshaking\");\n step_handshake = false;\n handshake_state = SSLEngineResult.HandshakeStatus.FINISHED;\n unknown_state_count = 0;\n\n // Only update peer certificate chain when we've finished\n // handshaking.\n try {\n PK11Cert[] peer_chain = SSL.PeerCertificateChain(ssl_fd);\n session.setPeerCertificates(peer_chain);\n } catch (Exception e) {\n String msg = \"Unable to get peer's certificate chain: \";\n msg += e.getMessage();\n\n seen_exception = true;\n ssl_exception = new SSLException(msg, e);\n }\n\n // Also update our session information here.\n session.refreshData();\n\n // Finally, fire any handshake completed event listeners now.\n fireHandshakeComplete(new SSLHandshakeCompletedEvent(this));\n\n return;\n }\n\n if (Buffer.ReadCapacity(read_buf) == 0 && handshake_state != SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {\n // Set NEED_UNWRAP when we have no data to read from the client.\n debug(\"JSSEngine.updateHandshakeState() - can read \" + Buffer.ReadCapacity(read_buf) + \" bytes, NEED_UNWRAP to give us more\");\n handshake_state = SSLEngineResult.HandshakeStatus.NEED_UNWRAP;\n unknown_state_count = 0;\n return;\n }\n\n unknown_state_count += 1;\n if (unknown_state_count >= 4) {\n if (handshake_state == SSLEngineResult.HandshakeStatus.NEED_WRAP) {\n handshake_state = SSLEngineResult.HandshakeStatus.NEED_UNWRAP;\n } else {\n handshake_state = SSLEngineResult.HandshakeStatus.NEED_WRAP;\n }\n unknown_state_count = 1;\n }\n }\n\n private void logUnwrap(ByteBuffer src) {\n if (debug_port <= 0 || src == null || src.remaining() == 0) {\n return;\n }\n\n loggingSocketConsumeAllBytes();\n\n OutputStream stream = c_ostream;\n\n if (!as_server) {\n // An unwrap from the client means we write data to the outbound\n // side of the server socket.\n stream = s_ostream;\n }\n\n WritableByteChannel channel = Channels.newChannel(stream);\n\n int pos = src.position();\n try {\n debug(\"JSSEngine: logUnwrap() - writing \" + src.remaining() + \" bytes.\");\n channel.write(src);\n stream.flush();\n } catch (Exception e) {\n throw new RuntimeException(\"Unable to log contents of unwrap's src to debug socket: \" + e.getMessage(), e);\n } finally {\n src.position(pos);\n }\n }\n\n @Override\n public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts, int offset, int length) throws IllegalArgumentException, SSLException {\n debug(\"JSSEngine: unwrap(ssl_fd=\" + ssl_fd + \")\");\n\n // In this method, we're taking the network wire contents of src and\n // passing them as the read side of our buffer. If there's any data\n // for us to read from the remote peer (via ssl_fd), we place it in\n // the various dsts.\n //\n // However, we also need to detect if the handshake is still ongoing;\n // if so, we can't send data (from src) until then.\n\n if (ssl_fd == null) {\n beginHandshake();\n }\n\n // Before going much further, check to see if we need to run a\n // delegated task. So far, the only delegated tasks we have are\n // for checking TrustManagers.\n if (checkNeedCertValidation()) {\n return new SSLEngineResult(SSLEngineResult.Status.OK, handshake_state, 0, 0);\n }\n\n boolean handshake_already_complete = ssl_fd.handshakeComplete;\n int src_capacity = src.remaining();\n\n logUnwrap(src);\n\n // Order of operations:\n // 1. Read data from srcs\n // 2. Update handshake status\n // 3. Write data to dsts\n //\n // Since srcs is coming from the data, it could affect our ability to\n // handshake. It could also affect our ability to write data to dsts,\n // as src could provide new data to decrypt. When no new data from src\n // is present, we could have residual steps in handshake(), in which\n // case no data would be written to dsts. Lastly, even if no new data\n // from srcs, could still have residual data in read_buf, so we should\n // attempt to read from the ssl_fd.\n //\n // In order to handle large buffer sizes, wrap everything in a\n // do-while loop.\n\n // wire_data is the number of bytes from src we've written into\n // read_buf.\n int wire_data = 0;\n\n // Actual amount of data written to the buffer.\n int app_data = 0;\n\n int this_src_write;\n int this_dst_write;\n\n do {\n this_src_write = 0;\n this_dst_write = 0;\n\n if (src != null) {\n this_src_write = Math.min((int) Buffer.WriteCapacity(read_buf), src.remaining());\n\n // When we have data from src, write it to read_buf.\n if (this_src_write > 0) {\n byte[] wire_buffer = new byte[this_src_write];\n src.get(wire_buffer);\n\n this_src_write = (int) Buffer.Write(read_buf, wire_buffer);\n\n wire_data += this_src_write;\n debug(\"JSSEngine.unwrap(): Wrote \" + this_src_write + \" bytes to read_buf.\");\n }\n }\n\n // In the above, we should always try to read and write data. Check to\n // see if we need to step our handshake process or not.\n updateHandshakeState();\n\n int max_dst_size = computeSize(dsts, offset, length);\n byte[] app_buffer = PR.Read(ssl_fd, max_dst_size);\n int error = PR.GetError();\n debug(\"JSSEngine.unwrap() - \" + app_buffer + \" error=\" + errorText(error));\n if (app_buffer != null) {\n this_dst_write = putData(app_buffer, dsts, offset, length);\n app_data += this_dst_write;\n } else if (max_dst_size > 0) {\n // There are two scenarios we need to ignore here:\n // 1. WOULD_BLOCK_ERRORs are safe, because we're expecting\n // not to block. Usually this means we don't have space\n // to write any more data.\n // 2. SOCKET_SHUTDOWN_ERRORs are safe, because if the\n // underling cause was fatal, we'd catch it after exiting\n // the do-while loop, in checkSSLAlerts().\n if (error != 0 && error != PRErrors.WOULD_BLOCK_ERROR && error != PRErrors.SOCKET_SHUTDOWN_ERROR) {\n ssl_exception = new SSLException(\"Unexpected return from PR.Read(): \" + errorText(error));\n seen_exception = true;\n }\n }\n } while (this_src_write != 0 || this_dst_write != 0);\n\n if (seen_exception == false && ssl_exception == null) {\n ssl_exception = checkSSLAlerts();\n seen_exception = (ssl_exception != null);\n }\n\n // Before we return, check if an exception occurred and throw it if\n // one did.\n if (ssl_exception != null) {\n info(\"JSSEngine.unwrap() - Got SSLException: \" + ssl_exception);\n SSLException excpt = ssl_exception;\n ssl_exception = null;\n handshake_state = SSLEngineResult.HandshakeStatus.NEED_WRAP;\n tryCleanup();\n throw excpt;\n }\n\n SSLEngineResult.Status handshake_status = SSLEngineResult.Status.OK;\n\n if (is_inbound_closed) {\n debug(\"Socket is currently closed.\");\n handshake_status = SSLEngineResult.Status.CLOSED;\n } else if (handshake_already_complete && src_capacity > 0 && app_data == 0) {\n debug(\"Underflowed: produced no application data when we expected to.\");\n handshake_status = SSLEngineResult.Status.BUFFER_UNDERFLOW;\n }\n\n // Need a way to introspect the open/closed state of the TLS\n // connection.\n\n debug(\"JSSEngine.unwrap() - Finished\");\n debug(\" - Status: \" + handshake_status);\n debug(\" - Handshake State: \" + handshake_state);\n debug(\" - wire_data: \" + wire_data);\n debug(\" - app_data: \" + app_data);\n\n if (handshake_state == SSLEngineResult.HandshakeStatus.FINISHED) {\n returned_finished = true;\n }\n\n tryCleanup();\n return new SSLEngineResult(handshake_status, handshake_state, wire_data, app_data);\n }\n\n public int writeData(ByteBuffer[] srcs, int offset, int length) {\n debug(\"JSSEngine: writeData()\");\n // This is the tough end of reading/writing. There's two potential\n // places buffering could occur:\n //\n // - Inside the NSS library (unclear if this happens).\n // - write_buf\n //\n // So when we call PR.Write(ssl_fd, data), it isn't guaranteed that\n // we can write all of data to ssl_fd (unlike with all our other read\n // or write operations where we have a clear bound). In the event that\n // our Write call is truncated, we have to put data back into the\n // buffer from whence it was read.\n //\n // However, we do use Buffer.WriteCapacity(write_buf) as a proxy\n // metric for how much we can write without having to place data back\n // in a src buffer.\n //\n // When we don't perform an actual NSPR write call, make a dummy\n // invocation to ensure we always attempt to flush these buffers.\n int data_length = 0;\n\n int index = offset;\n int max_index = offset + length;\n\n boolean attempted_write = false;\n\n while (index < max_index) {\n // If we don't have any remaining bytes in this buffer, skip it.\n if (srcs[index] == null || srcs[index].remaining() <= 0) {\n index += 1;\n continue;\n }\n debug(\"JSSEngine.writeData(): index=\" + index + \" max_index=\" + max_index);\n\n // We expect (i.e., need to construct a buffer) to write up to\n // this much. Note that this is non-zero since we're taking the\n // max here and we guarantee with the previous statement that\n // srcs[index].remaining() > 0. There's no point in getting more\n // than BUFFER_SIZE bytes either; so cap at the minimum of the\n // two sizes.\n int expected_write = Math.min(srcs[index].remaining(), BUFFER_SIZE);\n debug(\"JSSEngine.writeData(): expected_write=\" + expected_write + \" write_cap=\" + Buffer.WriteCapacity(write_buf) + \" read_cap=\" + Buffer.ReadCapacity(read_buf));\n\n // Get data from our current srcs[index] buffer.\n byte[] app_data = new byte[expected_write];\n srcs[index].get(app_data);\n\n // Actual amount written. Since this is a PR.Write call, mark\n // attempted_write.\n int this_write = PR.Write(ssl_fd, app_data);\n attempted_write = true;\n\n // Reset our buffer's position in event of sub-optimal write.\n if (this_write < expected_write) {\n int pos = srcs[index].position();\n\n // When this_write < 0, we want to reset to the beginning\n // because we assume we haven't written any data due to an\n // error before writing.\n int delta = expected_write - Math.max(0, this_write);\n\n srcs[index].position(pos - delta);\n }\n\n debug(\"JSSEngine.writeData(): this_write=\" + this_write);\n if (this_write < 0) {\n int error = PR.GetError();\n if (error == PRErrors.SOCKET_SHUTDOWN_ERROR) {\n debug(\"NSPR reports outbound socket is shutdown.\");\n is_outbound_closed = true;\n } else if (error != PRErrors.WOULD_BLOCK_ERROR) {\n throw new RuntimeException(\"Unable to write to internal ssl_fd: \" + errorText(PR.GetError()));\n }\n\n break;\n }\n\n data_length += this_write;\n\n if (this_write < expected_write) {\n // If we didn't get an error but we got less than our expected\n // write, it is best to exit to give us time to drain the\n // buffers before attempting another write. We're guaranteed\n // to be called again because we wrote a non-zero amount here.\n break;\n }\n }\n\n // When we didn't call PR.Write, invoke a dummy call to PR.Write to\n // ensure we always attempt to write to push data from NSS's internal\n // buffers into our network buffers.\n if (!attempted_write) {\n PR.Write(ssl_fd, null);\n }\n\n debug(\"JSSEngine.writeData(): data_length=\" + data_length);\n\n return data_length;\n }\n\n private void logWrap(ByteBuffer dst) {\n if (debug_port <= 0 || dst == null || dst.remaining() == 0) {\n return;\n }\n\n loggingSocketConsumeAllBytes();\n\n OutputStream stream = s_ostream;\n\n if (!as_server) {\n // A wrap from the client means we write data to the outbound\n // side of the client socket.\n stream = c_ostream;\n }\n\n WritableByteChannel channel = Channels.newChannel(stream);\n\n int pos = dst.position();\n try {\n dst.flip();\n debug(\"JSSEngine: logWrap() - writing \" + dst.remaining() + \" bytes.\");\n channel.write(dst);\n stream.flush();\n dst.flip();\n } catch (Exception e) {\n throw new RuntimeException(\"Unable to log contents of wrap's dst to debug socket: \" + e.getMessage(), e);\n } finally {\n dst.position(pos);\n }\n }\n\n @Override\n public SSLEngineResult wrap(ByteBuffer[] srcs, int offset, int length, ByteBuffer dst) throws IllegalArgumentException, SSLException {\n debug(\"JSSEngine: wrap(ssl_fd=\" + ssl_fd + \")\");\n // In this method, we're taking the application data from the various\n // srcs and writing it to the remote peer (via ssl_fd). If there's any\n // data for us to send to the remote peer, we place it in dst.\n //\n // However, we also need to detect if the handshake is still ongoing;\n // if so, we can't send data (from src) until then.\n\n if (ssl_fd == null) {\n beginHandshake();\n }\n\n // Before going much further, check to see if we need to run a\n // delegated task. So far, the only delegated tasks we have are\n // for checking TrustManagers.\n if (checkNeedCertValidation()) {\n return new SSLEngineResult(SSLEngineResult.Status.OK, handshake_state, 0, 0);\n }\n\n // Order of operations:\n // 1. Step the handshake\n // 2. Write data from srcs to ssl_fd\n // 3. Write data from write_buf to dst\n //\n // This isn't technically locally optimal: it could be that write_buf\n // is full while we're handshaking so step 1 could be a no-op, but\n // we could read from write_buf and step the handshake then. However,\n // on our next call to wrap() would also step the handshake, which\n // two in a row would almost certainly result in one being a no-op.\n // Both steps 1 and 2 could write data to dsts. At best 2 will fail if\n // write_buf is full, however, we'd again end up calling wrap() again\n // anyways.\n //\n // Note that allowances are given for underflow but not overflow: a\n // single call to PR.Write() might not suffice for step 2; we might\n // need to execute step 3 and come back and re-execute steps 2 and 3\n // multiple times in order to send all data. However, since this could\n // technically also be true of the handshake itself, wrap everything\n // in the do-while loop.\n\n // Actual amount of data read from srcs (and written to ssl_fd). This\n // is determined by the PR.Write(...) call on ssl_fd.\n int app_data = 0;\n\n // wire_data is the number of bytes written to dst. This is bounded\n // above by two fields: the number of bytes we can read from\n // write_buf, and the size of dst, if present.\n int wire_data = 0;\n\n if (is_inbound_closed && !is_outbound_closed) {\n closeOutbound();\n }\n\n int this_src_write;\n int this_dst_write;\n do {\n this_src_write = 0;\n this_dst_write = 0;\n\n // First we try updating the handshake state.\n updateHandshakeState();\n if (ssl_exception == null && seen_exception) {\n if (handshake_state != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {\n // In the event that:\n //\n // 1. We saw an exception in the past\n // --> (seen_exception is true),\n // 2. We've already thrown it from wrap or unwrap,\n // --> (ssl_exception is null),\n // 3. We were previously handshaking\n // --> (handshake_state is a handshaking state),\n //\n // we need to make sure wrap is called again to ensure the\n // alert is actually written to the wire. So here we are,\n // in wrap and the above hold true; we can mark the handshake\n // status as \"FINISHED\" (because well, it is over due to the\n // alert). That leaves the return state to be anything other\n // than OK to indicate the error.\n handshake_state = SSLEngineResult.HandshakeStatus.FINISHED;\n }\n }\n\n // Try writing data from srcs to the other end of the connection. Note\n // that we always attempt this, even if the handshake isn't yet marked\n // as finished. This is because we need the call to PR.Write(...) to\n // tell if an alert is getting sent.\n this_src_write = writeData(srcs, offset, length);\n if (this_src_write > 0) {\n app_data += this_src_write;\n debug(\"JSSEngine.wrap(): wrote \" + this_src_write + \" from srcs to buffer.\");\n } else {\n debug(\"JSSEngine.wrap(): not writing from srcs to buffer: this_src_write=\" + this_src_write);\n }\n\n if (dst != null) {\n // Get an estimate for the expected write to dst; this is\n // the minimum of write_buf read capacity and dst.remaining\n // capacity.\n this_dst_write = Math.min((int) Buffer.ReadCapacity(write_buf), dst.remaining());\n\n // Try reading data from write_buf to dst; always do this, even\n // if we didn't write.\n if (this_dst_write > 0) {\n byte[] wire_buffer = Buffer.Read(write_buf, this_dst_write);\n dst.put(wire_buffer);\n this_dst_write = wire_buffer.length;\n wire_data += this_dst_write;\n\n debug(\"JSSEngine.wrap() - Wrote \" + wire_buffer.length + \" bytes to dst.\");\n } else {\n debug(\"JSSEngine.wrap(): not writing from write_buf into dst: this_dst_write=0 write_buf.read_capacity=\" + Buffer.ReadCapacity(write_buf) + \" dst.remaining=\" + dst.remaining());\n }\n } else {\n debug(\"JSSEngine.wrap(): not writing from write_buf into NULL dst\");\n }\n } while (this_src_write != 0 || this_dst_write != 0);\n\n if (seen_exception == false && ssl_exception == null) {\n ssl_exception = checkSSLAlerts();\n seen_exception = (ssl_exception != null);\n }\n\n logWrap(dst);\n\n // Before we return, check if an exception occurred and throw it if\n // one did.\n if (ssl_exception != null) {\n info(\"JSSEngine.wrap() - Got SSLException: \" + ssl_exception);\n SSLException excpt = ssl_exception;\n ssl_exception = null;\n cleanup();\n throw excpt;\n }\n\n // Need a way to introspect the open/closed state of the TLS\n // connection.\n\n SSLEngineResult.Status handshake_status = SSLEngineResult.Status.OK;\n\n if (ssl_exception == null && seen_exception) {\n debug(\"Seen and processed exception; closing inbound and outbound because this was the last wrap(...)\");\n closeInbound();\n closeOutbound();\n }\n\n if (is_outbound_closed) {\n debug(\"Socket is currently closed.\");\n handshake_status = SSLEngineResult.Status.CLOSED;\n }\n\n debug(\"JSSEngine.wrap() - Finished\");\n debug(\" - Status: \" + handshake_status);\n debug(\" - Handshake State: \" + handshake_state);\n debug(\" - wire_data: \" + wire_data);\n debug(\" - app_data: \" + app_data);\n\n if (handshake_state == SSLEngineResult.HandshakeStatus.FINISHED) {\n returned_finished = true;\n }\n\n tryCleanup();\n return new SSLEngineResult(handshake_status, handshake_state, app_data, wire_data);\n }\n\n /**\n * Calls cleanup only if both inbound and outbound data streams are\n * closed.\n *\n * This prevents accidental cleanup in the case of a partially open\n * connection.\n */\n @Override\n public void tryCleanup() {\n debug(\"JSSEngine: tryCleanup()\");\n if (is_inbound_closed && is_outbound_closed) {\n // throw new RuntimeException(\"Probably shouldn't be here!\");\n cleanup();\n }\n }\n\n /**\n * Performs cleanup of internal data, closing both inbound and outbound\n * data streams if still open.\n */\n @Override\n public void cleanup() {\n debug(\"JSSEngine: cleanup()\");\n\n if (!is_inbound_closed) {\n debug(\"JSSEngine: cleanup() - closing opened inbound socket\");\n closeInbound();\n }\n\n if (!is_outbound_closed) {\n debug(\"JSSEngine: cleanup() - closing opened outbound socket\");\n closeOutbound();\n }\n\n // First cleanup any debugging ports, if any.\n cleanupLoggingSocket();\n\n // Then clean up the NSS state.\n cleanupSSLFD();\n\n // Clean up the session.\n if (session != null) {\n session.close();\n session = null;\n }\n }\n\n private void cleanupLoggingSocket() {\n if (debug_port > 0) {\n try {\n s_socket.close();\n } catch (Exception e) {}\n\n try {\n c_socket.close();\n } catch (Exception e) {}\n\n try {\n ss_socket.close();\n } catch (Exception e) {}\n }\n }\n\n private void cleanupSSLFD() {\n if (!closed_fd && ssl_fd != null) {\n try {\n SSL.RemoveCallbacks(ssl_fd);\n ssl_fd.close();\n ssl_fd = null;\n } catch (Exception e) {\n logger.error(\"Got exception trying to cleanup SSLFD\", e);\n } finally {\n closed_fd = true;\n }\n }\n\n if (read_buf != null) {\n Buffer.Free(read_buf);\n read_buf = null;\n }\n\n if (write_buf != null) {\n Buffer.Free(write_buf);\n write_buf = null;\n }\n }\n\n // During testing with Tomcat 8.5, most instances did not call\n // cleanup, so all the JNI resources end up getting leaked: ssl_fd\n // (and its global ref), read_buf, and write_buf.\n @Override\n protected void finalize() {\n cleanup();\n }\n\n private class CertValidationTask extends CertAuthHandler {\n public CertValidationTask(SSLFDProxy fd) {\n super(fd);\n }\n\n public String findAuthType(SSLFDProxy ssl_fd, PK11Cert[] chain) throws Exception {\n // Java's CryptoManager is supposed to validate that the auth type\n // chosen by the underlying protocol is compatible with the\n // certificates in the channel. With TLSv1.3, this is less of a\n // concern. However, NSS doesn't directly expose an authType\n // compatible with Java; we're left inquiring for similar\n // information from the channel info.\n\n SSLPreliminaryChannelInfo info = SSL.GetPreliminaryChannelInfo(ssl_fd);\n if (info == null) {\n String msg = \"Expected non-null result from GetPreliminaryChannelInfo!\";\n throw new RuntimeException(msg);\n }\n\n if (!info.haveProtocolVersion()) {\n String msg = \"Expected SSLPreliminaryChannelInfo (\";\n msg += info + \") to have protocol information.\";\n throw new RuntimeException(msg);\n }\n\n if (!info.haveCipherSuite()) {\n String msg = \"Expected SSLPreliminaryChannelInfo (\";\n msg += info + \") to have cipher suite information.\";\n throw new RuntimeException(msg);\n }\n\n SSLVersion version = info.getProtocolVersion();\n SSLCipher suite = info.getCipherSuite();\n\n if (version.value() < SSLVersion.TLS_1_3.value()) {\n // When we're doing a TLSv1.2 or earlier protocol exchange,\n // we can simply check the cipher suite value for the\n // authentication type.\n if (suite.requiresRSACert()) {\n // Java expects RSA_EXPORT to be handled properly.\n // However, rather than checking the actual bits in\n // the RSA certificate, return it purely based on\n // cipher suite name. In modern reality, these ciphers\n // should _NEVER_ be negotiated!\n if (suite.name().contains(\"RSA_EXPORT\")) {\n return \"RSA_EXPORT\";\n }\n\n return \"RSA\";\n } else if (suite.requiresECDSACert()) {\n return \"ECDSA\";\n } else if (suite.requiresDSSCert()) {\n // Notably, DSS is the same as DSA, but the suite names\n // all use DSS while the JDK uses DSA.\n return \"DSA\";\n }\n // Implicit else: authType == null, causing TrustManager\n // check to fail.\n } else {\n // For TLSv1.3 and any later protocols, we can't rely on\n // the above requires() checks, because the actual\n // signature type depends on the type of the certificate\n // provided. This makes the TrustManager field redundant,\n // but yet we still have to provide it.\n if (chain != null && chain.length > 0 && chain[0] != null) {\n PK11Cert cert = chain[0];\n PublicKey key = cert.getPublicKey();\n return key.getAlgorithm();\n }\n // Implicit else here and above: authType == null, which\n // will cause the TrustManager check to fail.\n }\n\n return null;\n }\n\n @Override\n public int check(SSLFDProxy fd) {\n // Needs to be available for assignException() below.\n PK11Cert[] chain = null;\n\n try {\n chain = SSL.PeerCertificateChain(fd);\n String authType = findAuthType(fd, chain);\n debug(\"CertAuthType: \" + authType);\n\n if (chain == null || chain.length == 0) {\n // When the chain is NULL, we'd always fail in the\n // TrustManager calls, beacuse they expect a non-NULL,\n // non-empty chain. However, this is sometimes desired,\n // for instance, if we requested the peer to provide a\n // certificate chain and they didn't.\n if (as_server == true && !need_client_auth) {\n // Since we're a server validating the client's\n // chain (and they didn't provide one), we should\n // ignore it instead of forcing the problem.\n debug(\"No client certificate chain and client cert not needed.\");\n return 0;\n }\n }\n\n for (X509TrustManager tm : trust_managers) {\n // X509ExtendedTrustManager lets the TM access the\n // SSLEngine while validating certificates. Otherwise,\n // the X509TrustManager doesn't have access to that\n // parameter. Facilitate it if possible.\n if (tm instanceof X509ExtendedTrustManager) {\n X509ExtendedTrustManager etm = (X509ExtendedTrustManager) tm;\n if (as_server) {\n etm.checkClientTrusted(chain, authType, JSSEngineReferenceImpl.this);\n } else {\n etm.checkServerTrusted(chain, authType, JSSEngineReferenceImpl.this);\n }\n } else {\n if (as_server) {\n tm.checkClientTrusted(chain, authType);\n } else {\n tm.checkServerTrusted(chain, authType);\n }\n }\n }\n } catch (Exception excpt) {\n return assignException(excpt, chain);\n }\n\n return 0;\n }\n\n private int assignException(Exception excpt, PK11Cert[] chain) {\n int nss_code = Cert.MatchExceptionToNSSError(excpt);\n\n if (seen_exception) {\n return nss_code;\n }\n\n String msg = \"Got exception while trying to validate \";\n msg += \"peer's certificate chain:\\n\";\n if (chain == null) {\n msg += \" - (null chain)\\n\";\n } else if (chain.length == 0) {\n msg += \" - (0 length chain)\\n\";\n } else {\n for (PK11Cert cert : chain) {\n msg += \" - \" + cert + \"\\n\";\n }\n }\n msg += \"with given TrustManagers:\\n\";\n if (trust_managers == null) {\n msg += \" - (null TrustManagers)\\n\";\n } else if (trust_managers.length == 0) {\n msg += \" - (0 length TrustManagers)\\n\";\n } else {\n for (X509TrustManager tm : trust_managers) {\n msg += \" - \" + tm + \"\\n\";\n }\n }\n msg += \"exception message: \" + excpt.getMessage();\n\n seen_exception = true;\n ssl_exception = new SSLException(msg, excpt);\n return nss_code;\n }\n }\n\n private class BypassBadHostname extends BadCertHandler {\n public BypassBadHostname(SSLFDProxy fd, int error) {\n super(fd, error);\n }\n\n @Override\n public int check(SSLFDProxy fd, int error) {\n // NSS enforces strict hostname verification via the SSL_SetURL\n // function call. Java doesn't pass this information to either\n // SSLSocket or SSLEngine, so we can at best try and infer this\n // information. However, since this only needs to be validated\n // on the client side, we can elide this check (like the JCA\n // suggests and as the SunJSSE implementation does). In order\n // to do so, we need to check for the BAD_CERT_DOMAIN error\n // and tell NSS to ignore it.\n //\n // This makes the assumptions:\n // 1. The hostname check is the very last check in the NSS\n // certificate validation handler.\n // 2. As a consequence of (1) an otherwise valid certificate\n // will pass all other checks but fail due to hostname==NULL.\n // 3. As a consequence of (1), all invalid certificates will\n // fail earlier.\n // 4. No other paths report BAD_CERT_DOMAIN earlier than the\n // final hostname check.\n if (error == SSLErrors.BAD_CERT_DOMAIN) {\n return 0;\n }\n\n return error;\n }\n }\n}\n\n", "input": "", "output": "【漏洞类型】对象注入漏洞\n【严重性】Medium\n【漏洞摘要】此代码在处理SSL警报事件时存在对象注入漏洞。具体来说,`checkSSLAlerts`方法在处理`SSLAlertEvent`对象时,调用了`event.setEngine(this)`。这可能导致攻击者通过构造恶意的`SSLAlertEvent`对象来注入一个他们控制的`SSLEngine`实例,从而绕过安全检查或执行其他恶意操作。\n【关键代码模式】`checkSSLAlerts`方法中的`event.setEngine(this)`调用。\n【修复方法说明】通过移除`event.setEngine(this)`调用,确保`SSLAlertEvent`对象不能被注入自定义的`SSLEngine`实例,从而防止潜在的安全风险。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nclient/src/com/aerospike/client/Value.java\n### 代码内容:\npackage com.aerospike.client;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.ObjectOutputStream;\nimport java.nio.ByteBuffer;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.SortedMap;\nimport java.util.UUID;\n\nimport org.luaj.vm2.LuaBoolean;\nimport org.luaj.vm2.LuaDouble;\nimport org.luaj.vm2.LuaInteger;\nimport org.luaj.vm2.LuaNil;\nimport org.luaj.vm2.LuaString;\nimport org.luaj.vm2.LuaValue;\n\nimport com.aerospike.client.cdt.MapOrder;\nimport com.aerospike.client.command.Buffer;\nimport com.aerospike.client.command.ParticleType;\nimport com.aerospike.client.lua.LuaBytes;\nimport com.aerospike.client.lua.LuaInstance;\nimport com.aerospike.client.util.Packer;\n\n/**\n * Polymorphic value classes used to efficiently serialize objects into the wire protocol.\n */\npublic abstract class Value {\n\t/**\n\t * Should client send boolean particle type for a boolean bin. If false,\n\t * an integer particle type (1 or 0) is sent instead. Must be false for server\n\t * versions less than 5.6 which do not support boolean bins. Can set to true for\n\t * server 5.6+.\n\t */\n\tpublic static boolean UseBoolBin = false;\n\n\t/**\n\t * Null value.\n\t */\n\tpublic static final Value NULL = NullValue.INSTANCE;\n\n\t/**\n\t * Infinity value to be used in CDT range comparisons only.\n\t */\n\tpublic static final Value INFINITY = new InfinityValue();\n\n\t/**\n\t * Wildcard value to be used in CDT range comparisons only.\n\t */\n\tpublic static final Value WILDCARD = new WildcardValue();\n\n\t/**\n\t * Get string or null value instance.\n\t */\n\tpublic static Value get(String value) {\n\t\treturn (value == null)? NullValue.INSTANCE : new StringValue(value);\n\t}\n\n\t/**\n\t * Get byte array or null value instance.\n\t */\n\tpublic static Value get(byte[] value) {\n\t\treturn (value == null)? NullValue.INSTANCE : new BytesValue(value);\n\t}\n\n\t/**\n\t * Get byte array with type or null value instance.\n\t */\n\tpublic static Value get(byte[] value, int type) {\n\t\treturn (value == null)? NullValue.INSTANCE : new BytesValue(value, type);\n\t}\n\n\t/**\n\t * Get byte segment or null value instance.\n\t */\n\tpublic static Value get(byte[] value, int offset, int length) {\n\t\treturn (value == null)? NullValue.INSTANCE : new ByteSegmentValue(value, offset, length);\n\t}\n\n\t/**\n\t * Get byte segment or null value instance.\n\t */\n\tpublic static Value get(ByteBuffer bb) {\n\t\treturn (bb == null)? NullValue.INSTANCE : new BytesValue(bb.array());\n\t}\n\n\t/**\n\t * Get byte value instance.\n\t */\n\tpublic static Value get(byte value) {\n\t\treturn new ByteValue(value);\n\t}\n\n\t/**\n\t * Get integer value instance.\n\t */\n\tpublic static Value get(int value) {\n\t\treturn new IntegerValue(value);\n\t}\n\n\t/**\n\t * Get long value instance.\n\t */\n\tpublic static Value get(long value) {\n\t\treturn new LongValue(value);\n\t}\n\n\t/**\n\t * Get double value instance.\n\t */\n\tpublic static Value get(double value) {\n\t\treturn new DoubleValue(value);\n\t}\n\n\t/**\n\t * Get float value instance.\n\t */\n\tpublic static Value get(float value) {\n\t\treturn new FloatValue(value);\n\t}\n\n\t/**\n\t * Get boolean value instance.\n\t */\n\tpublic static Value get(boolean value) {\n\t\tif (UseBoolBin) {\n\t\t\treturn new BooleanValue(value);\n\t\t}\n\t\telse {\n\t\t\treturn new BoolIntValue(value);\n\t\t}\n\t}\n\n\t/**\n\t * Get enum value string instance.\n\t */\n\tpublic static Value get(Enum<?> value) {\n\t\treturn (value == null)? NullValue.INSTANCE : new StringValue(value.toString());\n\t}\n\n\t/**\n\t * Get UUID value string instance.\n\t */\n\tpublic static Value get(UUID value) {\n\t\treturn (value == null)? NullValue.INSTANCE : new StringValue(value.toString());\n\t}\n\n\t/**\n\t * Get list or null value instance.\n\t */\n\tpublic static Value get(List<?> value) {\n\t\treturn (value == null)? NullValue.INSTANCE : new ListValue(value);\n\t}\n\n\t/**\n\t * Get map or null value instance.\n\t */\n\tpublic static Value get(Map<?,?> value) {\n\t\treturn (value == null)? NullValue.INSTANCE : new MapValue(value);\n\t}\n\n\t/**\n\t * Get map or null value instance.\n\t */\n\tpublic static Value get(Map<?,?> value, MapOrder order) {\n\t\treturn (value == null)? NullValue.INSTANCE : new MapValue(value, order);\n\t}\n\n\t/**\n\t * Get sorted map or null value instance.\n\t */\n\tpublic static Value get(List<? extends Entry<?,?>> value, MapOrder mapOrder) {\n\t\treturn (value == null)? NullValue.INSTANCE : new SortedMapValue(value, mapOrder);\n\t}\n\n\t/**\n\t * Get value array instance.\n\t */\n\tpublic static Value get(Value[] value) {\n\t\treturn (value == null)? NullValue.INSTANCE : new ValueArray(value);\n\t}\n\n\t/**\n\t * Get blob or null value instance.\n\t */\n\tpublic static Value getAsBlob(Object value) {\n\t\treturn (value == null)? NullValue.INSTANCE : new BlobValue(value);\n\t}\n\n\t/**\n\t * Get GeoJSON or null value instance.\n\t */\n\tpublic static Value getAsGeoJSON(String value) {\n\t\treturn (value == null)? NullValue.INSTANCE : new GeoJSONValue(value);\n\t}\n\n\t/**\n\t * Get HyperLogLog or null value instance.\n\t */\n\tpublic static Value getAsHLL(byte[] value) {\n\t\treturn (value == null)? NullValue.INSTANCE : new HLLValue(value);\n\t}\n\n\t/**\n\t * Get null value instance.\n\t */\n\tpublic static Value getAsNull() {\n\t\treturn NullValue.INSTANCE;\n\t}\n\n\t/**\n\t * Determine value given generic object.\n\t * This is the slowest of the Value get() methods.\n\t * Useful when copying records from one cluster to another.\n\t */\n\tpublic static Value get(Object value) {\n\t\tif (value == null) {\n\t\t\treturn NullValue.INSTANCE;\n\t\t}\n\n\t\tif (value instanceof Value) {\n\t\t\treturn (Value)value;\n\t\t}\n\n\t\tif (value instanceof byte[]) {\n\t\t\treturn new BytesValue((byte[])value);\n\t\t}\n\n\t\tif (value instanceof String) {\n\t\t\treturn new StringValue((String)value);\n\t\t}\n\n\t\tif (value instanceof Integer) {\n\t\t\treturn new IntegerValue((Integer)value);\n\t\t}\n\n\t\tif (value instanceof Long) {\n\t\t\treturn new LongValue((Long)value);\n\t\t}\n\n\t\tif (value instanceof Double) {\n\t\t\treturn new DoubleValue((Double)value);\n\t\t}\n\n\t\tif (value instanceof Float) {\n\t\t\treturn new FloatValue((Float)value);\n\t\t}\n\n\t\tif (value instanceof Boolean) {\n\t\t\tif (UseBoolBin) {\n\t\t\t\treturn new BooleanValue((Boolean)value);\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn new BoolIntValue((Boolean)value);\n\t\t\t}\n\t\t}\n\n\t\tif (value instanceof Byte) {\n\t\t\treturn new ByteValue((byte)value);\n\t\t}\n\n\t\tif (value instanceof Enum) {\n \treturn new StringValue(value.toString());\n\t\t}\n\n\t\tif (value instanceof UUID) {\n\t\t\treturn new StringValue(value.toString());\n\t\t}\n\n\t\tif (value instanceof List<?>) {\n\t\t\treturn new ListValue((List<?>)value);\n\t\t}\n\n\t\tif (value instanceof Map<?,?>) {\n\t\t\treturn new MapValue((Map<?,?>)value);\n\t\t}\n\n\t\tif (value instanceof ByteBuffer) {\n\t\t\tByteBuffer bb = (ByteBuffer)value;\n\t\t\treturn new BytesValue(bb.array());\n\t\t}\n\n\t\treturn new BlobValue(value);\n\t}\n\n\t/**\n\t * Get value from Record object. Useful when copying records from one cluster to another.\n\t * @deprecated\n\t * <p> Use {@link Value#get(Object)} instead.\n\t */\n\t@Deprecated\n\tpublic static Value getFromRecordObject(Object value) {\n\t\treturn Value.get(value);\n\t}\n\n\t/**\n\t * Calculate number of bytes necessary to serialize the value in the wire protocol.\n\t */\n\tpublic abstract int estimateSize() throws AerospikeException;\n\n\t/**\n\t * Serialize the value in the wire protocol.\n\t */\n\tpublic abstract int write(byte[] buffer, int offset) throws AerospikeException;\n\n\t/**\n\t * Serialize the value using MessagePack.\n\t */\n\tpublic abstract void pack(Packer packer);\n\n\t/**\n\t * Validate if value type can be used as a key.\n\t * @throws AerospikeException\tif type can't be used as a key.\n\t */\n\tpublic void validateKeyType() throws AerospikeException {\n\t}\n\n\t/**\n\t * Get wire protocol value type.\n\t */\n\tpublic abstract int getType();\n\n\t/**\n\t * Return original value as an Object.\n\t */\n\tpublic abstract Object getObject();\n\n\t/**\n\t * Return value as an Object.\n\t */\n\tpublic abstract LuaValue getLuaValue(LuaInstance instance);\n\n\t/**\n\t * Return value as an integer.\n\t */\n\tpublic int toInteger() {\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Return value as a long.\n\t */\n\tpublic long toLong() {\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Empty value.\n\t */\n\tpublic static final class NullValue extends Value {\n\t\tpublic static final NullValue INSTANCE = new NullValue();\n\n\t\t@Override\n\t\tpublic int estimateSize() {\n\t\t\treturn 0;\n\t\t}\n\n\t\t@Override\n\t\tpublic int write(byte[] buffer, int offset) {\n\t\t\treturn 0;\n\t\t}\n\n\t\t@Override\n\t\tpublic void pack(Packer packer) {\n\t\t\tpacker.packNil();\n\t\t}\n\n\t\t@Override\n\t\tpublic void validateKeyType() {\n\t\t\tthrow new AerospikeException(ResultCode.PARAMETER_ERROR, \"Invalid key type: null\");\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn ParticleType.NULL;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getObject() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic LuaValue getLuaValue(LuaInstance instance) {\n\t\t\treturn LuaNil.NIL;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object other) {\n\t\t\tif (other == null) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn this.getClass().equals(other.getClass());\n\t\t}\n\n\t\t@Override\n\t\tpublic final int hashCode() {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t/**\n\t * Byte array value.\n\t */\n\tpublic static final class BytesValue extends Value {\n\t\tprivate final byte[] bytes;\n\t\tprivate final int type;\n\n\t\tpublic BytesValue(byte[] bytes) {\n\t\t\tthis.bytes = bytes;\n\t\t\tthis.type = ParticleType.BLOB;\n\t\t}\n\n\t\tpublic BytesValue(byte[] bytes, int type) {\n\t\t\tthis.bytes = bytes;\n\t\t\tthis.type = type;\n\t\t}\n\n\t\t@Override\n\t\tpublic int estimateSize() {\n\t\t\treturn bytes.length;\n\t\t}\n\n\t\t@Override\n\t\tpublic int write(byte[] buffer, int offset) {\n\t\t\tSystem.arraycopy(bytes, 0, buffer, offset, bytes.length);\n\t\t\treturn bytes.length;\n\t\t}\n\n\t\t@Override\n\t\tpublic void pack(Packer packer) {\n\t\t\tpacker.packParticleBytes(bytes, type);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn type;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getObject() {\n\t\t\treturn bytes;\n\t\t}\n\n\t\t@Override\n\t\tpublic LuaValue getLuaValue(LuaInstance instance) {\n\t\t\treturn new LuaBytes(instance, bytes, type);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn Buffer.bytesToHexString(bytes);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object other) {\n\t\t\treturn (other != null &&\n\t\t\t\tthis.getClass().equals(other.getClass()) &&\n\t\t\t\tArrays.equals(this.bytes, ((BytesValue)other).bytes));\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Arrays.hashCode(bytes);\n\t\t}\n\t}\n\n\t/**\n\t * Byte segment value.\n\t */\n\tpublic static final class ByteSegmentValue extends Value {\n\t\tprivate final byte[] bytes;\n\t\tprivate final int offset;\n\t\tprivate final int length;\n\n\t\tpublic ByteSegmentValue(byte[] bytes, int offset, int length) {\n\t\t\tthis.bytes = bytes;\n\t\t\tthis.offset = offset;\n\t\t\tthis.length = length;\n\t\t}\n\n\t\t@Override\n\t\tpublic int estimateSize() {\n\t\t\treturn length;\n\n\t\t}\n\n\t\t@Override\n\t\tpublic int write(byte[] buffer, int targetOffset) {\n\t\t\tSystem.arraycopy(bytes, offset, buffer, targetOffset, length);\n\t\t\treturn length;\n\t\t}\n\n\t\t@Override\n\t\tpublic void pack(Packer packer) {\n\t\t\tpacker.packParticleBytes(bytes, offset, length);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn ParticleType.BLOB;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getObject() {\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic LuaValue getLuaValue(LuaInstance instance) {\n\t\t\treturn LuaString.valueOf(bytes, offset, length);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn Buffer.bytesToHexString(bytes, offset, length);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\tif (obj == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (! this.getClass().equals(obj.getClass())) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tByteSegmentValue other = (ByteSegmentValue)obj;\n\n\t\t\tif (this.length != other.length) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < length; i++) {\n\t\t\t\tif (this.bytes[this.offset + i] != other.bytes[other.offset + i]) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\tint result = 1;\n\t\t\tfor (int i = 0; i < length; i++) {\n\t\t\t\tresult = 31 * result + bytes[offset+i];\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t\tpublic byte[] getBytes() {\n\t\t\treturn bytes;\n\t\t}\n\n\t\tpublic int getOffset() {\n\t\t\treturn offset;\n\t\t}\n\n\t\tpublic int getLength() {\n\t\t\treturn length;\n\t\t}\n\t}\n\n\t/**\n\t * Byte value.\n\t */\n\tpublic static final class ByteValue extends Value {\n\t\tprivate final byte value;\n\n\t\tpublic ByteValue(byte value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t\t@Override\n\t\tpublic int estimateSize() {\n\t\t\treturn 8;\n\t\t}\n\n\t\t@Override\n\t\tpublic int write(byte[] buffer, int offset) {\n\t\t\tBuffer.longToBytes(value, buffer, offset);\n\t\t\treturn 8;\n\t\t}\n\n\t\t@Override\n\t\tpublic void pack(Packer packer) {\n\t\t\tpacker.packByte(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\t// The server does not natively handle one byte, so store as long (8 byte integer).\n\t\t\treturn ParticleType.INTEGER;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getObject() {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic LuaValue getLuaValue(LuaInstance instance) {\n\t\t\treturn LuaInteger.valueOf(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn Byte.toString(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object other) {\n\t\t\treturn (other != null &&\n\t\t\t\tthis.getClass().equals(other.getClass()) &&\n\t\t\t\tthis.value == ((ByteValue)other).value);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic int toInteger() {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic long toLong() {\n\t\t\treturn value;\n\t\t}\n\t}\n\n\t/**\n\t * String value.\n\t */\n\tpublic static final class StringValue extends Value {\n\t\tprivate final String value;\n\n\t\tpublic StringValue(String value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t\t@Override\n\t\tpublic int estimateSize() {\n\t\t\treturn Buffer.estimateSizeUtf8(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic int write(byte[] buffer, int offset) {\n\t\t\treturn Buffer.stringToUtf8(value, buffer, offset);\n\t\t}\n\n\t\t@Override\n\t\tpublic void pack(Packer packer) {\n\t\t\tpacker.packParticleString(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn ParticleType.STRING;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getObject() {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic LuaValue getLuaValue(LuaInstance instance) {\n\t\t\treturn LuaString.valueOf(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object other) {\n\t\t\treturn (other != null &&\n\t\t\t\tthis.getClass().equals(other.getClass()) &&\n\t\t\t\tthis.value.equals(((StringValue)other).value));\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn value.hashCode();\n\t\t}\n\t}\n\n\t/**\n\t * Integer value.\n\t */\n\tpublic static final class IntegerValue extends Value {\n\t\tprivate final int value;\n\n\t\tpublic IntegerValue(int value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t\t@Override\n\t\tpublic int estimateSize() {\n\t\t\treturn 8;\n\t\t}\n\n\t\t@Override\n\t\tpublic int write(byte[] buffer, int offset) {\n\t\t\tBuffer.longToBytes(value, buffer, offset);\n\t\t\treturn 8;\n\t\t}\n\n\t\t@Override\n\t\tpublic void pack(Packer packer) {\n\t\t\tpacker.packInt(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn ParticleType.INTEGER;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getObject() {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic LuaValue getLuaValue(LuaInstance instance) {\n\t\t\treturn LuaInteger.valueOf(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn Integer.toString(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object other) {\n\t\t\treturn (other != null &&\n\t\t\t\tthis.getClass().equals(other.getClass()) &&\n\t\t\t\tthis.value == ((IntegerValue)other).value);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic int toInteger() {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic long toLong() {\n\t\t\treturn value;\n\t\t}\n\t}\n\n\t/**\n\t * Long value.\n\t */\n\tpublic static final class LongValue extends Value {\n\t\tprivate final long value;\n\n\t\tpublic LongValue(long value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t\t@Override\n\t\tpublic int estimateSize() {\n\t\t\treturn 8;\n\t\t}\n\n\t\t@Override\n\t\tpublic int write(byte[] buffer, int offset) {\n\t\t\tBuffer.longToBytes(value, buffer, offset);\n\t\t\treturn 8;\n\t\t}\n\n\t\t@Override\n\t\tpublic void pack(Packer packer) {\n\t\t\tpacker.packLong(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn ParticleType.INTEGER;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getObject() {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic LuaValue getLuaValue(LuaInstance instance) {\n\t\t\treturn LuaInteger.valueOf(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn Long.toString(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object other) {\n\t\t\treturn (other != null &&\n\t\t\t\tthis.getClass().equals(other.getClass()) &&\n\t\t\t\tthis.value == ((LongValue)other).value);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn (int)(value ^ (value >>> 32));\n\t\t}\n\n\t\t@Override\n\t\tpublic int toInteger() {\n\t\t\treturn (int)value;\n\t\t}\n\n\t\t@Override\n\t\tpublic long toLong() {\n\t\t\treturn value;\n\t\t}\n\t}\n\n\t/**\n\t * Double value.\n\t */\n\tpublic static final class DoubleValue extends Value {\n\t\tprivate final double value;\n\n\t\tpublic DoubleValue(double value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t\t@Override\n\t\tpublic int estimateSize() {\n\t\t\treturn 8;\n\t\t}\n\n\t\t@Override\n\t\tpublic int write(byte[] buffer, int offset) {\n\t\t\tBuffer.doubleToBytes(value, buffer, offset);\n\t\t\treturn 8;\n\t\t}\n\n\t\t@Override\n\t\tpublic void pack(Packer packer) {\n\t\t\tpacker.packDouble(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn ParticleType.DOUBLE;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getObject() {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic LuaValue getLuaValue(LuaInstance instance) {\n\t\t\treturn LuaDouble.valueOf(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn Double.toString(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object other) {\n\t\t\treturn (other != null &&\n\t\t\t\tthis.getClass().equals(other.getClass()) &&\n\t\t\t\tthis.value == ((DoubleValue)other).value);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\tlong bits = Double.doubleToLongBits(value);\n\t\t\treturn (int)(bits ^ (bits >>> 32));\n\t\t}\n\n\t\t@Override\n\t\tpublic int toInteger() {\n\t\t\treturn (int)value;\n\t\t}\n\n\t\t@Override\n\t\tpublic long toLong() {\n\t\t\treturn (long)value;\n\t\t}\n\t}\n\n\t/**\n\t * Float value.\n\t */\n\tpublic static final class FloatValue extends Value {\n\t\tprivate final float value;\n\n\t\tpublic FloatValue(float value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t\t@Override\n\t\tpublic int estimateSize() {\n\t\t\treturn 8;\n\t\t}\n\n\t\t@Override\n\t\tpublic int write(byte[] buffer, int offset) {\n\t\t\tBuffer.doubleToBytes(value, buffer, offset);\n\t\t\treturn 8;\n\t\t}\n\n\t\t@Override\n\t\tpublic void pack(Packer packer) {\n\t\t\tpacker.packFloat(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn ParticleType.DOUBLE;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getObject() {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic LuaValue getLuaValue(LuaInstance instance) {\n\t\t\treturn LuaDouble.valueOf(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn Float.toString(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object other) {\n\t\t\treturn (other != null &&\n\t\t\t\tthis.getClass().equals(other.getClass()) &&\n\t\t\t\tthis.value == ((FloatValue)other).value);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Float.floatToIntBits(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic int toInteger() {\n\t\t\treturn (int)value;\n\t\t}\n\n\t\t@Override\n\t\tpublic long toLong() {\n\t\t\treturn (long)value;\n\t\t}\n\t}\n\n\t/**\n\t * Boolean value.\n\t */\n\tpublic static final class BooleanValue extends Value {\n\t\tprivate final boolean value;\n\n\t\tpublic BooleanValue(boolean value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t\t@Override\n\t\tpublic int estimateSize() {\n\t\t\treturn 1;\n\t\t}\n\n\t\t@Override\n\t\tpublic int write(byte[] buffer, int offset) {\n\t\t\tbuffer[offset] = value? (byte)1 : (byte)0;\n\t\t\treturn 1;\n\t\t}\n\n\t\t@Override\n\t\tpublic void pack(Packer packer) {\n\t\t\tpacker.packBoolean(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic void validateKeyType() {\n\t\t\tthrow new AerospikeException(ResultCode.PARAMETER_ERROR, \"Invalid key type: boolean\");\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn ParticleType.BOOL;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getObject() {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic LuaValue getLuaValue(LuaInstance instance) {\n\t\t\treturn LuaBoolean.valueOf(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn Boolean.toString(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object other) {\n\t\t\treturn (other != null &&\n\t\t\t\tthis.getClass().equals(other.getClass()) &&\n\t\t\t\tthis.value == ((BooleanValue)other).value);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn value ? 1231 : 1237;\n\t\t}\n\n\t\t@Override\n\t\tpublic int toInteger() {\n\t\t\treturn value? 1 : 0;\n\t\t}\n\n\t\t@Override\n\t\tpublic long toLong() {\n\t\t\treturn value? 1L : 0L;\n\t\t}\n\t}\n\n\t/**\n\t * Boolean value that converts to integer when sending a bin to the server.\n\t * This class will be deleted once full conversion to boolean particle type\n\t * is complete.\n\t */\n\tpublic static final class BoolIntValue extends Value {\n\t\tprivate final boolean value;\n\n\t\tpublic BoolIntValue(boolean value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t\t@Override\n\t\tpublic int estimateSize() {\n\t\t\treturn 8;\n\t\t}\n\n\t\t@Override\n\t\tpublic int write(byte[] buffer, int offset) {\n\t\t\tBuffer.longToBytes(value? 1L : 0L, buffer, offset);\n\t\t\treturn 8;\n\t\t}\n\n\t\t@Override\n\t\tpublic void pack(Packer packer) {\n\t\t\tpacker.packBoolean(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic void validateKeyType() {\n\t\t\tthrow new AerospikeException(ResultCode.PARAMETER_ERROR, \"Invalid key type: BoolIntValue\");\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\t// The server does not natively handle boolean, so store as long (8 byte integer).\n\t\t\treturn ParticleType.INTEGER;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getObject() {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic LuaValue getLuaValue(LuaInstance instance) {\n\t\t\treturn LuaBoolean.valueOf(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn Boolean.toString(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object other) {\n\t\t\treturn (other != null &&\n\t\t\t\tthis.getClass().equals(other.getClass()) &&\n\t\t\t\tthis.value == ((BoolIntValue)other).value);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn value ? 1231 : 1237;\n\t\t}\n\n\t\t@Override\n\t\tpublic int toInteger() {\n\t\t\treturn value? 1 : 0;\n\t\t}\n\n\t\t@Override\n\t\tpublic long toLong() {\n\t\t\treturn value? 1L : 0L;\n\t\t}\n\t}\n\n\t/**\n\t * Blob value.\n\t */\n\tpublic static final class BlobValue extends Value {\n\t\tprivate final Object object;\n\t\tprivate byte[] bytes;\n\n\t\tpublic BlobValue(Object object) {\n\t\t\tthis.object = object;\n\t\t}\n\n\t\t@Override\n\t\tpublic int estimateSize() throws AerospikeException.Serialize {\n\t\t\ttry {\n\t\t\t\tByteArrayOutputStream bstream = new ByteArrayOutputStream();\n\t\t\t\tObjectOutputStream ostream = new ObjectOutputStream(bstream);\n\t\t\t\tostream.writeObject(object);\n\t\t\t\tostream.close();\n\t\t\t\tbytes = bstream.toByteArray();\n\t\t\t\treturn bytes.length;\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tthrow new AerospikeException.Serialize(e);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic int write(byte[] buffer, int offset) {\n\t\t\tSystem.arraycopy(bytes, 0, buffer, offset, bytes.length);\n\t\t\treturn bytes.length;\n\t\t}\n\n\t\t@Override\n\t\tpublic void pack(Packer packer) {\n\t\t\tpacker.packBlob(object);\n\t\t}\n\n\t\t@Override\n\t\tpublic void validateKeyType() {\n\t\t\tthrow new AerospikeException(ResultCode.PARAMETER_ERROR, \"Invalid key type: jblob\");\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn ParticleType.JBLOB;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getObject() {\n\t\t\treturn object;\n\t\t}\n\n\t\t@Override\n\t\tpublic LuaValue getLuaValue(LuaInstance instance) {\n\t\t\treturn LuaString.valueOf(bytes);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn Buffer.bytesToHexString(bytes);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object other) {\n\t\t\treturn (other != null &&\n\t\t\t\tthis.getClass().equals(other.getClass()) &&\n\t\t\t\tthis.object.equals(((BlobValue)other).object));\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn object.hashCode();\n\t\t}\n\t}\n\n\t/**\n\t * GeoJSON value.\n\t */\n\tpublic static final class GeoJSONValue extends Value {\n\t\tprivate final String value;\n\n\t\tpublic GeoJSONValue(String value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t\t@Override\n\t\tpublic int estimateSize() {\n\t\t\t// flags + ncells + jsonstr\n\t\t\treturn 1 + 2 + Buffer.estimateSizeUtf8(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic int write(byte[] buffer, int offset) {\n\t\t\tbuffer[offset] = 0; // flags\n\t\t\tBuffer.shortToBytes(0, buffer, offset + 1); // ncells\n\t\t\treturn 1 + 2 + Buffer.stringToUtf8(value, buffer, offset + 3); // jsonstr\n\t\t}\n\n\t\t@Override\n\t\tpublic void pack(Packer packer) {\n\t\t\tpacker.packGeoJSON(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic void validateKeyType() {\n\t\t\tthrow new AerospikeException(ResultCode.PARAMETER_ERROR, \"Invalid key type: GeoJson\");\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn ParticleType.GEOJSON;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getObject() {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic LuaValue getLuaValue(LuaInstance instance) {\n\t\t\treturn LuaString.valueOf(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object other) {\n\t\t\treturn (other != null &&\n\t\t\t\tthis.getClass().equals(other.getClass()) &&\n\t\t\t\tthis.value.equals(((GeoJSONValue)other).value));\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn value.hashCode();\n\t\t}\n\t}\n\n\t/**\n\t * HyperLogLog value.\n\t */\n\tpublic static final class HLLValue extends Value {\n\t\tprivate final byte[] bytes;\n\n\t\tpublic HLLValue(byte[] bytes) {\n\t\t\tthis.bytes = bytes;\n\t\t}\n\n\t\t@Override\n\t\tpublic int estimateSize() {\n\t\t\treturn bytes.length;\n\t\t}\n\n\t\t@Override\n\t\tpublic int write(byte[] buffer, int offset) {\n\t\t\tSystem.arraycopy(bytes, 0, buffer, offset, bytes.length);\n\t\t\treturn bytes.length;\n\t\t}\n\n\t\t@Override\n\t\tpublic void pack(Packer packer) {\n\t\t\tpacker.packParticleBytes(bytes);\n\t\t}\n\n\t\t@Override\n\t\tpublic void validateKeyType() {\n\t\t\tthrow new AerospikeException(ResultCode.PARAMETER_ERROR, \"Invalid key type: HLL\");\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn ParticleType.HLL;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getObject() {\n\t\t\treturn bytes;\n\t\t}\n\n\t\tpublic byte[] getBytes() {\n\t\t\treturn bytes;\n\t\t}\n\n\t\t@Override\n\t\tpublic LuaValue getLuaValue(LuaInstance instance) {\n\t\t\treturn new LuaBytes(instance, bytes);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn Buffer.bytesToHexString(bytes);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object other) {\n\t\t\treturn (other != null &&\n\t\t\t\tthis.getClass().equals(other.getClass()) &&\n\t\t\t\tArrays.equals(this.bytes, ((HLLValue)other).bytes));\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Arrays.hashCode(bytes);\n\t\t}\n\t}\n\n\t/**\n\t * Value array.\n\t */\n\tpublic static final class ValueArray extends Value {\n\t\tprivate final Value[] array;\n\t\tprivate byte[] bytes;\n\n\t\tpublic ValueArray(Value[] array) {\n\t\t\tthis.array = array;\n\t\t}\n\n\t\t@Override\n\t\tpublic int estimateSize() throws AerospikeException {\n\t\t\tbytes = Packer.pack(array);\n\t\t\treturn bytes.length;\n\t\t}\n\n\t\t@Override\n\t\tpublic int write(byte[] buffer, int offset) {\n\t\t\tSystem.arraycopy(bytes, 0, buffer, offset, bytes.length);\n\t\t\treturn bytes.length;\n\t\t}\n\n\t\t@Override\n\t\tpublic void pack(Packer packer) {\n\t\t\tpacker.packValueArray(array);\n\t\t}\n\n\t\t@Override\n\t\tpublic void validateKeyType() {\n\t\t\tthrow new AerospikeException(ResultCode.PARAMETER_ERROR, \"Invalid key type: value[]\");\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn ParticleType.LIST;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getObject() {\n\t\t\treturn array;\n\t\t}\n\n\t\t@Override\n\t\tpublic LuaValue getLuaValue(LuaInstance instance) {\n\t\t\treturn instance.getLuaList(array);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn Arrays.toString(array);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object other) {\n\t\t\treturn (other != null &&\n\t\t\t\tthis.getClass().equals(other.getClass()) &&\n\t\t\t\tArrays.equals(this.array, ((ValueArray)other).array));\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Arrays.hashCode(array);\n\t\t}\n\t}\n\n\t/**\n\t * List value.\n\t */\n\tpublic static final class ListValue extends Value {\n\t\tprivate final List<?> list;\n\t\tprivate byte[] bytes;\n\n\t\tpublic ListValue(List<?> list) {\n\t\t\tthis.list = list;\n\t\t}\n\n\t\t@Override\n\t\tpublic int estimateSize() throws AerospikeException {\n\t\t\tbytes = Packer.pack(list);\n\t\t\treturn bytes.length;\n\t\t}\n\n\t\t@Override\n\t\tpublic int write(byte[] buffer, int offset) {\n\t\t\tSystem.arraycopy(bytes, 0, buffer, offset, bytes.length);\n\t\t\treturn bytes.length;\n\t\t}\n\n\t\t@Override\n\t\tpublic void pack(Packer packer) {\n\t\t\tpacker.packList(list);\n\t\t}\n\n\t\t@Override\n\t\tpublic void validateKeyType() {\n\t\t\tthrow new AerospikeException(ResultCode.PARAMETER_ERROR, \"Invalid key type: list\");\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn ParticleType.LIST;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getObject() {\n\t\t\treturn list;\n\t\t}\n\n\t\t@Override\n\t\tpublic LuaValue getLuaValue(LuaInstance instance) {\n\t\t\treturn instance.getLuaList(list);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn list.toString();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object other) {\n\t\t\treturn (other != null &&\n\t\t\t\tthis.getClass().equals(other.getClass()) &&\n\t\t\t\tthis.list.equals(((ListValue)other).list));\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn list.hashCode();\n\t\t}\n\t}\n\n\t/**\n\t * Map value.\n\t */\n\tpublic static final class MapValue extends Value {\n\t\tprivate final Map<?,?> map;\n\t\tprivate final MapOrder order;\n\t\tprivate byte[] bytes;\n\n\t\tpublic MapValue(Map<?,?> map) {\n\t\t\tthis.map = map;\n\t\t\tthis.order = getMapOrder(map);\n\t\t}\n\n\t\tpublic MapValue(Map<?,?> map, MapOrder order) {\n\t\t\tthis.map = map;\n\t\t\tthis.order = order;\n\t\t}\n\n\t\tpublic MapOrder getOrder() {\n\t\t\treturn order;\n\t\t}\n\n\t\t@Override\n\t\tpublic int estimateSize() throws AerospikeException {\n\t\t\tbytes = Packer.pack(map, order);\n\t\t\treturn bytes.length;\n\t\t}\n\n\t\t@Override\n\t\tpublic int write(byte[] buffer, int offset) {\n\t\t\tSystem.arraycopy(bytes, 0, buffer, offset, bytes.length);\n\t\t\treturn bytes.length;\n\t\t}\n\n\t\t@Override\n\t\tpublic void pack(Packer packer) {\n\t\t\tpacker.packMap(map, order);\n\t\t}\n\n\t\t@Override\n\t\tpublic void validateKeyType() {\n\t\t\tthrow new AerospikeException(ResultCode.PARAMETER_ERROR, \"Invalid key type: map\");\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn ParticleType.MAP;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getObject() {\n\t\t\treturn map;\n\t\t}\n\n\t\t@Override\n\t\tpublic LuaValue getLuaValue(LuaInstance instance) {\n\t\t\treturn instance.getLuaMap(map);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn map.toString();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object other) {\n\t\t\treturn (other != null &&\n\t\t\t\tthis.getClass().equals(other.getClass()) &&\n\t\t\t\tthis.map.equals(((MapValue)other).map));\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn map.hashCode();\n\t\t}\n\n\t\tpublic static MapOrder getMapOrder(Map<?,?> map) {\n\t\t\treturn (map instanceof SortedMap<?,?>)? MapOrder.KEY_ORDERED : MapOrder.UNORDERED;\n\t\t}\n\t}\n\n\t/**\n\t * Sorted map value.\n\t */\n\tpublic static final class SortedMapValue extends Value {\n\t\tprivate final List<? extends Entry<?,?>> list;\n\t\tprivate byte[] bytes;\n\t\tprivate final MapOrder order;\n\n\t\tpublic SortedMapValue(List<? extends Entry<?,?>> list, MapOrder order) {\n\t\t\tthis.list = list;\n\t\t\tthis.order = order;\n\t\t}\n\n\t\t@Override\n\t\tpublic int estimateSize() throws AerospikeException {\n\t\t\tbytes = Packer.pack(list, order);\n\t\t\treturn bytes.length;\n\t\t}\n\n\t\t@Override\n\t\tpublic int write(byte[] buffer, int offset) {\n\t\t\tSystem.arraycopy(bytes, 0, buffer, offset, bytes.length);\n\t\t\treturn bytes.length;\n\t\t}\n\n\t\t@Override\n\t\tpublic void pack(Packer packer) {\n\t\t\tpacker.packMap(list, order);\n\t\t}\n\n\t\t@Override\n\t\tpublic void validateKeyType() {\n\t\t\tthrow new AerospikeException(ResultCode.PARAMETER_ERROR, \"Invalid key type: map\");\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn ParticleType.MAP;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getObject() {\n\t\t\treturn list;\n\t\t}\n\n\t\t@Override\n\t\tpublic LuaValue getLuaValue(LuaInstance instance) {\n\t\t\treturn instance.getLuaList(list);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn list.toString();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object other) {\n\t\t\tif (other == null || ! this.getClass().equals(other.getClass())) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tSortedMapValue o = (SortedMapValue)other;\n\t\t\treturn this.order == o.order && this.list.equals(o.list);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn list.hashCode();\n\t\t}\n\t}\n\n\t/**\n\t * Infinity value.\n\t */\n\tpublic static final class InfinityValue extends Value {\n\t\t@Override\n\t\tpublic int estimateSize() {\n\t\t\treturn 0;\n\t\t}\n\n\t\t@Override\n\t\tpublic int write(byte[] buffer, int offset) {\n\t\t\treturn 0;\n\t\t}\n\n\t\t@Override\n\t\tpublic void pack(Packer packer) {\n\t\t\tpacker.packInfinity();\n\t\t}\n\n\t\t@Override\n\t\tpublic void validateKeyType() {\n\t\t\tthrow new AerospikeException(ResultCode.PARAMETER_ERROR, \"Invalid key type: INF\");\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\tthrow new AerospikeException(ResultCode.PARAMETER_ERROR, \"Invalid particle type: INF\");\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getObject() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic LuaValue getLuaValue(LuaInstance instance) {\n\t\t\tthrow new AerospikeException(ResultCode.PARAMETER_ERROR, \"Invalid lua type: INF\");\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"INF\";\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object other) {\n\t\t\treturn (other != null &&\n\t\t\t\t\tthis.getClass().equals(other.getClass()));\n\t\t}\n\n\t\t@Override\n\t\tpublic final int hashCode() {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t/**\n\t * Wildcard value.\n\t */\n\tpublic static final class WildcardValue extends Value {\n\t\t@Override\n\t\tpublic int estimateSize() {\n\t\t\treturn 0;\n\t\t}\n\n\t\t@Override\n\t\tpublic int write(byte[] buffer, int offset) {\n\t\t\treturn 0;\n\t\t}\n\n\t\t@Override\n\t\tpublic void pack(Packer packer) {\n\t\t\tpacker.packWildcard();\n\t\t}\n\n\t\t@Override\n\t\tpublic void validateKeyType() {\n\t\t\tthrow new AerospikeException(ResultCode.PARAMETER_ERROR, \"Invalid key type: wildcard\");\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\tthrow new AerospikeException(ResultCode.PARAMETER_ERROR, \"Invalid particle type: wildcard\");\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getObject() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic LuaValue getLuaValue(LuaInstance instance) {\n\t\t\tthrow new AerospikeException(ResultCode.PARAMETER_ERROR, \"Invalid lua type: wildcard\");\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"*\";\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object other) {\n\t\t\treturn (other != null &&\n\t\t\t\t\tthis.getClass().equals(other.getClass()));\n\t\t}\n\n\t\t@Override\n\t\tpublic final int hashCode() {\n\t\t\treturn 0;\n\t\t}\n\t}\n}\n\n## 内容2\n### 文件路径:\nclient/src/com/aerospike/client/command/Buffer.java\n### 代码内容:\npackage com.aerospike.client.command;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ObjectInputStream;\nimport java.io.UnsupportedEncodingException;\nimport java.math.BigInteger;\nimport java.util.Arrays;\n\nimport com.aerospike.client.AerospikeException;\nimport com.aerospike.client.Value;\nimport com.aerospike.client.util.Unpacker;\n\npublic final class Buffer {\n\n\tpublic static Value bytesToKeyValue(int type, byte[] buf, int offset, int len)\n\t\tthrows AerospikeException {\n\n\t\tswitch (type) {\n\t\tcase ParticleType.STRING:\n\t\t\treturn Value.get(Buffer.utf8ToString(buf, offset, len));\n\n\t\tcase ParticleType.INTEGER:\n\t\t\treturn bytesToLongValue(buf, offset, len);\n\n\t\tcase ParticleType.DOUBLE:\n\t\t\treturn new Value.DoubleValue(Buffer.bytesToDouble(buf, offset));\n\n\t\tcase ParticleType.BLOB:\n\t\t\treturn Value.get(Arrays.copyOfRange(buf, offset, offset+len));\n\n\t\tdefault:\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic static Object bytesToParticle(int type, byte[] buf, int offset, int len)\n\t\tthrows AerospikeException {\n\n\t\tswitch (type) {\n\t\tcase ParticleType.STRING:\n\t\t\treturn Buffer.utf8ToString(buf, offset, len);\n\n\t\tcase ParticleType.INTEGER:\n\t\t\treturn Buffer.bytesToNumber(buf, offset, len);\n\n\t\tcase ParticleType.BOOL:\n\t\t\treturn Buffer.bytesToBool(buf, offset, len);\n\n\t\tcase ParticleType.DOUBLE:\n\t\t\treturn Buffer.bytesToDouble(buf, offset);\n\n\t\tcase ParticleType.BLOB:\n\t\t\treturn Arrays.copyOfRange(buf, offset, offset+len);\n\n\t\tcase ParticleType.JBLOB:\n\t\t\treturn Buffer.bytesToObject(buf, offset, len);\n\n\t\tcase ParticleType.GEOJSON:\n\t\t\treturn Buffer.bytesToGeoJSON(buf, offset, len);\n\n\t\tcase ParticleType.HLL:\n\t\t\treturn Buffer.bytesToHLL(buf, offset, len);\n\n\t\tcase ParticleType.LIST:\n\t\t\treturn Unpacker.unpackObjectList(buf, offset, len);\n\n\t\tcase ParticleType.MAP:\n\t\t\treturn Unpacker.unpackObjectMap(buf, offset, len);\n\n\t\tdefault:\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/*\n\tprivate static Object parseList(byte[] buf, int offset, int len) throws AerospikeException {\n\t\tint limit = offset + len;\n\t\tint itemCount = Buffer.bytesToInt(buf, offset);\n\t\toffset += 4;\n\t\tArrayList<Object> list = new ArrayList<Object>(itemCount);\n\n\t\twhile (offset < limit) {\n\t\t\tint sz = Buffer.bytesToInt(buf, offset);\n\t\t\toffset += 4;\n\t\t\tint type = buf[offset];\n\t\t\toffset++;\n\t\t\tlist.add(bytesToParticle(type, buf, offset, sz));\n\t\t\toffset += sz;\n\t\t}\n\t\treturn list;\n\t}\n\n\tprivate static Object parseMap(byte[] buf, int offset, int len) throws AerospikeException {\n\t\tObject key;\n\t\tObject value;\n\n\t\tint limit = offset + len;\n\t\tint n_items = Buffer.bytesToInt(buf, offset);\n\t\toffset += 4;\n\t\tHashMap<Object, Object> map = new HashMap<Object, Object>(n_items);\n\n\t\twhile (offset < limit) {\n\t\t\t// read out the key\n\t\t\tint sz = Buffer.bytesToInt(buf, offset);\n\t\t\toffset += 4;\n\t\t\tint type = buf[offset];\n\t\t\toffset++;\n\n\t\t\tkey = bytesToParticle(type, buf, offset, len);\n\t\t\toffset += sz;\n\n\t\t\t// read out the value\n\t\t\tsz = Buffer.bytesToInt(buf, offset);\n\t\t\toffset += 4;\n\t\t\ttype = buf[offset];\n\t\t\toffset++;\n\n\t\t\tvalue = bytesToParticle(type, buf, offset, len);\n\t\t\toffset += sz;\n\n\t\t\tmap.put(key, value);\n\t\t}\n\t\treturn map;\n\t}\n\t*/\n\n\t/**\n\t * Estimate size of Utf8 encoded bytes without performing the actual encoding.\n\t */\n\tpublic static int estimateSizeUtf8(String value) {\n\t\tif (value == null) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tint max = value.length();\n\t\tint count = 0;\n\n\t\tfor (int i = 0; i < max; i++) {\n\t\t\tchar ch = value.charAt(i);\n\n\t\t\tif (ch < 0x80) {\n\t\t\t\tcount++;\n\t\t\t}\n\t\t\telse if (ch < 0x800) {\n\t\t\t\tcount += 2;\n\t\t\t}\n\t\t\telse if (Character.isHighSurrogate(ch)) {\n\t\t\t\tcount += 4;\n\t\t\t\t++i;\n\t\t\t} else {\n\t\t\t\tcount += 3;\n\t\t\t}\n\t\t}\n\t\treturn count;\n\t}\n\n\tpublic static byte[] stringToUtf8(String s) {\n\t\tif (s == null || s.length() == 0) {\n\t\t\treturn new byte[0];\n\t\t}\n\t\tint size = estimateSizeUtf8(s);\n\t\tbyte[] bytes = new byte[size];\n\t\tstringToUtf8(s, bytes, 0);\n\t\treturn bytes;\n\t}\n\n\t/**\n\t * Convert input string to UTF-8, copies into buffer (at given offset).\n\t * Returns number of bytes in the string.\n\t *\n\t * Java's internal UTF8 conversion is very, very slow.\n\t * This is, rather amazingly, 8x faster than the to-string method.\n\t * Returns the number of bytes this translated into.\n\t */\n\tpublic static int stringToUtf8(String s, byte[] buf, int offset) {\n\t\tif (s == null) {\n\t\t\treturn 0;\n\t\t}\n\t\tint length = s.length();\n\t\tint startOffset = offset;\n\n\t\tfor (int i = 0; i < length; i++) {\n\t\t\tint c = s.charAt(i);\n\t\t\tif (c < 0x80) {\n\t\t\t\tbuf[offset++] = (byte) c;\n\t\t\t}\n\t\t\telse if (c < 0x800) {\n\t\t\t\tbuf[offset++] = (byte)(0xc0 | ((c >> 6)));\n\t\t\t\tbuf[offset++] = (byte)(0x80 | (c & 0x3f));\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Encountered a different encoding other than 2-byte UTF8. Let java handle it.\n\t\t\t\ttry {\n\t\t\t\t\tbyte[] value = s.getBytes(\"UTF8\");\n\t\t\t\t\tSystem.arraycopy(value, 0, buf, startOffset, value.length);\n\t\t\t\t\treturn value.length;\n\t\t\t\t}\n\t\t\t\tcatch (UnsupportedEncodingException uee) {\n\t\t\t\t\tthrow new RuntimeException(\"UTF8 encoding is not supported.\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn offset - startOffset;\n\t}\n\n\tpublic static String utf8ToString(byte[] buf, int offset, int length) {\n\t\t// A Thread local implementation does not help here, so\n\t\t// allocate character buffer each time.\n\t\tif (length == 0) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tchar[] charBuffer = new char[length];\n\t\tint charCount = 0;\n\t\tint limit = offset + length;\n\t\tint origoffset = offset;\n\n\t\twhile (offset < limit ) {\n\t\t\tint b1 = buf[offset];\n\n\t\t\tif (b1 >= 0) {\n\t\t\t\tcharBuffer[charCount++] = (char)b1;\n\t\t\t\toffset++;\n\t\t\t}\n\t\t\telse if ((b1 >> 5) == -2) {\n\t\t\t\tint b2 = buf[offset + 1];\n\t\t\t\tcharBuffer[charCount++] = (char) (((b1 << 6) ^ b2) ^ 0x0f80);\n\t\t\t\toffset += 2;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Encountered an UTF encoding which uses more than 2 bytes.\n\t\t\t\t// Use a native function to do the conversion.\n\t\t\t\ttry {\n\t\t\t\t\treturn new String(buf, origoffset, length, \"UTF8\");\n\t\t\t\t}\n\t\t\t\tcatch (UnsupportedEncodingException uee) {\n\t\t\t\t\tthrow new RuntimeException(\"UTF8 decoding is not supported.\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn new String(charBuffer, 0, charCount);\n\t}\n\n\tpublic static String utf8ToString(byte[] buf, int offset, int length, StringBuilder sb) {\n\t\tif (length == 0) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\t// This method is designed to accommodate multiple string conversions on the same\n\t\t// thread, but without the ThreadLocal overhead. The StringBuilder instance is\n\t\t// created on the stack and passed in each method invocation.\n\t\tsb.setLength(0);\n\t\tint limit = offset + length;\n\t\tint origoffset = offset;\n\n\t\twhile (offset < limit ) {\n\t\t\tif ((buf[offset] & 0x80) == 0) { // 1 byte\n\t\t\t\tchar c = (char) buf[offset];\n\t\t\t\tsb.append(c);\n\t\t\t\toffset++;\n\t\t\t}\n\t\t\telse if ((buf[offset] & 0xE0) == 0xC0) { // 2 bytes\n\t\t\t\tchar c = (char) (((buf[offset] & 0x1f) << 6) | (buf[offset+1] & 0x3f));\n\t\t\t\tsb.append(c);\n\t\t\t\toffset += 2;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Encountered an UTF encoding which uses more than 2 bytes.\n\t\t\t\t// Use a native function to do the conversion.\n\t\t\t\ttry {\n\t\t\t\t\treturn new String(buf, origoffset, length, \"UTF8\");\n\t\t\t\t}\n\t\t\t\tcatch (UnsupportedEncodingException uee) {\n\t\t\t\t\tthrow new RuntimeException(\"UTF8 decoding is not supported.\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\t/**\n\t * Convert UTF8 numeric digits to an integer. Negative integers are not supported.\n\t *\n\t * Input format: 1234\n\t */\n\tpublic static int utf8DigitsToInt(byte[] buf, int begin, int end) {\n\t\tint val = 0;\n\t\tint mult = 1;\n\n\t\tfor (int i = end - 1; i >= begin; i--) {\n\t\t\tval += ((int)buf[i] - 48) * mult;\n\t\t\tmult *= 10;\n\t\t}\n\t\treturn val;\n\t}\n\n\tpublic static String bytesToHexString(byte[] buf) {\n\t\tif (buf == null || buf.length == 0) {\n\t\t\treturn \"\";\n\t\t}\n\t\tStringBuilder sb = new StringBuilder(buf.length * 2);\n\n\t\tfor (int i = 0; i < buf.length; i++) {\n\t\t\tsb.append(String.format(\"%02x\", buf[i]));\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic static String bytesToHexString(byte[] buf, int offset, int length) {\n\t\tStringBuilder sb = new StringBuilder(length * 2);\n\n\t\tfor (int i = offset; i < length; i++) {\n\t\t\tsb.append(String.format(\"%02x\", buf[i]));\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic static Object bytesToObject(byte[] buf, int offset, int length)\n\t\tthrows AerospikeException.Serialize {\n\n\t\tif (length <= 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\ttry {\n\t\t\tByteArrayInputStream bastream = new ByteArrayInputStream(buf, offset, length);\n\t\t\tObjectInputStream oistream = new ObjectInputStream(bastream);\n\t\t\treturn oistream.readObject();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new AerospikeException.Serialize(e);\n\t\t}\n\t}\n\n\tpublic static Value bytesToLongValue(byte[] buf, int offset, int len) {\n\t\tlong val = 0;\n\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tval <<= 8;\n\t\t\tval |= buf[offset+i] & 0xFF;\n\t\t}\n\n\t\treturn new Value.LongValue(val);\n\t}\n\n\tpublic static Object bytesToGeoJSON(byte[] buf, int offset, int len) {\n\t\t// Ignore the flags for now\n\t\tint ncells = bytesToShort(buf, offset + 1);\n\t\tint hdrsz = 1 + 2 + (ncells * 8);\n\t\treturn Value.getAsGeoJSON(Buffer.utf8ToString(buf, offset + hdrsz, len - hdrsz));\n\t}\n\n\tpublic static Object bytesToHLL(byte[] buf, int offset, int len) {\n\t\tbyte[] bytes = Arrays.copyOfRange(buf, offset, offset+len);\n\t\treturn Value.getAsHLL(bytes);\n\t}\n\n\tpublic static Object bytesToNumber(byte[] buf, int offset, int len) {\n\t\t// Server always returns 8 for integer length.\n\t\tif (len == 8) {\n\t\t\treturn bytesToLong(buf, offset);\n\t\t}\n\n\t\t// Handle other lengths just in case server changes.\n\t\tif (len == 0) {\n\t\t\treturn new Long(0);\n\t\t}\n\n\t\tif (len == 4) {\n\t\t\treturn new Long(bytesToInt(buf, offset));\n\t\t}\n\n\t\tif (len > 8) {\n\t\t\treturn bytesToBigInteger(buf, offset, len);\n\t\t}\n\n\t\t// Handle variable length integer.\n\t\tlong val = 0;\n\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tval <<= 8;\n\t\t\tval |= buf[offset+i] & 0xFF;\n\t\t}\n\t\treturn new Long(val);\n\t}\n\n\tpublic static Object bytesToBigInteger(byte[] buf, int offset, int len) {\n\t\tboolean negative = false;\n\n\t\tif ((buf[offset] & 0x80) != 0) {\n\t\t\tnegative = true;\n\t\t\tbuf[offset] &= 0x7f;\n\t\t}\n\t\tbyte[] bytes = new byte[len];\n\t\tSystem.arraycopy(buf, offset, bytes, 0, len);\n\n\t\tBigInteger big = new BigInteger(bytes);\n\n\t\tif (negative) {\n\t\t\tbig = big.negate();\n\t\t}\n\t\treturn big;\n\t}\n\n\tpublic static boolean bytesToBool(byte[] buf, int offset, int len) {\n\t\tif (len <= 0) {\n\t\t\treturn false;\n\t\t}\n\t\treturn (buf[offset] == 0)? false : true;\n\t}\n\n\t//-------------------------------------------------------\n\t// 64 bit double conversions.\n\t//-------------------------------------------------------\n\n\tpublic static double bytesToDouble(byte[] buf, int offset) {\n\t\treturn Double.longBitsToDouble(bytesToLong(buf, offset));\n\t}\n\n\tpublic static void doubleToBytes(double v, byte[] buf, int offset) {\n\t\tBuffer.longToBytes(Double.doubleToLongBits(v), buf, offset);\n\t}\n\n\t//-------------------------------------------------------\n\t// 64 bit number conversions.\n\t//-------------------------------------------------------\n\n\t/**\n\t * Convert long to big endian signed or unsigned 64 bits.\n\t * The bit pattern will be the same regardless of sign.\n\t */\n\tpublic static void longToBytes(long v, byte[] buf, int offset) {\n\t\tbuf[offset++] = (byte)(v >>> 56);\n\t\tbuf[offset++] = (byte)(v >>> 48);\n\t\tbuf[offset++] = (byte)(v >>> 40);\n\t\tbuf[offset++] = (byte)(v >>> 32);\n\t\tbuf[offset++] = (byte)(v >>> 24);\n\t\tbuf[offset++] = (byte)(v >>> 16);\n\t\tbuf[offset++] = (byte)(v >>> 8);\n\t\tbuf[offset] = (byte)(v >>> 0);\n\t}\n\n\t/**\n\t * Convert long to little endian signed or unsigned 64 bits.\n\t * The bit pattern will be the same regardless of sign.\n\t */\n\tpublic static void longToLittleBytes(long v, byte[] buf, int offset) {\n\t\tbuf[offset++] = (byte)(v >>> 0);\n\t\tbuf[offset++] = (byte)(v >>> 8);\n\t\tbuf[offset++] = (byte)(v >>> 16);\n\t\tbuf[offset++] = (byte)(v >>> 24);\n\t\tbuf[offset++] = (byte)(v >>> 32);\n\t\tbuf[offset++] = (byte)(v >>> 40);\n\t\tbuf[offset++] = (byte)(v >>> 48);\n\t\tbuf[offset] = (byte)(v >>> 56);\n\t}\n\n\t/**\n\t * Convert big endian signed 64 bits to long.\n\t */\n\tpublic static long bytesToLong(byte[] buf, int offset) {\n\t\treturn (\n\t\t\t((long)(buf[offset] & 0xFF) << 56) |\n\t\t\t((long)(buf[offset+1] & 0xFF) << 48) |\n\t\t\t((long)(buf[offset+2] & 0xFF) << 40) |\n\t\t\t((long)(buf[offset+3] & 0xFF) << 32) |\n\t\t\t((long)(buf[offset+4] & 0xFF) << 24) |\n\t\t\t((long)(buf[offset+5] & 0xFF) << 16) |\n\t\t\t((long)(buf[offset+6] & 0xFF) << 8) |\n\t\t\t((long)(buf[offset+7] & 0xFF) << 0)\n\t\t\t);\n\t}\n\n\t/**\n\t * Convert little endian signed 64 bits to long.\n\t */\n\tpublic static long littleBytesToLong(byte[] buf, int offset) {\n\t\treturn (\n\t\t\t((long)(buf[offset] & 0xFF) << 0) |\n\t\t\t((long)(buf[offset+1] & 0xFF) << 8) |\n\t\t\t((long)(buf[offset+2] & 0xFF) << 16) |\n\t\t\t((long)(buf[offset+3] & 0xFF) << 24) |\n\t\t\t((long)(buf[offset+4] & 0xFF) << 32) |\n\t\t\t((long)(buf[offset+5] & 0xFF) << 40) |\n\t\t\t((long)(buf[offset+6] & 0xFF) << 48) |\n\t\t\t((long)(buf[offset+7] & 0xFF) << 56)\n\t\t\t);\n\t}\n\n\t//-------------------------------------------------------\n\t// 32 bit number conversions.\n\t//-------------------------------------------------------\n\n\t/**\n\t * Convert int to big endian signed or unsigned 32 bits.\n\t * The bit pattern will be the same regardless of sign.\n\t */\n\tpublic static void intToBytes(int v, byte[] buf, int offset) {\n\t\tbuf[offset++] = (byte)(v >>> 24);\n\t\tbuf[offset++] = (byte)(v >>> 16);\n\t\tbuf[offset++] = (byte)(v >>> 8);\n\t\tbuf[offset] = (byte)(v >>> 0);\n\t}\n\n\t/**\n\t * Convert int to little endian signed or unsigned 32 bits.\n\t * The bit pattern will be the same regardless of sign.\n\t */\n\tpublic static void intToLittleBytes(int v, byte[] buf, int offset) {\n\t\tbuf[offset++] = (byte)(v >>> 0);\n\t\tbuf[offset++] = (byte)(v >>> 8);\n\t\tbuf[offset++] = (byte)(v >>> 16);\n\t\tbuf[offset] = (byte)(v >>> 24);\n\t}\n\n\t/**\n\t * Convert big endian signed 32 bits to int.\n\t */\n\tpublic static int bytesToInt(byte[] buf, int offset) {\n\t\treturn (\n\t\t\t((buf[offset] & 0xFF) << 24) |\n\t\t\t((buf[offset+1] & 0xFF) << 16) |\n\t\t\t((buf[offset+2] & 0xFF) << 8) |\n\t\t\t((buf[offset+3] & 0xFF) << 0)\n\t\t\t);\n\t}\n\n\t/**\n\t * Convert little endian signed 32 bits to int.\n\t */\n\tpublic static int littleBytesToInt(byte[] buf, int offset) {\n\t\treturn (\n\t\t\t((buf[offset] & 0xFF) << 0) |\n\t\t\t((buf[offset+1] & 0xFF) << 8) |\n\t\t\t((buf[offset+2] & 0xFF) << 16) |\n\t\t\t((buf[offset+3] & 0xFF) << 24)\n\t\t\t);\n\t}\n\n\t/**\n\t * Convert big endian unsigned 32 bits to long.\n\t */\n\tpublic static long bigUnsigned32ToLong(byte[] buf, int offset) {\n\t\treturn (\n\t\t\t((long)(buf[offset] & 0xFF) << 24) |\n\t\t\t((long)(buf[offset+1] & 0xFF) << 16) |\n\t\t\t((long)(buf[offset+2] & 0xFF) << 8) |\n\t\t\t((long)(buf[offset+3] & 0xFF) << 0)\n\t\t\t);\n\t}\n\n\t//-------------------------------------------------------\n\t// 16 bit number conversions.\n\t//-------------------------------------------------------\n\n\t/**\n\t * Convert int to big endian signed or unsigned 16 bits.\n\t * The bit pattern will be the same regardless of sign.\n\t */\n\tpublic static void shortToBytes(int v, byte[] buf, int offset) {\n\t\tbuf[offset++] = (byte)(v >>> 8);\n\t\tbuf[offset] = (byte)(v >>> 0);\n\t}\n\n\t/**\n\t * Convert int to little endian signed or unsigned 16 bits.\n\t * The bit pattern will be the same regardless of sign.\n\t */\n\tpublic static void shortToLittleBytes(int v, byte[] buf, int offset) {\n\t\tbuf[offset++] = (byte)(v >>> 0);\n\t\tbuf[offset] = (byte)(v >>> 8);\n\t}\n\n\t/**\n\t * Convert big endian unsigned 16 bits to int.\n\t */\n\tpublic static int bytesToShort(byte[] buf, int offset) {\n\t\treturn (\n\t\t\t((buf[offset] & 0xFF) << 8) |\n\t\t\t((buf[offset+1] & 0xFF) << 0)\n\t\t\t);\n\t}\n\n\t/**\n\t * Convert little endian unsigned 16 bits to int.\n\t */\n\tpublic static int littleBytesToShort(byte[] buf, int offset) {\n\t\treturn (\n\t\t\t((buf[offset] & 0xFF) << 0) |\n\t\t\t((buf[offset+1] & 0xFF) << 8)\n\t\t\t);\n\t}\n\n\t/**\n\t * Convert big endian signed 16 bits to short.\n\t */\n\tpublic static short bigSigned16ToShort(byte[] buf, int offset) {\n\t\treturn (short)(\n\t\t\t((buf[offset] & 0xFF) << 8) |\n\t\t\t((buf[offset+1] & 0xFF) << 0)\n\t\t\t);\n\t}\n\n\t//-------------------------------------------------------\n\t// Variable byte number conversions.\n\t//-------------------------------------------------------\n\n\t/**\n\t *\tEncode an integer in variable 7-bit format.\n\t *\tThe high bit indicates if more bytes are used.\n\t * Return byte size of integer.\n\t */\n\tpublic static int intToVarBytes(int v, byte[] buf, int offset) {\n\t\tint i = offset;\n\n\t\twhile (i < buf.length && v >= 0x80) {\n\t\t\tbuf[i++] = (byte)(v | 0x80);\n\t\t\tv >>>= 7;\n\t\t}\n\n\t\tif (i < buf.length) {\n\t\t\tbuf[i++] = (byte)v;\n\t\t\treturn i - offset;\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t *\tDecode an integer in variable 7-bit format.\n\t *\tThe high bit indicates if more bytes are used.\n\t * Return value and byte size in array.\n\t */\n\tpublic static int[] varBytesToInt(byte[] buf, int offset) {\n\t\tint i = offset;\n\t\tint val = 0;\n\t\tint shift = 0;\n\t\tbyte b;\n\n\t\tdo {\n\t\t\tb = buf[i++];\n\t\t\tval |= (b & 0x7F) << shift;\n\t\t\tshift += 7;\n\t\t} while ((b & 0x80) != 0);\n\n\t\treturn new int[] {val, i - offset};\n\t}\n}\n\n## 内容3\n### 文件路径:\nclient/src/com/aerospike/client/util/Packer.java\n### 代码内容:\npackage com.aerospike.client.util;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.ObjectOutputStream;\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport com.aerospike.client.AerospikeException;\nimport com.aerospike.client.Value;\nimport com.aerospike.client.cdt.MapOrder;\nimport com.aerospike.client.command.Buffer;\nimport com.aerospike.client.command.ParticleType;\n\nimport static com.aerospike.client.Value.MapValue.getMapOrder;\n\n/**\n * Serialize collection objects using MessagePack format specification:\n *\n * https://github.com/msgpack/msgpack/blob/master/spec.md\n */\npublic final class Packer {\n\n\tpublic static byte[] pack(Value[] val) {\n\t\ttry {\n\t\t\tPacker packer = new Packer();\n\t\t\tpacker.packValueArray(val);\n\t\t\treturn packer.toByteArray();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new AerospikeException.Serialize(e);\n\t\t}\n\t}\n\n\tpublic static byte[] pack(List<?> val) {\n\t\ttry {\n\t\t\tPacker packer = new Packer();\n\t\t\tpacker.packList(val);\n\t\t\treturn packer.toByteArray();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new AerospikeException.Serialize(e);\n\t\t}\n\t}\n\n\tpublic static byte[] pack(Map<?,?> val, MapOrder order) {\n\t\ttry {\n\t\t\tPacker packer = new Packer();\n\t\t\tpacker.packMap(val, order);\n\t\t\treturn packer.toByteArray();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new AerospikeException.Serialize(e);\n\t\t}\n\t}\n\n\tpublic static byte[] pack(List<? extends Entry<?,?>> val, MapOrder order) {\n\t\ttry {\n\t\t\tPacker packer = new Packer();\n\t\t\tpacker.packMap(val, order);\n\t\t\treturn packer.toByteArray();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new AerospikeException.Serialize(e);\n\t\t}\n\t}\n\n\tprivate byte[] buffer;\n\tprivate int offset;\n\tprivate ArrayList<BufferItem> bufferList;\n\n\tpublic Packer() {\n\t\tthis.buffer = ThreadLocalData.getBuffer();\n\t}\n\n\tpublic void packValueArray(Value[] values) {\n\t\tpackArrayBegin(values.length);\n\t\tfor (Value value : values) {\n\t\t\tvalue.pack(this);\n\t\t}\n\t}\n\n\tpublic void packValueList(List<Value> list) {\n\t\tpackArrayBegin(list.size());\n\t\tfor (Value value : list) {\n\t\t\tvalue.pack(this);\n\t\t}\n\t}\n\n\tpublic void packList(List<?> list) {\n\t\tpackArrayBegin(list.size());\n\t\tfor (Object obj : list) {\n\t\t\tpackObject(obj);\n\t\t}\n\t}\n\n\tpublic void packArrayBegin(int size) {\n\t\tif (size < 16) {\n\t\t\tpackByte(0x90 | size);\n\t\t}\n\t\telse if (size < 65536) {\n\t\t\tpackShort(0xdc, size);\n\t\t}\n\t\telse {\n\t\t\tpackInt(0xdd, size);\n\t\t}\n\t}\n\n\tpublic void packValueMap(Map<Value,Value> map) {\n\t\tMapOrder order = getMapOrder(map);\n\t\tpackMapBegin(map.size(), order);\n\n\t\tfor (Entry<Value,Value> entry : map.entrySet()) {\n\t\t\tentry.getKey().pack(this);\n\t\t\tentry.getValue().pack(this);\n\t\t}\n\t}\n\n\tpublic void packMap(Map<?,?> map) {\n\t\tMapOrder order = getMapOrder(map);\n\t\tpackMap(map, order);\n\t}\n\n\tpublic void packMap(Map<?,?> map, MapOrder order) {\n\t\tpackMapBegin(map.size(), order);\n\n\t\tfor (Entry<?,?> entry : map.entrySet()) {\n\t\t\tpackObject(entry.getKey());\n\t\t\tpackObject(entry.getValue());\n\t\t}\n\t}\n\n\tpublic void packMap(List<? extends Entry<?,?>> list, MapOrder order) {\n\t\tpackMapBegin(list.size(), order);\n\n\t\tfor (Entry<?,?> entry : list) {\n\t\t\tpackObject(entry.getKey());\n\t\t\tpackObject(entry.getValue());\n\t\t}\n\t}\n\n\tpublic void packMapBegin(int size, MapOrder order) {\n\t\tif (order == MapOrder.UNORDERED) {\n\t\t\tpackMapBegin(size);\n\t\t}\n\t\telse {\n\t\t\t// Map is sorted.\n\t\t\tpackMapBegin(size + 1);\n\t\t\tpackByte(0xc7);\n\t\t\tpackByte(0);\n\t\t\tpackByte(order.attributes);\n\t\t\tpackByte(0xc0);\n\t\t}\n\t}\n\n\tpublic void packMapBegin(int size) {\n\t\tif (size < 16) {\n\t\t\tpackByte(0x80 | size);\n\t\t}\n\t\telse if (size < 65536) {\n\t\t\tpackShort(0xde, size);\n\t\t}\n\t\telse {\n\t\t\tpackInt(0xdf, size);\n\t\t}\n\t}\n\n\tpublic void packBytes(byte[] b) {\n\t\tpackByteArrayBegin(b.length);\n\t\tpackByteArray(b, 0, b.length);\n\t}\n\n\tpublic void packParticleBytes(byte[] b) {\n\t\tpackByteArrayBegin(b.length + 1);\n\t\tpackByte(ParticleType.BLOB);\n\t\tpackByteArray(b, 0, b.length);\n\t}\n\n\tpublic void packParticleBytes(byte[] b, int type) {\n\t\tpackByteArrayBegin(b.length + 1);\n\t\tpackByte(type);\n\t\tpackByteArray(b, 0, b.length);\n\t}\n\n\tpublic void packParticleBytes(byte[] b, int offset, int length) {\n\t\tpackByteArrayBegin(length + 1);\n\t\tpackByte(ParticleType.BLOB);\n\t\tpackByteArray(b, offset, length);\n\t}\n\n\tpublic void packBlob(Object val) {\n\t\tByteArrayOutputStream bstream = new ByteArrayOutputStream();\n\n\t\ttry {\n\t\t\tObjectOutputStream ostream = new ObjectOutputStream(bstream);\n\t\t\tostream.writeObject(val);\n\t\t\tostream.close();\n\t\t}\n\t\tcatch (IOException ioe) {\n\t\t\tthrow new AerospikeException.Serialize(ioe);\n\t\t}\n\n\t\tbyte[] bytes = bstream.toByteArray();\n\t\tpackByteArrayBegin(bytes.length + 1);\n\t\tpackByte(ParticleType.JBLOB);\n\t\tpackByteArray(bytes, 0, bytes.length);\n\t}\n\n\tpublic void packGeoJSON(String val) {\n\t\tbyte[] buffer = Buffer.stringToUtf8(val);\n\t\tpackByteArrayBegin(buffer.length + 1);\n\t\tpackByte(ParticleType.GEOJSON);\n\t\tpackByteArray(buffer, 0, buffer.length);\n\t}\n\n\tprivate void packByteArrayBegin(int size) {\n\t\t// Use string header codes for byte arrays.\n\t\tpackStringBegin(size);\n\t\t/*\n\t\tif (size < 256) {\n\t\t\tpackByte(0xc4, size);\n\t\t}\n\t\telse if (size < 65536) {\n\t\t\tpackShort(0xc5, size);\n\t\t}\n\t\telse {\n\t\t\tpackInt(0xc6, size);\n\t\t}\n\t\t*/\n\t}\n\n\tpublic void packObject(Object obj) {\n\t\tif (obj == null) {\n\t\t\tpackNil();\n\t\t\treturn;\n\t\t}\n\n\t\tif (obj instanceof Value) {\n\t\t\tValue value = (Value) obj;\n\t\t\tvalue.pack(this);\n\t\t\treturn;\n\t\t}\n\n\t\tif (obj instanceof byte[]) {\n\t\t\tpackParticleBytes((byte[]) obj);\n\t\t\treturn;\n\t\t}\n\n\t\tif (obj instanceof String) {\n\t\t\tpackParticleString((String) obj);\n\t\t\treturn;\n\t\t}\n\n\t\tif (obj instanceof Integer) {\n\t\t\tpackInt((Integer) obj);\n\t\t\treturn;\n\t\t}\n\n\t\tif (obj instanceof Long) {\n\t\t\tpackLong((Long) obj);\n\t\t\treturn;\n\t\t}\n\n\t\tif (obj instanceof Double) {\n\t\t\tpackDouble((Double) obj);\n\t\t\treturn;\n\t\t}\n\n\t\tif (obj instanceof Float) {\n\t\t\tpackFloat((Float) obj);\n\t\t\treturn;\n\t\t}\n\n\t\tif (obj instanceof Boolean) {\n\t\t\tpackBoolean((Boolean)obj);\n\t\t\treturn;\n\t\t}\n\n\t\tif (obj instanceof List<?>) {\n\t\t\tpackList((List<?>) obj);\n\t\t\treturn;\n\t\t}\n\n\t\tif (obj instanceof Map<?,?>) {\n\t\t\tpackMap((Map<?,?>) obj);\n\t\t\treturn;\n\t\t}\n\n\t\tif (obj instanceof ByteBuffer) {\n\t\t\tpackByteBuffer((ByteBuffer) obj);\n\t\t\treturn;\n\t\t}\n\n\t\tpackBlob(obj);\n\t}\n\n\tpublic void packByteBuffer(ByteBuffer bb) {\n\t\tbyte[] b = bb.array();\n\t\tpackParticleBytes(b);\n\t}\n\n\tpublic void packLong(long val) {\n\t\tif (val >= 0L) {\n\t\t\tif (val < 128L) {\n\t\t\t\tpackByte((int)val);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (val < 256L) {\n\t\t\t\tpackByte(0xcc, (int)val);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (val < 65536L) {\n\t\t\t\tpackShort(0xcd, (int)val);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (val < 4294967296L) {\n\t\t\t\tpackInt(0xce, (int)val);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpackLong(0xcf, val);\n\t\t}\n\t\telse {\n\t\t\tif (val >= -32) {\n\t\t\t\tpackByte(0xe0 | ((int)val + 32));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (val >= Byte.MIN_VALUE) {\n\t\t\t\tpackByte(0xd0, (int)val);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (val >= Short.MIN_VALUE) {\n\t\t\t\tpackShort(0xd1, (int)val);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (val >= Integer.MIN_VALUE) {\n\t\t\t\tpackInt(0xd2, (int)val);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpackLong(0xd3, val);\n\t\t}\n\t}\n\n\tpublic void packInt(int val) {\n\t\tif (val >= 0) {\n\t\t\tif (val < 128) {\n\t\t\t\tpackByte(val);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (val < 256) {\n\t\t\t\tpackByte(0xcc, val);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (val < 65536) {\n\t\t\t\tpackShort(0xcd, val);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpackInt(0xce, val);\n\t\t}\n\t\telse {\n\t\t\tif (val >= -32) {\n\t\t\t\tpackByte(0xe0 | (val + 32));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (val >= Byte.MIN_VALUE) {\n\t\t\t\tpackByte(0xd0, val);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (val >= Short.MIN_VALUE) {\n\t\t\t\tpackShort(0xd1, val);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpackInt(0xd2, val);\n\t\t}\n\t}\n\n\tpublic void packString(String val) {\n\t\tint size = Buffer.estimateSizeUtf8(val);\n\t\tpackStringBegin(size);\n\n\t\tif (offset + size > buffer.length) {\n\t\t\tresize(size);\n\t\t}\n\t\toffset += Buffer.stringToUtf8(val, buffer, offset);\n\t}\n\n\tpublic void packParticleString(String val) {\n\t\tint size = Buffer.estimateSizeUtf8(val) + 1;\n\t\tpackStringBegin(size);\n\n\t\tif (offset + size > buffer.length) {\n\t\t\tresize(size);\n\t\t}\n\t\tbuffer[offset++] = (byte)ParticleType.STRING;\n\t\toffset += Buffer.stringToUtf8(val, buffer, offset);\n\t}\n\n\tprivate void packStringBegin(int size) {\n\t\tif (size < 32) {\n\t\t\tpackByte(0xa0 | size);\n\t\t}\n\t\telse if (size < 256) {\n\t\t\tpackByte(0xd9, size);\n\t\t}\n\t\telse if (size < 65536) {\n\t\t\tpackShort(0xda, size);\n\t\t}\n\t\telse {\n\t\t\tpackInt(0xdb, size);\n\t\t}\n\t}\n\n\tpublic void packByteArray(byte[] src, int srcOffset, int srcLength) {\n\t \tif (offset + srcLength > buffer.length) {\n\t\t\tresize(srcLength);\n\t\t}\n \t\tSystem.arraycopy(src, srcOffset, buffer, offset, srcLength);\n\t\toffset += srcLength;\n\t}\n\n\tpublic void packDouble(double val) {\n\t\tif (offset + 9 > buffer.length) {\n\t\t\tresize(9);\n\t\t}\n\t\tbuffer[offset++] = (byte)0xcb;\n\t\tBuffer.longToBytes(Double.doubleToLongBits(val), buffer, offset);\n\t\toffset += 8;\n\t}\n\n\tpublic void packFloat(float val) {\n\t\tif (offset + 5 > buffer.length) {\n\t\t\tresize(5);\n\t\t}\n\t\tbuffer[offset++] = (byte)0xca;\n\t\tBuffer.intToBytes(Float.floatToIntBits(val), buffer, offset);\n\t\toffset += 4;\n\t}\n\n\tprivate void packLong(int type, long val) {\n\t\tif (offset + 9 > buffer.length) {\n\t\t\tresize(9);\n\t\t}\n\t\tbuffer[offset++] = (byte)type;\n\t\tBuffer.longToBytes(val, buffer, offset);\n\t\toffset += 8;\n\t}\n\n\tprivate void packInt(int type, int val) {\n\t\tif (offset + 5 > buffer.length) {\n\t\t\tresize(5);\n\t\t}\n\t\tbuffer[offset++] = (byte)type;\n\t\tBuffer.intToBytes(val, buffer, offset);\n\t\toffset += 4;\n\t}\n\n\tprivate void packShort(int type, int val) {\n\t\tif (offset + 3 > buffer.length) {\n\t\t\tresize(3);\n\t\t}\n\t\tbuffer[offset++] = (byte)type;\n\t\tBuffer.shortToBytes(val, buffer, offset);\n\t\toffset += 2;\n\t}\n\n\tpublic void packRawShort(int val) {\n\t\t// WARNING. This method is not compatible with message pack standard.\n\t\tif (offset + 2 > buffer.length) {\n\t\t\tresize(2);\n\t\t}\n\t\tBuffer.shortToBytes(val, buffer, offset);\n\t\toffset += 2;\n\t}\n\n\tprivate void packByte(int type, int val) {\n\t\tif (offset + 2 > buffer.length) {\n\t\t\tresize(2);\n\t\t}\n\t\tbuffer[offset++] = (byte)type;\n\t\tbuffer[offset++] = (byte)val;\n\t}\n\n\tpublic void packBoolean(boolean val) {\n\t\tif (offset + 1 > buffer.length) {\n\t\t\tresize(1);\n\t\t}\n\n\t\tif (val) {\n\t\t\tbuffer[offset++] = (byte)0xc3;\n\t\t}\n\t\telse {\n\t\t\tbuffer[offset++] = (byte)0xc2;\n\t\t}\n\t}\n\n\tpublic void packNil() {\n\t\tif (offset >= buffer.length) {\n\t\t\tresize(1);\n\t\t}\n\t\tbuffer[offset++] = (byte)0xc0;\n\t}\n\n\tpublic void packInfinity() {\n\t\tif (offset + 3 > buffer.length) {\n\t\t\tresize(3);\n\t\t}\n\t\tbuffer[offset++] = (byte)0xd4;\n\t\tbuffer[offset++] = (byte)0xff;\n\t\tbuffer[offset++] = (byte)0x01;\n\t}\n\n\tpublic void packWildcard() {\n\t\tif (offset + 3 > buffer.length) {\n\t\t\tresize(3);\n\t\t}\n\t\tbuffer[offset++] = (byte)0xd4;\n\t\tbuffer[offset++] = (byte)0xff;\n\t\tbuffer[offset++] = (byte)0x00;\n\t}\n\n\tpublic void packByte(int val) {\n\t\tif (offset >= buffer.length) {\n\t\t\tresize(1);\n\t\t}\n\t\tbuffer[offset++] = (byte)val;\n\t}\n\n\tprivate void resize(int size) {\n\t\tif (bufferList == null) {\n\t\t\tbufferList = new ArrayList<BufferItem>();\n\t\t}\n\t\tbufferList.add(new BufferItem(buffer, offset));\n\n\t\tif (size < buffer.length) {\n\t\t\tsize = buffer.length;\n\t\t}\n\t\tbuffer = new byte[size];\n\t\toffset = 0;\n\t}\n\n\tpublic byte[] toByteArray() {\n\t\tif (bufferList != null) {\n\t\t\tint size = offset;\n\t\t\tfor (BufferItem item : bufferList) {\n\t\t\t\tsize += item.length;\n\t\t\t}\n\n\t\t\tbyte[] target = new byte[size];\n\t\t\tsize = 0;\n\t\t\tfor (BufferItem item : bufferList) {\n\t\t\t\tSystem.arraycopy(item.buffer, 0, target, size, item.length);\n\t\t\t\tsize += item.length;\n\t\t\t}\n\n\t\t\tSystem.arraycopy(buffer, 0, target, size, offset);\n\t\t\treturn target;\n\t\t}\n\t\telse {\n\t\t\tbyte[] target = new byte[offset];\n\t\t\tSystem.arraycopy(buffer, 0, target, 0, offset);\n\t\t\treturn target;\n\t\t}\n\t}\n\n\tprivate static final class BufferItem {\n\t\tprivate final byte[] buffer;\n\t\tprivate final int length;\n\n\t\tprivate BufferItem(byte[] buffer, int length) {\n\t\t\tthis.buffer = buffer;\n\t\t\tthis.length = length;\n\t\t}\n\t}\n}\n\n## 内容4\n### 文件路径:\nclient/src/com/aerospike/client/util/Unpacker.java\n### 代码内容:\npackage com.aerospike.client.util;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.nio.ByteBuffer;\nimport java.util.AbstractMap;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.TreeMap;\n\nimport com.aerospike.client.AerospikeException;\nimport com.aerospike.client.Value;\nimport com.aerospike.client.command.Buffer;\nimport com.aerospike.client.command.ParticleType;\n\n/**\n * De-serialize collection objects using MessagePack format specification:\n *\n * https://github.com/msgpack/msgpack/blob/master/spec.md\n */\npublic abstract class Unpacker<T> {\n\n\tprivate final byte[] buffer;\n\tprivate int offset;\n\tprivate final int length;\n\n\tpublic Unpacker(byte[] buffer, int offset, int length) {\n\t\tthis.buffer = buffer;\n\t\tthis.offset = offset;\n\t\tthis.length = length;\n\t}\n\n\tpublic final T unpackList() throws AerospikeException {\n\t\tif (length <= 0) {\n\t\t\treturn getList(new ArrayList<T>(0));\n\t\t}\n\n\t\ttry {\n\t\t\tint type = buffer[offset++] & 0xff;\n\t\t\tint count;\n\n\t\t\tif ((type & 0xf0) == 0x90) {\n\t\t\t\tcount = type & 0x0f;\n\t\t\t}\n\t\t\telse if (type == 0xdc) {\n\t\t\t\tcount = Buffer.bytesToShort(buffer, offset);\n\t\t\t\toffset += 2;\n\t\t\t}\n\t\t\telse if (type == 0xdd) {\n\t\t\t\tcount = Buffer.bytesToInt(buffer, offset);\n\t\t\t\toffset += 4;\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn getList(new ArrayList<T>(0));\n\t\t\t}\n\t\t\treturn unpackList(count);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new AerospikeException.Serialize(e);\n\t\t}\n\t}\n\n\tprivate T unpackList(int count) throws IOException, ClassNotFoundException {\n\t\tif (count <= 0) {\n\t\t\treturn getList(new ArrayList<T>(0));\n\t\t}\n\n\t\t// Extract first object.\n\t\tint mark = offset;\n\t\tint size = count;\n\t\tT val = unpackObject();\n\n\t\tif (val == null) {\n\t\t\t// Determine if null value is because of an extension type.\n\t\t\tint type = buffer[mark] & 0xff;\n\n\t\t\tif (type != 0xc0) { // not nil type\n\t\t\t\t// Ignore extension type.\n\t\t\t\tsize--;\n\t\t\t}\n\t\t}\n\n\t\tArrayList<T> out = new ArrayList<T>(size);\n\n\t\tif (size == count) {\n\t\t\tout.add(val);\n\t\t}\n\n\t\tfor (int i = 1; i < count; i++) {\n\t\t\tout.add(unpackObject());\n\t\t}\n\t\treturn getList(out);\n\t}\n\n\tpublic final T unpackMap() throws AerospikeException {\n\t\tif (length <= 0) {\n\t\t\treturn getMap(new HashMap<T,T>(0));\n\t\t}\n\n\t\ttry {\n\t\t\tint type = buffer[offset++] & 0xff;\n\t\t\tint count;\n\n\t\t\tif ((type & 0xf0) == 0x80) {\n\t\t\t\tcount = type & 0x0f;\n\t\t\t}\n\t\t\telse if (type == 0xde) {\n\t\t\t\tcount = Buffer.bytesToShort(buffer, offset);\n\t\t\t\toffset += 2;\n\t\t\t}\n\t\t\telse if (type == 0xdf) {\n\t\t\t\tcount = Buffer.bytesToInt(buffer, offset);\n\t\t\t\toffset += 4;\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn getMap(new HashMap<T,T>(0));\n\t\t\t}\n\t\t\treturn unpackMap(count);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new AerospikeException.Serialize(e);\n\t\t}\n\t}\n\n\tprivate T unpackMap(int count) throws IOException, ClassNotFoundException {\n\t\tif (count <= 0) {\n\t\t\treturn getMap(new HashMap<T,T>(0));\n\t\t}\n\n\t\t// Peek at buffer to determine map type, but do not advance.\n\t\tint type = buffer[offset] & 0xff;\n\n\t\t// Check for extension that the server uses.\n\t\tif (type == 0xc7) {\n\t\t\tint extensionType = buffer[offset + 1] & 0xff;\n\n\t\t\tif (extensionType == 0) {\n\t\t\t\tint mapBits = buffer[offset + 2] & 0xff;\n\n\t\t\t\t// Extension is a map type. Determine which one.\n\t\t\t\tif ((mapBits & (0x04 | 0x08)) != 0) {\n\t\t\t\t\t// Index/rank range result where order needs to be preserved.\n\t\t\t\t\treturn unpackMapAsList(count);\n\t\t\t\t}\n\t\t\t\telse if ((mapBits & 0x01) != 0) {\n\t\t\t\t\t// Sorted map\n\t\t\t\t\treturn unpackTreeMap(count);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn unpackHashMap(count);\n\t}\n\n\tprivate T unpackHashMap(int count) throws IOException, ClassNotFoundException {\n\t\tHashMap<T,T> map = new HashMap<T,T>(count);\n\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tT key = unpackObject();\n\t\t\tT val = unpackObject();\n\n\t\t\tif (key != null) {\n\t\t\t\tmap.put(key, val);\n\t\t\t}\n\t\t}\n\t\treturn getMap(map);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate T unpackTreeMap(int count) throws IOException, ClassNotFoundException {\n\t\tTreeMap<T,T> map = new TreeMap<T,T>();\n\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tT key = unpackObject();\n\t\t\tT val = unpackObject();\n\n\t\t\tif (key != null) {\n\t\t\t\tif (key instanceof byte[]) {\n\t\t\t\t\t// TreeMap does not support byte[] keys,\n\t\t\t\t\t// so wrap byte[] in a ByteBuffer.\n\t\t\t\t\tkey = (T)ByteBuffer.wrap((byte[])key);\n\t\t\t\t}\n\t\t\t\tmap.put(key, val);\n\t\t\t}\n\t\t}\n\t\treturn getMap(map);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate T unpackMapAsList(int count) throws IOException, ClassNotFoundException {\n\t\t// Index/rank range result where order needs to be preserved.\n\t\t// Store in List<Entry<?,?> to preserve order.\n\t\t// The first entry is going to be null (ignored), so use \"count - 1\" size.\n\t\tList<Entry<T,T>> list = new ArrayList<Entry<T,T>>(count - 1);\n\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tT key = unpackObject();\n\t\t\tT val = unpackObject();\n\n\t\t\tif (key != null) {\n\t\t\t\tlist.add(new AbstractMap.SimpleEntry<T,T>(key, val));\n\t\t\t}\n\t\t}\n\t\treturn getList((List<T>)list);\n\t}\n\n\tprivate T unpackBlob(int count) throws IOException, ClassNotFoundException {\n\t\tint type = buffer[offset++] & 0xff;\n\t\tcount--;\n\t\tT val;\n\n\t\tswitch (type) {\n\t\tcase ParticleType.STRING:\n\t\t\tval = getString(Buffer.utf8ToString(buffer, offset, count));\n\t\t\tbreak;\n\n\t\tcase ParticleType.JBLOB:\n\t\t\tByteArrayInputStream bastream = new ByteArrayInputStream(buffer, offset, count);\n\t\t\tObjectInputStream oistream = new ObjectInputStream(bastream);\n\t\t\tval = getJavaBlob(oistream.readObject());\n\t\t\tbreak;\n\n\t\tcase ParticleType.GEOJSON:\n\t\t\tval = getGeoJSON(Buffer.utf8ToString(buffer, offset, count));\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tval = getBlob(Arrays.copyOfRange(buffer, offset, offset + count));\n\t\t\tbreak;\n\t\t}\n\t\toffset += count;\n\t\treturn val;\n\t}\n\n\tpublic T unpackObject() throws IOException, ClassNotFoundException {\n\t\tint type = buffer[offset++] & 0xff;\n\n\t\tswitch (type) {\n\t\t\tcase 0xc0: { // nil\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tcase 0xc3: { // boolean true\n\t\t\t\treturn getBoolean(true);\n\t\t\t}\n\n\t\t\tcase 0xc2: { // boolean false\n\t\t\t\treturn getBoolean(false);\n\t\t\t}\n\n\t\t\tcase 0xca: { // float\n\t\t\t\tfloat val = Float.intBitsToFloat(Buffer.bytesToInt(buffer, offset));\n\t\t\t\toffset += 4;\n\t\t\t\treturn getDouble(val);\n\t\t\t}\n\n\t\t\tcase 0xcb: { // double\n\t\t\t\tdouble val = Double.longBitsToDouble(Buffer.bytesToLong(buffer, offset));\n\t\t\t\toffset += 8;\n\t\t\t\treturn getDouble(val);\n\t\t\t}\n\n\t\t\tcase 0xd0: { // signed 8 bit integer\n\t\t\t\treturn getLong(buffer[offset++]);\n\t\t\t}\n\n\t\t\tcase 0xcc: { // unsigned 8 bit integer\n\t\t\t\treturn getLong(buffer[offset++] & 0xff);\n\t\t\t}\n\n\t\t\tcase 0xd1: { // signed 16 bit integer\n\t\t\t\tshort val = Buffer.bigSigned16ToShort(buffer, offset);\n\t\t\t\toffset += 2;\n\t\t\t\treturn getLong(val);\n\t\t\t}\n\n\t\t\tcase 0xcd: { // unsigned 16 bit integer\n\t\t\t\tint val = Buffer.bytesToShort(buffer, offset);\n\t\t\t\toffset += 2;\n\t\t\t\treturn getLong(val);\n\t\t\t}\n\n\t\t\tcase 0xd2: { // signed 32 bit integer\n\t\t\t\tint val = Buffer.bytesToInt(buffer, offset);\n\t\t\t\toffset += 4;\n\t\t\t\treturn getLong(val);\n\t\t\t}\n\n\t\t\tcase 0xce: { // unsigned 32 bit integer\n\t\t\t\tlong val = Buffer.bigUnsigned32ToLong(buffer, offset);\n\t\t\t\toffset += 4;\n\t\t\t\treturn getLong(val);\n\t\t\t}\n\n\t\t\tcase 0xd3: { // signed 64 bit integer\n\t\t\t\tlong val = Buffer.bytesToLong(buffer, offset);\n\t\t\t\toffset += 8;\n\t\t\t\treturn getLong(val);\n\t\t\t}\n\n\t\t\tcase 0xcf: { // unsigned 64 bit integer\n\t\t\t\t// Java is constrained to signed longs, so that is the best we can do here.\n\t\t\t\tlong val = Buffer.bytesToLong(buffer, offset);\n\t\t\t\toffset += 8;\n\t\t\t\treturn getLong(val);\n\t\t\t}\n\n\t\t\tcase 0xc4:\n\t\t\tcase 0xd9: { // string/raw bytes with 8 bit header\n\t\t\t\tint count = buffer[offset++] & 0xff;\n\t\t\t\treturn (T)unpackBlob(count);\n\t\t\t}\n\n\t\t\tcase 0xc5:\n\t\t\tcase 0xda: { // string/raw bytes with 16 bit header\n\t\t\t\tint count = Buffer.bytesToShort(buffer, offset);\n\t\t\t\toffset += 2;\n\t\t\t\treturn (T)unpackBlob(count);\n\t\t\t}\n\n\t\t\tcase 0xc6:\n\t\t\tcase 0xdb: { // string/raw bytes with 32 bit header\n\t\t\t\t// Java array length is restricted to positive int values (0 - Integer.MAX_VALUE).\n\t\t\t\tint count = Buffer.bytesToInt(buffer, offset);\n\t\t\t\toffset += 4;\n\t\t\t\treturn (T)unpackBlob(count);\n\t\t\t}\n\n\t\t\tcase 0xdc: { // list with 16 bit header\n\t\t\t\tint count = Buffer.bytesToShort(buffer, offset);\n\t\t\t\toffset += 2;\n\t\t\t\treturn unpackList(count);\n\t\t\t}\n\n\t\t\tcase 0xdd: { // list with 32 bit header\n\t\t\t\t// Java array length is restricted to positive int values (0 - Integer.MAX_VALUE).\n\t\t\t\tint count = Buffer.bytesToInt(buffer, offset);\n\t\t\t\toffset += 4;\n\t\t\t\treturn unpackList(count);\n\t\t\t}\n\n\t\t\tcase 0xde: { // map with 16 bit header\n\t\t\t\tint count = Buffer.bytesToShort(buffer, offset);\n\t\t\t\toffset += 2;\n\t\t\t\treturn unpackMap(count);\n\t\t\t}\n\n\t\t\tcase 0xdf: { // map with 32 bit header\n\t\t\t\t// Java array length is restricted to positive int values (0 - Integer.MAX_VALUE).\n\t\t\t\tint count = Buffer.bytesToInt(buffer, offset);\n\t\t\t\toffset += 4;\n\t\t\t\treturn unpackMap(count);\n\t\t\t}\n\n\t\t\tcase 0xd4: { // Skip over type extension with 1 byte\n\t\t\t\toffset += 1 + 1;\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tcase 0xd5: { // Skip over type extension with 2 bytes\n\t\t\t\toffset += 1 + 2;\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tcase 0xd6: { // Skip over type extension with 4 bytes\n\t\t\t\toffset += 1 + 4;\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tcase 0xd7: { // Skip over type extension with 8 bytes\n\t\t\t\toffset += 1 + 8;\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tcase 0xd8: { // Skip over type extension with 16 bytes\n\t\t\t\toffset += 1 + 16;\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tcase 0xc7: { // Skip over type extension with 8 bit header and bytes\n\t\t\t\tint count = buffer[offset] & 0xff;\n\t\t\t\toffset += count + 1 + 1;\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tcase 0xc8: { // Skip over type extension with 16 bit header and bytes\n\t\t\t\tint count = Buffer.bytesToShort(buffer, offset);\n\t\t\t\toffset += count + 1 + 2;\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tcase 0xc9: { // Skip over type extension with 32 bit header and bytes\n\t\t\t\tint count = Buffer.bytesToInt(buffer, offset);\n\t\t\t\toffset += count + 1 + 4;\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tif ((type & 0xe0) == 0xa0) { // raw bytes with 8 bit combined header\n\t\t\t\t\treturn unpackBlob(type & 0x1f);\n\t\t\t\t}\n\n\t\t\t\tif ((type & 0xf0) == 0x80) { // map with 8 bit combined header\n\t\t\t\t\treturn unpackMap(type & 0x0f);\n\t\t\t\t}\n\n\t\t\t\tif ((type & 0xf0) == 0x90) { // list with 8 bit combined header\n\t\t\t\t\treturn unpackList(type & 0x0f);\n\t\t\t\t}\n\n\t\t\t\tif (type < 0x80) { // 8 bit combined unsigned integer\n\t\t\t\t\treturn getLong(type);\n\t\t\t\t}\n\n\t\t\t\tif (type >= 0xe0) { // 8 bit combined signed integer\n\t\t\t\t\treturn getLong(type - 0xe0 - 32);\n\t\t\t\t}\n\t\t\t\tthrow new IOException(\"Unknown unpack type: \" + type);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected abstract T getMap(Map<T,T> value);\n\tprotected abstract T getList(List<T> value);\n\tprotected abstract T getJavaBlob(Object value);\n\tprotected abstract T getBlob(byte[] value);\n\tprotected abstract T getString(String value);\n\tprotected abstract T getLong(long value);\n\tprotected abstract T getDouble(double value);\n\tprotected abstract T getBoolean(boolean value);\n\tprotected abstract T getGeoJSON(String value);\n\n\tpublic static Object unpackObjectList(byte[] buffer, int offset, int length) throws AerospikeException {\n\t\tObjectUnpacker unpacker = new ObjectUnpacker(buffer, offset, length);\n\t\treturn unpacker.unpackList();\n\t}\n\n\tpublic static Object unpackObjectMap(byte[] buffer, int offset, int length) throws AerospikeException {\n\t\tObjectUnpacker unpacker = new ObjectUnpacker(buffer, offset, length);\n\t\treturn unpacker.unpackMap();\n\t}\n\n\tpublic static Object unpackObject(byte[] buffer, int offset, int length) throws AerospikeException {\n\t\ttry {\n\t\t\tif (length <= 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tObjectUnpacker unpacker = new ObjectUnpacker(buffer, offset, length);\n\t\t\treturn unpacker.unpackObject();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new AerospikeException.Serialize(e);\n\t\t}\n\t}\n\n\tpublic static final class ObjectUnpacker extends Unpacker<Object> {\n\n\t\tpublic ObjectUnpacker(byte[] buffer, int offset, int length) {\n\t\t\tsuper(buffer, offset, length);\n\t\t}\n\n\t\t@Override\n\t\tprotected Object getMap(Map<Object,Object> value) {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tprotected Object getList(List<Object> value) {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tprotected Object getJavaBlob(Object value) {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tprotected Object getBlob(byte[] value) {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tprotected Object getString(String value) {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tprotected Object getLong(long value) {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tprotected Object getDouble(double value) {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tprotected Object getBoolean(boolean value) {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tprotected Object getGeoJSON(String value) {\n\t\t\treturn Value.getAsGeoJSON(value);\n\t\t}\n\t}\n}\n\n", "input": "", "output": "【漏洞类型】反序列化漏洞\n【严重性】高\n【漏洞摘要】反序列化漏洞是指在程序中使用不安全的反序列化方法,如Java的`ObjectInputStream`,这可能导致攻击者通过构造恶意序列化数据来执行任意代码。在提供的代码中,`Value.java`、`Buffer.java`和`Packer.java`文件中的`bytesToObject`和`packBlob`方法分别使用了`ObjectInputStream`进行反序列化和`ObjectOutputStream`进行序列化。这些方法没有对输入数据进行验证和过滤,直接反序列化用户提供的数据,从而存在代码执行的安全风险。\n【关键代码模式】`ObjectInputStream`、`ObjectOutputStream`、`bytesToObject`、`packBlob`\n【修复方法说明】禁用了Java运行时的序列化和反序列化功能,通过抛出`AerospikeException`异常来防止用户输入的恶意数据被反序列化。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\npom.xml\n### 代码内容:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>io.onedev</groupId>\n\t\t<artifactId>parent</artifactId>\n\t\t<version>1.0.5</version>\n\t</parent>\n\t<artifactId>server</artifactId>\n\t<version>4.0.0</version>\n\t<packaging>pom</packaging>\n\t<build>\n\t\t<finalName>${project.groupId}.${project.artifactId}-${project.version}</finalName>\n\t\t<pluginManagement>\n\t\t\t<plugins>\n\t\t\t\t<plugin>\n\t\t\t\t\t<groupId>org.antlr</groupId>\n\t\t\t\t\t<artifactId>antlr4-maven-plugin</artifactId>\n\t\t\t\t\t<version>${antlr.version}</version>\n\t\t\t\t\t<executions>\n\t\t\t\t\t\t<execution>\n\t\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t\t<goal>antlr4</goal>\n\t\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t</execution>\n\t\t\t\t\t</executions>\n\t\t\t\t\t<configuration>\n\t\t\t\t\t\t<sourceDirectory>${basedir}/src/main/java</sourceDirectory>\n\t\t\t\t\t\t<listener>true</listener>\n\t\t\t\t\t\t<visitor>true</visitor>\n\t\t\t\t\t</configuration>\n\t\t\t\t</plugin>\n\t\t\t\t<plugin>\n\t\t\t\t\t<groupId>org.eclipse.m2e</groupId>\n\t\t\t\t\t<artifactId>lifecycle-mapping</artifactId>\n\t\t\t\t\t<version>1.0.0</version>\n\t\t\t\t\t<configuration>\n\t\t\t\t\t\t<lifecycleMappingMetadata>\n\t\t\t\t\t\t\t<pluginExecutions>\n\t\t\t\t\t\t\t\t<pluginExecution>\n\t\t\t\t\t\t\t\t\t<pluginExecutionFilter>\n\t\t\t\t\t\t\t\t\t\t<groupId>io.onedev</groupId>\n\t\t\t\t\t\t\t\t\t\t<artifactId>plugin-maven</artifactId>\n\t\t\t\t\t\t\t\t\t\t<versionRange>[2.0.0,]</versionRange>\n\t\t\t\t\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t\t\t\t\t<goal>populate-resources</goal>\n\t\t\t\t\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t\t\t\t</pluginExecutionFilter>\n\t\t\t\t\t\t\t\t\t<action>\n\t\t\t\t\t\t\t\t\t\t<execute>\n\t\t\t\t\t\t\t\t\t\t\t<runOnIncremental>true</runOnIncremental>\n\t\t\t\t\t\t\t\t\t\t\t<runOnConfiguration>true</runOnConfiguration>\n\t\t\t\t\t\t\t\t\t\t</execute>\n\t\t\t\t\t\t\t\t\t</action>\n\t\t\t\t\t\t\t\t</pluginExecution>\n\t\t\t\t\t\t\t\t<pluginExecution>\n\t\t\t\t\t\t\t\t\t<pluginExecutionFilter>\n\t\t\t\t\t\t\t\t\t\t<groupId>org.codehaus.gmaven</groupId>\n\t\t\t\t\t\t\t\t\t\t<artifactId>groovy-maven-plugin</artifactId>\n\t\t\t\t\t\t\t\t\t\t<versionRange>[2.0,]</versionRange>\n\t\t\t\t\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t\t\t\t\t<goal>execute</goal>\n\t\t\t\t\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t\t\t\t</pluginExecutionFilter>\n\t\t\t\t\t\t\t\t\t<action>\n\t\t\t\t\t\t\t\t\t\t<execute>\n\t\t\t\t\t\t\t\t\t\t\t<runOnIncremental>true</runOnIncremental>\n\t\t\t\t\t\t\t\t\t\t\t<runOnConfiguration>true</runOnConfiguration>\n\t\t\t\t\t\t\t\t\t\t</execute>\n\t\t\t\t\t\t\t\t\t</action>\n\t\t\t\t\t\t\t\t</pluginExecution>\n\t\t\t\t\t\t\t\t<pluginExecution>\n\t\t\t\t\t\t\t\t\t<pluginExecutionFilter>\n\t\t\t\t\t\t\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t\t\t\t\t\t\t<artifactId>maven-jar-plugin</artifactId>\n\t\t\t\t\t\t\t\t\t\t<versionRange>[1.0.0,]</versionRange>\n\t\t\t\t\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t\t\t\t\t<goal>jar</goal>\n\t\t\t\t\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t\t\t\t</pluginExecutionFilter>\n\t\t\t\t\t\t\t\t\t<action>\n\t\t\t\t\t\t\t\t\t\t<ignore />\n\t\t\t\t\t\t\t\t\t</action>\n\t\t\t\t\t\t\t\t</pluginExecution>\n\t\t\t\t\t\t\t</pluginExecutions>\n\t\t\t\t\t\t</lifecycleMappingMetadata>\n\t\t\t\t\t</configuration>\n\t\t\t\t</plugin>\n\t\t\t\t<plugin>\n\t\t\t\t\t<artifactId>maven-jar-plugin</artifactId>\n\t\t\t\t\t<version>3.0.2</version>\n\t\t\t\t\t<executions>\n\t\t\t\t\t\t<execution>\n\t\t\t\t\t\t\t<id>default-jar</id>\n\t\t\t\t\t\t\t<phase>never</phase>\n\t\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t\t<finalName>unwanted</finalName>\n\t\t\t\t\t\t\t\t<classifier>unwanted</classifier>\n\t\t\t\t\t\t\t</configuration>\n\t\t\t\t\t\t</execution>\n\t\t\t\t\t</executions>\n\t\t\t\t</plugin>\n\t\t\t</plugins>\n\t\t</pluginManagement>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>io.onedev</groupId>\n\t\t\t\t<artifactId>plugin-maven</artifactId>\n\t\t\t\t<version>2.0.5</version>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>populate-resources</id>\n\t\t\t\t\t\t<phase>process-resources</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>populate-resources</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>package-artifacts</id>\n\t\t\t\t\t\t<phase>package</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>package-artifacts</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\t\t</plugins>\n\t\t<resources>\n\t\t\t<resource>\n\t\t\t\t<directory>src/main/java</directory>\n\t\t\t\t<excludes>\n\t\t\t\t\t<exclude>**/*.java</exclude>\n\t\t\t\t\t<exclude>**/*.scss</exclude>\n\t\t\t\t</excludes>\n\t\t\t</resource>\n\t\t\t<resource>\n\t\t\t\t<directory>src/main/resources</directory>\n\t\t\t</resource>\n\t\t\t<resource>\n\t\t\t\t<directory>target/generated-sources/antlr4</directory>\n\t\t\t\t<includes>\n\t\t\t\t\t<include>*.tokens</include>\n\t\t\t\t</includes>\n\t\t\t</resource>\n\t\t</resources>\n\t</build>\n\t<modules>\n\t\t<module>server-core</module>\n\t\t<module>server-plugin</module>\n\t\t<module>server-product</module>\n\t</modules>\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>io.onedev</groupId>\n\t\t\t\t<artifactId>commons-launcher-loader</artifactId>\n\t\t\t\t<version>${commons.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>io.onedev</groupId>\n\t\t\t\t<artifactId>commons-launcher-loader</artifactId>\n\t\t\t\t<type>test-jar</type>\n\t\t\t\t<version>${commons.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>io.onedev</groupId>\n\t\t\t\t<artifactId>commons-utils</artifactId>\n\t\t\t\t<version>${commons.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>io.onedev</groupId>\n\t\t\t\t<artifactId>commons-codeassist</artifactId>\n\t\t\t\t<version>${commons.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>io.onedev</groupId>\n\t\t\t\t<artifactId>commons-jsymbol</artifactId>\n\t\t\t\t<version>${commons.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>io.onedev</groupId>\n\t\t\t\t<artifactId>commons-jsyntax</artifactId>\n\t\t\t\t<version>${commons.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.slf4j</groupId>\n\t\t\t\t<artifactId>slf4j-api</artifactId>\n\t\t\t\t<version>${slf4j.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.slf4j</groupId>\n\t\t\t\t<artifactId>jul-to-slf4j</artifactId>\n\t\t\t\t<version>${slf4j.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>ch.qos.logback</groupId>\n\t\t\t\t<artifactId>logback-core</artifactId>\n\t\t\t\t<version>${logback.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>ch.qos.logback</groupId>\n\t\t\t\t<artifactId>logback-classic</artifactId>\n\t\t\t\t<version>${logback.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.eclipse.jetty</groupId>\n\t\t\t\t<artifactId>jetty-server</artifactId>\n\t\t\t\t<version>${jetty.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.eclipse.jetty</groupId>\n\t\t\t\t<artifactId>jetty-servlets</artifactId>\n\t\t\t\t<version>${jetty.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.eclipse.jetty</groupId>\n\t\t\t\t<artifactId>jetty-servlet</artifactId>\n\t\t\t\t<version>${jetty.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.eclipse.jetty.websocket</groupId>\n\t\t\t\t<artifactId>websocket-server</artifactId>\n\t\t\t\t<version>${jetty.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.wicket</groupId>\n\t\t\t\t<artifactId>wicket-core</artifactId>\n\t\t\t\t<version>${wicket.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.wicket</groupId>\n\t\t\t\t<artifactId>wicket-extensions</artifactId>\n\t\t\t\t<version>${wicket.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.wicket</groupId>\n\t\t\t\t<artifactId>wicket-request</artifactId>\n\t\t\t\t<version>${wicket.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.wicket</groupId>\n\t\t\t\t<artifactId>wicket-util</artifactId>\n\t\t\t\t<version>${wicket.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.wicket</groupId>\n\t\t\t\t<artifactId>wicket-devutils</artifactId>\n\t\t\t\t<version>${wicket.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.wicket</groupId>\n\t\t\t\t<artifactId>wicket-native-websocket-core</artifactId>\n\t\t\t\t<version>${wicket.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.glassfish.jersey.containers</groupId>\n\t\t\t\t<artifactId>jersey-container-servlet</artifactId>\n\t\t\t\t<version>${jersey.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.glassfish.jersey.ext</groupId>\n\t\t\t\t<artifactId>jersey-bean-validation</artifactId>\n\t\t\t\t<version>${jersey.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.dom4j</groupId>\n\t\t\t\t<artifactId>dom4j</artifactId>\n\t\t\t\t<version>2.1.3</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.hibernate</groupId>\n\t\t\t\t<artifactId>hibernate-validator</artifactId>\n\t\t\t\t<version>5.3.6.Final</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.hibernate</groupId>\n\t\t\t\t<artifactId>hibernate-core</artifactId>\n\t\t\t\t<version>${hibernate.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.hibernate</groupId>\n\t\t\t\t<artifactId>hibernate-jcache</artifactId>\n\t\t\t\t<version>${hibernate.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.hibernate</groupId>\n\t\t\t\t<artifactId>hibernate-entitymanager</artifactId>\n\t\t\t\t<version>${hibernate.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.hibernate</groupId>\n\t\t\t\t<artifactId>hibernate-hikaricp</artifactId>\n\t\t\t\t<version>${hibernate.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.ehcache</groupId>\n\t\t\t\t<artifactId>ehcache</artifactId>\n\t\t\t\t<version>3.8.0</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.nimbusds</groupId>\n\t\t\t\t<artifactId>oauth2-oidc-sdk</artifactId>\n\t\t\t\t<version>3.4.1</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.zaxxer</groupId>\n\t\t\t\t<artifactId>HikariCP</artifactId>\n\t\t\t\t<version>2.7.9</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.commons</groupId>\n\t\t\t\t<artifactId>commons-collections4</artifactId>\n\t\t\t\t<version>4.1</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.commons</groupId>\n\t\t\t\t<artifactId>commons-compress</artifactId>\n\t\t\t\t<version>1.19</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>commons-io</groupId>\n\t\t\t\t<artifactId>commons-io</artifactId>\n\t\t\t\t<version>2.5</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>commons-codec</groupId>\n\t\t\t\t<artifactId>commons-codec</artifactId>\n\t\t\t\t<version>1.7</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.commons</groupId>\n\t\t\t\t<artifactId>commons-lang3</artifactId>\n\t\t\t\t<version>3.8.1</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.commons</groupId>\n\t\t\t\t<artifactId>commons-text</artifactId>\n\t\t\t\t<version>1.6</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.google.guava</groupId>\n\t\t\t\t<artifactId>guava</artifactId>\n\t\t\t\t<version>25.1-android</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>commons-fileupload</groupId>\n\t\t\t\t<artifactId>commons-fileupload</artifactId>\n\t\t\t\t<version>1.3.3</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>commons-validator</groupId>\n\t\t\t\t<artifactId>commons-validator</artifactId>\n\t\t\t\t<version>1.4.0</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.ocpsoft.prettytime</groupId>\n\t\t\t\t<artifactId>prettytime</artifactId>\n\t\t\t\t<version>4.0.1.Final</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.httpcomponents</groupId>\n\t\t\t\t<artifactId>httpclient</artifactId>\n\t\t\t\t<version>4.5.6</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.glassfish.hk2</groupId>\n\t\t\t\t<artifactId>guice-bridge</artifactId>\n\t\t\t\t<version>2.5.0</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.jetbrains.xodus</groupId>\n\t\t\t\t<artifactId>xodus-entity-store</artifactId>\n\t\t\t\t<version>1.2.3</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.tika</groupId>\n\t\t\t\t<artifactId>tika-core</artifactId>\n\t\t\t\t<version>1.24.1</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.commons</groupId>\n\t\t\t\t<artifactId>commons-email</artifactId>\n\t\t\t\t<version>1.5</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.velocity</groupId>\n\t\t\t\t<artifactId>velocity</artifactId>\n\t\t\t\t<version>1.7</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>commons-httpclient</groupId>\n\t\t\t\t<artifactId>commons-httpclient</artifactId>\n\t\t\t\t<version>3.1</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>joda-time</groupId>\n\t\t\t\t<artifactId>joda-time</artifactId>\n\t\t\t\t<version>2.2</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.yaml</groupId>\n\t\t\t\t<artifactId>snakeyaml</artifactId>\n\t\t\t\t<version>1.21</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.bouncycastle</groupId>\n\t\t\t\t<artifactId>bcprov-jdk15on</artifactId>\n\t\t\t\t<version>1.62</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.unbescape</groupId>\n\t\t\t\t<artifactId>unbescape</artifactId>\n\t\t\t\t<version>1.1.2.RELEASE</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.quartz-scheduler</groupId>\n\t\t\t\t<artifactId>quartz</artifactId>\n\t\t\t\t<version>2.3.2</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.shiro</groupId>\n\t\t\t\t<artifactId>shiro-core</artifactId>\n\t\t\t\t<version>${shiro.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<artifactId>lucene-core</artifactId>\n\t\t\t\t<groupId>org.apache.lucene</groupId>\n\t\t\t\t<version>${lucene.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<artifactId>lucene-queries</artifactId>\n\t\t\t\t<groupId>org.apache.lucene</groupId>\n\t\t\t\t<version>${lucene.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<artifactId>lucene-analyzers-common</artifactId>\n\t\t\t\t<groupId>org.apache.lucene</groupId>\n\t\t\t\t<version>${lucene.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.shiro</groupId>\n\t\t\t\t<artifactId>shiro-web</artifactId>\n\t\t\t\t<version>${shiro.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.shiro</groupId>\n\t\t\t\t<artifactId>shiro-guice</artifactId>\n\t\t\t\t<version>${shiro.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>javax.xml.bind</groupId>\n\t\t\t\t<artifactId>jaxb-api</artifactId>\n\t\t\t\t<version>2.3.0</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.glassfish</groupId>\n\t\t\t\t<artifactId>javax.el</artifactId>\n\t\t\t\t<version>3.0.0</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.hsqldb</groupId>\n\t\t\t\t<artifactId>hsqldb</artifactId>\n\t\t\t\t<version>2.4.0</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>jaxen</groupId>\n\t\t\t\t<artifactId>jaxen</artifactId>\n\t\t\t\t<version>1.1.6</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>io.onedev</groupId>\n\t\t\t\t<artifactId>k8s-helper</artifactId>\n\t\t\t\t<version>${k8shelper.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.thoughtworks.xstream</groupId>\n\t\t\t\t<artifactId>xstream</artifactId>\n\t\t\t\t<version>1.4.14</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.joestelmach</groupId>\n\t\t\t\t<artifactId>natty</artifactId>\n\t\t\t\t<version>0.13</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.codehaus.groovy</groupId>\n\t\t\t\t<artifactId>groovy</artifactId>\n\t\t\t\t<version>${groovy.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.googlecode.juniversalchardet</groupId>\n\t\t\t\t<artifactId>juniversalchardet</artifactId>\n\t\t\t\t<version>1.0.3</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.vladsch.flexmark</groupId>\n\t\t\t\t<artifactId>flexmark</artifactId>\n\t\t\t\t<version>${flexmark.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.vladsch.flexmark</groupId>\n\t\t\t\t<artifactId>flexmark-ext-autolink</artifactId>\n\t\t\t\t<version>${flexmark.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.vladsch.flexmark</groupId>\n\t\t\t\t<artifactId>flexmark-ext-anchorlink</artifactId>\n\t\t\t\t<version>${flexmark.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.vladsch.flexmark</groupId>\n\t\t\t\t<artifactId>flexmark-ext-definition</artifactId>\n\t\t\t\t<version>${flexmark.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.vladsch.flexmark</groupId>\n\t\t\t\t<artifactId>flexmark-ext-tables</artifactId>\n\t\t\t\t<version>${flexmark.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.vladsch.flexmark</groupId>\n\t\t\t\t<artifactId>flexmark-ext-gfm-tasklist</artifactId>\n\t\t\t\t<version>${flexmark.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.vladsch.flexmark</groupId>\n\t\t\t\t<artifactId>flexmark-ext-toc</artifactId>\n\t\t\t\t<version>${flexmark.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.eclipse.jgit</groupId>\n\t\t\t\t<artifactId>org.eclipse.jgit.http.server</artifactId>\n\t\t\t\t<version>${jgit.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.eclipse.jgit</groupId>\n\t\t\t\t<artifactId>org.eclipse.jgit.archive</artifactId>\n\t\t\t\t<version>${jgit.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t \t<groupId>net.i2p.crypto</groupId>\n\t\t\t \t<artifactId>eddsa</artifactId>\n\t\t\t \t<version>0.3.0</version>\n\t\t\t</dependency>\t\t\t\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.sshd</groupId>\n\t\t\t\t<artifactId>sshd-core</artifactId>\n\t\t\t\t<version>2.2.0</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.json</groupId>\n\t\t\t\t<artifactId>json</artifactId>\n\t\t\t\t<version>20090211</version>\n\t\t\t</dependency>\n \t\t</dependencies>\n\t</dependencyManagement>\n\t<repositories>\n\t\t<repository>\n\t\t\t<id>onedev</id>\n\t\t\t<url>https://artifact.onedev.io/artifactory/onedev</url>\n\t\t\t<releases>\n\t\t\t\t<enabled>true</enabled>\n\t\t\t\t<updatePolicy>never</updatePolicy>\n\t\t\t\t<checksumPolicy>fail</checksumPolicy>\n\t\t\t</releases>\n\t\t\t<snapshots>\n\t\t\t\t<enabled>true</enabled>\n\t\t\t\t<updatePolicy>always</updatePolicy>\n\t\t\t\t<checksumPolicy>fail</checksumPolicy>\n\t\t\t</snapshots>\n\t\t</repository>\n\t</repositories>\n\t<properties>\n\t\t<commons.version>1.1.21</commons.version>\n\t\t<k8shelper.version>1.0.20</k8shelper.version>\n\t\t<slf4j.version>1.7.5</slf4j.version>\n\t\t<logback.version>1.0.11</logback.version>\n\t\t<antlr.version>4.7.2</antlr.version>\n\t\t<jetty.version>9.4.24.v20191120</jetty.version>\n\t\t<wicket.version>7.17.0</wicket.version>\n\t\t<jersey.version>2.26</jersey.version>\n\t\t<hibernate.version>5.4.9.Final</hibernate.version>\n\t\t<wicketbootstrap.version>0.10.18</wicketbootstrap.version>\n\t\t<lucene.version>7.1.0</lucene.version>\n\t\t<shiro.version>1.4.2</shiro.version>\n\t\t<jgit.version>5.7.0.202003110725-r</jgit.version>\n\t\t<flexmark.version>0.18.3</flexmark.version>\n\t\t<groovy.version>3.0.5</groovy.version>\n\t\t<servlet.version>3.1.0</servlet.version>\n\t</properties>\n</project>\n\n## 内容2\n### 文件路径:\nserver-core/pom.xml\n### 代码内容:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<artifactId>server-core</artifactId>\n\t<parent>\n\t\t<groupId>io.onedev</groupId>\n\t\t<artifactId>server</artifactId>\n\t\t<version>4.0.0</version>\n\t</parent>\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.antlr</groupId>\n\t\t\t\t<artifactId>antlr4-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>io.onedev</groupId>\n\t\t\t<artifactId>commons-launcher-loader</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.onedev</groupId>\n\t\t\t<artifactId>commons-launcher-loader</artifactId>\n\t\t\t<type>test-jar</type>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.onedev</groupId>\n\t\t\t<artifactId>commons-codeassist</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.onedev</groupId>\n\t\t\t<artifactId>commons-jsymbol</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.onedev</groupId>\n\t\t\t<artifactId>commons-jsyntax</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-codec</groupId>\n\t\t\t<artifactId>commons-codec</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.commons</groupId>\n\t\t\t<artifactId>commons-compress</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.google.guava</groupId>\n\t\t\t<artifactId>guava</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.jetbrains.xodus</groupId>\n\t\t\t<artifactId>xodus-entity-store</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.tika</groupId>\n\t\t\t<artifactId>tika-core</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.commons</groupId>\n\t\t\t<artifactId>commons-email</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.velocity</groupId>\n\t\t\t<artifactId>velocity</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-httpclient</groupId>\n\t\t\t<artifactId>commons-httpclient</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>joda-time</groupId>\n\t\t\t<artifactId>joda-time</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.yaml</groupId>\n\t\t\t<artifactId>snakeyaml</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.bouncycastle</groupId>\n\t\t\t<artifactId>bcprov-jdk15on</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.unbescape</groupId>\n\t\t\t<artifactId>unbescape</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.quartz-scheduler</groupId>\n\t\t\t<artifactId>quartz</artifactId>\n\t\t\t<exclusions>\n\t\t\t\t<exclusion>\n\t\t\t\t\t<groupId>c3p0</groupId>\n\t\t\t\t\t<artifactId>c3p0</artifactId>\n\t\t\t\t</exclusion>\n\t\t\t</exclusions>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.shiro</groupId>\n\t\t\t<artifactId>shiro-core</artifactId>\n\t\t\t<exclusions>\n\t\t\t\t<exclusion>\n\t\t\t\t\t<groupId>org.slf4j</groupId>\n\t\t\t\t\t<artifactId>slf4j-api</artifactId>\n\t\t\t\t</exclusion>\n\t\t\t</exclusions>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<artifactId>lucene-core</artifactId>\n\t\t\t<groupId>org.apache.lucene</groupId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<artifactId>lucene-queries</artifactId>\n\t\t\t<groupId>org.apache.lucene</groupId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<artifactId>lucene-analyzers-common</artifactId>\n\t\t\t<groupId>org.apache.lucene</groupId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.shiro</groupId>\n\t\t\t<artifactId>shiro-web</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.shiro</groupId>\n\t\t\t<artifactId>shiro-guice</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>javax.xml.bind</groupId>\n\t\t\t<artifactId>jaxb-api</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.glassfish</groupId>\n\t\t\t<artifactId>javax.el</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.hsqldb</groupId>\n\t\t\t<artifactId>hsqldb</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>jaxen</groupId>\n\t\t\t<artifactId>jaxen</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.thoughtworks.xstream</groupId>\n\t\t\t<artifactId>xstream</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.joestelmach</groupId>\n\t\t\t<artifactId>natty</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.codehaus.groovy</groupId>\n\t\t\t<artifactId>groovy</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.googlecode.juniversalchardet</groupId>\n\t\t\t<artifactId>juniversalchardet</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.vladsch.flexmark</groupId>\n\t\t\t<artifactId>flexmark</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.vladsch.flexmark</groupId>\n\t\t\t<artifactId>flexmark-ext-autolink</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.vladsch.flexmark</groupId>\n\t\t\t<artifactId>flexmark-ext-anchorlink</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.vladsch.flexmark</groupId>\n\t\t\t<artifactId>flexmark-ext-definition</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.vladsch.flexmark</groupId>\n\t\t\t<artifactId>flexmark-ext-tables</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.vladsch.flexmark</groupId>\n\t\t\t<artifactId>flexmark-ext-gfm-tasklist</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.vladsch.flexmark</groupId>\n\t\t\t<artifactId>flexmark-ext-toc</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.eclipse.jgit</groupId>\n\t\t\t<artifactId>org.eclipse.jgit.http.server</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.eclipse.jgit</groupId>\n\t\t\t<artifactId>org.eclipse.jgit.archive</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.sshd</groupId>\n\t\t\t<artifactId>sshd-core</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>net.i2p.crypto</groupId>\n\t\t\t<artifactId>eddsa</artifactId>\n\t\t</dependency>\t\t\n\t\t<dependency>\n\t\t\t<groupId>org.eclipse.jetty</groupId>\n\t\t\t<artifactId>jetty-server</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.eclipse.jetty</groupId>\n\t\t\t<artifactId>jetty-servlets</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.eclipse.jetty</groupId>\n\t\t\t<artifactId>jetty-servlet</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.eclipse.jetty.websocket</groupId>\n\t\t\t<artifactId>websocket-server</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.glassfish.jersey.containers</groupId>\n\t\t\t<artifactId>jersey-container-servlet</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.glassfish.jersey.ext</groupId>\n\t\t\t<artifactId>jersey-bean-validation</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.hibernate</groupId>\n\t\t\t<artifactId>hibernate-validator</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.wicket</groupId>\n\t\t\t<artifactId>wicket-core</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.wicket</groupId>\n\t\t\t<artifactId>wicket-extensions</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.wicket</groupId>\n\t\t\t<artifactId>wicket-request</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.wicket</groupId>\n\t\t\t<artifactId>wicket-util</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.wicket</groupId>\n\t\t\t<artifactId>wicket-devutils</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.wicket</groupId>\n\t\t\t<artifactId>wicket-native-websocket-core</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-fileupload</groupId>\n\t\t\t<artifactId>commons-fileupload</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-validator</groupId>\n\t\t\t<artifactId>commons-validator</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.ocpsoft.prettytime</groupId>\n\t\t\t<artifactId>prettytime</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.httpcomponents</groupId>\n\t\t\t<artifactId>httpclient</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.glassfish.hk2</groupId>\n\t\t\t<artifactId>guice-bridge</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.hibernate</groupId>\n\t\t\t<artifactId>hibernate-core</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.hibernate</groupId>\n\t\t\t<artifactId>hibernate-jcache</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.ehcache</groupId>\n\t\t\t<artifactId>ehcache</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.hibernate</groupId>\n\t\t\t<artifactId>hibernate-entitymanager</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.hibernate</groupId>\n\t\t\t<artifactId>hibernate-hikaricp</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.zaxxer</groupId>\n\t\t\t<artifactId>HikariCP</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.dom4j</groupId>\n\t\t\t<artifactId>dom4j</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.json</groupId>\n\t\t\t<artifactId>json</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.onedev</groupId>\n\t\t\t<artifactId>k8s-helper</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\t<properties>\n\t\t<moduleClass>io.onedev.server.CoreModule</moduleClass>\n\t</properties>\n</project>\n\n## 内容3\n### 文件路径:\nserver-plugin/pom.xml\n### 代码内容:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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/maven-v4_0_0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<artifactId>server-plugin</artifactId>\n\t<packaging>pom</packaging>\n\t<parent>\n\t\t<groupId>io.onedev</groupId>\n\t\t<artifactId>server</artifactId>\n\t\t<version>4.0.0</version>\n\t</parent>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>io.onedev</groupId>\n\t\t\t<artifactId>server-core</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t</dependencies>\n\t<modules>\n\t\t<module>server-plugin-archetype</module>\n\t\t<module>server-plugin-report-html</module>\n\t\t<module>server-plugin-executor-kubernetes</module>\n\t\t<module>server-plugin-executor-docker</module>\n\t\t<module>server-plugin-buildspec-maven</module>\n \t<module>server-plugin-buildspec-gradle</module>\n \t<module>server-plugin-buildspec-node</module>\n\t <module>server-plugin-authenticator-ldap</module>\n\t <module>server-plugin-sso-openid</module>\n \t<module>server-plugin-report-markdown</module>\n </modules>\n</project>\n## 内容4\n### 文件路径:\nserver-plugin/server-plugin-archetype/pom.xml\n### 代码内容:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<artifactId>server-plugin-archetype</artifactId>\n\t<parent>\n\t\t<groupId>io.onedev</groupId>\n\t\t<artifactId>server-plugin</artifactId>\n\t\t<version>4.0.0</version>\n\t</parent>\n\t<build>\n\t\t<resources>\n\t\t <resource>\n\t\t\t<directory>src/main/resources</directory>\n\t\t\t<filtering>true</filtering>\n\t\t\t<includes>\n\t\t\t <include>archetype-resources/pom.xml</include>\n\t\t\t</includes>\n\t\t </resource>\n\t\t <resource>\n\t\t\t<directory>src/main/resources</directory>\n\t\t\t<filtering>false</filtering>\n\t\t\t<excludes>\n\t\t\t <exclude>archetype-resources/pom.xml</exclude>\n\t\t\t</excludes>\n\t\t </resource>\n\t\t</resources>\n\t\t<pluginManagement>\n\t\t <plugins>\n\t\t\t<plugin>\n\t\t\t <groupId>org.apache.maven.plugins</groupId>\n\t\t\t <artifactId>maven-resources-plugin</artifactId>\n\t\t\t <version>2.5</version>\n\t\t\t <configuration>\n\t\t\t\t<escapeString>\\</escapeString>\n\t\t\t </configuration>\n\t\t\t</plugin>\n\t\t </plugins>\n\t\t</pluginManagement>\n\t\t<plugins>\n <plugin> \n <artifactId>maven-archetype-plugin</artifactId> \n\t\t\t\t<version>2.2</version>\n\t\t\t\t<executions> \n\t\t\t\t\t<execution> \n\t\t\t\t\t\t<id>install-archetype</id> \n\t\t\t\t\t\t<phase>install</phase> \n\t\t\t\t\t\t<goals> \n\t\t\t\t\t\t\t<goal>update-local-catalog</goal> \n\t\t\t\t\t\t</goals> \n\t\t\t\t\t</execution> \n\t\t\t\t</executions> \n </plugin> \n\t\t</plugins>\n\t</build>\n</project>\n## 内容5\n### 文件路径:\nserver-plugin/server-plugin-authenticator-ldap/pom.xml\n### 代码内容:\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<artifactId>server-plugin-authenticator-ldap</artifactId>\n\t<parent>\n\t\t<groupId>io.onedev</groupId>\n\t\t<artifactId>server-plugin</artifactId>\n\t\t<version>4.0.0</version>\n\t</parent>\n\t<properties>\n\t\t<moduleClass>io.onedev.server.plugin.authenticator.ldap.LdapModule</moduleClass>\n\t</properties>\n</project>\n\n## 内容6\n### 文件路径:\nserver-plugin/server-plugin-buildspec-gradle/pom.xml\n### 代码内容:\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<artifactId>server-plugin-buildspec-gradle</artifactId>\n\t<parent>\n\t\t<groupId>io.onedev</groupId>\n\t\t<artifactId>server-plugin</artifactId>\n\t\t<version>4.0.0</version>\n\t</parent>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>io.onedev</groupId>\n\t\t\t<artifactId>server-plugin-report-html</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t</dependencies>\n\t<properties>\n\t\t<moduleClass>io.onedev.server.plugin.buildspec.gradle.GradleModule</moduleClass>\n\t</properties>\n</project>\n\n## 内容7\n### 文件路径:\nserver-plugin/server-plugin-buildspec-maven/pom.xml\n### 代码内容:\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<artifactId>server-plugin-buildspec-maven</artifactId>\n\t<parent>\n\t\t<groupId>io.onedev</groupId>\n\t\t<artifactId>server-plugin</artifactId>\n\t\t<version>4.0.0</version>\n\t</parent>\n\t<properties>\n\t\t<moduleClass>io.onedev.server.plugin.buildspec.maven.MavenModule</moduleClass>\n\t</properties>\n</project>\n\n## 内容8\n### 文件路径:\nserver-plugin/server-plugin-buildspec-node/pom.xml\n### 代码内容:\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<artifactId>server-plugin-buildspec-node</artifactId>\n\t<parent>\n\t\t<groupId>io.onedev</groupId>\n\t\t<artifactId>server-plugin</artifactId>\n\t\t<version>4.0.0</version>\n\t</parent>\n\t<properties>\n\t\t<moduleClass>io.onedev.server.plugin.buildspec.node.NodePluginModule</moduleClass>\n\t</properties>\n\n</project>\n\n## 内容9\n### 文件路径:\nserver-plugin/server-plugin-executor-docker/pom.xml\n### 代码内容:\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<artifactId>server-plugin-executor-docker</artifactId>\n\t<parent>\n\t\t<groupId>io.onedev</groupId>\n\t\t<artifactId>server-plugin</artifactId>\n\t\t<version>4.0.0</version>\n\t</parent>\n\t<properties>\n\t\t<moduleClass>io.onedev.server.plugin.docker.DockerModule</moduleClass>\n\t</properties>\n</project>\n\n## 内容10\n### 文件路径:\nserver-plugin/server-plugin-executor-kubernetes/pom.xml\n### 代码内容:\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<artifactId>server-plugin-executor-kubernetes</artifactId>\n\t<parent>\n\t\t<groupId>io.onedev</groupId>\n\t\t<artifactId>server-plugin</artifactId>\n\t\t<version>4.0.0</version>\n\t</parent>\n\t<properties>\n\t\t<moduleClass>io.onedev.server.plugin.executor.kubernetes.KubernetesModule</moduleClass>\n\t</properties>\n</project>\n\n## 内容11\n### 文件路径:\nserver-plugin/server-plugin-executor-kubernetes/src/main/java/io/onedev/server/plugin/executor/kubernetes/KubernetesResource.java\n### 代码内容:\npackage io.onedev.server.plugin.executor.kubernetes;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.UUID;\n\nimport javax.inject.Inject;\nimport javax.inject.Singleton;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.ws.rs.Consumes;\nimport javax.ws.rs.GET;\nimport javax.ws.rs.POST;\nimport javax.ws.rs.Path;\nimport javax.ws.rs.Produces;\nimport javax.ws.rs.core.Context;\nimport javax.ws.rs.core.MediaType;\nimport javax.ws.rs.core.Response;\nimport javax.ws.rs.core.StreamingOutput;\n\nimport org.apache.commons.lang.SerializationUtils;\n\nimport com.google.common.collect.Lists;\n\nimport io.onedev.commons.utils.TarUtils;\nimport io.onedev.k8shelper.CacheAllocationRequest;\nimport io.onedev.k8shelper.CacheInstance;\nimport io.onedev.server.GeneralException;\nimport io.onedev.server.buildspec.job.Job;\nimport io.onedev.server.buildspec.job.JobContext;\nimport io.onedev.server.buildspec.job.JobManager;\n\n@Path(\"/k8s\")\n@Consumes(MediaType.WILDCARD)\n@Singleton\npublic class KubernetesResource {\n\n\tpublic static final String TEST_JOB_TOKEN = UUID.randomUUID().toString();\n\t\n\tprivate final JobManager jobManager;\n\t\n @Context\n private HttpServletRequest request;\n \n @Inject\n public KubernetesResource(JobManager jobManager) {\n \tthis.jobManager = jobManager;\n\t}\n \n\t@Path(\"/job-context\")\n\t@Produces(MediaType.APPLICATION_OCTET_STREAM)\n @GET\n public byte[] getJobContext() {\n\t\tJobContext context = jobManager.getJobContext(getJobToken(), true);\n\t\tMap<String, Object> contextMap = new HashMap<>();\n\t\tcontextMap.put(\"commands\", context.getCommands());\n\t\tcontextMap.put(\"retrieveSource\", context.isRetrieveSource());\n\t\tcontextMap.put(\"cloneDepth\", context.getCloneDepth());\n\t\tcontextMap.put(\"projectName\", context.getProjectName());\n\t\tcontextMap.put(\"cloneInfo\", context.getCloneInfo());\n\t\tcontextMap.put(\"commitHash\", context.getCommitId().name());\n\t\tcontextMap.put(\"collectFiles.includes\", context.getCollectFiles().getIncludes());\n\t\tcontextMap.put(\"collectFiles.excludes\", context.getCollectFiles().getExcludes());\n\t\treturn SerializationUtils.serialize((Serializable) contextMap);\n }\n\t\n\t@Path(\"/allocate-job-caches\")\n\t@Consumes(MediaType.APPLICATION_OCTET_STREAM)\n\t@Produces(MediaType.APPLICATION_OCTET_STREAM)\n @POST\n public byte[] allocateJobCaches(byte[] cacheAllocationRequestBytes) {\n\t\tCacheAllocationRequest allocationRequest = (CacheAllocationRequest) SerializationUtils\n\t\t\t\t.deserialize(cacheAllocationRequestBytes);\n\t\treturn SerializationUtils.serialize((Serializable) jobManager.allocateJobCaches(\n\t\t\t\tgetJobToken(), allocationRequest.getCurrentTime(), allocationRequest.getInstances()));\n }\n\t\n\t@Path(\"/report-job-caches\")\n\t@Consumes(MediaType.APPLICATION_OCTET_STREAM)\n\t@POST\n\tpublic void reportJobCaches(byte[] cacheInstanceBytes) {\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tCollection<CacheInstance> cacheInstances = (Collection<CacheInstance>) SerializationUtils\n\t\t\t\t.deserialize(cacheInstanceBytes);\n\t\tjobManager.reportJobCaches(getJobToken(), cacheInstances);\n\t}\n\t\n\t@Path(\"/download-dependencies\")\n\t@Produces(MediaType.APPLICATION_OCTET_STREAM)\n\t@GET\n\tpublic Response downloadDependencies() {\n\t\tStreamingOutput os = new StreamingOutput() {\n\n\t\t\t@Override\n\t\t public void write(OutputStream output) throws IOException {\n\t\t\t\tJobContext context = jobManager.getJobContext(getJobToken(), true);\n\t\t\t\tTarUtils.tar(context.getServerWorkspace(), Lists.newArrayList(\"**\"), \n\t\t\t\t\t\tnew ArrayList<>(), output);\n\t\t\t\toutput.flush();\n\t\t }\t\t\t\t \n\t\t \n\t\t};\n\t\treturn Response.ok(os).build();\n\t}\n\t\n\t@POST\n\t@Path(\"/upload-outcomes\")\n\t@Consumes(MediaType.APPLICATION_OCTET_STREAM)\t\n\tpublic Response uploadOutcomes(InputStream is) {\n\t\tJobContext context = jobManager.getJobContext(getJobToken(), true);\n\t\tTarUtils.untar(is, context.getServerWorkspace());\n\t\treturn Response.ok().build();\n\t}\n\t\n\t@GET\n\t@Path(\"/test\")\n\tpublic Response test() {\n\t\tString jobToken = Job.getToken(request);\n\t\tif (TEST_JOB_TOKEN.equals(jobToken))\n\t\t\treturn Response.ok().build();\n\t\telse \n\t\t\treturn Response.status(400).entity(\"Invalid or missing job token\").build();\n\t}\n\t\n\tprivate String getJobToken() {\n\t\tString jobToken = Job.getToken(request);\n\t\tif (jobToken != null)\n\t\t\treturn jobToken;\n\t\telse\n\t\t\tthrow new GeneralException(\"Job token is expected\");\n\t}\n\t\n}\n\n## 内容12\n### 文件路径:\nserver-plugin/server-plugin-report-html/pom.xml\n### 代码内容:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<artifactId>server-plugin-report-html</artifactId>\n\t<parent>\n\t\t<groupId>io.onedev</groupId>\n\t\t<artifactId>server-plugin</artifactId>\n\t\t<version>4.0.0</version>\n\t</parent>\n\t<properties>\n\t\t<moduleClass>io.onedev.server.plugin.report.html.HtmlReportModule</moduleClass>\n\t</properties>\n</project>\n## 内容13\n### 文件路径:\nserver-plugin/server-plugin-report-markdown/pom.xml\n### 代码内容:\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<artifactId>server-plugin-report-markdown</artifactId>\n\t<parent>\n\t\t<groupId>io.onedev</groupId>\n\t\t<artifactId>server-plugin</artifactId>\n\t\t<version>4.0.0</version>\n\t</parent>\n\t<properties>\n\t\t<moduleClass>io.onedev.server.plugin.report.markdown.MarkdownReportModule</moduleClass>\n\t</properties>\n</project>\n\n## 内容14\n### 文件路径:\nserver-plugin/server-plugin-sso-openid/pom.xml\n### 代码内容:\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<artifactId>server-plugin-sso-openid</artifactId>\n\t<parent>\n\t\t<groupId>io.onedev</groupId>\n\t\t<artifactId>server-plugin</artifactId>\n\t\t<version>4.0.0</version>\n\t</parent>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>com.nimbusds</groupId>\n\t\t\t<artifactId>oauth2-oidc-sdk</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\t<properties>\n\t\t<moduleClass>io.onedev.server.plugin.sso.openid.OpenIdModule</moduleClass>\n\t\t<okta-jwt.version>0.4.0</okta-jwt.version>\n\t</properties>\n</project>\n\n## 内容15\n### 文件路径:\nserver-product/pom.xml\n### 代码内容:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<artifactId>server-product</artifactId>\n\t<parent>\n\t\t<groupId>io.onedev</groupId>\n\t\t<artifactId>server</artifactId>\n\t\t<version>4.0.0</version>\n\t</parent>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>io.onedev</groupId>\n\t\t\t<artifactId>server-core</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.onedev</groupId>\n\t\t\t<artifactId>server-plugin-report-html</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.onedev</groupId>\n\t\t\t<artifactId>server-plugin-buildspec-maven</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.onedev</groupId>\n\t\t\t<artifactId>server-plugin-buildspec-node</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.onedev</groupId>\n\t\t\t<artifactId>server-plugin-buildspec-gradle</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.onedev</groupId>\n\t\t\t<artifactId>server-plugin-executor-kubernetes</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.onedev</groupId>\n\t\t\t<artifactId>server-plugin-executor-docker</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.onedev</groupId>\n\t\t\t<artifactId>server-plugin-authenticator-ldap</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.onedev</groupId>\n\t\t\t<artifactId>server-plugin-sso-openid</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.onedev</groupId>\n\t\t\t<artifactId>server-plugin-report-markdown</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t</dependencies>\n\t<properties>\n\t\t<executables>bin/*.sh, boot/wrapper-*</executables>\n\t\t<moduleClass>io.onedev.server.product.ProductModule</moduleClass>\n\t</properties>\n\n</project>\n", "input": "", "output": "【漏洞类型】反序列化漏洞\n\n【严重性】High\n\n【漏洞摘要】反序列化漏洞是指将不受信任的数据反序列化为对象,可能导致任意代码执行(RCE)或其他安全问题。在原始代码中,`KubernetesResource` 类中的 `allocateJobCaches` 和 `reportJobCaches` 方法使用了 `SerializationUtils.deserialize` 来反序列化用户提供的数据。例如,在 `allocateJobCaches` 方法中,`CacheAllocationRequest` 对象是通过反序列化 `cacheAllocationRequestBytes` 来创建的。在 `reportJobCaches` 方法中,`CacheInstance` 对象集合是通过反序列化 `cacheInstanceBytes` 来创建的。这些操作使得攻击者可以通过构造恶意序列化数据来执行任意代码,从而对系统造成严重的安全威胁。\n\n【关键代码模式】\n- `SerializationUtils.deserialize`\n- `byte[]` 类型的输入参数\n- `@Consumes(MediaType.APPLICATION_OCTET_STREAM)`\n\n【修复方法说明】\n修复方法是将用户提供的数据从 `byte[]` 类型转换为字符串,并通过自定义的 `fromString` 方法来解析数据,而不是直接反序列化。这样可以避免潜在的反序列化漏洞,提高系统的安全性。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nREADME.md\n### 代码内容:\n[](https://app.shippable.com/projects/55cfbb00edd7f2c052a980a5)\n\n# Excel Streaming Reader\n\nIf you've used [Apache POI](http://poi.apache.org) in the past to read in Excel files, you probably noticed that it's not very memory efficient. Reading in an entire workbook will cause a severe memory usage spike, which can wreak havoc on a server. \n\nThere are plenty of good reasons for why Apache has to read in the whole workbook, but most of them have to do with the fact that the library allows you to read and write with random addresses. If (and only if) you just want to read the contents of an Excel file in a fast and memory effecient way, you probably don't need this ability. Unfortunately, the only thing in the POI library for reading a streaming workbook requires your code to use a SAX-like parser. All of the friendly classes like `Row` and `Cell` are missing from that API.\n\nThis library serves as a wrapper around that streaming API while preserving the syntax of the standard POI API. Read on to see if it's right for you.\n\n# Important! Read first!\n\nThe latest versions of this library (2.x) have dropped support for Java 7. This is due to POI 4.0 requiring Java 8; as that is a core dependency of this library, it cannot support older versions of Java. The older 1.x and 0.x versions will no longer be maintained.\n\n# Include\n\nThis library is available from from Maven Central, and you can optionally install it yourself. The Maven installation instructions can be found on the [release](https://github.com/monitorjbl/excel-streaming-reader/releases) page.\n\nTo use it, add this to your POM:\n\n```\n<dependencies>\n <dependency>\n <groupId>com.monitorjbl</groupId>\n <artifactId>xlsx-streamer</artifactId>\n <version>2.0.0</version>\n </dependency>\n</dependencies> \n```\n\n# Usage\n\nThis library is very specific in how it is meant to be used. You should initialize it like so:\n\n```java\nimport com.monitorjbl.xlsx.StreamingReader;\n\nInputStream is = new FileInputStream(new File(\"/path/to/workbook.xlsx\"));\nWorkbook workbook = StreamingReader.builder()\n .rowCacheSize(100) // number of rows to keep in memory (defaults to 10)\n .bufferSize(4096) // buffer size to use when reading InputStream to file (defaults to 1024)\n .open(is); // InputStream or File for XLSX file (required)\n```\n\nOnce you've done this, you can then iterate through the rows and cells like so:\n\n```java\nfor (Sheet sheet : workbook){\n System.out.println(sheet.getSheetName());\n for (Row r : sheet) {\n for (Cell c : r) {\n System.out.println(c.getStringCellValue());\n }\n }\n}\n```\n\nOr open a sheet by name or index:\n\n```java\nSheet sheet = workbook.getSheet(\"My Sheet\")\n```\n\nThe StreamingWorkbook is an autoclosable resource, and it's important that you close it to free the filesystem resource it consumed. With Java 7, you can do this:\n\n```java\ntry (\n InputStream is = new FileInputStream(new File(\"/path/to/workbook.xlsx\"));\n Workbook workbook = StreamingReader.builder()\n .rowCacheSize(100)\n .bufferSize(4096)\n .open(is)) {\n for (Sheet sheet : workbook){\n System.out.println(sheet.getSheetName());\n for (Row r : sheet) {\n for (Cell c : r) {\n System.out.println(c.getStringCellValue());\n }\n }\n }\n}\n```\n\nYou may access cells randomly within a row, as the entire row is cached. **However**, there is no way to randomly access rows. As this is a streaming implementation, only a small number of rows are kept in memory at any given time.\n\n# Supported Methods\n\nNot all POI Cell and Row functions are supported. The most basic ones are (`Cell.getStringCellValue()`, `Cell.getColumnIndex()`, etc.), but don't be surprised if you get a `NotSupportedException` on the more advanced ones.\n\nI'll try to add more support as time goes on, but some items simply can't be read in a streaming fashion. Methods that require dependent values will not have said dependencies available at the point in the stream in which they are read.\n\nThis is a brief and very generalized list of things that are not supported for reads:\n\n* Functions\n* Macros\n* Styled cells (the styles are kept at the end of the ZIP file)\n\n# Logging\n\nThis library uses SLF4j logging. This is a rare use case, but you can plug in your logging provider and get some potentially useful output. Below is an example of doing this with log4j:\n\n**pom.xml**\n\n```\n<dependencies>\n <dependency>\n <groupId>com.monitorjbl</groupId>\n <artifactId>xlsx-streamer</artifactId>\n <version>2.0.0</version>\n </dependency>\n <dependency>\n <groupId>org.slf4j</groupId>\n <artifactId>slf4j-log4j12</artifactId>\n <version>1.7.6</version>\n </dependency>\n <dependency>\n <groupId>log4j</groupId>\n <artifactId>log4j</artifactId>\n <version>1.2.17</version>\n </dependency>\n</dependencies>\n```\n\n**log4j.properties**\n\n```\nlog4j.rootLogger=DEBUG, A1\nlog4j.appender.A1=org.apache.log4j.ConsoleAppender\nlog4j.appender.A1.layout=org.apache.log4j.PatternLayout\nlog4j.appender.A1.layout.ConversionPattern=%d{ISO8601} [%c] %p: %m%n\n\nlog4j.category.com.monitorjbl=DEBUG\n```\n\n# Implementation Details\n\nThis library will take a provided `InputStream` and output it to the file system. The stream is piped safely through a configurable-sized buffer to prevent large usage of memory. Once the file is created, it is then streamed into memory from the file system.\n\nThe reason for needing the stream being outputted in this manner has to do with how ZIP files work. Because the XLSX file format is basically a ZIP file, it's not possible to find all of the entries without reading the entire InputStream.\n\nThis is a problem that can't really be gotten around for POI, as it needs a complete list of ZIP entries. The default implementation of reading from an `InputStream` in POI is to read the entire stream directly into memory. This library works by reading out the stream into a temporary file. As part of the auto-close action, the temporary file is deleted.\n\nIf you need more control over how the file is created/disposed of, there is an option to initialize the library with a `java.io.File`. This file will not be written to or removed:\n\n```java\nFile f = new File(\"/path/to/workbook.xlsx\");\nWorkbook workbook = StreamingReader.builder()\n .rowCacheSize(100) \n .bufferSize(4096) \n .open(f);\n```\n\nThis library will ONLY work with XLSX files. The older XLS format is not capable of being streamed.\n\n", "input": "", "output": "【漏洞类型】XML外部实体(XXE)注入\n【严重性】High\n【漏洞摘要】原始代码中使用的Xerxes XML解析库在默认设置下允许实体扩展,这可能导致攻击者通过在Excel文件中插入恶意XML文档来利用XXE漏洞。攻击者可以访问服务器上的任意文件,导致潜在的敏感数据泄露或系统危害。\n【关键代码模式】\n- Apache POI库\n- Xerxes XML解析库\n- `StreamingReader.builder().open(is)` 方法\n【修复方法说明】通过更新到最新版本(2.1.0),禁用了实体扩展,从而防止了XXE攻击。最佳实践是始终使用最新的库版本,并确保所有外部输入都经过适当的验证和过滤。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nserver/src/main/java/com/thoughtworks/go/server/controller/ArtifactsController.java\n### 代码内容:\npackage com.thoughtworks.go.server.controller;\n\nimport com.thoughtworks.go.domain.ConsoleConsumer;\nimport com.thoughtworks.go.domain.JobIdentifier;\nimport com.thoughtworks.go.domain.exception.IllegalArtifactLocationException;\nimport com.thoughtworks.go.server.cache.ZipArtifactCache;\nimport com.thoughtworks.go.server.dao.JobInstanceDao;\nimport com.thoughtworks.go.server.security.HeaderConstraint;\nimport com.thoughtworks.go.server.service.ArtifactsService;\nimport com.thoughtworks.go.server.service.ConsoleActivityMonitor;\nimport com.thoughtworks.go.server.service.ConsoleService;\nimport com.thoughtworks.go.server.service.RestfulService;\nimport com.thoughtworks.go.server.util.ErrorHandler;\nimport com.thoughtworks.go.server.view.artifacts.ArtifactsView;\nimport com.thoughtworks.go.server.view.artifacts.LocalArtifactsView;\nimport com.thoughtworks.go.server.web.ArtifactFolderViewFactory;\nimport com.thoughtworks.go.server.web.FileModelAndView;\nimport com.thoughtworks.go.server.web.ResponseCodeView;\nimport com.thoughtworks.go.util.ArtifactLogUtil;\nimport com.thoughtworks.go.util.SystemEnvironment;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.multipart.MultipartFile;\nimport org.springframework.web.multipart.MultipartHttpServletRequest;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.charset.Charset;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static com.thoughtworks.go.server.web.ZipArtifactFolderViewFactory.zipViewFactory;\nimport static com.thoughtworks.go.util.ArtifactLogUtil.isConsoleOutput;\nimport static com.thoughtworks.go.util.GoConstants.*;\nimport static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;\n\n@Controller\npublic class ArtifactsController {\n private static final Logger LOGGER = LoggerFactory.getLogger(ArtifactsController.class);\n\n private final JobInstanceDao jobInstanceDao;\n private final ConsoleActivityMonitor consoleActivityMonitor;\n private final ArtifactFolderViewFactory folderViewFactory;\n private final ArtifactFolderViewFactory jsonViewFactory;\n private final ArtifactFolderViewFactory zipViewFactory;\n private final Charset consoleLogCharset;\n private ArtifactsService artifactsService;\n private RestfulService restfulService;\n private ConsoleService consoleService;\n private HeaderConstraint headerConstraint;\n\n @Autowired\n ArtifactsController(ArtifactsService artifactsService, RestfulService restfulService, ZipArtifactCache zipArtifactCache, JobInstanceDao jobInstanceDao,\n ConsoleActivityMonitor consoleActivityMonitor, ConsoleService consoleService, SystemEnvironment systemEnvironment) {\n this.artifactsService = artifactsService;\n this.restfulService = restfulService;\n this.jobInstanceDao = jobInstanceDao;\n this.consoleActivityMonitor = consoleActivityMonitor;\n this.consoleService = consoleService;\n\n this.folderViewFactory = FileModelAndView.htmlViewFactory();\n this.jsonViewFactory = FileModelAndView.jsonViewfactory();\n this.zipViewFactory = zipViewFactory(zipArtifactCache);\n this.headerConstraint = new HeaderConstraint(systemEnvironment);\n this.consoleLogCharset = systemEnvironment.consoleLogCharsetAsCharset();\n }\n\n /* RESTful URLs */\n @RequestMapping(value = \"/repository/restful/artifact/GET/html\", method = RequestMethod.GET)\n public ModelAndView getArtifactAsHtml(@RequestParam(\"pipelineName\") String pipelineName,\n @RequestParam(\"pipelineCounter\") String pipelineCounter,\n @RequestParam(\"stageName\") String stageName,\n @RequestParam(value = \"stageCounter\", required = false) String stageCounter,\n @RequestParam(\"buildName\") String buildName,\n @RequestParam(\"filePath\") String filePath,\n @RequestParam(value = \"sha1\", required = false) String sha,\n @RequestParam(value = \"serverAlias\", required = false) String serverAlias) throws Exception {\n return getArtifact(filePath, folderViewFactory, pipelineName, pipelineCounter, stageName, stageCounter, buildName, sha, serverAlias);\n }\n\n @RequestMapping(value = \"/repository/restful/artifact/GET/json\", method = RequestMethod.GET)\n public ModelAndView getArtifactAsJson(@RequestParam(\"pipelineName\") String pipelineName,\n @RequestParam(\"pipelineCounter\") String pipelineCounter,\n @RequestParam(\"stageName\") String stageName,\n @RequestParam(value = \"stageCounter\", required = false) String stageCounter,\n @RequestParam(\"buildName\") String buildName,\n @RequestParam(\"filePath\") String filePath,\n @RequestParam(value = \"sha1\", required = false) String sha\n ) throws Exception {\n return getArtifact(filePath, jsonViewFactory, pipelineName, pipelineCounter, stageName, stageCounter, buildName, sha, null);\n }\n\n @RequestMapping(value = \"/repository/restful/artifact/GET/zip\", method = RequestMethod.GET)\n public ModelAndView getArtifactAsZip(@RequestParam(\"pipelineName\") String pipelineName,\n @RequestParam(\"pipelineCounter\") String pipelineCounter,\n @RequestParam(\"stageName\") String stageName,\n @RequestParam(value = \"stageCounter\", required = false) String stageCounter,\n @RequestParam(\"buildName\") String buildName,\n @RequestParam(\"filePath\") String filePath,\n @RequestParam(value = \"sha1\", required = false) String sha\n ) throws Exception {\n if (filePath.equals(\".zip\")) {\n filePath = \"./.zip\";\n }\n return getArtifact(filePath, zipViewFactory, pipelineName, pipelineCounter, stageName, stageCounter, buildName, sha, null);\n }\n\n @RequestMapping(value = \"/repository/restful/artifact/GET/*\", method = RequestMethod.GET)\n public void fetch(HttpServletRequest request, HttpServletResponse response) throws Exception {\n request.getRequestDispatcher(\"/repository/restful/artifact/GET/html\").forward(request, response);\n }\n\n @RequestMapping(value = \"/repository/restful/artifact/POST/*\", method = RequestMethod.POST)\n public ModelAndView postArtifact(@RequestParam(\"pipelineName\") String pipelineName,\n @RequestParam(\"pipelineCounter\") String pipelineCounter,\n @RequestParam(\"stageName\") String stageName,\n @RequestParam(value = \"stageCounter\", required = false) String stageCounter,\n @RequestParam(\"buildName\") String buildName,\n @RequestParam(value = \"buildId\", required = false) Long buildId,\n @RequestParam(\"filePath\") String filePath,\n @RequestParam(value = \"attempt\", required = false) Integer attempt,\n MultipartHttpServletRequest request) throws Exception {\n JobIdentifier jobIdentifier;\n if (!headerConstraint.isSatisfied(request)) {\n return ResponseCodeView.create(HttpServletResponse.SC_BAD_REQUEST, \"Missing required header 'Confirm'\");\n }\n try {\n jobIdentifier = restfulService.findJob(pipelineName, pipelineCounter, stageName, stageCounter,\n buildName, buildId);\n } catch (Exception e) {\n return buildNotFound(pipelineName, pipelineCounter, stageName, stageCounter,\n buildName);\n }\n\n int convertedAttempt = attempt == null ? 1 : attempt;\n\n try {\n File artifact = artifactsService.findArtifact(jobIdentifier, filePath);\n if (artifact.exists() && artifact.isFile()) {\n return FileModelAndView.fileAlreadyExists(filePath);\n }\n\n MultipartFile multipartFile = multipartFile(request);\n if (multipartFile == null) {\n return FileModelAndView.invalidUploadRequest();\n }\n\n boolean success = saveFile(convertedAttempt, artifact, multipartFile, shouldUnzipStream(multipartFile));\n\n if (!success) {\n return FileModelAndView.errorSavingFile(filePath);\n }\n\n success = updateChecksumFile(request, jobIdentifier, filePath);\n\n if (!success) {\n return FileModelAndView.errorSavingChecksumFile(filePath);\n }\n\n return FileModelAndView.fileCreated(filePath);\n\n } catch (IllegalArtifactLocationException e) {\n return FileModelAndView.forbiddenUrl(filePath);\n }\n }\n\n private boolean updateChecksumFile(MultipartHttpServletRequest request, JobIdentifier jobIdentifier, String filePath) throws IOException, IllegalArtifactLocationException {\n MultipartFile checksumMultipartFile = getChecksumFile(request);\n if (checksumMultipartFile != null) {\n String checksumFilePath = String.format(\"%s/%s/%s\", artifactsService.findArtifactRoot(jobIdentifier), ArtifactLogUtil.CRUISE_OUTPUT_FOLDER, ArtifactLogUtil.MD5_CHECKSUM_FILENAME);\n File checksumFile = artifactsService.getArtifactLocation(checksumFilePath);\n synchronized (checksumFilePath.intern()) {\n return artifactsService.saveOrAppendFile(checksumFile, checksumMultipartFile.getInputStream());\n }\n } else {\n LOGGER.warn(\"[Artifacts Upload] Checksum file not uploaded for artifact at path '{}'\", filePath);\n }\n return true;\n }\n\n private boolean saveFile(int convertedAttempt, File artifact, MultipartFile multipartFile, boolean shouldUnzip) throws IOException {\n try (InputStream inputStream = multipartFile.getInputStream()) {\n return artifactsService.saveFile(artifact, inputStream, shouldUnzip, convertedAttempt);\n }\n }\n\n @RequestMapping(value = \"/repository/restful/artifact/PUT/*\", method = RequestMethod.PUT)\n public ModelAndView putArtifact(@RequestParam(\"pipelineName\") String pipelineName,\n @RequestParam(\"pipelineCounter\") String pipelineCounter,\n @RequestParam(\"stageName\") String stageName,\n @RequestParam(value = \"stageCounter\", required = false) String stageCounter,\n @RequestParam(\"buildName\") String buildName,\n @RequestParam(value = \"buildId\", required = false) Long buildId,\n @RequestParam(\"filePath\") String filePath,\n @RequestParam(value = \"agentId\", required = false) String agentId,\n HttpServletRequest request\n ) throws Exception {\n if (filePath.contains(\"..\")) {\n return FileModelAndView.forbiddenUrl(filePath);\n }\n\n JobIdentifier jobIdentifier;\n try {\n jobIdentifier = restfulService.findJob(pipelineName, pipelineCounter, stageName, stageCounter, buildName, buildId);\n } catch (Exception e) {\n return buildNotFound(pipelineName, pipelineCounter, stageName, stageCounter, buildName);\n }\n\n if (isConsoleOutput(filePath)) {\n return putConsoleOutput(jobIdentifier, request.getInputStream());\n } else {\n return putArtifact(jobIdentifier, filePath, request.getInputStream());\n }\n }\n\n /* Other URLs */\n\n @RequestMapping(value = \"/**/consoleout.json\", method = RequestMethod.GET)\n public ModelAndView consoleout(@RequestParam(\"pipelineName\") String pipelineName,\n @RequestParam(\"pipelineCounter\") String pipelineCounter,\n @RequestParam(\"stageName\") String stageName,\n @RequestParam(\"buildName\") String buildName,\n @RequestParam(value = \"stageCounter\", required = false) String stageCounter,\n @RequestParam(value = \"startLineNumber\", required = false) Long start\n ) {\n start = start == null ? 0L : start;\n\n try {\n JobIdentifier identifier = restfulService.findJob(pipelineName, pipelineCounter, stageName, stageCounter, buildName);\n if (jobInstanceDao.isJobCompleted(identifier) && !consoleService.doesLogExist(identifier)) {\n return logsNotFound(identifier);\n }\n ConsoleConsumer streamer = consoleService.getStreamer(start, identifier);\n return new ModelAndView(new ConsoleOutView(streamer, consoleLogCharset));\n } catch (Exception e) {\n return buildNotFound(pipelineName, pipelineCounter, stageName, stageCounter, buildName);\n }\n }\n\n @ErrorHandler\n public ModelAndView handleError(HttpServletRequest request, HttpServletResponse response, Exception e) {\n LOGGER.error(\"Error loading artifacts: \", e);\n Map model = new HashMap();\n model.put(ERROR_FOR_PAGE, \"Artifact does not exist.\");\n return new ModelAndView(\"exceptions_page\", model);\n }\n\n ModelAndView getArtifact(String filePath, ArtifactFolderViewFactory folderViewFactory, String pipelineName, String counterOrLabel, String stageName, String stageCounter, String buildName, String sha, String serverAlias) throws Exception {\n LOGGER.info(\"[Artifact Download] Trying to resolve '{}' for '{}/{}/{}/{}/{}'\", filePath, pipelineName, counterOrLabel, stageName, stageCounter, buildName);\n long before = System.currentTimeMillis();\n ArtifactsView view;\n //Work out the job that we are trying to retrieve\n JobIdentifier translatedId;\n try {\n translatedId = restfulService.findJob(pipelineName, counterOrLabel, stageName, stageCounter, buildName);\n } catch (Exception e) {\n return buildNotFound(pipelineName, counterOrLabel, stageName, stageCounter, buildName);\n }\n\n if (filePath.contains(\"..\")) {\n return FileModelAndView.forbiddenUrl(filePath);\n }\n\n view = new LocalArtifactsView(folderViewFactory, artifactsService, translatedId, consoleService);\n\n ModelAndView createdView = view.createView(filePath, sha);\n LOGGER.info(\"[Artifact Download] Successfully resolved '{}' for '{}/{}/{}/{}/{}'. It took: {}ms\", filePath, pipelineName, counterOrLabel, stageName, stageCounter, buildName, System.currentTimeMillis() - before);\n return createdView;\n }\n\n private boolean shouldUnzipStream(MultipartFile multipartFile) {\n return multipartFile.getName().equals(ZIP_MULTIPART_FILENAME);\n }\n\n private MultipartFile multipartFile(MultipartHttpServletRequest request) {\n MultipartFile multipartFile = request.getFile(REGULAR_MULTIPART_FILENAME);\n if (multipartFile == null) {\n multipartFile = request.getFile(ZIP_MULTIPART_FILENAME);\n }\n return multipartFile;\n }\n\n private MultipartFile getChecksumFile(MultipartHttpServletRequest request) {\n return request.getFile(CHECKSUM_MULTIPART_FILENAME);\n }\n\n private ModelAndView putConsoleOutput(final JobIdentifier jobIdentifier, final InputStream inputStream) throws Exception {\n File consoleLogFile = consoleService.consoleLogFile(jobIdentifier);\n boolean updated = consoleService.updateConsoleLog(consoleLogFile, inputStream);\n if (updated) {\n consoleActivityMonitor.consoleUpdatedFor(jobIdentifier);\n return FileModelAndView.fileAppended(consoleLogFile.getPath());\n } else {\n return FileModelAndView.errorSavingFile(consoleLogFile.getPath());\n }\n }\n\n private ModelAndView putArtifact(JobIdentifier jobIdentifier, String filePath,\n InputStream inputStream) throws Exception {\n File artifact = artifactsService.findArtifact(jobIdentifier, filePath);\n if (artifactsService.saveOrAppendFile(artifact, inputStream)) {\n return FileModelAndView.fileAppended(filePath);\n } else {\n return FileModelAndView.errorSavingFile(filePath);\n }\n }\n\n private ModelAndView buildNotFound(String pipelineName, String counterOrLabel, String stageName,\n String stageCounter,\n String buildName) {\n return ResponseCodeView.create(SC_NOT_FOUND, String.format(\"Job %s/%s/%s/%s/%s not found.\", pipelineName,\n counterOrLabel, stageName, stageCounter, buildName));\n }\n\n private ModelAndView logsNotFound(JobIdentifier identifier) {\n String notFound = String.format(\"Console log for %s is unavailable as it may have been purged by Go or deleted externally.\", identifier.toFullString());\n return ResponseCodeView.create(SC_NOT_FOUND, notFound);\n }\n}\n\n", "input": "", "output": "【漏洞类型】路径遍历漏洞\n【严重性】中高\n【漏洞摘要】原始代码在处理文件路径时,未对用户输入的`filePath`参数进行充分验证,导致攻击者可以通过构造特殊的`filePath`参数来访问或操作服务器上的任意文件。具体表现为`postArtifact`和`putArtifact`方法中,`filePath`参数直接用于文件路径的构建和操作,而没有进行路径规范化或验证。\n【关键代码模式】\n- `postArtifact`方法中的:\n ```java\n File artifact = artifactsService.findArtifact(jobIdentifier, filePath);\n ```\n- `putArtifact`方法中的:\n ```java\n File artifact = artifactsService.findArtifact(jobIdentifier, filePath);\n ```\n- 未验证的用户输入`filePath`参数。\n【修复方法说明】修复方法通过添加`isValidStageCounter`方法来验证`stageCounter`参数是否为空或为正整数,从而防止路径遍历漏洞。适用的最佳实践包括对所有用户输入进行严格的验证和规范化处理,以确保路径安全。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nserver-plugin/server-plugin-authenticator-ldap/src/main/java/io/onedev/server/plugin/authenticator/ldap/LdapAuthenticator.java\n### 代码内容:\npackage io.onedev.server.plugin.authenticator.ldap;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Hashtable;\nimport java.util.List;\n\nimport javax.annotation.Nullable;\nimport javax.naming.AuthenticationException;\nimport javax.naming.CompositeName;\nimport javax.naming.Context;\nimport javax.naming.InvalidNameException;\nimport javax.naming.Name;\nimport javax.naming.NamingEnumeration;\nimport javax.naming.NamingException;\nimport javax.naming.PartialResultException;\nimport javax.naming.directory.Attribute;\nimport javax.naming.directory.Attributes;\nimport javax.naming.directory.DirContext;\nimport javax.naming.directory.InitialDirContext;\nimport javax.naming.directory.SearchControls;\nimport javax.naming.directory.SearchResult;\nimport javax.validation.constraints.NotNull;\n\nimport org.apache.shiro.authc.AccountException;\nimport org.apache.shiro.authc.UnknownAccountException;\nimport org.apache.shiro.authc.UsernamePasswordToken;\nimport org.hibernate.validator.constraints.NotEmpty;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.onedev.commons.utils.StringUtils;\nimport io.onedev.server.model.support.administration.authenticator.Authenticated;\nimport io.onedev.server.model.support.administration.authenticator.Authenticator;\nimport io.onedev.server.web.editable.annotation.Editable;\nimport io.onedev.server.web.editable.annotation.Password;\n\n@Editable(name=\"Generic LDAP\", order=200)\npublic class LdapAuthenticator extends Authenticator {\n\n\tprivate static final long serialVersionUID = 1L;\n\t\n\tprivate static final Logger logger = LoggerFactory.getLogger(LdapAuthenticator.class);\n\n\tprivate String ldapUrl;\n\t\n private String managerDN;\n \n private String managerPassword;\n \n private String userSearchBase;\n \n private String userSearchFilter;\n \n private String userFullNameAttribute = \"displayName\";\n \n private String userEmailAttribute = \"mail\";\n \n private String userSshKeyAttribute;\n \n private GroupRetrieval groupRetrieval = new DoNotRetrieveGroups();\n \n @Editable(order=100, name=\"LDAP URL\", description=\n \t\"Specifies LDAP URL, for example: <i>ldap://localhost</i>, or <i>ldaps://localhost</i>.\")\n @NotEmpty\n\tpublic String getLdapUrl() {\n\t\treturn ldapUrl;\n\t}\n\n\tpublic void setLdapUrl(String ldapUrl) {\n\t\tthis.ldapUrl = ldapUrl;\n\t}\n\n\t@Editable(order=300, description=\"\"\n\t\t\t+ \"To authenticate user against LDAP and retrieve associated attributes and groups, OneDev would have to \"\n\t\t\t+ \"first authenticate itself against the LDAP server and OneDev does that by sending 'manager' DN and \"\n\t\t\t+ \"password\")\n\t@NotEmpty\n\tpublic String getManagerDN() {\n\t\treturn managerDN;\n\t}\n\n\tpublic void setManagerDN(String managerDN) {\n\t\tthis.managerDN = managerDN;\n\t}\n\n\t@Editable(order=400, description=\"Specifies password of above manager DN\")\n\t@NotEmpty\n\t@Password\n\tpublic String getManagerPassword() {\n\t\treturn managerPassword;\n\t}\n\n\tpublic void setManagerPassword(String managerPassword) {\n\t\tthis.managerPassword = managerPassword;\n\t}\n\n\t@Editable(order=500, description=\n\t\t\t\"Specifies the base node for user search. For example: <i>ou=users, dc=example, dc=com</i>\")\n\t@NotEmpty\n\tpublic String getUserSearchBase() {\n\t\treturn userSearchBase;\n\t}\n\n\tpublic void setUserSearchBase(String userSearchBase) {\n\t\tthis.userSearchBase = userSearchBase;\n\t}\n\n\t@Editable(order=600, description=\n\t\t \"This filter is used to determine the LDAP entry for current user. \" + \n\t\t \"For example: <i>(&(uid={0})(objectclass=person))</i>. In this example, \" +\n\t\t \"<i>{0}</i> represents login name of current user.\")\n\t@NotEmpty\n\tpublic String getUserSearchFilter() {\n\t\treturn userSearchFilter;\n\t}\n\n\tpublic void setUserSearchFilter(String userSearchFilter) {\n\t\tthis.userSearchFilter = userSearchFilter;\n\t}\n\n\t@Editable(order=700, description=\"\"\n\t\t\t+ \"Optionally specifies name of the attribute inside the user LDAP entry whose value will be taken as user \"\n\t\t\t+ \"full name. This field is normally set to <i>displayName</i> according to RFC 2798. If left empty, full \"\n\t\t\t+ \"name of the user will not be retrieved\")\n\tpublic String getUserFullNameAttribute() {\n\t\treturn userFullNameAttribute;\n\t}\n\n\tpublic void setUserFullNameAttribute(String userFullNameAttribute) {\n\t\tthis.userFullNameAttribute = userFullNameAttribute;\n\t}\n\n\t@Editable(order=800, description=\"\"\n\t\t\t+ \"Specifies name of the attribute inside the user LDAP entry whose value will be taken as user \"\n\t\t\t+ \"email. This field is normally set to <i>mail</i> according to RFC 2798\")\n\t@NotEmpty\n\tpublic String getUserEmailAttribute() {\n\t\treturn userEmailAttribute;\n\t}\n\n\tpublic void setUserEmailAttribute(String userEmailAttribute) {\n\t\tthis.userEmailAttribute = userEmailAttribute;\n\t}\n\n\t@Editable(name=\"User SSH Key Attribute\", order=850, description=\"\"\n\t\t\t+ \"Optionally specify name of the attribute inside the user LDAP entry whose values will be taken as user \"\n\t\t\t+ \"SSH keys. SSH keys will be managed by LDAP only if this field is set\")\n\tpublic String getUserSshKeyAttribute() {\n\t\treturn userSshKeyAttribute;\n\t}\n\n\tpublic void setUserSshKeyAttribute(String userSshKeyAttribute) {\n\t\tthis.userSshKeyAttribute = userSshKeyAttribute;\n\t}\n\n\t@Editable(order=900, description=\"Specify the strategy to retrieve group membership information. \"\n\t\t\t+ \"To give appropriate permissions to a LDAP group, a OneDev group with same name should \"\n\t\t\t+ \"be defined. Use strategy <tt>Do Not Retrieve Groups</tt> if you want to manage group \"\n\t\t\t+ \"memberships at OneDev side\")\n\t@NotNull(message=\"may not be empty\")\n\tpublic GroupRetrieval getGroupRetrieval() {\n\t\treturn groupRetrieval;\n\t}\n\n\tpublic void setGroupRetrieval(GroupRetrieval groupRetrieval) {\n\t\tthis.groupRetrieval = groupRetrieval;\n\t}\n\n\t@Override\n\tpublic Authenticated authenticate(UsernamePasswordToken token) {\n\t\tString fullName = null;\n\t\tString email = null;\n\t\tCollection<String> groupNames = null;\n Collection<String> sshKeys = null;\n\n Name userSearchBase;\n\t\ttry {\n\t\t\tuserSearchBase = new CompositeName().add(getUserSearchBase());\n\t\t} catch (InvalidNameException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n String userSearchFilter = StringUtils.replace(getUserSearchFilter(), \"{0}\", token.getUsername());\n userSearchFilter = StringUtils.replace(userSearchFilter, \"\\\\\", \"\\\\\\\\\");\n logger.debug(\"Evaluated user search filter: \" + userSearchFilter);\n \n SearchControls searchControls = new SearchControls();\n searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);\n List<String> attributeNames = new ArrayList<String>();\n if (getUserFullNameAttribute() != null)\n attributeNames.add(getUserFullNameAttribute());\n \n if (getUserSshKeyAttribute() != null)\n \tattributeNames.add(getUserSshKeyAttribute());\n \n attributeNames.add(getUserEmailAttribute());\n \n if (getGroupRetrieval() instanceof GetGroupsUsingAttribute) {\n \tGetGroupsUsingAttribute groupRetrieval = (GetGroupsUsingAttribute)getGroupRetrieval();\n attributeNames.add(groupRetrieval.getUserGroupsAttribute());\n }\n searchControls.setReturningAttributes((String[]) attributeNames.toArray(new String[0]));\n searchControls.setReturningObjFlag(true);\n\n Hashtable<String, String> ldapEnv = new Hashtable<>();\n ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY, \"com.sun.jndi.ldap.LdapCtxFactory\");\n ldapEnv.put(Context.PROVIDER_URL, getLdapUrl());\n ldapEnv.put(Context.SECURITY_AUTHENTICATION, \"simple\");\n ldapEnv.put(\"com.sun.jndi.ldap.connect.timeout\", String.valueOf(getTimeout()*1000L));\n ldapEnv.put(\"com.sun.jndi.ldap.read.timeout\", String.valueOf(getTimeout()*1000L));\n ldapEnv.put(Context.REFERRAL, \"follow\");\n \n ldapEnv.put(Context.SECURITY_PRINCIPAL, getManagerDN());\n ldapEnv.put(Context.SECURITY_CREDENTIALS, getManagerPassword());\n\n DirContext ctx = null;\n DirContext referralCtx = null;\n try {\n logger.debug(\"Binding to ldap url '\" + getLdapUrl() + \"'...\");\n try {\n \tctx = new InitialDirContext(ldapEnv);\n } catch (AuthenticationException e) {\n \t\tthrow new RuntimeException(\"Can not bind to ldap server '\" + getLdapUrl() + \"': \" + e.getMessage());\n }\n NamingEnumeration<SearchResult> results = ctx.search(userSearchBase, userSearchFilter, searchControls);\n if (results == null || !results.hasMore()) \n throw new UnknownAccountException(\"Unknown account\");\n \n SearchResult searchResult = (SearchResult) results.next();\n String userDN = searchResult.getNameInNamespace();\n if (!searchResult.isRelative()) {\n \tStringBuffer buffer = new StringBuffer();\n buffer.append(StringUtils.substringBefore(searchResult.getName(), \"//\"));\n buffer.append(\"//\");\n buffer.append(StringUtils.substringBefore(\n \t\tStringUtils.substringAfter(searchResult.getName(), \"//\"), \"/\"));\n \n ldapEnv.put(Context.PROVIDER_URL, buffer.toString());\n logger.debug(\"Binding to referral ldap url '\" + buffer.toString() + \"'...\");\n referralCtx = new InitialDirContext(ldapEnv);\n }\n if (userDN.startsWith(\"ldap\")) {\n \tuserDN = StringUtils.substringAfter(userDN, \"//\");\n \tuserDN = StringUtils.substringAfter(userDN, \"/\");\n }\n\n ldapEnv.put(Context.SECURITY_PRINCIPAL, userDN);\n ldapEnv.put(Context.SECURITY_CREDENTIALS, new String(token.getPassword()));\n DirContext userCtx = null;\n try {\n logger.debug(\"Authenticating user by binding as '\" + userDN + \"'...\");\n userCtx = new InitialDirContext(ldapEnv);\n } catch (AuthenticationException e) {\n \tthrow new org.apache.shiro.authc.AuthenticationException(\"Unable to bind as '\" + userDN + \"'\", e);\n } finally {\n if (userCtx != null) {\n try {\n userCtx.close();\n } catch (NamingException e) {\n }\n }\n }\n\n Attributes searchResultAttributes = searchResult.getAttributes();\n \n if (searchResultAttributes != null) {\n if (getUserFullNameAttribute() != null) {\n Attribute attribute = searchResultAttributes.get(getUserFullNameAttribute());\n if (attribute != null && attribute.get() != null)\n fullName = (String) attribute.get();\n }\n \n Attribute attribute = searchResultAttributes.get(getUserEmailAttribute());\n if (attribute != null && attribute.get() != null)\n email = (String) attribute.get();\n \n if (getGroupRetrieval() instanceof GetGroupsUsingAttribute) \n \tgroupNames = retrieveGroupsByAttribute(ctx, referralCtx, searchResultAttributes);\n \n if (getUserSshKeyAttribute() != null) \n \tsshKeys = retrieveSshKeys(searchResultAttributes);\n }\n \n if (getGroupRetrieval() instanceof SearchGroupsUsingFilter) \n \tgroupNames = retrieveGroupsByFilter(ctx, referralCtx, userDN);\n \n if (StringUtils.isBlank(email))\n \tthrow new AccountException(\"Email is required but not available in ldap directory\");\n else\n \treturn new Authenticated(email, fullName, groupNames, sshKeys);\n } catch (NamingException e) {\n \tthrow new RuntimeException(e);\n } finally {\n if (ctx != null) {\n try {\n ctx.close();\n } catch (NamingException e) {\n }\n }\n if (referralCtx != null) {\n try {\n referralCtx.close();\n } catch (NamingException e) {\n }\n }\n }\n\t}\n\t\n\tprivate Collection<String> retrieveGroupsByAttribute(DirContext ctx, DirContext referralCtx, \n\t\t\tAttributes searchResultAttributes) {\n\t\tCollection<String> groupNames = new HashSet<>();\n\t\ttry {\n \tGetGroupsUsingAttribute groupRetrieval = (GetGroupsUsingAttribute) getGroupRetrieval();\n Attribute attribute = searchResultAttributes.get(groupRetrieval.getUserGroupsAttribute());\n if (attribute != null) {\n for (NamingEnumeration<?> e = attribute.getAll(); e.hasMore();) {\n\n \t// use composite name instead of DN according to\n // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4307193\n Name groupDN = new CompositeName().add((String) e.next());\n logger.debug(\"Looking up group entry '\" + groupDN + \"'...\");\n \n DirContext groupCtx = null;\n try {\n if (referralCtx != null)\n groupCtx = (DirContext) referralCtx.lookup(groupDN);\n else\n groupCtx = (DirContext) ctx.lookup(groupDN);\n\n if (groupCtx == null) {\n throw new RuntimeException(\"Can not find group entry \" +\n \t\t\"identified by '\" + groupDN + \"'.\");\n }\n String groupNameAttribute = groupRetrieval.getGroupNameAttribute();\n Attributes groupAttributes = groupCtx.getAttributes(\"\", \n \t\tnew String[]{groupNameAttribute});\n if (groupAttributes == null \n \t\t|| groupAttributes.get(groupNameAttribute) == null\n || groupAttributes.get(groupNameAttribute).get() == null) {\n throw new RuntimeException(\"Can not find attribute '\" \n \t\t+ groupNameAttribute + \"' in returned group entry.\");\n }\n groupNames.add((String) groupAttributes.get(groupNameAttribute).get());\n } finally {\n if (groupCtx != null) {\n try {\n groupCtx.close();\n } catch (NamingException ne) {\n }\n }\n }\n }\n } else {\n logger.warn(\"No attribute identified by '\" + groupRetrieval.getUserGroupsAttribute() \n \t\t+ \"' inside fetched user LDAP entry.\");\n }\n\t\t} catch (NamingException e) {\n\t\t\tlogger.error(\"Error retrieving groups by attribute\");\n\t\t}\n\t\treturn groupNames;\n\t}\n\t\n\tprivate Collection<String> retrieveGroupsByFilter(DirContext ctx, DirContext referralCtx, String userDN) {\n\t\tCollection<String> groupNames = new HashSet<>();\n\t\ttry {\n\t \tSearchGroupsUsingFilter groupRetrieval = (SearchGroupsUsingFilter) getGroupRetrieval();\n\t \tString groupNameAttribute = groupRetrieval.getGroupNameAttribute();\n\t Name groupSearchBase = new CompositeName().add(groupRetrieval.getGroupSearchBase());\n\t String groupSearchFilter = StringUtils.replace(groupRetrieval.getGroupSearchFilter(), \"{0}\", userDN);\n\t groupSearchFilter = StringUtils.replace(groupSearchFilter, \"\\\\\", \"\\\\\\\\\");\n\t\n\t logger.debug(\"Evaluated group search filter: \" + groupSearchFilter);\n\t SearchControls searchControls = new SearchControls();\n\t searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);\n\t searchControls.setReturningAttributes(new String[]{groupNameAttribute});\n\t searchControls.setReturningObjFlag(true);\n\t\n\t \tNamingEnumeration<SearchResult> results;\n\t if (referralCtx != null)\n\t results = referralCtx.search(groupSearchBase, groupSearchFilter, searchControls);\n\t else\n\t results = ctx.search(groupSearchBase, groupSearchFilter, searchControls);\n\t if (results != null) {\n\t while (results.hasMore()) {\n\t \tSearchResult searchResult = (SearchResult) results.next();\n\t Attributes searchResultAttributes = searchResult.getAttributes();\n\t if (searchResultAttributes == null \n\t \t\t|| searchResultAttributes.get(groupNameAttribute) == null\n\t || searchResultAttributes.get(groupNameAttribute).get() == null) {\n\t throw new RuntimeException(\"Can not find attribute '\" \n\t \t\t+ groupNameAttribute + \"' in the returned group object.\");\n\t }\n\t groupNames.add((String) searchResultAttributes.get(groupNameAttribute).get());\n\t }\n\t }\n } catch (PartialResultException pre) {\n logger.warn(\"Partial exception detected. You may try to set property \" +\n \t\t\"'follow referrals' to true to avoid this exception.\", pre);\n\t\t} catch (NamingException e) {\n\t\t\tlogger.error(\"Error retrieving groups by filter\", e);\n\t\t}\n\t\treturn groupNames;\n\t}\n\n\t@Nullable\n\tprivate Collection<String> retrieveSshKeys(Attributes searchResultAttributes) {\n\t\tAttribute attribute = searchResultAttributes.get(getUserSshKeyAttribute());\n\t\tif (attribute != null) {\n\t\t\tCollection<String> sshKeys = new ArrayList<>();\n\t\t\ttry {\n\t\t\t\tNamingEnumeration<?> ldapValues = attribute.getAll();\n\t\t\t\twhile (ldapValues.hasMore()) {\n\t\t\t\t\tObject value = ldapValues.next();\n\t\t\t\t\tif (value instanceof String) \n\t\t\t\t\t\tsshKeys.add((String) value);\n\t\t\t\t\telse \n\t\t\t\t\t\tlogger.error(\"SSH key from ldap is not a String\");\n\t\t\t\t}\n\n\t\t\t} catch (NamingException e) {\n\t\t\t\tlogger.error(\"Error retrieving SSH keys\", e);\n\t\t\t}\n\t\t\treturn sshKeys;\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isManagingMemberships() {\n\t\treturn !(getGroupRetrieval() instanceof DoNotRetrieveGroups);\n\t}\n\n\t@Override\n\tpublic boolean isManagingSshKeys() {\n\t\treturn getUserSshKeyAttribute() != null;\n\t}\n\t\n}\n\n", "input": "", "output": "【漏洞类型】LDAP注入漏洞\n【严重性】High\n【漏洞摘要】这是一个潜在的信息泄露漏洞,由于未对用户输入进行适当的转义处理,攻击者可以通过构造特殊的用户名来注入恶意的LDAP查询语句。具体表现为在`authenticate`方法中,用户输入的用户名(`token.getUsername()`)直接插入到`userSearchFilter`中,而没有进行任何逃逸处理。这使得攻击者可以利用特殊字符(如`*`、`(`、`)`、`\\`)来改变查询语句的含义,从而可能检索到未经授权的信息。\n【关键代码模式】与此漏洞相关的关键代码模式包括:\n- `StringUtils.replace(getUserSearchFilter(), \"{0}\", token.getUsername())`:该行代码将用户输入的用户名插入到LDAP搜索过滤器中。\n- `escape`函数:这是一个用于转义LDAP特殊字符的函数,修复代码中添加了这一函数来防止LDAP注入。\n【修复方法说明】修复方法通过添加`escape`函数来转义用户输入中的LDAP特殊字符,从而防止攻击者通过构造恶意用户名来注入恶意的LDAP查询语句。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nservices/vm-service/backend/src/main/java/com/fit2cloud/provider/impl/huawei/api/HuaweiSyncCloudApi.java\n### 代码内容:\npackage com.fit2cloud.provider.impl.huawei.api;\n\nimport com.aliyun.tea.TeaException;\nimport com.fit2cloud.common.exception.Fit2cloudException;\nimport com.fit2cloud.common.log.utils.LogUtil;\nimport com.fit2cloud.common.provider.entity.F2CEntityType;\nimport com.fit2cloud.common.provider.entity.F2CPerfMetricMonitorData;\nimport com.fit2cloud.common.provider.exception.ReTryException;\nimport com.fit2cloud.common.provider.exception.SkipPageException;\nimport com.fit2cloud.common.provider.util.CommonUtil;\nimport com.fit2cloud.common.provider.util.PageUtil;\nimport com.fit2cloud.common.utils.DateUtil;\nimport com.fit2cloud.common.utils.JsonUtil;\nimport com.fit2cloud.constants.ErrorCodeConstants;\nimport com.fit2cloud.provider.constants.*;\nimport com.fit2cloud.provider.entity.F2CDisk;\nimport com.fit2cloud.provider.entity.F2CImage;\nimport com.fit2cloud.provider.entity.F2CVirtualMachine;\nimport com.fit2cloud.provider.entity.request.BaseDiskRequest;\nimport com.fit2cloud.provider.entity.request.GetMetricsRequest;\nimport com.fit2cloud.provider.impl.huawei.constants.HuaweiDiskType;\nimport com.fit2cloud.provider.impl.huawei.constants.HuaweiPerfMetricConstants;\nimport com.fit2cloud.provider.impl.huawei.entity.*;\nimport com.fit2cloud.provider.impl.huawei.entity.credential.HuaweiVmCredential;\nimport com.fit2cloud.provider.impl.huawei.entity.request.*;\nimport com.fit2cloud.provider.impl.huawei.util.HuaweiMappingUtil;\nimport com.google.gson.Gson;\nimport com.huaweicloud.sdk.bss.v2.BssClient;\nimport com.huaweicloud.sdk.bss.v2.model.*;\nimport com.huaweicloud.sdk.ces.v1.CesClient;\nimport com.huaweicloud.sdk.ces.v1.model.*;\nimport com.huaweicloud.sdk.ecs.v2.EcsClient;\nimport com.huaweicloud.sdk.ecs.v2.model.*;\nimport com.huaweicloud.sdk.evs.v2.EvsClient;\nimport com.huaweicloud.sdk.evs.v2.model.ShowJobRequest;\nimport com.huaweicloud.sdk.evs.v2.model.ShowJobResponse;\nimport com.huaweicloud.sdk.evs.v2.model.*;\nimport com.huaweicloud.sdk.iam.v3.IamClient;\nimport com.huaweicloud.sdk.iam.v3.model.AuthProjectResult;\nimport com.huaweicloud.sdk.iam.v3.model.KeystoneListAuthProjectsRequest;\nimport com.huaweicloud.sdk.iam.v3.model.KeystoneListAuthProjectsResponse;\nimport com.huaweicloud.sdk.ims.v2.ImsClient;\nimport com.huaweicloud.sdk.ims.v2.model.ImageInfo;\nimport com.huaweicloud.sdk.ims.v2.model.ListImagesRequest;\nimport com.huaweicloud.sdk.ims.v2.model.ListImagesResponse;\nimport com.huaweicloud.sdk.vpc.v2.VpcClient;\nimport com.huaweicloud.sdk.vpc.v2.model.*;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.ObjectUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.BeanUtils;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * @Author:张少虎\n * @Date: 2022/9/22 2:44 PM\n * @Version 1.0\n * @注释:\n */\npublic class HuaweiSyncCloudApi {\n private static final Logger logger = LoggerFactory.getLogger(HuaweiSyncCloudApi.class);\n private static final int WAIT_COUNT = 50;\n private static final int SLEEP_TIME = 10000;\n\n /**\n * 获取华为云云主机数据\n *\n * @param listVirtualMachineRequest 获取华为云云主机请求对象\n * @return 云主机对象\n */\n public static List<F2CVirtualMachine> listVirtualMachine(ListVirtualMachineRequest listVirtualMachineRequest) {\n if (StringUtils.isEmpty(listVirtualMachineRequest.getRegionId())) {\n throw new Fit2cloudException(10002, \"区域为必填参数\");\n }\n if (StringUtils.isNotEmpty(listVirtualMachineRequest.getCredential())) {\n HuaweiVmCredential credential = JsonUtil.parseObject(listVirtualMachineRequest.getCredential(), HuaweiVmCredential.class);\n listVirtualMachineRequest.setLimit(PageUtil.DefaultPageSize);\n listVirtualMachineRequest.setOffset(PageUtil.DefaultCurrentPage);\n EcsClient ecsClient = credential.getEcsClient(listVirtualMachineRequest.getRegionId());\n // 分页查询云主机列表\n List<ServerDetail> instances = PageUtil.page(listVirtualMachineRequest, req -> listServersDetails(ecsClient, req),\n ListServersDetailsResponse::getServers,\n (req, res) -> req.getLimit() <= res.getServers().size(),\n req -> req.setOffset(req.getOffset() + 1));\n if (CollectionUtils.isNotEmpty(instances)) {\n List<Port> ports = listPorts(listVirtualMachineRequest.getCredential(), listVirtualMachineRequest.getRegionId());\n return instances.stream().map(server -> {\n F2CVirtualMachine virtualMachine = HuaweiMappingUtil.toF2CVirtualMachine(server, ports);\n // 获取包年包月机器的到期时间\n if (F2CChargeType.PRE_PAID.equalsIgnoreCase(virtualMachine.getInstanceChargeType())) {\n appendExpiredTime(credential, server, virtualMachine);\n }\n return virtualMachine;\n }).map(virtualMachine -> {\n virtualMachine.setRegion(listVirtualMachineRequest.getRegionId());\n return virtualMachine;\n }).map(virtualMachine -> appendDisk(listVirtualMachineRequest.getCredential(), listVirtualMachineRequest.getRegionId(), virtualMachine))\n .toList();\n }\n }\n return new ArrayList<>();\n }\n\n /**\n * 获取磁盘\n *\n * @param request 请求磁盘参数\n * @return 响应对象\n */\n public static List<F2CDisk> listDisk(ListDisksRequest request) {\n if (StringUtils.isNotEmpty(request.getCredential())) {\n HuaweiVmCredential credential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n EvsClient evsClient = credential.getEvsClient(request.getRegionId());\n try {\n ListVolumesResponse listVolumesResponse = evsClient.listVolumes(request);\n List<VolumeDetail> volumes = listVolumesResponse.getVolumes();\n return volumes.stream().map(HuaweiMappingUtil::toF2CDisk).toList();\n } catch (Exception e) {\n ReTryException.throwHuaweiReTry(e);\n SkipPageException.throwHuaweiSkip(e);\n throw new Fit2cloudException(10000, \"获取数据失败\" + e.getMessage());\n }\n }\n return new ArrayList<>();\n }\n\n /**\n * 同步镜像\n *\n * @param request 请求对象\n * @return 响应对象\n */\n public static List<F2CImage> lisImages(ListImageRequest request) {\n if (StringUtils.isNotEmpty(request.getCredential())) {\n HuaweiVmCredential credential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n ImsClient imsClient = credential.getImsClient(request.getRegionId());\n try {\n // 只查询公共镜像gold\n request.setImagetype(ListImagesRequest.ImagetypeEnum.GOLD);\n ListImagesResponse listImagesResponse = imsClient.listImages(request);\n List<ImageInfo> images = listImagesResponse.getImages();\n return images.stream().map(imageInfo -> HuaweiMappingUtil.toF2CImage(imageInfo, request.getRegionId())).filter(Objects::nonNull).toList();\n } catch (Exception e) {\n ReTryException.throwHuaweiReTry(e);\n SkipPageException.throwHuaweiSkip(e);\n throw new Fit2cloudException(10000, \"获取数据失败\" + e.getMessage());\n }\n }\n return new ArrayList<>();\n }\n\n /**\n * 给云主机添加磁盘数据\n *\n * @param credential 认证信息\n * @param regionId 区域信息\n * @param virtualMachine 云主机对象\n * @return 云主机对象\n */\n private static F2CVirtualMachine appendDisk(String credential, String regionId, F2CVirtualMachine virtualMachine) {\n ListDisksRequest listDisksRequest = new ListDisksRequest();\n listDisksRequest.setCredential(credential);\n listDisksRequest.setRegionId(regionId);\n List<F2CDisk> disks = listDisk(listDisksRequest);\n long sum = disks.stream().mapToLong(F2CDisk::getSize).sum();\n virtualMachine.setDisk((int) sum);\n return virtualMachine;\n }\n\n /**\n * 给云主机增加到期时间\n *\n * @param credential\n * @param serverDetail\n * @param virtualMachine\n */\n private static void appendExpiredTime(HuaweiVmCredential credential, ServerDetail serverDetail, F2CVirtualMachine virtualMachine) {\n String orderId = serverDetail.getMetadata().get(\"metering.order_id\");\n String productId = serverDetail.getMetadata().get(\"metering.product_id\");\n ShowCustomerOrderDetailsResponse response = getOrderDetailsById(orderId, credential.getBssClient());\n if (CollectionUtils.isNotEmpty(response.getOrderLineItems())) {\n String expireTime = response.getOrderLineItems().stream().filter(orderLineItemEntityV2 ->\n orderLineItemEntityV2.getProductId().equalsIgnoreCase(productId)\n ).collect(Collectors.toList()).get(0).getExpireTime();\n virtualMachine.setExpiredTime(new Date(CommonUtil.getUTCTime(expireTime, \"yyyy-MM-dd'T'HH:mm:ss'Z'\")).getTime());\n }\n }\n\n /**\n * 获取port列表\n *\n * @param credential 认证信息\n * @param regionId 区域id\n * @return port对象\n */\n private static List<Port> listPorts(String credential, String regionId) {\n try {\n HuaweiVmCredential huaweiVmCredential = JsonUtil.parseObject(credential, HuaweiVmCredential.class);\n VpcClient vpcClient = huaweiVmCredential.getVpcClient(regionId);\n ListPortsRequest listPortsRequest = new ListPortsRequest();\n ListPortsResponse listPortsResponse = vpcClient.listPorts(listPortsRequest);\n return listPortsResponse.getPorts();\n } catch (Exception e) {\n return new ArrayList<>();\n }\n }\n\n /**\n * @param ecsClient ecs客户端\n * @param req 请求对象\n * @return ListServersDetailsResponse\n */\n private static ListServersDetailsResponse listServersDetails(EcsClient ecsClient, ListServersDetailsRequest req) {\n try {\n return ecsClient.listServersDetails(req);\n } catch (Exception e) {\n ReTryException.throwHuaweiReTry(e);\n SkipPageException.throwHuaweiSkip(e);\n throw new Fit2cloudException(10000, \"获取数据失败\" + e.getMessage());\n }\n }\n\n public static boolean powerOff(HuaweiInstanceRequest request) {\n if (StringUtils.isEmpty(request.getRegionId())) {\n throw new Fit2cloudException(10002, \"区域为必填参数\");\n }\n if (StringUtils.isNotEmpty(request.getCredential())) {\n HuaweiVmCredential credential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n EcsClient client = credential.getEcsClient(request.getRegionId());\n\n try {\n ServerDetail serverDetail = getInstanceById(request.getUuid(), client);\n if (F2CInstanceStatus.Stopped.name().equalsIgnoreCase(HuaweiMappingUtil.toF2CInstanceStatus(serverDetail.getStatus()))) {\n return true;\n }\n BatchStopServersResponse batchStopServersResponse = client.batchStopServers(new BatchStopServersRequest()\n .withBody(new BatchStopServersRequestBody()\n .withOsStop(\n new BatchStopServersOption()\n .withServers(Arrays.asList(new ServerId().withId(request.getUuid())))\n .withType(request.getForce() ? BatchStopServersOption.TypeEnum.HARD : BatchStopServersOption.TypeEnum.SOFT))));\n\n checkEcsJobStatus(client, batchStopServersResponse.getJobId());\n return true;\n } catch (TeaException teaException) {\n throw new Fit2cloudException(ErrorCodeConstants.VM_POWER_OFF_FAIL.getCode(), teaException.getMessage());\n } catch (Exception e) {\n TeaException error = new TeaException(e.getMessage(), e);\n throw new Fit2cloudException(ErrorCodeConstants.VM_POWER_OFF_FAIL.getCode(), error.getMessage());\n }\n }\n return false;\n }\n\n public static boolean powerOn(HuaweiInstanceRequest request) {\n if (StringUtils.isEmpty(request.getRegionId())) {\n throw new Fit2cloudException(10002, \"区域为必填参数\");\n }\n if (StringUtils.isNotEmpty(request.getCredential())) {\n HuaweiVmCredential credential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n EcsClient client = credential.getEcsClient(request.getRegionId());\n try {\n ServerDetail serverDetail = getInstanceById(request.getUuid(), client);\n if (F2CInstanceStatus.Running.name().equalsIgnoreCase(HuaweiMappingUtil.toF2CInstanceStatus(serverDetail.getStatus()))) {\n return true;\n }\n BatchStartServersResponse batchStartServersResponse = client.batchStartServers(new BatchStartServersRequest()\n .withBody(new BatchStartServersRequestBody()\n .withOsStart(\n new BatchStartServersOption()\n .withServers(Arrays.asList(new ServerId().withId(request.getUuid()))))));\n checkEcsJobStatus(client, batchStartServersResponse.getJobId());\n return true;\n } catch (TeaException teaError) {\n throw new Fit2cloudException(ErrorCodeConstants.VM_POWER_ON_FAIL.getCode(), teaError.getMessage());\n } catch (Exception e) {\n TeaException error = new TeaException(e.getMessage(), e);\n throw new Fit2cloudException(ErrorCodeConstants.VM_POWER_ON_FAIL.getCode(), error.getMessage());\n }\n }\n return false;\n }\n\n public static boolean rebootInstance(HuaweiInstanceRequest request) {\n if (StringUtils.isEmpty(request.getRegionId())) {\n throw new Fit2cloudException(10002, \"区域为必填参数\");\n }\n if (StringUtils.isNotEmpty(request.getCredential())) {\n HuaweiVmCredential credential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n EcsClient client = credential.getEcsClient(request.getRegionId());\n try {\n BatchRebootServersResponse batchRebootServersResponse = client.batchRebootServers(new BatchRebootServersRequest()\n .withBody(new BatchRebootServersRequestBody()\n .withReboot(\n new BatchRebootSeversOption()\n .withServers(Arrays.asList(new ServerId().withId(request.getUuid())))\n .withType(request.getForce() ? BatchRebootSeversOption.TypeEnum.HARD : BatchRebootSeversOption.TypeEnum.SOFT))));\n checkEcsJobStatus(client, batchRebootServersResponse.getJobId());\n return true;\n } catch (TeaException teaException) {\n throw new Fit2cloudException(ErrorCodeConstants.VM_REBOOT_FAIL.getCode(), teaException.getMessage());\n } catch (Exception e) {\n TeaException error = new TeaException(e.getMessage(), e);\n throw new Fit2cloudException(ErrorCodeConstants.VM_REBOOT_FAIL.getCode(), error.getMessage());\n }\n }\n return false;\n }\n\n public static boolean deleteInstance(HuaweiInstanceRequest request) {\n if (StringUtils.isEmpty(request.getRegionId())) {\n throw new Fit2cloudException(10002, \"区域为必填参数\");\n }\n if (StringUtils.isNotEmpty(request.getCredential())) {\n HuaweiVmCredential credential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n EcsClient client = credential.getEcsClient(request.getRegionId());\n try {\n ShowServerResponse showServerResponse = client.showServer(new ShowServerRequest().withServerId(request.getUuid()));\n ServerDetail instance = showServerResponse.getServer();\n // 如果是包年包月 调用退订方法 否则直接删除\n Map<String, String> metadata = instance.getMetadata();\n if (ObjectUtils.isNotEmpty(metadata) && metadata.containsKey(\"charging_mode\") && StringUtils.equals(metadata.get(\"charging_mode\"), \"1\")) {\n BssClient bssClient = credential.getBssClient();\n CancelResourcesSubscriptionRequest cancelRequest = new CancelResourcesSubscriptionRequest();\n UnsubscribeResourcesReq body = new UnsubscribeResourcesReq();\n body.setResourceIds(new ArrayList<String>() {{\n add(request.getUuid());\n }});\n body.setUnsubscribeType(1);\n cancelRequest.setBody(body);\n CancelResourcesSubscriptionResponse cancelResponse = bssClient.cancelResourcesSubscription(cancelRequest);\n return cancelResponse.getHttpStatusCode() == 200;\n } else {\n DeleteServersResponse batchStartServersResponse = client.deleteServers(new DeleteServersRequest()\n .withBody(new DeleteServersRequestBody()\n .withServers(Arrays.asList(new ServerId().withId(request.getUuid())))));\n checkEcsJobStatus(client, batchStartServersResponse.getJobId());\n return true;\n }\n } catch (TeaException teaException) {\n throw new Fit2cloudException(ErrorCodeConstants.VM_DELETE_FAIL.getCode(), teaException.getMessage());\n } catch (Exception e) {\n TeaException error = new TeaException(e.getMessage(), e);\n throw new Fit2cloudException(ErrorCodeConstants.VM_DELETE_FAIL.getCode(), error.getMessage());\n }\n }\n return false;\n }\n\n private static void checkEcsJobStatus(EcsClient client, String jobId) {\n int count = 0;\n while (true) {\n try {\n Thread.sleep(SLEEP_TIME);\n com.huaweicloud.sdk.ecs.v2.model.ShowJobResponse jobResponse = client.showJob(new com.huaweicloud.sdk.ecs.v2.model.ShowJobRequest().withJobId(jobId));\n com.huaweicloud.sdk.ecs.v2.model.ShowJobResponse.StatusEnum status = jobResponse.getStatus();\n if (ShowJobResponse.StatusEnum.SUCCESS.getValue().equals(status.getValue())) {\n break;\n }\n if (ShowJobResponse.StatusEnum.FAIL.getValue().equals(status.getValue())) {\n throw new RuntimeException(jobResponse.getFailReason());\n }\n if (count >= WAIT_COUNT) {\n throw new RuntimeException(\"Check cloud server status timeout!\");\n }\n count++;\n } catch (Exception e) {\n throw new RuntimeException(\"Check cloud server status error: \" + e.getMessage());\n }\n }\n }\n\n /**\n * 根据可用区过滤磁盘种类\n *\n * @param request\n * @return\n */\n public static List<Map<String, String>> getDiskTypes(HuaweiGetDiskTypeRequest request) {\n HuaweiVmCredential huaweiVmCredential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n EvsClient evsClient = huaweiVmCredential.getEvsClient(request.getRegionId());\n\n CinderListVolumeTypesRequest cinderListVolumeTypesRequest = new CinderListVolumeTypesRequest();\n try {\n CinderListVolumeTypesResponse response = evsClient.cinderListVolumeTypes(cinderListVolumeTypesRequest);\n List<Map<String, String>> mapList = new ArrayList<>();\n response.getVolumeTypes().forEach(volumeType -> {\n if (StringUtils.isNoneEmpty(request.getZone())\n //这个名称的磁盘类型有问题,云上显示没有,但是接口会返回来,在这里特殊处理去掉\n && !StringUtils.equalsIgnoreCase(\"uh-l1\", volumeType.getName())\n && StringUtils.isNoneEmpty(volumeType.getExtraSpecs().getReSKEYAvailabilityZones())\n && volumeType.getExtraSpecs().getReSKEYAvailabilityZones().contains(request.getZone())\n && (StringUtils.isEmpty(volumeType.getExtraSpecs().getOsVendorExtendedSoldOutAvailabilityZones())\n || !volumeType.getExtraSpecs().getOsVendorExtendedSoldOutAvailabilityZones().contains(request.getZone())) && !volumeType.getName().startsWith(\"DESS_\")) {\n Map<String, String> vol = new HashMap<>();\n vol.put(\"id\", volumeType.getName());\n vol.put(\"name\", HuaweiDiskType.getName(volumeType.getName()));\n mapList.add(vol);\n }\n });\n return mapList;\n } catch (Exception e) {\n e.printStackTrace();\n }\n return new ArrayList<>();\n }\n\n /**\n * 创建磁盘\n *\n * @param request\n * @return\n */\n public static List<F2CDisk> createDisks(HuaweiCreateDisksRequest request) {\n List<F2CDisk> f2CDisks = new ArrayList<>();\n HuaweiVmCredential huaweiVmCredential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n EvsClient evsClient = huaweiVmCredential.getEvsClient(request.getRegionId());\n try {\n for (F2CDisk disk : request.getDisks()) {\n CreateVolumeResponse response = evsClient.createVolume(request.toCreateVolumeRequest(disk));\n ShowJobResponse showJobResponse = getJob(response.getJobId(), evsClient);\n String status = request.getInstanceUuid() == null ? F2CDiskStatus.AVAILABLE : \"in-use\";\n F2CDisk createdDisk = HuaweiMappingUtil.toF2CDisk(checkVolumeStatus(showJobResponse.getEntities().getVolumeId(), evsClient, status));\n createdDisk.setDeleteWithInstance(disk.getDeleteWithInstance());\n f2CDisks.add(createdDisk);\n }\n return f2CDisks;\n } catch (Exception e) {\n throw new RuntimeException(e.getMessage(), e);\n }\n }\n\n /**\n * 创建磁盘\n */\n public static F2CDisk createDisk(HuaweiCreateDiskRequest request) {\n HuaweiVmCredential huaweiVmCredential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n EvsClient evsClient = huaweiVmCredential.getEvsClient(request.getRegionId());\n try {\n CreateVolumeResponse response = evsClient.createVolume(request.toCreateVolumeRequest());\n String volumeId;\n // 华为云的 in-use 是中划线😭\n String status = request.getInstanceUuid() == null ? F2CDiskStatus.AVAILABLE : \"in-use\";\n if (StringUtils.isNotEmpty(response.getOrderId())) {\n volumeId = checkOrderResourceId(response.getOrderId(), huaweiVmCredential.getBssClient());\n } else {\n ShowJobResponse showJobResponse = getJob(response.getJobId(), evsClient);\n volumeId = showJobResponse.getEntities().getVolumeId();\n }\n F2CDisk createdDisk = HuaweiMappingUtil.toF2CDisk(checkVolumeStatus(volumeId, evsClient, status));\n\n // 单独调用接口设置磁盘是否随实例删除属性,不抛出异常\n if (DeleteWithInstance.YES.name().equalsIgnoreCase(request.getDeleteWithInstance())) {\n try {\n EcsClient ecsClient = huaweiVmCredential.getEcsClient(request.getRegionId());\n updateServerBlockDevice(ecsClient, request.getInstanceUuid(), createdDisk.getDiskId(), request.getDeleteWithInstance());\n createdDisk.setDeleteWithInstance(DeleteWithInstance.YES.name());\n } catch (Exception e) {\n createdDisk.setDeleteWithInstance(DeleteWithInstance.NO.name());\n LogUtil.error(\"Failed to modify disk.\" + e.getMessage(), e);\n }\n }\n\n return createdDisk;\n } catch (Exception e) {\n throw new RuntimeException(e.getMessage(), e);\n }\n }\n\n public static String checkOrderResourceId(String orderId, BssClient bssClient) {\n ListPayPerUseCustomerResourcesRequest resourceRequest = new ListPayPerUseCustomerResourcesRequest();\n QueryResourcesReq body = new QueryResourcesReq();\n body.setOrderId(orderId);\n resourceRequest.withBody(body);\n String resourceId = null;\n try {\n int count = 0;\n boolean b = true;\n while (b) {\n Thread.sleep(5000);\n count++;\n ListPayPerUseCustomerResourcesResponse resourcesResponse = bssClient.listPayPerUseCustomerResources(resourceRequest);\n List<OrderInstanceV2> disksInfo = resourcesResponse.getData();\n if (CollectionUtils.isNotEmpty(disksInfo)) {\n b = false;\n resourceId = disksInfo.get(0).getResourceId();\n }\n if (count >= WAIT_COUNT) {\n throw new RuntimeException(\"Check order resource info timeout!\");\n }\n }\n return resourceId;\n } catch (Exception e) {\n e.printStackTrace();\n throw new RuntimeException(e.getMessage(), e);\n }\n }\n\n /**\n * 扩容磁盘\n *\n * @param request\n * @return\n */\n public static boolean enlargeDisk(HuaweiResizeDiskRequest request) {\n try {\n HuaweiVmCredential huaweiVmCredential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n EvsClient evsClient = huaweiVmCredential.getEvsClient(request.getRegionId());\n\n String diskId = request.getDiskId();\n ShowVolumeRequest showVolumeRequest = new ShowVolumeRequest();\n showVolumeRequest.setVolumeId(diskId);\n VolumeDetail volume = evsClient.showVolume(showVolumeRequest).getVolume();\n if (volume == null) {\n throw new RuntimeException(\"Can not find disk: \" + request.getDiskId());\n }\n String status = volume.getStatus();\n evsClient.resizeVolume(request.toResizeVolumeRequest());\n if (\"in-use\".equalsIgnoreCase(status) || status.equalsIgnoreCase(F2CDiskStatus.AVAILABLE)) {\n checkVolumeStatus(diskId, evsClient, status);\n }\n return true;\n } catch (Exception e) {\n throw new RuntimeException(e.getMessage(), e);\n }\n }\n\n /**\n * 挂载磁盘\n *\n * @param request\n * @return\n */\n public static F2CDisk attachDisk(HuaweiAttachDiskRequest request) {\n try {\n HuaweiVmCredential huaweiVmCredential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n EvsClient evsClient = huaweiVmCredential.getEvsClient(request.getRegionId());\n EcsClient ecsClient = huaweiVmCredential.getEcsClient(request.getRegionId());\n ecsClient.attachServerVolume(request.toAttachServerVolumeRequest());\n F2CDisk f2CDisk = HuaweiMappingUtil.toF2CDisk(checkVolumeStatus(request.getDiskId(), evsClient, \"in-use\"));\n\n // 单独调用接口设置磁盘是否随实例删除属性,不抛出异常\n if (DeleteWithInstance.YES.name().equalsIgnoreCase(request.getDeleteWithInstance())) {\n try {\n updateServerBlockDevice(ecsClient, request.getInstanceUuid(), request.getDiskId(), request.getDeleteWithInstance());\n f2CDisk.setDeleteWithInstance(DeleteWithInstance.YES.name());\n } catch (Exception e) {\n f2CDisk.setDeleteWithInstance(DeleteWithInstance.NO.name());\n LogUtil.error(\"Failed to modify disk.\" + e.getMessage(), e);\n }\n }\n\n return f2CDisk;\n } catch (Exception e) {\n throw new RuntimeException(e.getMessage(), e);\n }\n }\n\n /**\n * 修改云服务器挂载的单个磁盘信息(是否随实例删除属性)\n *\n * @param client\n * @return\n */\n public static void updateServerBlockDevice(EcsClient client, String instanceId, String diskId, String deleteWithInstance) {\n if (StringUtils.isNotEmpty(instanceId)) {\n UpdateServerBlockDeviceRequest blockDeviceRequest =\n new UpdateServerBlockDeviceRequest()\n .withServerId(instanceId)\n .withVolumeId(diskId)\n .withBody(new UpdateServerBlockDeviceReq()\n .withBlockDevice(new UpdateServerBlockDeviceOption()\n .withDeleteOnTermination(DeleteWithInstance.YES.name().equals(deleteWithInstance))));\n client.updateServerBlockDevice(blockDeviceRequest);\n }\n }\n\n /**\n * 卸载磁盘\n *\n * @param request\n * @return\n */\n public static boolean detachDisk(HuaweiDetachDiskRequest request) {\n try {\n HuaweiVmCredential huaweiVmCredential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n EvsClient evsClient = huaweiVmCredential.getEvsClient(request.getRegionId());\n EcsClient ecsClient = huaweiVmCredential.getEcsClient(request.getRegionId());\n\n // 判断磁盘是否是系统盘\n ShowVolumeRequest showVolumeRequest = new ShowVolumeRequest();\n showVolumeRequest.setVolumeId(request.getDiskId());\n VolumeDetail volume = evsClient.showVolume(showVolumeRequest).getVolume();\n Optional.ofNullable(volume).orElseThrow(() -> new RuntimeException(\"Can not find the disk!\"));\n\n if (Boolean.valueOf(volume.getBootable())) {\n // 判断实例是否是关机状态\n ShowServerResponse showServerResponse = ecsClient.showServer(new ShowServerRequest().withServerId(request.getInstanceUuid()));\n ServerDetail server = showServerResponse.getServer();\n Optional.ofNullable(server).orElseThrow(() -> new RuntimeException(\"Can not find the server!\"));\n\n String serverStatus = server.getStatus();\n if (!\"stopped\".equalsIgnoreCase(serverStatus)) {\n // 系统盘需要实例关机方可卸载\n throw new RuntimeException(\"Server status must be stopped!\");\n }\n }\n\n ecsClient.detachServerVolume(request.toDetachServerVolumeRequest());\n checkVolumeStatus(request.getDiskId(), evsClient, F2CDiskStatus.AVAILABLE);\n return true;\n } catch (Exception e) {\n throw new RuntimeException(\"Failed to detach data disk!\" + e.getMessage(), e);\n }\n }\n\n /**\n * 删除磁盘\n *\n * @param request\n * @return\n */\n public static boolean deleteDisk(HuaweiDeleteDiskRequest request) {\n try {\n HuaweiVmCredential huaweiVmCredential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n EvsClient evsClient = huaweiVmCredential.getEvsClient(request.getRegionId());\n evsClient.deleteVolume(request.toDeleteVolumeRequest());\n return true;\n } catch (Exception e) {\n throw new RuntimeException(e.getMessage(), e);\n }\n }\n\n private static VolumeDetail checkVolumeStatus(String volumeId, EvsClient evsClient, String status) throws Exception {\n try {\n int count = 0;\n VolumeDetail volume = null;\n boolean b = true;\n while (b) {\n Thread.sleep(5000);\n count++;\n ShowVolumeRequest showVolumeRequest = new ShowVolumeRequest();\n showVolumeRequest.setVolumeId(volumeId);\n volume = evsClient.showVolume(showVolumeRequest).getVolume();\n\n if (volume != null && status.equalsIgnoreCase(volume.getStatus())) {\n b = false;\n }\n if (count >= WAIT_COUNT) {\n throw new RuntimeException(\"Check cloud disk status timeout!\");\n }\n }\n return volume;\n } catch (Exception e) {\n e.printStackTrace();\n throw new RuntimeException(e.getMessage(), e);\n }\n }\n\n private static ShowJobResponse getJob(String jobId, EvsClient evsClient) {\n com.huaweicloud.sdk.evs.v2.model.ShowJobRequest showJobRequest = new ShowJobRequest();\n showJobRequest.setJobId(jobId);\n try {\n int count = 0;\n while (true) {\n Thread.sleep(2000);\n count++;\n ShowJobResponse showJobResponse = evsClient.showJob(showJobRequest);\n if (\"FAIL\".equalsIgnoreCase(showJobResponse.getStatus().getValue())) {\n throw new RuntimeException(new Gson().toJson(showJobResponse));\n }\n if (\"SUCCESS\".equalsIgnoreCase(showJobResponse.getStatus().getValue())) {\n return showJobResponse;\n }\n if (count >= WAIT_COUNT) {\n throw new RuntimeException(\"Check job status timeout!\");\n }\n }\n } catch (Exception e) {\n throw new RuntimeException(e.getMessage(), e);\n }\n }\n\n public static List<F2CPerfMetricMonitorData> getF2CPerfMetricList(GetMetricsRequest getMetricsRequest) {\n existRegion(getMetricsRequest);\n List<F2CPerfMetricMonitorData> result = new ArrayList<>();\n //设置时间,根据syncTimeStampStr,默认一个小时\n Long startTime = DateUtil.beforeOneHourToTimestamp(Long.valueOf(getMetricsRequest.getSyncTimeStampStr()));\n //多获取过去30分钟的数据,防止同步线程时间不固定,导致数据不全的问题\n getMetricsRequest.setStartTime(String.valueOf(startTime - 1800000L));\n getMetricsRequest.setEndTime(getMetricsRequest.getSyncTimeStampStr());\n try {\n getMetricsRequest.setRegionId(getMetricsRequest.getRegionId());\n getMetricsRequest.setPeriod(300);\n result.addAll(getVmPerfMetric(getMetricsRequest));\n } catch (Exception e) {\n throw new SkipPageException(100021, \"获取监控数据失败-\" + getMetricsRequest.getRegionId() + \"-\" + e.getMessage());\n }\n return result;\n }\n\n private static void existRegion(GetMetricsRequest getMetricsRequest) {\n if (StringUtils.isEmpty(getMetricsRequest.getRegionId())) {\n throw new Fit2cloudException(10002, \"区域为必填参数\");\n }\n }\n\n /**\n * 获取虚拟机监控指标数据\n * 除了CPU,内存,磁盘与网络都是基础指标的数据,因为API无法获取\n *\n * @param getMetricsRequest 监控查询参数\n * @return 监控数据\n */\n private static List<F2CPerfMetricMonitorData> getVmPerfMetric(GetMetricsRequest getMetricsRequest) {\n HuaweiVmCredential credential = JsonUtil.parseObject(getMetricsRequest.getCredential(), HuaweiVmCredential.class);\n List<F2CPerfMetricMonitorData> result = new ArrayList<>();\n List<F2CVirtualMachine> vms = listVirtualMachine(getListVmRequest(getMetricsRequest));\n if (vms.size() == 0) {\n return result;\n }\n CesClient cesClient = credential.getCesClient(getMetricsRequest.getRegionId());\n // 循环云主机\n vms.forEach(vm -> {\n try {\n // 存储接口返回的数据\n Map<String, List<BatchMetricData>> apiResult = new HashMap<>(3);\n // 数据聚合查询接口\n List.of(\"average\", \"max\", \"min\").forEach(filter -> {\n try {\n BatchListMetricDataRequest request = batchListMetricRequest(getMetricsRequest, vm.getInstanceUUID(), filter);\n BatchListMetricDataResponse response = cesClient.batchListMetricData(request);\n apiResult.put(filter, response.getMetrics());\n } catch (Exception e) {\n LogUtil.error(\"华为云查询云主机 \" + vm.getName() + \"聚合值-\" + filter + \"- 监控数据失败:\" + e.getMessage());\n }\n });\n // 处理结果,映射数据到平台需要查询的指标\n Arrays.stream(HuaweiPerfMetricConstants.CloudServerPerfMetricEnum.values()).sorted().toList().forEach(perfMetric -> {\n // 存储时间节点数据,5分钟间隔,1个半小时,最多18条数据\n Map<Long, DatapointForBatchMetric> datapointMap = new HashMap<>(18);\n // 处理结果数据\n apiResult.keySet().forEach(key -> {\n // 过滤监控数据\n List<DatapointForBatchMetric> datapointList = new ArrayList<>();\n datapointList = getDatapointForBaseOrAgent(apiResult.get(key), perfMetric);\n datapointList.forEach(v -> {\n if (!datapointMap.containsKey(v.getTimestamp())) {\n datapointMap.put(v.getTimestamp(), v);\n }\n if (StringUtils.equalsIgnoreCase(key, \"average\")) {\n datapointMap.get(v.getTimestamp()).setAverage(v.getAverage());\n }\n if (StringUtils.equalsIgnoreCase(key, \"max\")) {\n datapointMap.get(v.getTimestamp()).setMax(v.getMax());\n }\n if (StringUtils.equalsIgnoreCase(key, \"min\")) {\n datapointMap.get(v.getTimestamp()).setMin(v.getMin());\n }\n });\n });\n datapointMap.forEach((k, v) -> {\n F2CPerfMetricMonitorData f2CEntityPerfMetric = HuaweiMappingUtil.toF2CPerfMetricMonitorData(v);\n f2CEntityPerfMetric.setEntityType(F2CEntityType.VIRTUAL_MACHINE.name());\n f2CEntityPerfMetric.setMetricName(perfMetric.name());\n f2CEntityPerfMetric.setPeriod(getMetricsRequest.getPeriod());\n f2CEntityPerfMetric.setInstanceId(vm.getInstanceUUID());\n f2CEntityPerfMetric.setUnit(perfMetric.getUnit());\n result.add(f2CEntityPerfMetric);\n });\n });\n } catch (Exception e) {\n LogUtil.error(\"同步 华为 云主机 \" + vm.getName() + \" 监控失败:\" + e.getMessage());\n }\n });\n return result;\n }\n\n /**\n * 过滤监控数据\n *\n * @param response 结果\n * @param perfMetric 指标\n * @return 监控数据\n */\n private static List<DatapointForBatchMetric> getDatapointForBaseOrAgent(List<BatchMetricData> response, HuaweiPerfMetricConstants.CloudServerPerfMetricEnum perfMetric) {\n List<DatapointForBatchMetric> agentData = getDatapointForBaseOrAgent(response, perfMetric, true);\n return CollectionUtils.isNotEmpty(agentData) ? agentData : getDatapointForBaseOrAgent(response, perfMetric, false);\n }\n\n /**\n * 获取 基础监控数据活着agent的监控数据datapoint\n *\n * @param response 数据结果\n * @param perfMetric 指标\n * @param agent 是否是agent\n * @return 数据\n */\n private static List<DatapointForBatchMetric> getDatapointForBaseOrAgent(List<BatchMetricData> response, HuaweiPerfMetricConstants.CloudServerPerfMetricEnum perfMetric, boolean agent) {\n List<BatchMetricData> agentMetricData = response.stream()\n .filter(v -> StringUtils.equalsIgnoreCase(agent ? \"AGT.ECS\" : \"SYS.ECS\", v.getNamespace()))\n .filter(v -> StringUtils.equalsIgnoreCase(agent ? perfMetric.getAgentMetricName() : perfMetric.getBaseMetricName(), v.getMetricName()))\n .filter(v -> v.getDatapoints().size() > 0).toList();\n return CollectionUtils.isNotEmpty(agentMetricData) ? agentMetricData.get(0).getDatapoints() : new ArrayList<>();\n }\n\n /**\n * 批量指标获取监控数据\n *\n * @param getMetricsRequest 参数来源\n * @param instanceId 云主机ID\n * @param filter 数据聚合方式,max为最大值,min为最小值,average为平均值\n * @return 批量查询参数\n */\n private static BatchListMetricDataRequest batchListMetricRequest(GetMetricsRequest getMetricsRequest, String instanceId, String filter) {\n BatchListMetricDataRequest request = new BatchListMetricDataRequest();\n BatchListMetricDataRequestBody body = new BatchListMetricDataRequestBody();\n List<MetricsDimension> listMetricsDimensions = new ArrayList<>();\n listMetricsDimensions.add(\n new MetricsDimension()\n .withName(\"instance_id\")\n .withValue(instanceId)\n );\n List<MetricInfo> listBodyMetrics = listMetricInfo(listMetricsDimensions);\n body.withTo(Long.valueOf(getMetricsRequest.getEndTime()));\n body.withFrom(Long.valueOf(getMetricsRequest.getStartTime()));\n body.withFilter(filter);\n body.withPeriod(\"300\");\n body.withMetrics(listBodyMetrics);\n request.withBody(body);\n return request;\n }\n\n /**\n * 获取所有指标两个namespace的数据(基础、agent)\n * listBodyMetrics最大支持500个\n * 目前10*2\n *\n * @param listMetricsDimensions 对象参数\n * @return 所有指标参数\n */\n private static List<MetricInfo> listMetricInfo(List<MetricsDimension> listMetricsDimensions) {\n List<MetricInfo> listBodyMetrics = new ArrayList<>();\n Arrays.stream(HuaweiPerfMetricConstants.CloudServerPerfMetricEnum.values()).sorted().toList().forEach(perfMetric -> {\n listBodyMetrics.add(\n new MetricInfo()\n .withNamespace(\"AGT.ECS\")\n .withMetricName(perfMetric.getAgentMetricName())\n .withDimensions(listMetricsDimensions)\n );\n listBodyMetrics.add(\n new MetricInfo()\n .withNamespace(\"SYS.ECS\")\n .withMetricName(perfMetric.getBaseMetricName())\n .withDimensions(listMetricsDimensions)\n );\n });\n return listBodyMetrics;\n }\n\n /**\n * 根据指标查询指定值的监控数据\n *\n * @param cesClient 查询客户端\n * @param vm 要查询监控数据的云主机\n * @param perfMetric 监控指标\n * @param request API参数\n * @return 监控数据时间戳\n */\n private static Map<Long, Datapoint> getVmMonitoringData(CesClient cesClient, F2CVirtualMachine vm, HuaweiPerfMetricConstants.CloudServerPerfMetricEnum perfMetric, ShowMetricDataRequest request) {\n Map<Long, Datapoint> datapointMap = new HashMap<>();\n List.of(\"average\", \"max\", \"min\").forEach(filter -> {\n try {\n request.withFilter(ShowMetricDataRequest.FilterEnum.fromValue(filter));\n //查询监控指标数据\n ShowMetricDataResponse response = cesClient.showMetricData(request);\n if (response.getHttpStatusCode() == 200 && CollectionUtils.isNotEmpty(response.getDatapoints())) {\n List<Datapoint> list = response.getDatapoints();\n list.forEach(v -> {\n if (!datapointMap.containsKey(v.getTimestamp())) {\n datapointMap.put(v.getTimestamp(), v);\n }\n if (StringUtils.equalsIgnoreCase(filter, \"average\")) {\n datapointMap.get(v.getTimestamp()).setAverage(v.getAverage());\n }\n if (StringUtils.equalsIgnoreCase(filter, \"max\")) {\n datapointMap.get(v.getTimestamp()).setMax(v.getMax());\n }\n if (StringUtils.equalsIgnoreCase(filter, \"min\")) {\n datapointMap.get(v.getTimestamp()).setMin(v.getMin());\n }\n });\n }\n } catch (Exception e) {\n LogUtil.error(\"查询 华为 云主机 \" + vm.getName() + \" 监控指标 \" + perfMetric.getDescription() + \" \" + filter + \"值失败:\" + e.getMessage());\n }\n });\n return datapointMap;\n }\n\n /**\n * 查询云主机监控数据参数\n *\n * @param getMetricsRequest 全部参数\n * @return API所需基本参数\n */\n @NotNull\n private static ShowMetricDataRequest getShowMetricDataRequest(GetMetricsRequest getMetricsRequest) {\n ShowMetricDataRequest request = new ShowMetricDataRequest();\n request.withPeriod(300);\n getMetricsRequest.setPeriod(request.getPeriod());\n request.withFrom(Long.valueOf(getMetricsRequest.getStartTime()));\n request.withTo(Long.valueOf(getMetricsRequest.getEndTime()));\n return request;\n }\n\n /**\n * 查询所有虚拟机参数\n *\n * @param getMetricsRequest\n * @return\n */\n public static ListVirtualMachineRequest getListVmRequest(GetMetricsRequest getMetricsRequest) {\n ListVirtualMachineRequest listVirtualMachineRequest = new ListVirtualMachineRequest();\n listVirtualMachineRequest.setCredential(getMetricsRequest.getCredential());\n listVirtualMachineRequest.setRegionId(getMetricsRequest.getRegionId());\n return listVirtualMachineRequest;\n }\n\n public static List<NovaAvailabilityZoneDTO> getAvailabilityZone(HuaweiVmCreateRequest request) {\n if (StringUtils.isEmpty(request.getRegionId())) {\n return new ArrayList<>();\n }\n List<NovaAvailabilityZoneDTO> result = new ArrayList<>();\n try {\n HuaweiVmCredential credential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n EcsClient client = credential.getEcsClient(request.getRegionId());\n NovaListAvailabilityZonesRequest getAz = new NovaListAvailabilityZonesRequest();\n NovaListAvailabilityZonesResponse response = client.novaListAvailabilityZones(getAz);\n for (NovaAvailabilityZone novaAvailabilityZone : response.getAvailabilityZoneInfo()) {\n NovaAvailabilityZoneDTO dto = new NovaAvailabilityZoneDTO(novaAvailabilityZone);\n String name = dto.getZoneName();\n int index = StringUtils.lowerCase(name).charAt(name.length() - 1) - 96;\n dto.setDisplayName(\"可用区\" + index);\n result.add(dto);\n }\n } catch (Exception e) {\n e.printStackTrace();\n }\n return result;\n }\n\n public static F2CVirtualMachine getSimpleServerByCreateRequest(HuaweiVmCreateRequest request) {\n F2CVirtualMachine virtualMachine = new F2CVirtualMachine();\n\n int index = request.getIndex();\n virtualMachine\n .setId(request.getId())\n .setName(request.getServerNameInfos().get(index).getName())\n .setIpArray(new ArrayList<>())\n .setInstanceType(request.getInstanceType());\n\n return virtualMachine;\n\n }\n\n public static Integer getPeriodNumber(String periodNumber) {\n if (Integer.valueOf(periodNumber) >= 12) {\n return Integer.valueOf(periodNumber) / 12;\n }\n return Integer.valueOf(periodNumber);\n }\n\n public static String getPeriodType(String periodNumber) {\n return Integer.valueOf(periodNumber) < 12 ? \"month\" : \"year\";\n }\n\n public static F2CVirtualMachine createServer(HuaweiVmCreateRequest request) {\n F2CVirtualMachine f2CVirtualMachine = new F2CVirtualMachine();\n try {\n request.setRegion(request.getRegionId());\n HuaweiVmCredential credential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n EcsClient client = credential.getEcsClient(request.getRegionId());\n //创建云主机参数\n CreateServersRequest createServersRequest = new CreateServersRequest();\n //参数实体\n CreateServersRequestBody body = new CreateServersRequestBody();\n\n //计费类类型\n PrePaidServerExtendParam extendparamServer = new PrePaidServerExtendParam();\n extendparamServer.withChargingMode(PrePaidServerExtendParam.ChargingModeEnum.fromValue(request.getBillingMode()))\n .withRegionID(request.getRegionId());\n if (StringUtils.equalsIgnoreCase(request.getBillingMode(), PrePaidServerExtendParam.ChargingModeEnum.PREPAID.getValue())) {\n extendparamServer.withPeriodType(PrePaidServerExtendParam.PeriodTypeEnum.fromValue(getPeriodType(request.getPeriodNum())));\n extendparamServer.withPeriodNum(getPeriodNumber(request.getPeriodNum()));\n extendparamServer.withIsAutoPay(PrePaidServerExtendParam.IsAutoPayEnum.TRUE);\n }\n //安全组\n List<PrePaidServerSecurityGroup> listServerSecurityGroups = new ArrayList<>();\n request.getSecurityGroups().forEach(v -> listServerSecurityGroups.add(new PrePaidServerSecurityGroup().withId(v)));\n\n //系统盘\n DiskConfig systemDisk = request.getDisks().get(0);\n PrePaidServerRootVolume rootVolumeServer = new PrePaidServerRootVolume();\n rootVolumeServer.withVolumetype(PrePaidServerRootVolume.VolumetypeEnum.fromValue(systemDisk.getDiskType()))\n .withSize(systemDisk.getSize())\n .withHwPassthrough(true);\n //数据盘\n List<PrePaidServerDataVolume> listServerDataVolumes = new ArrayList<>();\n for (int i = 0; i < request.getDisks().size(); i++) {\n if (i == 0) {\n continue;\n }\n DiskConfig dataDisk = request.getDisks().get(i);\n listServerDataVolumes.add(\n new PrePaidServerDataVolume()\n .withVolumetype(PrePaidServerDataVolume.VolumetypeEnum.fromValue(dataDisk.getDiskType()))\n .withSize(dataDisk.getSize())\n .withShareable(false)\n .withMultiattach(false)\n .withHwPassthrough(true)\n .withDataImageId(\"\")\n );\n }\n\n //公网IP\n PrePaidServerPublicip publicipServer = null;\n if (request.isUsePublicIp()) {\n PrePaidServerEipExtendParam extendparamEip = new PrePaidServerEipExtendParam();\n extendparamEip.withChargingMode(PrePaidServerEipExtendParam.ChargingModeEnum.fromValue(Objects.equals(request.getBillingMode(), \"1\") ? \"prePaid\" : \"postPaid\"));\n PrePaidServerEipBandwidth bandwidthEip = new PrePaidServerEipBandwidth();\n boolean isTraffic = StringUtils.equalsIgnoreCase(\"traffic\", request.getChargeMode());\n bandwidthEip.withSize(isTraffic ? request.getTrafficBandwidthSize() : request.getBandwidthSize())\n //PER,表示独享。WHOLE,表示共享\n .withSharetype(PrePaidServerEipBandwidth.SharetypeEnum.fromValue(\"PER\"))\n //traffic表示按流量计费,空或者不传为按带宽计费\n .withChargemode(StringUtils.equalsIgnoreCase(request.getChargeMode(), \"traffic\") ? \"traffic\" : \"\");\n\n PrePaidServerEip eipPublicip = new PrePaidServerEip();\n //固定\n eipPublicip.withIptype(\"5_bgp\")\n .withBandwidth(bandwidthEip)\n .withExtendparam(extendparamEip);\n publicipServer = new PrePaidServerPublicip();\n publicipServer.withEip(eipPublicip);\n //默认随实例删除\n publicipServer.setDeleteOnTermination(true);\n }\n\n // TODO 网卡 目前仅支持一个网卡,官方支持最多两个\n List<F2CHuaweiSubnet> networks = listSubnet(request);\n if (CollectionUtils.isEmpty(networks)) {\n throw new RuntimeException(\"No suitable network found!\");\n }\n F2CHuaweiSubnet network = networks.get(0);\n List<PrePaidServerNic> listServerNics = new ArrayList<>();\n listServerNics.add(\n new PrePaidServerNic()\n .withSubnetId(network.getUuid())\n .withIpAddress(\"\")\n );\n PrePaidServer serverbody = new PrePaidServer();\n //获取镜像ID,根据规格、操作系统、操作系统版本\n List<F2CImage> images = listCreateImages(request);\n if (CollectionUtils.isEmpty(images)) {\n throw new RuntimeException(\"No suitable image found!\");\n }\n serverbody.withImageRef(images.get(0).getId())\n .withFlavorRef(request.getInstanceType())\n .withName(request.getServerNameInfos().get(request.getIndex()).getName())\n .withVpcid(network.getVpcId())\n .withNics(listServerNics)\n .withCount(1)\n .withIsAutoRename(false)\n .withRootVolume(rootVolumeServer)\n .withDataVolumes(listServerDataVolumes)\n .withSecurityGroups(listServerSecurityGroups)\n .withAvailabilityZone(request.getAvailabilityZone())\n .withExtendparam(extendparamServer)\n //.withMetadata(listServerMetadata)\n .withDescription(\"\");\n if (publicipServer != null) {\n serverbody.withPublicip(publicipServer);\n }\n if (StringUtils.equalsIgnoreCase(\"pwd\", request.getLoginMethod())) {\n serverbody.withAdminPass(request.getPwd());\n } else {\n serverbody.withKeyName(request.getKeyPari());\n }\n body.withServer(serverbody);\n createServersRequest.withBody(body);\n CreateServersResponse response = client.createServers(createServersRequest);\n List<Port> ports = listPorts(request.getCredential(), request.getRegionId());\n ServerDetail serverDetail = getJobEntities(client, response.getJobId());\n f2CVirtualMachine = HuaweiMappingUtil.toF2CVirtualMachine(serverDetail, ports);\n f2CVirtualMachine.setRegion(request.getRegionId());\n f2CVirtualMachine.setId(request.getId());\n setServerHostName(client, f2CVirtualMachine, request);\n // 获取包年包月机器的到期时间\n if (F2CChargeType.PRE_PAID.equalsIgnoreCase(f2CVirtualMachine.getInstanceChargeType())) {\n appendExpiredTime(credential, serverDetail, f2CVirtualMachine);\n }\n } catch (Exception e) {\n e.printStackTrace();\n throw new Fit2cloudException(5000, \"Huawei create vm fail - \" + e.getMessage());\n }\n return f2CVirtualMachine;\n }\n\n private static void setServerHostName(EcsClient client, F2CVirtualMachine f2CVirtualMachine, HuaweiVmCreateRequest createRequest) {\n try {\n // 设置hostname\n UpdateServerRequest request = new UpdateServerRequest();\n request.withServerId(f2CVirtualMachine.getInstanceUUID());\n UpdateServerRequestBody body = new UpdateServerRequestBody();\n UpdateServerOption serverbody = new UpdateServerOption();\n serverbody.withHostname(createRequest.getServerNameInfos().get(createRequest.getIndex()).getHostName());\n body.withServer(serverbody);\n request.withBody(body);\n UpdateServerResponse response = client.updateServer(request);\n if (response.getHttpStatusCode() == 200) {\n if (createRequest.getServerNameInfos().get(createRequest.getIndex()).isAuthReboot()) {\n // 重启\n HuaweiInstanceRequest instanceRequest = new HuaweiInstanceRequest();\n instanceRequest.setCredential(createRequest.getCredential());\n instanceRequest.setRegionId(createRequest.getRegionId());\n instanceRequest.setUuid(f2CVirtualMachine.getInstanceUUID());\n rebootInstance(instanceRequest);\n f2CVirtualMachine.setHostname(response.getServer().getOsEXTSRVATTRHostname());\n }\n }\n\n } catch (Exception e) {\n e.printStackTrace();\n logger.error(\"{}-set hostname fail:{}\", f2CVirtualMachine.getName(), e.getMessage());\n }\n }\n\n /**\n * 获取创建主机镜像\n * 根据规格、操作系统、操作系统版本、状态\n *\n * @param createRequest 请求对象\n * @return 响应对象\n */\n public static List<F2CImage> listCreateImages(HuaweiVmCreateRequest createRequest) {\n ListImageRequest request = new ListImageRequest();\n request.setRegionId(createRequest.getRegionId());\n request.setCredential(createRequest.getCredential());\n request.setFlavorId(createRequest.getInstanceType());\n request.setPlatform(ListImagesRequest.PlatformEnum.valueOf(createRequest.getOs()));\n request.setStatus(ListImagesRequest.StatusEnum.ACTIVE);\n if (StringUtils.isNotEmpty(request.getCredential())) {\n HuaweiVmCredential credential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n ImsClient imsClient = credential.getImsClient(request.getRegionId());\n request.setImagetype(ListImagesRequest.ImagetypeEnum.GOLD);\n ListImagesResponse listImagesResponse = imsClient.listImages(request);\n List<ImageInfo> images = listImagesResponse.getImages();\n //根据用户输入的操作系统版本过滤\n return images.stream().filter(v -> filterImageByOsAndOsVersion(v, createRequest)).map(imageInfo -> HuaweiMappingUtil.toF2CImage(imageInfo, request.getRegionId())).filter(Objects::nonNull).toList();\n }\n return new ArrayList<>();\n }\n\n private static boolean filterImageByOsAndOsVersion(ImageInfo imageInfo, HuaweiVmCreateRequest createRequest) {\n return StringUtils.equalsIgnoreCase(imageInfo.getPlatform().getValue(), createRequest.getOs()) && StringUtils.equalsIgnoreCase(imageInfo.getId(), createRequest.getOsVersion());\n }\n\n private static ServerDetail getJobEntities(EcsClient client, String jobId) {\n int count = 0;\n while (true) {\n try {\n Thread.sleep(SLEEP_TIME);\n com.huaweicloud.sdk.ecs.v2.model.ShowJobResponse jobResponse = client.showJob(new com.huaweicloud.sdk.ecs.v2.model.ShowJobRequest().withJobId(jobId));\n com.huaweicloud.sdk.ecs.v2.model.ShowJobResponse.StatusEnum status = jobResponse.getStatus();\n if (ShowJobResponse.StatusEnum.SUCCESS.getValue().equals(status.getValue())) {\n String id = jobResponse.getEntities().getSubJobs().get(0).getEntities().getServerId();\n ShowServerRequest request = new ShowServerRequest();\n request.setServerId(id);\n ShowServerResponse response = client.showServer(request);\n return response.getServer();\n }\n if (ShowJobResponse.StatusEnum.FAIL.getValue().equalsIgnoreCase(status.getValue())) {\n String errorMsg = jobResponse.getFailReason();\n try {\n errorMsg = jobResponse.getEntities().getSubJobs().get(0).getEntities().getErrorcodeMessage();\n } catch (Exception e) {\n e.printStackTrace();\n }\n throw new RuntimeException(errorMsg);\n }\n if (count >= WAIT_COUNT) {\n throw new RuntimeException(\"check create server status timeout!\");\n }\n count++;\n } catch (Exception e) {\n throw new RuntimeException(\"getJobEntities fail jobId - \" + jobId + \"-\" + e.getMessage());\n }\n }\n }\n\n public static List<InstanceSpecType> getInstanceSpecTypes(HuaweiVmCreateRequest request) {\n List<InstanceSpecType> instanceSpecTypes = new ArrayList<>();\n if (StringUtils.isEmpty(request.getRegionId()) || StringUtils.isEmpty(request.getAvailabilityZone())) {\n return instanceSpecTypes;\n }\n try {\n HuaweiVmCredential credential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n EcsClient client = credential.getEcsClient(request.getRegionId());\n ListFlavorsResponse response = client.listFlavors(new ListFlavorsRequest()\n .withAvailabilityZone(request.getAvailabilityZone()));\n for (Flavor flavor : response.getFlavors()) {\n if (flavor.getOsExtraSpecs().getCondOperationAz().contains((request.getAvailabilityZone() + \"(normal)\"))) {\n InstanceSpecType instanceSpecType = HuaweiMappingUtil.toInstanceSpecType(flavor);\n instanceSpecTypes.add(instanceSpecType);\n }\n }\n } catch (Exception e) {\n e.printStackTrace();\n }\n return instanceSpecTypes;\n }\n\n public static List<Map<String, String>> getAllDiskTypes(HuaweiVmCreateRequest request) {\n if (StringUtils.isEmpty(request.getRegionId()) && StringUtils.isEmpty(request.getAvailabilityZone())) {\n return new ArrayList<>();\n }\n HuaweiGetDiskTypeRequest getDiskTypeRequest = new HuaweiGetDiskTypeRequest();\n getDiskTypeRequest.setZone(request.getAvailabilityZone());\n getDiskTypeRequest.setCredential(request.getCredential());\n getDiskTypeRequest.setRegion(request.getRegionId());\n getDiskTypeRequest.setLanguage(request.getLanguage());\n return getDiskTypes(getDiskTypeRequest);\n }\n\n public static List<F2CHuaweiSubnet> listSubnet(HuaweiVmCreateRequest request) {\n List<F2CHuaweiSubnet> result = new ArrayList<>();\n Map<String, F2CHuaweiVpc> vpcMap = listVpc(request).stream().collect(Collectors.toMap(F2CHuaweiVpc::getUuid, v -> v, (k1, k2) -> k1));\n try {\n HuaweiVmCredential huaweiVmCredential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n VpcClient vpcClient = huaweiVmCredential.getVpcClient(request.getRegionId());\n ListSubnetsRequest listSubnetsRequest = new ListSubnetsRequest();\n listSubnetsRequest.setLimit(1000);\n ListSubnetsResponse response = vpcClient.listSubnets(listSubnetsRequest);\n if (CollectionUtils.isNotEmpty(response.getSubnets())) {\n response.getSubnets().stream()\n .collect(Collectors.toList())\n .forEach(subnet -> {\n F2CHuaweiSubnet f2CHuaweiSubnet = HuaweiMappingUtil.toF2CHuaweiSubnet(subnet);\n if (vpcMap.containsKey(f2CHuaweiSubnet.getVpcId())) {\n f2CHuaweiSubnet.setVpcName(vpcMap.get(f2CHuaweiSubnet.getVpcId()).getName());\n }\n result.add(f2CHuaweiSubnet);\n });\n }\n } catch (Exception e) {\n e.printStackTrace();\n }\n if (!StringUtils.equalsIgnoreCase(\"random\", request.getAvailabilityZone())) {\n return result.stream().filter(v -> StringUtils.equalsIgnoreCase(request.getAvailabilityZone(), v.getAvailabilityZone())).collect(Collectors.toList());\n }\n if (StringUtils.isNotEmpty(request.getNetworkId())) {\n return result.stream().filter(v -> StringUtils.equalsIgnoreCase(v.getUuid(), request.getNetworkId())).toList();\n }\n return result;\n }\n\n public static List<F2CHuaweiVpc> listVpc(HuaweiVmCreateRequest request) {\n List<F2CHuaweiVpc> result = new ArrayList<>();\n try {\n HuaweiVmCredential huaweiVmCredential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n VpcClient vpcClient = huaweiVmCredential.getVpcClient(request.getRegionId());\n ListVpcsRequest listVpcsRequest = new ListVpcsRequest();\n listVpcsRequest.setLimit(1000);\n ListVpcsResponse response = vpcClient.listVpcs(listVpcsRequest);\n if (CollectionUtils.isNotEmpty(response.getVpcs())) {\n response.getVpcs().forEach(vpc -> {\n result.add(HuaweiMappingUtil.toF2CHuaweiVpc(vpc));\n });\n }\n } catch (Exception e) {\n e.printStackTrace();\n }\n return result;\n }\n\n public static List<F2CHuaweiSecurityGroups> listSecurityGroups(HuaweiVmCreateRequest request) {\n List<F2CHuaweiSecurityGroups> result = new ArrayList<>();\n try {\n HuaweiVmCredential huaweiVmCredential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n VpcClient vpcClient = huaweiVmCredential.getVpcClient(request.getRegionId());\n ListSecurityGroupsRequest listSecurityGroupsRequest = new ListSecurityGroupsRequest();\n listSecurityGroupsRequest.setLimit(1000);\n ListSecurityGroupsResponse response = vpcClient.listSecurityGroups(listSecurityGroupsRequest);\n if (CollectionUtils.isNotEmpty(response.getSecurityGroups())) {\n response.getSecurityGroups().forEach(securityGroup -> {\n result.add(HuaweiMappingUtil.toF2CHuaweiSecurityGroups(securityGroup));\n });\n }\n } catch (Exception e) {\n e.printStackTrace();\n }\n return result;\n }\n\n public static List<NovaSimpleKeypair> listKeyPairs(HuaweiVmCreateRequest request) {\n List<NovaSimpleKeypair> result = new ArrayList<>();\n try {\n HuaweiVmCredential huaweiVmCredential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n EcsClient client = huaweiVmCredential.getEcsClient(request.getRegionId());\n NovaListKeypairsRequest listKeypairsRequest = new NovaListKeypairsRequest();\n listKeypairsRequest.setLimit(1000);\n NovaListKeypairsResponse response = client.novaListKeypairs(listKeypairsRequest);\n if (CollectionUtils.isNotEmpty(response.getKeypairs())) {\n response.getKeypairs().forEach(keypair -> {\n result.add(keypair.getKeypair());\n });\n }\n } catch (Exception e) {\n e.printStackTrace();\n }\n return result;\n }\n\n /**\n * 询价\n *\n * @param request\n * @return\n */\n public static String calculatedPrice(Boolean trafficPriceOnly, HuaweiVmCreateRequest request) {\n StringBuilder result = new StringBuilder();\n try {\n if (StringUtils.isEmpty(request.getAvailabilityZone())) {\n return result.toString();\n }\n if (request.getCount() == 0) {\n request.setCount(1);\n }\n HuaweiVmCredential credential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n //查询项目\n KeystoneListAuthProjectsRequest projectsRequest = new KeystoneListAuthProjectsRequest();\n IamClient client = credential.getIamClient(request.getRegionId());\n KeystoneListAuthProjectsResponse projectsResponse = client.keystoneListAuthProjects(projectsRequest);\n List<AuthProjectResult> projectResults = projectsResponse.getProjects().stream()\n .filter(v -> StringUtils.equalsIgnoreCase(v.getName(), request.getRegionId())).collect(Collectors.toList());\n if (CollectionUtils.isNotEmpty(projectResults)) {\n String projectId = projectResults.get(0).getId();\n Double vmAmount = 0D;\n Double diskAmount = 0D;\n Double bandwidthAmount = 0D;\n //开启公网IP\n boolean isUsePublicIp = request.isUsePublicIp();\n //公网IP计费价格\n if (isUsePublicIp) {\n bandwidthAmount = bandwidthInquiryPriceForHour(request, credential, projectId);\n }\n if (trafficPriceOnly) {\n result.append(bandwidthAmount).append(\"元/GB\");\n return result.toString();\n }\n boolean isTraffic = StringUtils.equalsIgnoreCase(\"traffic\", request.getChargeMode());\n //按量计费\n if (StringUtils.equalsIgnoreCase(request.getBillingMode(), \"postPaid\")) {\n vmAmount = vmInquiryPriceForHour(request, credential, projectId);\n diskAmount = diskInquiryPriceForHour(request, credential, projectId);\n BigDecimal amountBig = new BigDecimal(vmAmount + diskAmount + (isTraffic ? 0 : bandwidthAmount));\n result.append(amountBig.setScale(4, RoundingMode.HALF_UP));\n result.append(\"元/小时\");\n }\n //包年包月\n if (StringUtils.equalsIgnoreCase(request.getBillingMode(), \"prePaid\")) {\n vmAmount = vmInquiryPriceForMonth(request, credential, projectId);\n diskAmount = diskInquiryPriceForMonth(request, credential, projectId);\n if (isUsePublicIp) {\n bandwidthAmount = bandwidthInquiryPriceForMonth(request, credential, projectId);\n }\n BigDecimal amountBig = new BigDecimal(vmAmount + diskAmount + (isTraffic ? 0 : bandwidthAmount));\n result.append(amountBig.setScale(4, RoundingMode.HALF_UP));\n result.append(\"元\");\n }\n }\n } catch (Exception e) {\n e.printStackTrace();\n }\n return result.toString();\n }\n\n /**\n * 虚拟机包年包月询价\n *\n * @param createRequest\n * @param credential\n * @param projectId\n * @return\n */\n private static Double vmInquiryPriceForMonth(HuaweiVmCreateRequest createRequest, HuaweiVmCredential credential, String projectId) {\n ListRateOnPeriodDetailRequest request = new ListRateOnPeriodDetailRequest();\n RateOnPeriodReq body = new RateOnPeriodReq();\n List<PeriodProductInfo> listPeriodProductInfo = new ArrayList<>();\n listPeriodProductInfo.add(new PeriodProductInfo()\n //唯一标识\n .withId(createRequest.getInstanceType())\n //云主机询价固定\n .withCloudServiceType(\"hws.service.type.ec2\")\n .withResourceType(\"hws.resource.type.vm\")\n //区分linux\\win,目前查询结果价格一致,官网这个价格,不根据操作系统的不同而改变价格,所以这里不做区分\n .withResourceSpec(createRequest.getInstanceType() + \".linux\")\n .withRegion(createRequest.getRegionId())\n //周期类型0:天2:月3:年4:小时\n .withPeriodType(StringUtils.equalsIgnoreCase(getPeriodType(createRequest.getPeriodNum()), \"month\") ? 2 : 3)\n //周期数 1个月\n .withPeriodNum(getPeriodNumber(createRequest.getPeriodNum()))\n //数量\n .withSubscriptionNum(createRequest.getCount()));\n body.withProductInfos(listPeriodProductInfo);\n body.withProjectId(projectId);\n request.withBody(body);\n try {\n if (CollectionUtils.isNotEmpty(body.getProductInfos())) {\n ListRateOnPeriodDetailResponse response = credential.getBssClient().listRateOnPeriodDetail(request);\n return response.getOfficialWebsiteRatingResult().getOfficialWebsiteAmount();\n }\n } catch (Exception e) {\n e.printStackTrace();\n }\n return 0D;\n }\n\n /**\n * 虚拟机按需询价\n *\n * @param createRequest\n * @param credential\n * @param projectId\n * @return\n */\n private static Double vmInquiryPriceForHour(HuaweiVmCreateRequest createRequest, HuaweiVmCredential credential, String projectId) {\n ListOnDemandResourceRatingsRequest request = new ListOnDemandResourceRatingsRequest();\n RateOnDemandReq body = new RateOnDemandReq();\n List<DemandProductInfo> listBodyProductInfos = new ArrayList<>();\n if (Objects.isNull(createRequest.getInstanceType())) {\n return 0D;\n }\n listBodyProductInfos.add(new DemandProductInfo()\n //唯一标识\n .withId(createRequest.getInstanceType())\n //云主机询价固定\n .withCloudServiceType(\"hws.service.type.ec2\")\n .withResourceType(\"hws.resource.type.vm\")\n //区分linux\\win,目前查询结果价格一致,官网这个价格,不根据操作系统的不同而改变价格,所以这里不做区分\n .withResourceSpec(createRequest.getInstanceType() + \".linux\")\n .withRegion(createRequest.getRegionId())\n //云服务器:Duration\n //云硬盘:Duration\n //弹性IP:Duration\n .withUsageFactor(\"Duration\")\n //按小时询价,使用量值为1,使用量单位为小时。\n .withUsageValue((double) 1)\n //调度单位小时为4\n .withUsageMeasureId(4)\n .withSubscriptionNum(createRequest.getCount()));\n body.withProductInfos(listBodyProductInfos);\n body.withProjectId(projectId);\n request.withBody(body);\n try {\n if (CollectionUtils.isNotEmpty(body.getProductInfos())) {\n ListOnDemandResourceRatingsResponse response = credential.getBssClient().listOnDemandResourceRatings(request);\n return response.getOfficialWebsiteAmount();\n }\n } catch (Exception e) {\n e.printStackTrace();\n }\n return 0D;\n }\n\n /**\n * 磁盘包年包月询价\n *\n * @param createRequest\n * @param credential\n * @param projectId\n * @return\n */\n private static Double diskInquiryPriceForMonth(HuaweiVmCreateRequest createRequest, HuaweiVmCredential credential, String projectId) {\n ListRateOnPeriodDetailRequest request = new ListRateOnPeriodDetailRequest();\n RateOnPeriodReq body = new RateOnPeriodReq();\n List<PeriodProductInfo> listbodyProductInfos = new ArrayList<>();\n for (int i = 0; i < createRequest.getDisks().size(); i++) {\n DiskConfig diskConfig = createRequest.getDisks().get(i);\n listbodyProductInfos.add(new PeriodProductInfo()\n .withId(String.valueOf(i))\n .withCloudServiceType(\"hws.service.type.ebs\")\n .withResourceType(\"hws.resource.type.volume\")\n .withResourceSpec(diskConfig.getDiskType())\n .withRegion(createRequest.getRegionId())\n .withResourceSize(diskConfig.getSize())\n //资源容量度量标识云盘GB17、15Mbps\n .withSizeMeasureId(17)\n //周期类型0:天2:月3:年4:小时\n .withPeriodType(StringUtils.equalsIgnoreCase(getPeriodType(createRequest.getPeriodNum()), \"month\") ? 2 : 3)\n //周期数 1个月\n .withPeriodNum(getPeriodNumber(createRequest.getPeriodNum()))\n .withSubscriptionNum(1));\n }\n body.withProductInfos(listbodyProductInfos);\n body.withProjectId(projectId);\n request.withBody(body);\n try {\n if (CollectionUtils.isNotEmpty(body.getProductInfos())) {\n ListRateOnPeriodDetailResponse response = credential.getBssClient().listRateOnPeriodDetail(request);\n return response.getOfficialWebsiteRatingResult().getOfficialWebsiteAmount();\n }\n } catch (Exception e) {\n e.printStackTrace();\n }\n return 0D;\n }\n\n /**\n * 磁盘按需询价\n *\n * @param createRequest\n * @param credential\n * @param projectId\n * @return\n */\n private static Double diskInquiryPriceForHour(HuaweiVmCreateRequest createRequest, HuaweiVmCredential credential, String projectId) {\n ListOnDemandResourceRatingsRequest request = new ListOnDemandResourceRatingsRequest();\n RateOnDemandReq body = new RateOnDemandReq();\n List<DemandProductInfo> listbodyProductInfos = new ArrayList<>();\n for (int i = 0; i < createRequest.getDisks().size(); i++) {\n DiskConfig diskConfig = createRequest.getDisks().get(i);\n listbodyProductInfos.add(new DemandProductInfo()\n .withId(String.valueOf(i))\n .withCloudServiceType(\"hws.service.type.ebs\")\n .withResourceType(\"hws.resource.type.volume\")\n .withResourceSpec(diskConfig.getDiskType())\n .withRegion(createRequest.getRegionId())\n //大小\n .withResourceSize(diskConfig.getSize())\n //资源容量度量标识云盘GB17、15Mbps\n .withSizeMeasureId(17)\n .withUsageFactor(\"Duration\")\n //按小时询价,使用量值为1,使用量单位为小时。\n .withUsageValue((double) 1)\n //调度单位小时为4\n .withUsageMeasureId(4)\n .withSubscriptionNum(createRequest.getCount()));\n }\n body.withProductInfos(listbodyProductInfos);\n body.withProjectId(projectId);\n request.withBody(body);\n try {\n if (CollectionUtils.isNotEmpty(body.getProductInfos())) {\n ListOnDemandResourceRatingsResponse response = credential.getBssClient().listOnDemandResourceRatings(request);\n return response.getOfficialWebsiteAmount();\n }\n } catch (Exception e) {\n e.printStackTrace();\n }\n return 0D;\n }\n\n /**\n * 公网带宽包年包月询价\n *\n * @param createRequest\n * @param credential\n * @param projectId\n * @return\n */\n private static Double bandwidthInquiryPriceForMonth(HuaweiVmCreateRequest createRequest, HuaweiVmCredential credential, String projectId) {\n boolean isTraffic = StringUtils.equalsIgnoreCase(\"traffic\", createRequest.getChargeMode());\n //按流量与周期无关\n if (isTraffic) {\n return bandwidthInquiryPriceForHour(createRequest, credential, projectId);\n }\n ListRateOnPeriodDetailRequest request = new ListRateOnPeriodDetailRequest();\n RateOnPeriodReq body = new RateOnPeriodReq();\n List<PeriodProductInfo> listbodyProductInfos = new ArrayList<>();\n listbodyProductInfos.add(\n new PeriodProductInfo()\n .withId(\"1\")\n .withCloudServiceType(\"hws.service.type.vpc\")\n .withResourceType(\"hws.resource.type.bandwidth\")\n .withResourceSpec(\"19_bgp\")\n .withRegion(createRequest.getRegionId())\n .withResourceSize(isTraffic ? createRequest.getTrafficBandwidthSize() : createRequest.getBandwidthSize())\n .withSizeMeasureId(15)\n //周期类型0:天2:月3:年4:小时\n .withPeriodType(StringUtils.equalsIgnoreCase(getPeriodType(createRequest.getPeriodNum()), \"month\") ? 2 : 3)\n //周期数 1个月\n .withPeriodNum(getPeriodNumber(createRequest.getPeriodNum()))\n .withSubscriptionNum(createRequest.getCount())\n );\n body.withProductInfos(listbodyProductInfos);\n body.withProjectId(projectId);\n request.withBody(body);\n try {\n if (CollectionUtils.isNotEmpty(body.getProductInfos())) {\n ListRateOnPeriodDetailResponse response = credential.getBssClient().listRateOnPeriodDetail(request);\n return response.getOfficialWebsiteRatingResult().getOfficialWebsiteAmount();\n }\n } catch (Exception e) {\n e.printStackTrace();\n }\n return 0D;\n }\n\n /**\n * 带宽按需询价\n * 12_bgp:动态BGP按流量计费带宽\n * 12_sbgp:静态BGP按流量计费带宽\n * 19_bgp:动态BGP按带宽计费带宽\n * 19_sbgp:静态BGP按带宽计费带宽\n * 19_share:按带宽计费共享带宽\n * IP:5_bgp:动态BGP公网\n * IP5_sbgp:静态BGP公网IP\n */\n private static Double bandwidthInquiryPriceForHour(HuaweiVmCreateRequest createRequest, HuaweiVmCredential credential, String projectId) {\n if (StringUtils.isEmpty(createRequest.getChargeMode())) {\n return 0D;\n }\n boolean isTraffic = StringUtils.equalsIgnoreCase(\"traffic\", createRequest.getChargeMode());\n ListOnDemandResourceRatingsRequest request = new ListOnDemandResourceRatingsRequest();\n RateOnDemandReq body = new RateOnDemandReq();\n List<DemandProductInfo> listbodyProductInfos = new ArrayList<>();\n DemandProductInfo demandProductInfo = new DemandProductInfo();\n demandProductInfo.withId(\"1\")\n .withCloudServiceType(\"hws.service.type.vpc\")\n .withResourceType(\"hws.resource.type.bandwidth\")\n .withUsageValue((double) 1)\n .withSizeMeasureId(15)\n .withSubscriptionNum(createRequest.getCount())\n .withRegion(createRequest.getRegionId())\n .withResourceSize(isTraffic ? createRequest.getTrafficBandwidthSize() : createRequest.getBandwidthSize());\n // 按流量\n if (isTraffic) {\n demandProductInfo.withUsageFactor(\"upflow\").withResourceSpec(\"12_bgp\").withUsageMeasureId(10);\n } else {\n // 按带宽\n demandProductInfo.withUsageFactor(\"Duration\").withResourceSpec(\"19_bgp\").withUsageMeasureId(4);\n }\n listbodyProductInfos.add(demandProductInfo);\n body.withProductInfos(listbodyProductInfos);\n body.withProjectId(projectId);\n request.withBody(body);\n try {\n if (CollectionUtils.isNotEmpty(body.getProductInfos())) {\n ListOnDemandResourceRatingsResponse response = credential.getBssClient().listOnDemandResourceRatings(request);\n return response.getAmount();\n }\n } catch (Exception e) {\n e.printStackTrace();\n }\n return 0D;\n }\n\n /**\n * 返回操作系统版本\n *\n * @param createRequest\n * @return\n */\n public static List<OsConfig> listOsVersion(HuaweiVmCreateRequest createRequest) {\n List<OsConfig> result = new ArrayList<>();\n if (StringUtils.isEmpty(createRequest.getOs())\n || (Objects.isNull(createRequest.getInstanceType()))) {\n return result;\n }\n try {\n ListImageRequest request = new ListImageRequest();\n request.setRegionId(createRequest.getRegionId());\n request.setCredential(createRequest.getCredential());\n request.setFlavorId(createRequest.getInstanceType());\n request.setPlatform(ListImagesRequest.PlatformEnum.valueOf(createRequest.getOs()));\n request.setStatus(ListImagesRequest.StatusEnum.ACTIVE);\n List<ImageInfo> osImages = new ArrayList<>();\n if (StringUtils.isNotEmpty(request.getCredential())) {\n HuaweiVmCredential credential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n ImsClient imsClient = credential.getImsClient(request.getRegionId());\n request.setImagetype(ListImagesRequest.ImagetypeEnum.GOLD);\n ListImagesResponse listImagesResponse = imsClient.listImages(request);\n List<ImageInfo> imagesAll = listImagesResponse.getImages();\n osImages = imagesAll.stream().filter(v -> StringUtils.equalsIgnoreCase(v.getPlatform().getValue(), createRequest.getOs())).collect(Collectors.toList());\n }\n osImages.forEach(v -> {\n OsConfig osConfig = new OsConfig();\n osConfig.setOs(v.getPlatform().getValue());\n osConfig.setOsVersion(v.getOsVersion());\n osConfig.setImageName(v.getName());\n osConfig.setImageId(v.getId());\n osConfig.setImageDiskMinSize(Long.valueOf(String.valueOf(v.getMinDisk())));\n result.add(osConfig);\n });\n return result.stream().sorted(Comparator.comparing(OsConfig::getOsVersion)).collect(Collectors.toList());\n } catch (Exception e) {\n e.printStackTrace();\n }\n return result;\n }\n\n public static List<Map<String, String>> listOs(String request) {\n List<Map<String, String>> result = new ArrayList<>(14);\n List<String> osList = Arrays.asList(\"Windows\", \"RedHat\", \"CentOS\", \"SUSE\", \"Debian\", \"OpenSUSE\", \"Oracle Linux\", \"Fedora\", \"Ubuntu\", \"EulerOS\", \"CoreOS\", \"ESXi\", \"Other\", \"openEuler\");\n osList.stream().sorted().forEach(v -> {\n Map<String, String> m = new HashMap<>();\n m.put(\"id\", v);\n m.put(\"name\", v);\n result.add(m);\n });\n return result;\n }\n\n /**\n * 云主机配置变更\n *\n * @param request\n * @return\n */\n public static F2CVirtualMachine changeVmConfig(HuaweiUpdateConfigRequest request) {\n HuaweiVmCredential huaweiVmCredential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n EcsClient ecsClient = huaweiVmCredential.getEcsClient(request.getRegionId());\n\n String instanceId = request.getInstanceUuid();\n String instanceType = request.getNewInstanceType();\n ServerDetail server = getInstanceById(instanceId, ecsClient);\n Optional.ofNullable(server).orElseThrow(() -> new RuntimeException(\"Can not find the server!\"));\n\n ResizeServerRequest resizeServerRequest = new ResizeServerRequest();\n resizeServerRequest.withServerId(instanceId);\n ResizeServerRequestBody body = new ResizeServerRequestBody();\n\n ResizeServerExtendParam extendParamResize = new ResizeServerExtendParam();\n extendParamResize.withIsAutoPay(\"true\");\n ResizePrePaidServerOption resizeBody = new ResizePrePaidServerOption();\n resizeBody.withFlavorRef(instanceType)\n .withMode(\"withStopServer\")\n .withExtendparam(extendParamResize);\n\n body.withResize(resizeBody);\n resizeServerRequest.withBody(body);\n\n ResizeServerResponse resizeResponse = ecsClient.resizeServer(resizeServerRequest);\n if (null == resizeResponse || StringUtils.isEmpty(resizeResponse.getJobId())) {\n throw new RuntimeException(\"Failed to change instance config.\");\n }\n try {\n checkEcsJobStatus(ecsClient, resizeResponse.getJobId());\n } catch (Exception e) {\n throw new RuntimeException(\"Failed to check ecs job status.\" + e.getMessage(), e);\n }\n\n return HuaweiMappingUtil.toF2CVirtualMachine(getInstanceById(instanceId, ecsClient));\n }\n\n public static List<InstanceSpecType> getInstanceTypesForConfigUpdate(HuaweiUpdateConfigRequest request) {\n HuaweiVmCredential huaweiVmCredential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n EcsClient ecsClient = huaweiVmCredential.getEcsClient(request.getRegionId());\n\n ListResizeFlavorsRequest listResizeFlavorsRequest = new ListResizeFlavorsRequest()\n .withInstanceUuid(request.getInstanceUuid())\n .withSourceFlavorId(request.getCurrentInstanceType());\n\n ListResizeFlavorsResponse response = ecsClient.listResizeFlavors(listResizeFlavorsRequest);\n List<InstanceSpecType> result = response.getFlavors().stream()\n .filter(listResizeFlavorsResult -> !listResizeFlavorsResult.getName().equalsIgnoreCase(request.getCurrentInstanceType()))\n .map(flavor -> {\n InstanceSpecType instanceSpecType = new InstanceSpecType();\n instanceSpecType.setSpecName(flavor.getName());\n instanceSpecType.setInstanceSpec(HuaweiMappingUtil.transInstanceSpecTypeDescription(flavor));\n instanceSpecType.setInstanceTypeDesc(instanceSpecType.getSpecName() + \"(\" + instanceSpecType.getInstanceSpec() + \")\");\n return instanceSpecType;\n }).collect(Collectors.toList());\n return result;\n }\n\n /**\n * 云主机配置变更询价\n *\n * @param request\n * @return\n */\n public static String calculateConfigUpdatePrice(HuaweiUpdateConfigRequest request) {\n HuaweiVmCredential huaweiVmCredential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n EcsClient ecsClient = huaweiVmCredential.getEcsClient(request.getRegionId());\n\n ServerDetail server = getInstanceById(request.getInstanceUuid(), ecsClient);\n Optional.ofNullable(server).orElseThrow(() -> new RuntimeException(\"Can not find the server!\"));\n\n String instanceChargeType;\n if (StringUtils.equalsIgnoreCase(server.getMetadata().get(\"charging_mode\"), \"2\")) {\n instanceChargeType = F2CChargeType.SPOT_PAID;\n } else {\n instanceChargeType = StringUtils.equalsIgnoreCase(server.getMetadata().get(\"charging_mode\"), \"0\") ? F2CChargeType.POST_PAID : F2CChargeType.PRE_PAID;\n }\n\n HuaweiVmCreateRequest createRequest = new HuaweiVmCreateRequest();\n BeanUtils.copyProperties(request, createRequest);\n createRequest.setInstanceType(request.getNewInstanceType());\n createRequest.setCount(1);\n String projectId = server.getTenantId();\n\n Double price;\n if (F2CChargeType.PRE_PAID.equalsIgnoreCase(instanceChargeType)) {\n BssClient bssClient = huaweiVmCredential.getBssClient();\n ShowCustomerOrderDetailsResponse response = getOrderDetailsById(server.getMetadata().get(\"metering.order_id\"), bssClient);\n response.getOrderLineItems().stream().forEach((item) -> {\n if (\"hws.service.type.ec2\".equalsIgnoreCase(item.getServiceTypeCode())) {\n if (item.getPeriodType() == 2) {\n createRequest.setPeriodNum(String.valueOf(item.getPeriodNum() == null ? 1 : item.getPeriodNum()));\n } else {\n createRequest.setPeriodNum(String.valueOf(item.getPeriodNum() * 12));\n }\n }\n });\n price = vmInquiryPriceForMonth(createRequest, huaweiVmCredential, projectId);\n } else {\n price = vmInquiryPriceForHour(createRequest, huaweiVmCredential, projectId);\n }\n return String.format(\"%.2f\", price) + PriceUnit.YUAN;\n }\n\n /**\n * 根据实例 ID 获取实例\n *\n * @param instanceId\n * @param ecsClient\n * @return\n */\n private static ServerDetail getInstanceById(String instanceId, EcsClient ecsClient) {\n ShowServerResponse showServerResponse = ecsClient.showServer(new ShowServerRequest().withServerId(instanceId));\n ServerDetail server = showServerResponse.getServer();\n return server;\n }\n\n /**\n * 查询订单详情\n *\n * @param orderId\n * @param bssClient\n * @return\n */\n private static ShowCustomerOrderDetailsResponse getOrderDetailsById(String orderId, BssClient bssClient) {\n ShowCustomerOrderDetailsRequest request = new ShowCustomerOrderDetailsRequest();\n request.setOrderId(orderId);\n bssClient.showCustomerOrderDetails(request);\n return bssClient.showCustomerOrderDetails(request);\n }\n\n /**\n * 获取云主机关联的磁盘\n *\n * @param request\n * @return\n */\n public static List<F2CDisk> getVmF2CDisks(BaseDiskRequest request) {\n HuaweiVmCredential credential = JsonUtil.parseObject(request.getCredential(), HuaweiVmCredential.class);\n EcsClient ecsClient = credential.getEcsClient(request.getRegionId());\n EvsClient evsClient = credential.getEvsClient(request.getRegionId());\n try {\n ListServerBlockDevicesResponse listServerBlockDevicesResponse = ecsClient.listServerBlockDevices(new ListServerBlockDevicesRequest().withServerId(request.getInstanceUuid()));\n List<ServerBlockDevice> volumeAttachments = listServerBlockDevicesResponse.getVolumeAttachments();\n return volumeAttachments.stream().map(attachment -> {\n ShowVolumeResponse showVolumeResponse = evsClient.showVolume(new ShowVolumeRequest().withVolumeId(attachment.getVolumeId()));\n VolumeDetail volume = showVolumeResponse.getVolume();\n F2CDisk f2CDisk = HuaweiMappingUtil.toF2CDisk(volume);\n return f2CDisk;\n }).collect(Collectors.toList());\n } catch (Exception e) {\n throw new RuntimeException(\"GetVmF2CDisks Error!\" + e.getMessage(), e);\n }\n }\n}\n\n", "input": "", "output": "【漏洞类型】业务逻辑漏洞\n\n【严重性】Medium\n\n【漏洞摘要】华为云同步监控数据时,未正确设置数据粒度(period),导致可能获取不到预期的监控数据。具体表现为在`getF2CPerfMetricList`方法中,虽然设置了默认的时间范围,但未设置数据粒度(period)。这可能导致监控数据不准确或未按预期获取。根本原因在于在处理监控数据请求时,未验证和设置`period`参数,导致可能使用无效或不准确的参数。\n\n【关键代码模式】\n- `getF2CPerfMetricList`方法\n- `GetMetricsRequest`类\n- 监控数据请求相关的函数和类,如`CesClient`、`BatchListMetricDataRequest`等\n- 未验证的用户输入或默认参数设置\n\n【修复方法说明】在`getF2CPerfMetricList`方法中,增加对`getMetricsRequest.setPeriod(300);`的调用,以确保数据粒度(period)被正确设置为300秒。这遵循了安全编码的最佳实践,即在处理用户请求时,确保所有必要的参数都经过验证和正确设置。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/main/java/com/googlecode/vfsjfilechooser2/utils/VFSURIValidator.java\n### 代码内容:\npackage com.googlecode.vfsjfilechooser2.utils;\n\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * VFS URIs validator\n * @author Stan Love <stanlove at users.sourceforge.net>\n * @version 0.0.4\n */\npublic class VFSURIValidator {\n\tprivate String local_uri, local_protocol, local_user, local_pass;\n\tprivate String local_hostname, local_port, local_file;\n\n\tpublic boolean assertEquals(String _s1, String _s2) {\n\t\tif ((_s1 == null) || (_s2 == null)) {\n\t\t\tSystem.out.println(\"FATAL assertEquals -- _s1 || _s2 == null\");\n\t\t\tSystem.out.println(\"_s1=\" + _s1 + \"=\");\n\t\t\tSystem.out.println(\"_s2=\" + _s2 + \"=\");\n\t\t\tException e = new Exception(\"\");\n\t\t\te.printStackTrace();\n\t\t\tSystem.exit(10);\n\t\t}\n\t\tif (_s1.equals(_s2)) {\n\t\t} else {\n\t\t\tSystem.out.println(\"FATAL assertEquals -- _s1 != _s2 \");\n\t\t\tSystem.out.println(\"_s1=\" + _s1 + \"=\");\n\t\t\tSystem.out.println(\"_s2=\" + _s2 + \"=\");\n\t\t\tException e = new Exception(\"\");\n\t\t\te.printStackTrace();\n\t\t\tSystem.exit(10);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic boolean assertNull(String _s1) {\n\t\tif (_s1 != null) {\n\t\t\tSystem.out.println(\"FATAL assertNull -- _s1 != null\");\n\t\t\tException e = new Exception(\"\");\n\t\t\te.printStackTrace();\n\t\t\tSystem.exit(10);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic boolean assertnotNull(String _s1) {\n\t\tif (_s1 == null) {\n\t\t\tSystem.out.println(\"FATAL assertnoNull -- _s1 != null\");\n\t\t\tException e = new Exception(\"\");\n\t\t\te.printStackTrace();\n\t\t\tSystem.exit(10);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic String getUri() {\n\t\tif (local_uri.equals(\"\"))\n\t\t\tlocal_uri = null;\n\t\treturn local_uri;\n\t}\n\n\tpublic String getProtocol() {\n\t\tif ((local_protocol != null) && (local_protocol.equals(\"\")))\n\t\t\tlocal_protocol = null;\n\t\treturn local_protocol;\n\t}\n\n\tpublic String getUser() {\n\t\tif ((local_user != null) && (local_user.equals(\"\")))\n\t\t\tlocal_user = null;\n\t\treturn local_user;\n\t}\n\n\tpublic String getPassword() {\n\t\tif ((local_pass != null) && (local_pass.equals(\"\")))\n\t\t\tlocal_pass = null;\n\t\treturn local_pass;\n\t}\n\n\tpublic String getHostname() {\n\t\tif ((local_hostname != null) && (local_hostname.equals(\"\")))\n\t\t\tlocal_hostname = null;\n\t\treturn local_hostname;\n\t}\n\n\tpublic String getPort() {\n\t\tif (local_port == null) {\n\t\t\treturn local_port;\n\t\t}\n\t\tif (local_port.startsWith(\":\")) {\n\t\t\tlocal_port = local_port.substring(1);\n\t\t}\n\t\tif ((local_port != null) && (local_port.equals(\"\")))\n\t\t\tlocal_port = null;\n\t\treturn local_port;\n\t}\n\n\tpublic String getFile() {\n\t\tif ((local_file != null) && (local_file.equals(\"\")))\n\t\t\tlocal_file = null;\n\t\treturn local_file;\n\t}\n\n\tpublic boolean isValid(String _uri) {\n\t\tboolean ret = false;\n\t\tboolean ends_with_slash = false;\n\n\t\tString protocol = null;\n\t\tString user_pass = null;\n\t\tString hostname = null;\n\t\tString port = null;\n\t\tString bad_port = null;\n\t\tString drive = null;\n\t\tString file = null;\n\n\t\t/*\n\t\tSystem.out.println();\n\t\tSystem.out.println();\n\t\tSystem.out.println();\n\t\t */\n\n\t\tlocal_uri = null;\n\t\tlocal_protocol = null;\n\t\tlocal_user = null;\n\t\tlocal_pass = null;\n\t\tlocal_hostname = null;\n\t\tlocal_port = null;\n\t\tlocal_file = null;\n\t\t//file://(drive_letter:)/\n\t\t//file://(drive_letter:)/file_path\n\t\t//Pattern p_file1 = Pattern.compile(\"(file|FILE)://([a-z][ \t]*:)*?(/.*)\");\n\t\tPattern p_file1 = Pattern\n\t\t\t\t.compile(\"(file|FILE)://(/*)([a-zA-Z][ \t]*:)*(.*)\");\n\t\tMatcher m_file1 = p_file1.matcher(_uri);\n\n\t\tif (m_file1.matches()) {\n\t\t\t//System.out.println(\"file matcher\");\n\t\t\tprotocol = m_file1.group(1);\n\t\t\tString path_start = m_file1.group(2);\n\t\t\tdrive = m_file1.group(3);\n\t\t\tfile = m_file1.group(4);\n\n\t\t\t/*\n\t\t\tSystem.out.println(\"uri=\"+_uri+\"=\");\n\t\t\tSystem.out.println(\"drive=\"+drive+\"=\");\n\t\t\tSystem.out.println(\"file=\"+file+\"=\");\n\t\t\tSystem.out.println(\"path_start=\"+path_start+\"=\");\n\t\t\t*/\n\t\t\tlocal_uri = _uri;\n\t\t\tlocal_protocol = protocol;\n\t\t\tlocal_user = null;\n\t\t\tlocal_pass = null;\n\t\t\tlocal_hostname = null;\n\t\t\tlocal_port = null;\n\t\t\tif ((drive != null) && (file != null)) {\n\t\t\t\tlocal_file = drive + file;\n\t\t\t} \n\t\t\telse if ((path_start != null) && (drive == null) && (file != null)) {\n\t\t\t\tlocal_file = path_start + file;\n\t\t\t} \n\t\t\telse if ((drive != null) && (file == null)) {\n\t\t\t\tlocal_file = drive;\n\t\t\t} \n\t\t\telse {\n\t\t\t\tlocal_file = file;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t/*\n\t\t//look for a bad port number\n\t\t//ftp://(username:pass)*?@hostname(:[0-9]+)*?/.*\n\t\tPattern p_ftp1 = Pattern.compile(\"(ftp|FTP|sftp|SFTP|http|HTTP|https|HTTPS|webdav|WEBDAV|smb|SMB)://(.*?:.*?@)*(.*?)?([ ]*:[^0-9]+)*?[ ]*[^:]/.*\");\n\t\tMatcher m_ftp1 = p_ftp1.matcher(_uri);\n\t\tif(m_ftp1.matches())return false;\n\n\t\tif (m_file1.matches()) {\n\t\t\t//System.out.println(\"file matcher\");\n\t\t\tprotocol = m_file1.group(1);\n\t\t\tdrive = m_file1.group(2);\n\t\t\tfile = m_file1.group(3);\n\n\t\t\t//System.out.println(\"uri=\"+_uri+\"=\");\n\t\t\t//System.out.println(\"file=\"+file+\"=\");\n\t\t\t//System.out.println(\"drive=\"+drive+\"=\");\n\t\t\tlocal_uri = _uri;\n\t\t\tlocal_protocol = protocol;\n\t\t\tlocal_user = null;\n\t\t\tlocal_pass = null;\n\t\t\tlocal_hostname = null;\n\t\t\tlocal_port = null;\n\t\t\tif ((drive != null) && (file != null)) {\n\t\t\t\tlocal_file = drive + file;\n\t\t\t} else {\n\t\t\t\tlocal_file = file;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t/*\n\t\t//look for a bad port number\n\t\t//ftp://(username:pass)*?@hostname(:[0-9]+)*?/.*\n\t\tPattern p_ftp1 = Pattern.compile(\"(ftp|FTP|sftp|SFTP|http|HTTP|https|HTTPS|webdav|WEBDAV|smb|SMB)://(.*?:.*?@)*(.*?)?([ ]*:[^0-9]+)*?[ ]*[^:]/.*\");\n\t\tMatcher m_ftp1 = p_ftp1.matcher(_uri);\n\t\tif(m_ftp1.matches())return false;\n\t\t */\n\n\t\t//remove trailing slash if present\n\t\tif (_uri.endsWith(\"/\")) {\n\t\t\tint iend = _uri.length();\n\t\t\t_uri = _uri.substring(0, iend - 1);\n\t\t\tends_with_slash = true;\n\t\t}\n\t\t//ftp://(username:pass)*?@hostname(:[0-9]+)*?/.*\n\t\t// \"(ftp|FTP|sftp|SFTP|http|HTTP|https|HTTPS|webdav|WEBDAV|smb|SMB)://(.*?:.*?@)*([^:]+)([ \t]*:[0-9]+)*([ \t]*:)*(/.*)\");\n\t\t//\"(ftp|FTP|sftp|SFTP|http|HTTP|https|HTTPS|webdav|WEBDAV|smb|SMB)://(.+:.+@)*([^:]+)([ \t]*:[0-9]+)*([ \t]*:)*(/.*)\");\n\t\tPattern p_ftp2 = Pattern\n\t\t\t\t.compile(\"(ftp|FTP|sftp|SFTP|http|HTTP|https|HTTPS|webdav|WEBDAV|smb|SMB)://(.+:.+@)*([^:]+?/*)([ \t]*:[0-9]+)*([ \t]*:)*(/.*)\");\n\t\tMatcher m_ftp2 = p_ftp2.matcher(_uri);\n\n\t\tPattern p_ftp3 = Pattern\n\t\t\t\t.compile(\"(ftp|FTP|sftp|SFTP|http|HTTP|https|HTTPS|webdav|WEBDAV|smb|SMB)://(.+:.+@)*([^:]+)([ \t]*:[0-9]+)*([ \t]*:)*(/*?.*)\");\n\t\tMatcher m_ftp3 = p_ftp3.matcher(_uri);\n\n\t\tif (m_ftp2.matches()) {\n\t\t\t//System.out.println(\"ftp2 matcher\");\n\t\t\tret = true;\n\t\t\tprotocol = m_ftp2.group(1);\n\t\t\tuser_pass = m_ftp2.group(2);\n\t\t\thostname = m_ftp2.group(3);\n\n\t\t\tport = m_ftp2.group(4);\n\t\t\tbad_port = m_ftp2.group(5); //this should be null on all valid port inputs\n\t\t\tfile = m_ftp2.group(6);\n\t\t\tif (ends_with_slash) {\n\t\t\t\tfile = file + \"/\";\n\t\t\t}\n\t\t\tif (hostname == null) {\n\t\t\t\tprotocol = null;\n\t\t\t\tuser_pass = null;\n\t\t\t\tport = null;\n\t\t\t\tbad_port = null;\n\t\t\t\tfile = null;\n\t\t\t\tret = false;\n\t\t\t}\n\n\t\t} else if (m_ftp3.matches()) {\n\t\t\t//System.out.println(\"ftp3 matcher\");\n\t\t\tret = true;\n\t\t\tprotocol = m_ftp3.group(1);\n\t\t\tuser_pass = m_ftp3.group(2);\n\t\t\thostname = m_ftp3.group(3);\n\n\t\t\tport = m_ftp3.group(4);\n\t\t\tbad_port = m_ftp3.group(5); //this should be null on all valid port inputs\n\t\t\tfile = m_ftp3.group(6);\n\t\t\tif (ends_with_slash) {\n\t\t\t\tfile = file + \"/\";\n\t\t\t}\n\t\t\tif (hostname == null) {\n\t\t\t\tprotocol = null;\n\t\t\t\tuser_pass = null;\n\t\t\t\tport = null;\n\t\t\t\tbad_port = null;\n\t\t\t\tfile = null;\n\t\t\t\tret = false;\n\t\t\t}\n\t\t} else {\n\t\t\t//System.out.println(\"did not match\");\n\t\t}\n\n\t\tif (ret == true) {\n\t\t\t//leave the various regex parts of the string here in case we want to do more validation/debugging in the future\n\t\t}\n\n\t\tif ((hostname != null) && hostname.contains(\"@\")) {\n\t\t\tuser_pass = hostname.substring(0, hostname.indexOf('@'));\n\t\t\thostname = hostname.substring(hostname.indexOf('@') + 1);\n\t\t}\n\t\t//System.out.println(\"uri=\"+_uri+\"=\");\n\t\t//System.out.println(\"protocol=\"+protocol+\"=\");\n\t\t//System.out.println(\"user_pass=\"+user_pass+\"=\");\n\t\t//System.out.println(\"hostname=\"+hostname+\"=\");\n\t\t//System.out.println(\"port=\"+port+\"=\");\n\t\t//System.out.println(\"bad_port=\"+bad_port+\"=\");\n\t\t//System.out.println(\"file=\"+file+\"=\");\n\n\t\tif ((hostname != null)\n\t\t\t\t&& (hostname.startsWith(\":\") || hostname.endsWith(\":\") || hostname\n\t\t\t\t\t\t.contains(\":\"))) {\n\t\t\t//System.out.println(\"bad hostname=\"+hostname+\"=\");\n\t\t\tret = false;\n\t\t}\n\n\t\tif (bad_port != null) {\n\t\t\t//System.out.println(\"bad_port found=\"+bad_port+\"=\");\n\t\t\tret = false;\n\t\t}\n\n\t\tif (ret == false) { //don't parse any bad inputs\n\t\t\treturn ret;\n\t\t}\n\t\tlocal_uri = _uri;\n\t\tlocal_protocol = protocol;\n\t\tint colon_position = -1;\n\t\tif ((user_pass == null) || (user_pass.equals(\"\"))) {\n\t\t\tcolon_position = -1;\n\t\t} else {\n\t\t\tcolon_position = user_pass.indexOf(':');\n\t\t}\n\t\tif ((user_pass == null) || (user_pass.equals(\"\"))) {\n\t\t\tlocal_user = null;\n\t\t\tlocal_pass = null;\n\t\t} else if (colon_position == -1) {\n\t\t\tlocal_user = user_pass;\n\t\t\tlocal_pass = null;\n\t\t} else {\n\t\t\tlocal_user = user_pass.substring(0, colon_position);\n\t\t\tlocal_pass = user_pass.substring(colon_position);\n\t\t}\n\t\t//System.out.println(\"raw local_pass=\"+local_pass+\"=\");\n\t\tif (local_pass != null) {\n\t\t\tif (local_pass.endsWith(\"@\")) {\n\t\t\t\tlocal_pass = local_pass.substring(0, local_pass.length() - 1);\n\t\t\t}\n\t\t\tif (local_pass.startsWith(\":\")) {\n\t\t\t\tlocal_pass = local_pass.substring(1);\n\t\t\t}\n\t\t}\n\t\tlocal_hostname = hostname;\n\t\tlocal_port = port;\n\t\tlocal_file = file;\n\n\t\treturn ret;\n\t}\n\n\tpublic void error_msg(String _s) {\n\t\tSystem.out.println(\"Error in test=\" + _s + \"=\");\n\t\tException e = new Exception(\"\");\n\t\te.printStackTrace();\n\t\tSystem.exit(10);\n\t}\n\n\t/**\n\t * @param args\n\t */\n\tpublic static void main(String[] args) {\n\t\t//test code\n\t\tString s;\n\n\t\t/*\n\t\tv.assertEquals(v.getProtocol(),\"files\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(),\"c:\");\n\t\t */\n\t\t//unknown protocol names\n\t\ts = \"files://c:\";\n\n\t\tVFSURIValidator v = new VFSURIValidator();\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"files://c:\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"FTPS://c:\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"ftps://c:\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"files123://c:\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"fiLE://c:\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\t//file tests\n\t\ts = \"file://c:\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"file\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"c:\");\n\n\t\ts = \"file://d:\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"file\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"d:\");\n\n\t\ts = \"file://e:\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"file\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"e:\");\n\n\t\ts = \"file://z:\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"file\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"z:\");\n\n\t\ts = \"file://c:/\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"file\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"c:/\");\n\n\t\ts = \"file://d:/\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"file\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"d:/\");\n\n\t\ts = \"file://e:/\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"file\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"e:/\");\n\n\t\ts = \"file://z:/\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"file\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"z:/\");\n\n\t\ts = \"file://c:/a\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"file\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"c:/a\");\n\n\t\ts = \"file://d:/a\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"file\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"d:/a\");\n\n\t\ts = \"file://e:/b\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"file\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"e:/b\");\n\n\t\ts = \"file://z:/b\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"file\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"z:/b\");\n\n\t\ts = \"FILE://c:\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"FILE\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"c:\");\n\n\t\ts = \"FILE://d:\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"FILE\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"d:\");\n\n\t\ts = \"FILE://e:\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"FILE\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"e:\");\n\n\t\ts = \"FILE://z:\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"FILE\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"z:\");\n\n\t\ts = \"FILE://c:/\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"FILE\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"c:/\");\n\n\t\ts = \"FILE://d:/\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"FILE\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"d:/\");\n\n\t\ts = \"FILE://e:/\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"FILE\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"e:/\");\n\n\t\ts = \"FILE://z:/\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"FILE\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"z:/\");\n\n\t\ts = \"FILE://c:/a\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"FILE\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"c:/a\");\n\n\t\ts = \"FILE://d:/a\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"FILE\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"d:/a\");\n\n\t\ts = \"FILE://e:/b\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"FILE\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"e:/b\");\n\n\t\ts = \"FILE://z:/b\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"FILE\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"z:/b\");\n\n\t\t//ftp tests\n\t\ts = \"ftp://machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"ftp\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"ftp://machine:1/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"ftp\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getPort(), \"1\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"ftp://machine:12345/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"ftp\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getPort(), \"12345\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"ftp://machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"ftp://user:pass@machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"ftp\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"ftp://user:pass@machine:123/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"ftp\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"123\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"ftp://user:pass@machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"ftp://user:pass:@machine/the_file\"; //can \":\" be part of a password?\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"ftp\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"ftp://user:pass:@machine/the_dir/\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"ftp\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_dir/\");\n\n\t\ts = \"ftp: //user:pass:@machine/the_file\"; //failure tests\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"ftp:/ /user:pass:@machine/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"ftp:/ /user:pass:@machine\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"ftp://user:pass:@:123/a\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"ftp://user:pass:@machine:a/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\t//System.exit(10);\n\t\ts = \"FTP://machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"FTP\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"FTP://machine:1/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"FTP\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"1\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"FTP://machine:12345/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"FTP\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"12345\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"FTP://machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"FTP://user:pass@machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"FTP\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"FTP://user:pass@machine:123/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"FTP\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"123\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"FTP://user:pass@machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"FTP://user:pass:@machine/the_file\"; //can \":\" be part of a password?\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"FTP\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"FTP://user:pass:@machine/the_dir/\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"FTP\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_dir/\");\n\n\t\ts = \"FTP: //user:pass:@machine/the_file\"; //failure tests\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"FTP:/ /user:pass:@machine/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"FTP:/ /user:pass:@machine\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"FTP://user:pass:@:123/a\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"FTP://user:pass:@machine:a/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\t//sftp tests\n\t\ts = \"sftp://machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"sftp\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"sftp://machine:1/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"sftp\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"1\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"sftp://machine:12345/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"sftp\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"12345\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"sftp://machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"sftp://user:pass@machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"sftp\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"sftp://user:pass@machine:123/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"sftp\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"123\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"sftp://user:pass@machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"sftp://user:pass:@machine/the_file\"; //can \":\" be part of a password?\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"sftp\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"sftp://user:pass:@machine/the_dir/\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"sftp\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_dir/\");\n\n\t\ts = \"sftp: //user:pass:@machine/the_file\"; //failure tests\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"sftp:/ /user:pass:@machine/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"sftp:/ /user:pass:@machine\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"sftp://user:pass:@:123/a\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"sftp://user:pass:@machine:a/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"SFTP://machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"SFTP\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"SFTP://machine:1/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"SFTP\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"1\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"SFTP://machine:12345/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"SFTP\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"12345\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"SFTP://machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"SFTP://user:pass@machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"SFTP\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"SFTP://user:pass@machine:123/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"SFTP\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"123\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"SFTP://user:pass@machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"SFTP://user:pass:@machine/the_file\"; //can \":\" be part of a password?\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"SFTP\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"SFTP://user:pass:@machine/the_dir/\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"SFTP\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_dir/\");\n\n\t\ts = \"SFTP: //user:pass:@machine/the_file\"; //failure tests\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"SFTP:/ /user:pass:@machine/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\n\t\ts = \"SFTP:/ /user:pass:@machine\";\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"SFTP://user:pass:@:123/a\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"SFTP://user:pass:@machine:a/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\t//http tests\n\t\ts = \"http://machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"http\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"http://machine:1/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"http\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"1\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"http://machine:12345/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"http\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"12345\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"http://machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"http://user:pass@machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"http\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"http://user:pass@machine:123/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"http\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"123\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"http://user:pass@machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"http://user:pass:@machine/the_file\"; //can \":\" be part of a password?\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"http\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"http://user:pass:@machine/the_dir/\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"http\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_dir/\");\n\n\t\ts = \"http: //user:pass:@machine/the_file\"; //failure tests\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"http:/ /user:pass:@machine/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"http:/ /user:pass:@machine\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"http://user:pass:@:123/a\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"http://user:pass:@machine:a/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"HTTP://machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"HTTP\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"HTTP://machine:1/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"HTTP\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"1\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"HTTP://machine:12345/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"HTTP\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"12345\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"HTTP://machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"HTTP://user:pass@machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"HTTP\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"HTTP://user:pass@machine:123/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"HTTP\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"123\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"HTTP://user:pass@machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"HTTP://user:pass:@machine/the_file\"; //can \":\" be part of a password?\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"HTTP\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"HTTP://user:pass:@machine/the_dir/\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"HTTP\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_dir/\");\n\n\t\ts = \"HTTP: //user:pass:@machine/the_file\"; //failure tests\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"HTTP:/ /user:pass:@machine/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"HTTP:/ /user:pass:@machine\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"HTTP://user:pass:@:123/a\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"HTTP://user:pass:@machine:a/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\t//https tests\n\t\ts = \"https://machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"https\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"https://machine:1/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"https\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"1\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"https://machine:12345/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"https\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"12345\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"https://machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"https://user:pass@machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"https\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"https://user:pass@machine:123/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"https\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"123\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"https://user:pass@machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"https://user:pass:@machine/the_file\"; //can \":\" be part of a password?\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"https\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"https://user:pass:@machine/the_dir/\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"https\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_dir/\");\n\n\t\ts = \"https: //user:pass:@machine/the_file\"; //failure tests\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"https:/ /user:pass:@machine/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"https:/ /user:pass:@machine\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"https://user:pass:@:123/a\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"https://user:pass:@machine:a/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"HTTPS://machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"HTTPS\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"HTTPS://machine:1/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"HTTPS\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"1\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"HTTPS://machine:12345/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"HTTPS\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"12345\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"HTTPS://machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"HTTPS://user:pass@machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"HTTPS\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"HTTPS://user:pass@machine:123/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"HTTPS\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"123\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"HTTPS://user:pass@machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"HTTPS://user:pass:@machine/the_file\"; //can \":\" be part of a password?\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"HTTPS\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"HTTPS://user:pass:@machine/the_dir/\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"HTTPS\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_dir/\");\n\n\t\ts = \"HTTPS: //user:pass:@machine/the_file\"; //failure tests\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"HTTPS:/ /user:pass:@machine/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"HTTPS:/ /user:pass:@machine\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"HTTPS://user:pass:@:123/a\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"HTTPS://user:pass:@machine:a/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\t//webdav tests\n\t\ts = \"webdav://machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"webdav\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"webdav://machine:1/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"webdav\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"1\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"webdav://machine:12345/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"webdav\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"12345\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"webdav://machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"webdav://user:pass@machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"webdav\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"webdav://user:pass@machine:123/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"webdav\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"123\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"webdav://user:pass@machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"webdav://user:pass:@machine/the_file\"; //can \":\" be part of a password?\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"webdav\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"webdav://user:pass:@machine/the_dir/\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\n\t\ts = \"webdav: //user:pass:@machine/the_file\"; //failure tests\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"webdav:/ /user:pass:@machine/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"webdav:/ /user:pass:@machine\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"webdav://user:pass:@:123/a\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"webdav://user:pass:@machine:a/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"WEBDAV://machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"WEBDAV\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"WEBDAV://machine:1/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"WEBDAV\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"1\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"WEBDAV://machine:12345/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"WEBDAV\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"12345\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"WEBDAV://machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"WEBDAV://user:pass@machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"WEBDAV\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"WEBDAV://user:pass@machine:123/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"WEBDAV\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"123\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"WEBDAV://user:pass@machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"WEBDAV://user:pass:@machine/the_file\"; //can \":\" be part of a password?\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"WEBDAV\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"WEBDAV://user:pass:@machine/the_dir/\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"WEBDAV\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_dir/\");\n\n\t\ts = \"WEBDAV: //user:pass:@machine/the_file\"; //failure tests\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"WEBDAV:/ /user:pass:@machine/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"WEBDAV:/ /user:pass:@machine\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"WEBDAV://user:pass:@:123/a\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"WEBDAV://user:pass:@machine:a/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\t//smb tests\n\t\ts = \"smb://machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"smb\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"smb://machine:1/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"smb\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"1\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"smb://machine:12345/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"smb\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"12345\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"smb://machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"smb://user:pass@machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"smb\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"smb://user:pass@machine:123/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"smb\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"123\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"smb://user:pass@machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"smb://user:pass:@machine/the_file\"; //can \":\" be part of a password?\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"smb\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"smb://user:pass:@machine/the_dir/\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"smb\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_dir/\");\n\n\t\ts = \"smb: //user:pass:@machine/the_file\"; //failure tests\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"smb:/ /user:pass:@machine/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"smb:/ /user:pass:@machine\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"smb://user:pass:@:123/a\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"smb://user:pass:@machine:a/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"SMB://machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"SMB\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"SMB://machine:1/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"SMB\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"1\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"SMB://machine:12345/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"SMB\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"12345\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"SMB://machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"SMB://user:pass@machine/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"SMB\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"SMB://user:pass@machine:123/the_file\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"SMB\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertEquals(v.getPort(), \"123\");\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"SMB://user:pass@machine:/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"SMB://user:pass:@machine/the_file\"; //can \":\" be part of a password?\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"SMB\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_file\");\n\n\t\ts = \"SMB://user:pass:@machine/the_dir/\";\n\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"SMB\");\n\t\tv.assertEquals(v.getUser(), \"user\");\n\t\tv.assertEquals(v.getPassword(), \"pass:\");\n\t\tv.assertEquals(v.getHostname(), \"machine\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/the_dir/\");\n\n\t\ts = \"SMB: //user:pass:@machine/the_file\"; //failure tests\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"SMB:/ /user:pass:@machine/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"SMB:/ /user:pass:@machine\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"SMB://user:pass:@:123/a\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"SMB://user:pass:@machine:a/the_file\";\n\n\t\tif (v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertNull(v.getProtocol());\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\t//add tests from Yves\n\t\ts = \"sftp://shell.sf.net\";\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"sftp\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"shell.sf.net\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"file:///C:/home/birdman\";\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"file\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"C:/home/birdman\");\n\n\t\ts = \"file:///home/birdman\";\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"file\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/home/birdman\");\n\n\t\ts = \"file://home/birdman\";\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(),\"file\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertNull(v.getHostname());\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(),\"home/birdman\");\n\n\t\ts = \"webdav://myserver.net/home/yves\";\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"webdav\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"myserver.net\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertEquals(v.getFile(), \"/home/yves\");\n\n\t\ts = \"ftp://ftp.ca.freebsd.org\";\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"ftp\");\n\t\tv.assertNull(v.getUser());\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"ftp.ca.freebsd.org\");\n\t\tv.assertNull(v.getPort());\n\t\tv.assertNull(v.getFile());\n\n\t\ts = \"sftp://yves@shell.sf.net:28\";\n\t\tif (!v.isValid(s)) {\n\t\t\tv.error_msg(s);\n\t\t}\n\t\tv.assertEquals(v.getProtocol(), \"sftp\");\n\t\tv.assertEquals(v.getUser(), \"yves\");\n\t\tv.assertNull(v.getPassword());\n\t\tv.assertEquals(v.getHostname(), \"shell.sf.net\");\n\t\tv.assertEquals(v.getPort(), \"28\");\n\t\tv.assertNull(v.getFile());\n\n\t\tSystem.out.println(\"all done\");\n\t}\n}\n\n", "input": "", "output": "【漏洞类型】Regex Denial of Service (ReDoS)\n【严重性】High\n【漏洞摘要】此代码存在Regex Denial of Service (ReDoS)漏洞,主要通过`Pattern.compile`方法和`Matcher.matches`方法进行URI验证。原始代码中的正则表达式如`Pattern p_file1 = Pattern.compile(\"(file|FILE)://([a-z][ \t]*:)*?(/.*)\");`和`Pattern p_ftp2 = Pattern.compile(\"(ftp|FTP|sftp|SFTP|http|HTTP|https|HTTPS|webdav|WEBDAV|smb|SMB)://(.+:.+@)*([^:]+?/*)([ \t]*:[0-9]+)*([ \t]*:)*(/.*)\");`可能会导致正则表达式执行时间过长,进而引发拒绝服务攻击。具体问题在于正则表达式中使用了贪婪匹配和可选重复,这可能导致在处理恶意输入时,匹配过程耗时过长。\n【关键代码模式】Pattern.compile, Matcher.matches, 贪婪匹配, 可选重复\n【修复方法说明】修复方法通过修改正则表达式以避免贪婪匹配和可选重复,从而提高了正则表达式的性能和安全性。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nxwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-ui/src/main/resources/IconThemesCode/IconPickerMacro.xml\n### 代码内容:\n<?xml version=\"1.1\" encoding=\"UTF-8\"?>\n\n<!--\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n-->\n\n<xwikidoc version=\"1.1\">\n <web>IconThemesCode</web>\n <name>IconPickerMacro</name>\n <language/>\n <defaultLanguage/>\n <translation>0</translation>\n <creator>xwiki:XWiki.Admin</creator>\n <parent>IconThemes.WebHome</parent>\n <author>xwiki:XWiki.Admin</author>\n <contentAuthor>xwiki:XWiki.Admin</contentAuthor>\n <version>1.1</version>\n <title>Icon Picker Macro</title>\n <comment/>\n <minorEdit>false</minorEdit>\n <syntaxId>xwiki/2.1</syntaxId>\n <hidden>true</hidden>\n <content>= Usage =\n{{code}}\n{{iconPicker id=\"\" class=\"\" prefix=\"\" /}}\n{{/code}}\n\n**Where:**\n|=id (optional)|DOM id of the input field where the picker will apply\n|=class (optional)|CSS class of inputs where the picker will apply\n|=prefix (optional)|Prefix to add before the name of the icon in the input field (default: \"{{{image:icon:}}}\")\n== Live example ==\n{{code}}\n{{html}}\n <p>Field 1: <input type=\"text\" id=\"myPicker\" /></p>\n <p>Field 2: <input type=\"text\" class=\"fieldWithPicker\" /></p>\n{{/html}}\n\n{{iconPicker id=\"myPicker\" class=\"fieldWithPicker\" prefix=\"icon:\" /}}\n{{/code}}\n== Play with it ==\n{{html}}\n <p>Field 1: <input type=\"text\" id=\"myPicker\" /></p>\n <p>Field 2: <input type=\"text\" class=\"fieldWithPicker\" /></p>\n{{/html}}\n\n{{iconPicker id=\"myPicker\" class=\"fieldWithPicker\" prefix=\"icon:\" /}}</content>\n <object>\n <name>IconThemesCode.IconPickerMacro</name>\n <number>0</number>\n <className>XWiki.WikiMacroClass</className>\n <guid>a5daaf5a-bdf5-4a63-b0de-db25061f5874</guid>\n <class>\n <name>XWiki.WikiMacroClass</name>\n <customClass/>\n <customMapping/>\n <defaultViewSheet/>\n <defaultEditSheet/>\n <defaultWeb/>\n <nameField/>\n <validationScript/>\n <code>\n <disabled>0</disabled>\n <name>code</name>\n <number>9</number>\n <prettyName>Macro code</prettyName>\n <rows>20</rows>\n <size>40</size>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.TextAreaClass</classType>\n </code>\n <contentDescription>\n <disabled>0</disabled>\n <name>contentDescription</name>\n <number>8</number>\n <prettyName>Content description (Not applicable for \"No content\" type)</prettyName>\n <rows>5</rows>\n <size>40</size>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.TextAreaClass</classType>\n </contentDescription>\n <contentType>\n <cache>0</cache>\n <disabled>0</disabled>\n <displayType>select</displayType>\n <multiSelect>0</multiSelect>\n <name>contentType</name>\n <number>7</number>\n <prettyName>Macro content type</prettyName>\n <relationalStorage>0</relationalStorage>\n <separator>|</separator>\n <separators>|</separators>\n <size>1</size>\n <unmodifiable>0</unmodifiable>\n <values>Optional|Mandatory|No content</values>\n <classType>com.xpn.xwiki.objects.classes.StaticListClass</classType>\n </contentType>\n <defaultCategory>\n <disabled>0</disabled>\n <name>defaultCategory</name>\n <number>4</number>\n <prettyName>Default category</prettyName>\n <size>30</size>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.StringClass</classType>\n </defaultCategory>\n <description>\n <disabled>0</disabled>\n <name>description</name>\n <number>3</number>\n <prettyName>Macro description</prettyName>\n <rows>5</rows>\n <size>40</size>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.TextAreaClass</classType>\n </description>\n <id>\n <disabled>0</disabled>\n <name>id</name>\n <number>1</number>\n <prettyName>Macro id</prettyName>\n <size>30</size>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.StringClass</classType>\n </id>\n <name>\n <disabled>0</disabled>\n <name>name</name>\n <number>2</number>\n <prettyName>Macro name</prettyName>\n <size>30</size>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.StringClass</classType>\n </name>\n <supportsInlineMode>\n <disabled>0</disabled>\n <displayFormType>select</displayFormType>\n <displayType>yesno</displayType>\n <name>supportsInlineMode</name>\n <number>5</number>\n <prettyName>Supports inline mode</prettyName>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.BooleanClass</classType>\n </supportsInlineMode>\n <visibility>\n <cache>0</cache>\n <disabled>0</disabled>\n <displayType>select</displayType>\n <multiSelect>0</multiSelect>\n <name>visibility</name>\n <number>6</number>\n <prettyName>Macro visibility</prettyName>\n <relationalStorage>0</relationalStorage>\n <separator>|</separator>\n <separators>|</separators>\n <size>1</size>\n <unmodifiable>0</unmodifiable>\n <values>Current User|Current Wiki|Global</values>\n <classType>com.xpn.xwiki.objects.classes.StaticListClass</classType>\n </visibility>\n </class>\n <property>\n <code>{{velocity output=\"false\"}}\n $xwiki.ssx.use('IconThemesCode.IconPicker')\n ## The icons themes may need some SSX, so we ask for a rendering of an icon of each icon theme, to be able to display\n ## all icon themes in the picker\n ## ToDo: since it is a bit hacky, a better system would be to dynamically load the needed SSX on demand\n #foreach($iconSetName in $services.icon.iconSetNames)\n $services.icon.render('wiki', $iconSetName)\n #end\n{{/velocity}}\n\n{{velocity}}\n{{html clean=\"false\"}}\n<script>\n require.config({\n paths: {\n 'xwiki-icon-picker': '$xwiki.getURL($services.model.createDocumentReference('', 'IconThemesCode', 'IconPicker'), 'jsx', \"minify=$!{escapetool.url($request.minify)}\")'\n }\n });\n require(['jquery', 'xwiki-icon-picker'], function($) {\n var options = {};\n #if($xcontext.macro.params.parameterNames.contains('prefix'))\n options['prefix'] = '$escapetool.javascript($xcontext.macro.params.prefix)';\n #end\n #if(\"$!xcontext.macro.params.id\" != '')\n $('#${xcontext.macro.params.id}').xwikiIconPicker(options);\n #end\n #if(\"$!xcontext.macro.params.get('class')\" != '')\n $('.${xcontext.macro.params.get('class')}').xwikiIconPicker(options);\n #end\n });\n</script>\n{{/html}}\n{{/velocity}}</code>\n </property>\n <property>\n <contentDescription/>\n </property>\n <property>\n <contentType>No content</contentType>\n </property>\n <property>\n <defaultCategory>Development</defaultCategory>\n </property>\n <property>\n <description>Select an icon within the XWiki icon set.</description>\n </property>\n <property>\n <id>iconPicker</id>\n </property>\n <property>\n <name>Icon Picker</name>\n </property>\n <property>\n <supportsInlineMode>1</supportsInlineMode>\n </property>\n <property>\n <visibility>Current Wiki</visibility>\n </property>\n </object>\n <object>\n <name>IconThemesCode.IconPickerMacro</name>\n <number>0</number>\n <className>XWiki.WikiMacroParameterClass</className>\n <guid>bff4d631-8c3a-47fd-b54a-a542874bb81e</guid>\n <class>\n <name>XWiki.WikiMacroParameterClass</name>\n <customClass/>\n <customMapping/>\n <defaultViewSheet/>\n <defaultEditSheet/>\n <defaultWeb/>\n <nameField/>\n <validationScript/>\n <defaultValue>\n <disabled>0</disabled>\n <name>defaultValue</name>\n <number>4</number>\n <prettyName>Parameter default value</prettyName>\n <size>30</size>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.StringClass</classType>\n </defaultValue>\n <description>\n <disabled>0</disabled>\n <name>description</name>\n <number>2</number>\n <prettyName>Parameter description</prettyName>\n <rows>5</rows>\n <size>40</size>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.TextAreaClass</classType>\n </description>\n <mandatory>\n <disabled>0</disabled>\n <displayFormType>select</displayFormType>\n <displayType>yesno</displayType>\n <name>mandatory</name>\n <number>3</number>\n <prettyName>Parameter mandatory</prettyName>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.BooleanClass</classType>\n </mandatory>\n <name>\n <disabled>0</disabled>\n <name>name</name>\n <number>1</number>\n <prettyName>Parameter name</prettyName>\n <size>30</size>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.StringClass</classType>\n </name>\n </class>\n <property>\n <defaultValue/>\n </property>\n <property>\n <description>DOM id of the input field where the picker will apply</description>\n </property>\n <property>\n <mandatory>0</mandatory>\n </property>\n <property>\n <name>id</name>\n </property>\n </object>\n <object>\n <name>IconThemesCode.IconPickerMacro</name>\n <number>1</number>\n <className>XWiki.WikiMacroParameterClass</className>\n <guid>f9169e0d-a47c-4598-b0d7-4211940d1886</guid>\n <class>\n <name>XWiki.WikiMacroParameterClass</name>\n <customClass/>\n <customMapping/>\n <defaultViewSheet/>\n <defaultEditSheet/>\n <defaultWeb/>\n <nameField/>\n <validationScript/>\n <defaultValue>\n <disabled>0</disabled>\n <name>defaultValue</name>\n <number>4</number>\n <prettyName>Parameter default value</prettyName>\n <size>30</size>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.StringClass</classType>\n </defaultValue>\n <description>\n <disabled>0</disabled>\n <name>description</name>\n <number>2</number>\n <prettyName>Parameter description</prettyName>\n <rows>5</rows>\n <size>40</size>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.TextAreaClass</classType>\n </description>\n <mandatory>\n <disabled>0</disabled>\n <displayFormType>select</displayFormType>\n <displayType>yesno</displayType>\n <name>mandatory</name>\n <number>3</number>\n <prettyName>Parameter mandatory</prettyName>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.BooleanClass</classType>\n </mandatory>\n <name>\n <disabled>0</disabled>\n <name>name</name>\n <number>1</number>\n <prettyName>Parameter name</prettyName>\n <size>30</size>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.StringClass</classType>\n </name>\n </class>\n <property>\n <defaultValue/>\n </property>\n <property>\n <description>CSS class of inputs where the picker will apply</description>\n </property>\n <property>\n <mandatory>0</mandatory>\n </property>\n <property>\n <name>class</name>\n </property>\n </object>\n <object>\n <name>IconThemesCode.IconPickerMacro</name>\n <number>2</number>\n <className>XWiki.WikiMacroParameterClass</className>\n <guid>3a99d1ba-b108-45c6-a828-e76902f25783</guid>\n <class>\n <name>XWiki.WikiMacroParameterClass</name>\n <customClass/>\n <customMapping/>\n <defaultViewSheet/>\n <defaultEditSheet/>\n <defaultWeb/>\n <nameField/>\n <validationScript/>\n <defaultValue>\n <disabled>0</disabled>\n <name>defaultValue</name>\n <number>4</number>\n <prettyName>Parameter default value</prettyName>\n <size>30</size>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.StringClass</classType>\n </defaultValue>\n <description>\n <disabled>0</disabled>\n <name>description</name>\n <number>2</number>\n <prettyName>Parameter description</prettyName>\n <rows>5</rows>\n <size>40</size>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.TextAreaClass</classType>\n </description>\n <mandatory>\n <disabled>0</disabled>\n <displayFormType>select</displayFormType>\n <displayType>yesno</displayType>\n <name>mandatory</name>\n <number>3</number>\n <prettyName>Parameter mandatory</prettyName>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.BooleanClass</classType>\n </mandatory>\n <name>\n <disabled>0</disabled>\n <name>name</name>\n <number>1</number>\n <prettyName>Parameter name</prettyName>\n <size>30</size>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.StringClass</classType>\n </name>\n </class>\n <property>\n <defaultValue/>\n </property>\n <property>\n <description>Prefix to add before the name of the icon in the input field (default: \"image:icon:\")</description>\n </property>\n <property>\n <mandatory>0</mandatory>\n </property>\n <property>\n <name>prefix</name>\n </property>\n </object>\n</xwikidoc>\n\n", "input": "", "output": "【漏洞类型】未验证的用户输入\n【严重性】Medium\n【漏洞摘要】此代码存在未验证用户输入的漏洞,特别是在处理`id`和`class`参数时。攻击者可以通过传递恶意的`id`或`class`值,从而执行潜在的跨站脚本攻击(XSS)。具体表现为在JavaScript代码中直接使用用户输入的参数,而没有进行适当的转义或验证处理(例如,问题行:`$('#${xcontext.macro.params.id}').xwikiIconPicker(options);`和`$('.${xcontext.macro.params.get('class')}').xwikiIconPicker(options);`)。\n【关键代码模式】`xcontext.macro.params.id`和`xcontext.macro.params.get('class')`,未经过滤或转义的用户输入,直接在HTML和JavaScript代码中使用。\n【修复方法说明】通过使用`$escapetool.javascript`对用户输入的参数进行转义处理,修复了未验证用户输入导致的XSS漏洞。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/gribbit/auth/Cookie.java\n### 代码内容:\npackage gribbit.auth;\n\nimport gribbit.server.config.GribbitProperties;\nimport gribbit.util.RandomTokenGenerator;\nimport gribbit.util.WebUtils;\nimport io.netty.handler.codec.http.DefaultCookie;\nimport io.netty.handler.codec.http.ServerCookieEncoder;\n\n/**\n * Cookies!\n */\npublic class Cookie {\n\n private final String name;\n\n private final String path;\n\n /** The unencoded cookie value. */\n private String value;\n\n private long maxAgeSeconds;\n\n private boolean discardAtEndOfBrowserSession;\n\n // -----------------------------------------------------------------------------------------------------\n\n /** The name of the email address cookie. Used to notify the Persona client as to who is logged in. */\n public static final String EMAIL_COOKIE_NAME = \"_email\";\n\n /** The name of the flash cookie. */\n public static final String FLASH_COOKIE_NAME = \"_flash\";\n\n /** The name of the cookie that indicates the auth-required URI the user was trying to visit before logging in. */\n public static final String REDIRECT_AFTER_LOGIN_COOKIE_NAME = \"_redir\";\n\n // -----------------------------------------------------------------------------------------------------\n\n /** The name of the session cookie. */\n public static final String SESSION_COOKIE_NAME = \"_session\";\n\n /** How long a session cookie lasts for. */\n public static final int SESSION_COOKIE_MAX_AGE_SECONDS = 30 * 24 * 60 * 60;\n\n /** Session cookie length (number of random bytes generated before base 64 encoding) */\n public static final int SESSION_COOKIE_LENGTH = 20;\n\n public static String generateRandomSessionToken() {\n return RandomTokenGenerator.generateRandomTokenBase64(Cookie.SESSION_COOKIE_LENGTH);\n }\n\n // ------------------------------------------------------------------------------------------------------\n\n // Valid characters for cookie fields and values\n private static final boolean[] VALID_CHAR = new boolean[256];\n static {\n for (int i = 33; i <= 126; i++)\n VALID_CHAR[i] = true;\n for (char c : new char[] { '\\'', '\"', ',', ';', '\\\\' })\n VALID_CHAR[c] = false;\n }\n\n // Check the chars in a cookie's name and path are valid\n private static void checkValidCookieFieldStr(String str) {\n if (str.length() > 3500) {\n throw new RuntimeException(\"Cookie value too long: \" + str);\n }\n for (int i = 0, n = str.length(); i < n; i++) {\n char c = str.charAt(i);\n if (c > 255 || !VALID_CHAR[c]) {\n throw new RuntimeException(\"Invalid cookie field: \" + str);\n }\n }\n }\n\n // -----------------------------------------------------------------------------------------------------\n\n /**\n * Create a cookie.\n */\n public Cookie(String name, String path, String cookieValue, long maxAgeSeconds, boolean discardAtEndOfBrowserSession) {\n this.name = name;\n checkValidCookieFieldStr(name);\n this.path = path;\n if (path != null) {\n checkValidCookieFieldStr(path);\n }\n this.value = cookieValue;\n this.maxAgeSeconds = maxAgeSeconds;\n this.discardAtEndOfBrowserSession = discardAtEndOfBrowserSession;\n\n if (this.maxAgeSeconds <= 0 && this.maxAgeSeconds != Long.MIN_VALUE) {\n // If maxAge <= 0, cookie is expired immediately (so there is nothing to encode)\n this.value = \"\";\n this.maxAgeSeconds = 0;\n } else {\n // if maxAge == Long.MIN_VALUE or discardAtEndOfBrowserSession is true, cookie expires at end of session\n if (maxAgeSeconds == Long.MIN_VALUE) {\n this.discardAtEndOfBrowserSession = true;\n } else if (this.discardAtEndOfBrowserSession) {\n this.maxAgeSeconds = Long.MIN_VALUE;\n }\n }\n }\n\n /**\n * Create a cookie with the discard flag set to false (cookie is not discarded when browser session closes).\n */\n public Cookie(String name, String path, String cookieValue, long maxAgeInSeconds) {\n this(name, path, cookieValue, maxAgeInSeconds, false);\n }\n\n /**\n * Create a cookie with path unset (meaning, according to the HTTP spec, it will default to the path of the object\n * currently being requested), and the discard flag set to false (cookie is not discarded when browser session\n * closes).\n */\n public Cookie(String name, String cookieValue, long maxAgeInSeconds) {\n this(name, null, cookieValue, maxAgeInSeconds, false);\n }\n\n // -----------------------------------------------------------------------------------------------------\n\n /**\n * Parse a cookie from a Netty Cookie. Will throw an exception if cookie decoding failed for some reason (in this\n * case, ignore the cookie).\n */\n public Cookie(io.netty.handler.codec.http.Cookie nettyCookie) {\n this.name = nettyCookie.name();\n this.path = nettyCookie.path();\n this.value = WebUtils.unescapeCookieValue(nettyCookie.value());\n this.maxAgeSeconds = nettyCookie.maxAge();\n this.discardAtEndOfBrowserSession = nettyCookie.isDiscard();\n }\n\n /** Create a Netty cookie from this Cookie object. */\n public io.netty.handler.codec.http.Cookie toNettyCookie() {\n io.netty.handler.codec.http.Cookie nettyCookie = new DefaultCookie(name, WebUtils.escapeCookieValue(value));\n if (path != null && !path.isEmpty()) {\n nettyCookie.setPath(path);\n }\n nettyCookie.setMaxAge(maxAgeSeconds);\n nettyCookie.setDiscard(discardAtEndOfBrowserSession);\n nettyCookie.setHttpOnly(true); // TODO\n if (GribbitProperties.SSL) {\n nettyCookie.setSecure(true); // TODO\n }\n return nettyCookie;\n }\n\n // -----------------------------------------------------------------------------------------------------\n\n /**\n * Create a cookie that, if set in response, overwrites and deletes the named cookie (by setting maxAgeSeconds to\n * zero). Have to specify the path since there can be multiple cookies with the same name but with different paths;\n * this will only delete the cookie with the matching path.\n */\n public static Cookie deleteCookie(String name, String path) {\n return new Cookie(name, path, \"\", 0, false);\n }\n\n /**\n * Create a cookie that, if set in response, overwrites and deletes the cookie with the same name and path (by\n * setting maxAgeSeconds to zero).\n */\n public static Cookie deleteCookie(Cookie cookie) {\n return new Cookie(cookie.getName(), cookie.getPath(), \"\", 0, false);\n }\n\n // -----------------------------------------------------------------------------------------------------\n\n /**\n * Get the cookie as an HTTP header string, including all cookie headers, with the value escaped or base64-encoded.\n */\n @Override\n public String toString() {\n return ServerCookieEncoder.encode(toNettyCookie());\n }\n\n /** Get the name of the cookie. */\n public String getName() {\n return name;\n }\n\n /** Get the cookie path, or \"\" if the cookie path is not set. */\n public String getPath() {\n return path == null ? \"\" : path;\n }\n\n /** Get unencoded value of cookie. */\n public String getValue() {\n return value;\n }\n\n /** Return true if the cookie has expired. */\n public boolean hasExpired() {\n return maxAgeSeconds <= 0 && maxAgeSeconds != Long.MIN_VALUE;\n }\n}\n\n## 内容2\n### 文件路径:\nsrc/gribbit/auth/User.java\n### 代码内容:\npackage gribbit.auth;\n\nimport gribbit.auth.User.Token.TokenType;\nimport gribbit.exception.UnauthorizedException;\nimport gribbit.model.DBModelStringKey;\nimport gribbit.request.Request;\nimport gribbit.response.Response;\nimport gribbit.server.GribbitServer;\nimport gribbit.server.siteresources.Database;\nimport gribbit.util.AppException;\nimport gribbit.util.Hash;\nimport gribbit.util.WebUtils;\n\nimport java.time.Instant;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.temporal.ChronoUnit;\nimport java.util.HashMap;\n\nimport org.mongojack.MongoCollection;\n\n/**\n * Used to store user identity and authentication information in the database.\n */\n@MongoCollection(name = \"users\")\npublic class User extends DBModelStringKey {\n\n /** Key/value data for user */\n public HashMap<String, String> data;\n\n public String passwordHash;\n\n public String createdDate;\n\n public Boolean emailValidated;\n\n /**\n * Auth token, stored in both encrypted session-in-client cookie and server. Allows for browser cookie to be\n * revoked.\n */\n public Token sessionTok;\n\n /** Store CSRF token in User object to avoid re-calculating it where possible */\n public String csrfTok;\n\n /** Token used for validating email address. */\n public Token emailValidationTok;\n\n /** Token used for resetting password. */\n public Token passwordResetTok;\n\n // -----------------------------------------------------------------------------------------------------------------\n\n // Placeholder in password field for federated logins\n public static final String FEDERATED_LOGIN_PASSWORD_HASH_PLACEHOLDER = \"FEDERATED LOGIN\";\n\n // -----------------------------------------------------------------------------------------------------------------\n\n public User() {\n }\n\n public User(String email) {\n super(email);\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n /** An authentication token, a password reset token or an email address verification token */\n public static class Token {\n public String token;\n public Long expires;\n public Token.TokenType tokType;\n\n public enum TokenType {\n SESSION, PW_RESET, EMAIL_VERIF\n };\n\n public Token() {\n }\n\n public Token(Token.TokenType tokType, Instant expires) {\n if (expires.isBefore(Instant.now())) {\n throw new IllegalArgumentException(\"Token already expired\");\n }\n this.tokType = tokType;\n // Generate token as random base64-encoded number\n this.token = Cookie.generateRandomSessionToken();\n this.expires = expires.toEpochMilli();\n }\n\n public Token(Token.TokenType tokType, int numSecondsValid) {\n this(tokType, Instant.now().plus(numSecondsValid, ChronoUnit.SECONDS));\n }\n\n public boolean hasExpired() {\n return expires == null || expires < Instant.now().toEpochMilli();\n }\n\n @Override\n public String toString() {\n return tokType + \"\\t\" + token + \"\\t\" + expires;\n }\n }\n\n /**\n * Take an encrypted token, decrypt it, extract the username and token, look up the user, and make sure that the\n * copy of the auth token in the user matches the copy in the encrypted token, and that the token has not expired.\n * Throws an exception if any of this fails. Returns the user if it all succeeds.\n */\n // FIXME: remove this, and store tokens in user\n public static User validateTok(String email, String suppliedToken, TokenType tokType) throws Exception {\n if (email.isEmpty() || email.equals(\"null\") || email.indexOf('@') < 0) {\n throw new AppException(\"Invalid email address\");\n }\n if (suppliedToken == null || suppliedToken.isEmpty()) {\n throw new AppException(\"Invalid token\");\n }\n\n // Look up user with this email addr\n User user = User.findByEmail(email);\n if (user == null) {\n throw new AppException(\"User account does not exist\");\n }\n\n switch (tokType) {\n\n case SESSION:\n if (user.sessionTok == null || user.sessionTok.hasExpired() || !user.sessionTok.token.equals(suppliedToken)) {\n // Clear token if there is a mismatch, this will prevent users that manage to crack the cookie\n // encryption key from doing much, because they would also have to guess the token to log in.\n // Each attempt to guess the auth token will log them out and require them to log in successfully\n // to generate a new token. The account cannot be accessed using any old session-in-client cookie,\n // because there is no auth token on the server until the next successful login.\n user.clearSessionTok();\n } else {\n // User exists and token is valid; return user\n return user;\n }\n break;\n\n case EMAIL_VERIF:\n if (user.emailValidationTok == null || user.emailValidationTok.hasExpired()\n || !user.emailValidationTok.token.equals(suppliedToken)) {\n // Clear token if there is a mismatch, this means that if a user uses an old email validation link,\n // it will invalidate the most recent link.\n user.clearEmailValidationTok();\n } else {\n // User exists and token is valid; return user\n return user;\n }\n break;\n\n case PW_RESET:\n if (user.passwordResetTok == null || user.passwordResetTok.hasExpired()\n || !user.passwordResetTok.token.equals(suppliedToken)) {\n // Clear token if there is a mismatch, this means that if a user uses an old password reset link,\n // it will invalidate the most recent link.\n user.clearPasswordResetTok();\n } else {\n // User exists and token is valid; return user\n return user;\n }\n break;\n\n default:\n break;\n }\n throw new AppException(\"Token has expired or does not match\");\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n /**\n * Puts a key-value pair into the user data HashMap. N.B. does not save user, you need to do that manually once all\n * key-value pairs have been put.\n */\n public void putData(String key, String val) {\n if (this.data == null) {\n this.data = new HashMap<>();\n }\n this.data.put(key, val);\n }\n\n /** Gets the value corresponding to a given key in the user data HashMap, or null if not found. */\n public String getData(String key) {\n return this.data == null ? null : this.data.get(key);\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n /** Return true if auth token is absent for user or has expired */\n public boolean sessionTokHasExpired() {\n return sessionTok == null || sessionTok.hasExpired();\n }\n\n /**\n * Create a new email validation token, store it in the user's account, and return the token. Expires after 1 day.\n */\n public String generateNewEmailValidationTok() {\n emailValidationTok = new Token(TokenType.EMAIL_VERIF, 1);\n emailValidated = false;\n save();\n return emailValidationTok.token;\n }\n\n /**\n * Create a new password reset token, store it in the user's account, and return the token. Expires after 1 day.\n */\n public String generateNewPasswordResetTok() {\n passwordResetTok = new Token(TokenType.PW_RESET, 1);\n save();\n return passwordResetTok.token;\n }\n\n /** Clear the auth token, meaning the user will have to log in again to get another token. */\n public void clearSessionTok() {\n sessionTok = null;\n csrfTok = null;\n save();\n }\n\n /** Clear the password reset token, and save user account */\n public void clearPasswordResetTok() {\n passwordResetTok = null;\n save();\n }\n\n /** Clear the email validation token, and save user account */\n public void clearEmailValidationTok() {\n emailValidationTok = null;\n save();\n }\n\n public void markEmailValidated() {\n emailValidated = true;\n save();\n }\n\n public boolean emailIsValidated() {\n return emailValidated != null && emailValidated;\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n /**\n * Change a user's password, forcing other clients to be logged out, then log this client in with a new\n * authentication token.\n * \n * @throws AppException\n * if new password is too short or invalid, or if user is not logged in, or session has expired.\n * @throws UnauthorizedException\n * if user is not whitelisted for login\n */\n public void changePassword(String newPassword, Response response) throws UnauthorizedException, AppException {\n if (sessionTokHasExpired()) {\n throw new UnauthorizedException(\"Session has expired\");\n }\n // Re-hash user password\n passwordHash = Hash.hashPassword(newPassword);\n // Generate a new session token and save user.\n // Invalidates current session, forcing other clients to be logged out.\n logIn(response);\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n public static User findByEmail(String email) {\n if (email == null || email.isEmpty())\n return null;\n String emailNormalized = WebUtils.validateAndNormalizeEmailAddr(email);\n if (emailNormalized == null)\n return null;\n return Database.findOneById(User.class, email);\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n /**\n * Authenticate a user based on values of POST params \"email\" and \"password\".\n * \n * @return User if successfully authenticated, null otherwise\n */\n public static User authenticate(String email, String password, Response response) throws UnauthorizedException {\n // FIXME: Allow only one login attempt per email address every 5 seconds. Add email addrs to a ConcurrentTreeSet\n // or something (*if* the email addr is already in the database, to prevent DoS), and every 5s, purge old\n // entries from the tree. If an attempt is made in less than 5s, then return an error rather than blocking for\n // up to 5s, again to prevent DoS. \n User user = findByEmail(email);\n if (user != null) {\n // Get the hash password from the salt + clear password (N.B. It takes 80ms to run BCrypt)\n if (!FEDERATED_LOGIN_PASSWORD_HASH_PLACEHOLDER.equals(user.passwordHash)\n && Hash.checkPassword(password, user.passwordHash)) {\n // User successfully authenticated.\n user.logIn(response);\n return user;\n }\n }\n // If user was not successfully logged in for some reason, delete cookie to be extra paranoid\n user.logOut(response);\n return null;\n }\n\n /**\n * Decrypt the session cookie in the HttpRequest, look up the user, and return the user if the user's auth token is\n * valid (i.e. if their session has not expired or been revoked).\n * \n * @return Returns null if session is invalid or user is no longer allowed to log in.\n */\n public static User getLoggedInUser(Request req) {\n // Get email address from cookie\n Cookie emailCookie = req.getCookie(Cookie.EMAIL_COOKIE_NAME);\n if (emailCookie != null && !emailCookie.hasExpired()) {\n String email = emailCookie.getValue();\n\n // Check user against login whitelist, if it exists (in case whitelist has changed)\n if (GribbitServer.loginWhitelistChecker == null\n || GribbitServer.loginWhitelistChecker.allowUserToLogin(email)) {\n\n // Get session cookie\n Cookie sessionCookie = req.getCookie(Cookie.SESSION_COOKIE_NAME);\n if (sessionCookie != null && !sessionCookie.hasExpired()) {\n try {\n // Look up email address in database, and check session cookie against session token stored in\n // the database for that email address\n User user = validateTok(email, sessionCookie.getValue(), TokenType.SESSION);\n // If no exception thrown, user is logged in and auth token is valid\n return user;\n\n } catch (Exception e) {\n }\n }\n }\n }\n return null;\n }\n\n /** Delete session cookies */\n public static void removeLoginCookies(Response response) {\n response.deleteCookie(Cookie.SESSION_COOKIE_NAME);\n response.deleteCookie(Cookie.EMAIL_COOKIE_NAME);\n }\n\n /**\n * Invalidate all current login sessions for this user.\n */\n public void logOut(Response response) {\n clearSessionTok();\n removeLoginCookies(response);\n }\n\n /**\n * See if there is a logged in user, and log them out if they are logged in.\n */\n public static void logOutUser(Request request, Response response) {\n User user = getLoggedInUser(request);\n if (user != null) {\n user.logOut(response);\n } else {\n // If no logged in user, just delete the session cookies\n removeLoginCookies(response);\n }\n }\n\n /**\n * Create a new authentication token for user and save session cookie.\n * \n * @throws UnauthorizedException\n * if the user is not whitelisted for login, or their login session has expired.\n */\n public void logIn(Response response) throws UnauthorizedException {\n // Check user against login whitelist, if it exists\n if (GribbitServer.loginWhitelistChecker == null || GribbitServer.loginWhitelistChecker.allowUserToLogin(id)) {\n\n // Create new session token\n sessionTok = new Token(TokenType.SESSION, Cookie.SESSION_COOKIE_MAX_AGE_SECONDS);\n csrfTok = CSRF.generateRandomCSRFToken();\n save();\n if (sessionTokHasExpired()) {\n // Shouldn't happen, since we just created session tok, but just in case\n clearSessionTok();\n throw new UnauthorizedException(\"Couldn't create auth session\");\n }\n\n // Save login cookies in result\n response.setCookie(new Cookie(Cookie.SESSION_COOKIE_NAME, \"/\", sessionTok.token,\n Cookie.SESSION_COOKIE_MAX_AGE_SECONDS));\n response.setCookie(new Cookie(Cookie.EMAIL_COOKIE_NAME, \"/\", id, Cookie.SESSION_COOKIE_MAX_AGE_SECONDS));\n\n } else {\n // User is not authorized\n throw new UnauthorizedException(\"User is not whitelisted for login: \" + id);\n }\n }\n\n // -----------------------------------------------------------------------------------------------------\n\n /**\n * Create a user and log them in.\n * \n * @throws UnauthorizedException\n * if a user with this email addr already exists.\n */\n private static User create(String email, String passwordHash, boolean validateEmail, Response response)\n throws UnauthorizedException {\n // Check user against login whitelist, if it exists\n if (GribbitServer.loginWhitelistChecker == null || GribbitServer.loginWhitelistChecker.allowUserToLogin(email)) {\n\n // Check if a user of this name already exists, and if not, create user record in database.\n // Should probably be a transaction, although if the record is created twice within a\n // short period of time, one of the two account creation operations will simply have its\n // authorization cookie overwritten by the other, so the first session will be logged out.\n // Either way, users creating an account with the given email address must be in control\n // of that email account, so this is still secure.\n if (findByEmail(email) != null) {\n throw new UnauthorizedException(\"Could not create new user: user \\\"\" + email + \"\\\" already exists\");\n }\n\n User user = new User(email);\n\n user.passwordHash = passwordHash;\n\n user.createdDate = ZonedDateTime.now().format(DateTimeFormatter.ISO_ZONED_DATE_TIME);\n user.emailValidated = validateEmail;\n\n // Log in and save user \n user.logIn(response);\n\n return user;\n\n } else {\n // User is not authorized\n throw new UnauthorizedException(\"User is not whitelisted for account creation: \" + email);\n }\n }\n\n /**\n * Create a user from email and password hash, and log them in.\n * \n * @throws UnauthorizedException\n * if a user with this email addr already exists.\n */\n public static User create(String email, String passwordHash, Response response) throws UnauthorizedException {\n return create(email, passwordHash, /* validateEmail = */false, response);\n }\n\n /**\n * Create a user from a Persona login, and log them in.\n *\n * @throws UnauthorizedException\n * if a user with this email addr already exists.\n */\n public static User createFederatedLoginUser(String email, Response response) throws UnauthorizedException {\n return create(email, FEDERATED_LOGIN_PASSWORD_HASH_PLACEHOLDER, /* validateEmail = */true, response);\n }\n}\n\n## 内容3\n### 文件路径:\nsrc/gribbit/request/HttpRequestHandler.java\n### 代码内容:\npackage gribbit.request;\n\nimport static io.netty.handler.codec.http.HttpHeaderNames.CACHE_CONTROL;\nimport static io.netty.handler.codec.http.HttpHeaderNames.ACCEPT_ENCODING;\nimport static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION;\nimport static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_ENCODING;\nimport static io.netty.handler.codec.http.HttpHeaderNames.SERVER;\nimport static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;\nimport static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;\nimport static io.netty.handler.codec.http.HttpHeaderNames.DATE;\nimport static io.netty.handler.codec.http.HttpHeaderNames.ETAG;\nimport static io.netty.handler.codec.http.HttpHeaderNames.EXPECT;\nimport static io.netty.handler.codec.http.HttpHeaderNames.EXPIRES;\nimport static io.netty.handler.codec.http.HttpHeaderNames.LAST_MODIFIED;\nimport static io.netty.handler.codec.http.HttpHeaderNames.PRAGMA;\nimport static io.netty.handler.codec.http.HttpHeaderNames.SET_COOKIE;\nimport static io.netty.handler.codec.http.HttpHeaderValues.GZIP;\nimport static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE;\nimport gribbit.auth.Cookie;\nimport gribbit.auth.User;\nimport gribbit.response.ErrorResponse;\nimport gribbit.response.HTMLPageResponse;\nimport gribbit.response.HTMLResponse;\nimport gribbit.response.NotModifiedResponse;\nimport gribbit.response.Response;\nimport gribbit.response.flashmsg.FlashMessage;\nimport gribbit.route.RouteHandler;\nimport gribbit.route.RouteHandlerAuthAndValidatedEmailRequired;\nimport gribbit.route.RouteHandlerAuthRequired;\nimport gribbit.route.RouteInfo;\nimport gribbit.server.GribbitServer;\nimport gribbit.server.config.GribbitProperties;\nimport gribbit.server.siteresources.CacheExtension;\nimport gribbit.server.siteresources.CacheExtension.HashInfo;\nimport gribbit.util.Log;\nimport gribbit.util.WebUtils;\nimport io.netty.buffer.ByteBuf;\nimport io.netty.buffer.ByteBufOutputStream;\nimport io.netty.buffer.Unpooled;\nimport io.netty.channel.ChannelFuture;\nimport io.netty.channel.ChannelFutureListener;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.channel.DefaultFileRegion;\nimport io.netty.channel.SimpleChannelInboundHandler;\nimport io.netty.handler.codec.http.DefaultFullHttpRequest;\nimport io.netty.handler.codec.http.DefaultFullHttpResponse;\nimport io.netty.handler.codec.http.DefaultHttpResponse;\nimport io.netty.handler.codec.http.FullHttpRequest;\nimport io.netty.handler.codec.http.FullHttpResponse;\nimport io.netty.handler.codec.http.HttpChunkedInput;\nimport io.netty.handler.codec.http.HttpContent;\nimport io.netty.handler.codec.http.HttpHeaderUtil;\nimport io.netty.handler.codec.http.HttpHeaders;\nimport io.netty.handler.codec.http.HttpMethod;\nimport io.netty.handler.codec.http.HttpRequest;\nimport io.netty.handler.codec.http.HttpResponseStatus;\nimport io.netty.handler.codec.http.HttpVersion;\nimport io.netty.handler.codec.http.LastHttpContent;\nimport io.netty.handler.codec.http.ServerCookieEncoder;\nimport io.netty.handler.codec.http.multipart.Attribute;\nimport io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;\nimport io.netty.handler.codec.http.multipart.DiskAttribute;\nimport io.netty.handler.codec.http.multipart.DiskFileUpload;\nimport io.netty.handler.codec.http.multipart.FileUpload;\nimport io.netty.handler.codec.http.multipart.HttpDataFactory;\nimport io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;\nimport io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException;\nimport io.netty.handler.codec.http.multipart.InterfaceHttpData;\nimport io.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType;\nimport io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;\nimport io.netty.handler.codec.http.websocketx.PingWebSocketFrame;\nimport io.netty.handler.codec.http.websocketx.PongWebSocketFrame;\nimport io.netty.handler.codec.http.websocketx.TextWebSocketFrame;\nimport io.netty.handler.codec.http.websocketx.WebSocketFrame;\nimport io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;\nimport io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;\nimport io.netty.handler.ssl.NotSslRecordException;\nimport io.netty.handler.ssl.SslHandler;\nimport io.netty.handler.stream.ChunkedFile;\nimport io.netty.util.CharsetUtil;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.RandomAccessFile;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.nio.charset.Charset;\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map.Entry;\nimport java.util.zip.GZIPOutputStream;\n\npublic class HttpRequestHandler extends SimpleChannelInboundHandler<Object> {\n\n private Request request;\n\n private boolean closeAfterWrite = false;\n private boolean addKeepAliveHeader = false;\n\n // -----------------------------------------------------------------------------------------------------------------\n\n // Use disk for HTTP data if size >16kB \n private static final HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE);\n\n static {\n // Delete temporary files on exit\n DiskFileUpload.deleteOnExitTemporaryFile = true;\n DiskAttribute.deleteOnExitTemporaryFile = true;\n // Use system temp directory\n DiskFileUpload.baseDirectory = null;\n DiskAttribute.baseDirectory = null;\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n private HttpPostRequestDecoder decoder;\n\n private void destroyDecoder() {\n if (request != null) {\n try {\n // Release any temporary files\n request.releasePostFileUploadParams();\n } catch (Exception e) {\n }\n }\n if (decoder != null) {\n try {\n decoder.cleanFiles();\n decoder.destroy();\n decoder = null;\n } catch (Exception e) {\n }\n }\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n /**\n * Set Date and cache-related headers.\n * \n * TODO: read http://www.mobify.com/blog/beginners-guide-to-http-cache-headers/ TODO: read\n * https://www.mnot.net/cache_docs/\n */\n private void setDateAndCacheHeaders(HttpHeaders httpHeaders, ZonedDateTime timeNow, long lastModifiedEpochSeconds,\n long hashKeyMaxRemainingAgeSeconds, String hashKey) {\n // Date header uses server time, and should use the same clock as Expires and Last-Modified\n httpHeaders.set(DATE, timeNow.format(DateTimeFormatter.RFC_1123_DATE_TIME));\n\n // Last-Modified is used to determine whether a Not Modified response should be returned on the next request\n if (lastModifiedEpochSeconds > 0) {\n // Add last modified header to cacheable resources. This is needed because Chrome sends\n // \"Cache-Control: max-age=0\" when the user types in a URL and hits enter, or hits refresh.\n // In these circumstances, sending back \"Cache-Control: public, max-age=31536000\" does\n // no good, because the browser has already requested the resource rather than relying on\n // its cache. By setting the last modified header for all cacheable resources, we can\n // at least send \"Not Modified\" as a response if the resource has not been modified,\n // which doesn't save on roundtrips, but at least saves on re-transferring the resources\n // to the browser when they're already in the browser's cache.\n httpHeaders.set(\n LAST_MODIFIED,\n ZonedDateTime.ofInstant(Instant.ofEpochSecond(lastModifiedEpochSeconds), ZoneId.of(\"UTC\")).format(\n DateTimeFormatter.RFC_1123_DATE_TIME));\n }\n\n // Cache hashed URIs forever (or for the specified amount of time)\n if (hashKey != null) {\n // Negative max age => cache indefinitely (although the spec only allows for one year, or 31536000 seconds)\n long maxAgeSeconds = hashKeyMaxRemainingAgeSeconds < 0 ? 31536000 : hashKeyMaxRemainingAgeSeconds;\n\n // Cache hash URIs for one year\n httpHeaders.set(CACHE_CONTROL, \"public, max-age=\" + maxAgeSeconds);\n httpHeaders.set(EXPIRES, timeNow.plusSeconds(maxAgeSeconds).format(DateTimeFormatter.RFC_1123_DATE_TIME));\n httpHeaders.set(ETAG, hashKey);\n }\n\n if (hashKey == null && lastModifiedEpochSeconds == 0) {\n // Disable caching for all resources that don't have a last modified time (all static file\n // resources have a last modified time, so these headers will never be set for files, and \n // they won't be set for dynamic resources that don't manually set a last modified time).\n // Without these headers, the server will not have a last modified timestamp to check\n // against its own timestamp on subsequent requests, so cannot return Not Modified.\n // This is the minimum necessary set of headers for disabling caching, see http://goo.gl/yXGd2x\n httpHeaders.add(CACHE_CONTROL, \"no-cache, no-store, must-revalidate\"); // HTTP 1.1\n httpHeaders.add(PRAGMA, \"no-cache\"); // HTTP 1.0\n httpHeaders.add(EXPIRES, \"0\"); // Proxies\n }\n\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n /** Call the route handler for the given route. */\n private static Response getResponseForRoute(RouteInfo route, Request req, User user) throws Exception {\n Response response;\n try {\n // Call the RestHandler for the route\n response = route.callHandler(req, user);\n\n } catch (Exception e) {\n Log.exception(\"Exception while handling URI \" + req.getURI(), e);\n try {\n // Call Internal Server Error handler on exception\n response = GribbitServer.siteResources.getInternalServerErrorRoute().callHandler(req, user);\n } catch (Exception e1) {\n // Fallback in case there's an exception in the Internal Server Error handler\n Log.exception(\"Error in internal server error handler while handling URI \" + req.getURI(), e1);\n response = new ErrorResponse(HttpResponseStatus.INTERNAL_SERVER_ERROR, \"Internal Server Error\");\n }\n }\n return response;\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n /** Call the given error handler route. */\n private static Response getResponseForErrorHandlerRoute(RouteInfo route, Request req, User user) throws Exception {\n // Temporarily replace the method on the original route with a GET request on the error handler's route\n String origURI = req.getURI();\n HttpMethod origMethod = req.getMethod();\n req.setURI(route.getRoutePath());\n req.setMethod(HttpMethod.GET);\n // Call the error handler\n Response response = getResponseForRoute(route, req, user);\n req.setURI(origURI);\n req.setMethod(origMethod);\n return response;\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n /** Serve a static file. */\n private void serveStaticFile(String reqURI, String hashKey, File staticResourceFile, long lastModifiedEpochSeconds,\n ChannelHandlerContext ctx) throws Exception {\n\n RandomAccessFile fileToServe = null;\n try {\n // Create new RandomAccessFile (which allows us to find file length etc.)\n fileToServe = new RandomAccessFile(staticResourceFile, \"r\");\n\n // -----------------------------------------\n // Serve a static file (not authenticated)\n // -----------------------------------------\n\n DefaultHttpResponse httpRes = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);\n httpRes.headers().add(\"Server\", GribbitServer.SERVER_IDENTIFIER);\n\n long fileLength = fileToServe.length();\n httpRes.headers().set(CONTENT_LENGTH, Long.toString(fileLength));\n WebUtils.setContentTypeHeaders(httpRes.headers(), staticResourceFile.getPath());\n\n // If the file contents have changed since the last time the file was hashed,\n // schedule the file to be hashed in the background so that future references to the\n // file's URI in a src/href attribute of served HTML templates will include a hash\n // URI rather than the original URI for the file, allowing the browser to cache the\n // file indefinitely until it changes.\n CacheExtension.updateHashURI(reqURI, staticResourceFile);\n\n // If file was already cached, and the request URI included the hash key, then this is\n // the first time this client has fetched this file since the browser cache was last\n // cleared. Mark this resource as indefinitely cached. If the file is not being served\n // on a hash URI, then at least set the Last-Modified header, so that if the client\n // requests the same unmodified resource again on the same non-hash URI, the server can\n // return Not Modified instead of serving the contents of the file.\n setDateAndCacheHeaders(httpRes.headers(), ZonedDateTime.now(), lastModifiedEpochSeconds, //\n hashKey != null ? /* cache indefinitely: */-1 : /* ignored if hashKey == null: */0, hashKey);\n\n if (addKeepAliveHeader) {\n httpRes.headers().add(CONNECTION, KEEP_ALIVE);\n }\n\n // Write HTTP headers to channel\n ctx.write(httpRes);\n\n // Write file content to channel.\n // Can add ChannelProgressiveFutureListener to sendFileFuture if we need to track\n // progress (e.g. to update user's UI over a web socket to show download progress.)\n ChannelFuture sendFileFuture;\n ChannelFuture lastContentFuture;\n if (ctx.pipeline().get(SslHandler.class) == null) {\n // Use FileRegions if possible, which supports zero-copy / mmio\n sendFileFuture =\n ctx.write(new DefaultFileRegion(fileToServe.getChannel(), 0, fileLength),\n ctx.newProgressivePromise());\n // Write the end marker\n lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);\n } else {\n // Can't use FileRegions / zero-copy with SSL\n sendFileFuture =\n ctx.write(new HttpChunkedInput(new ChunkedFile(fileToServe, 0, fileLength, 1)),\n ctx.newProgressivePromise());\n // HttpChunkedInput will write the end marker (LastHttpContent) for us.\n // See https://github.com/netty/netty/commit/4ba2ce3cbbc55391520cfc98a7d4227630fbf978\n lastContentFuture = sendFileFuture;\n }\n\n // sendFileFuture.addListener(new ChannelProgressiveFutureListener() {\n // @Override\n // public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) {\n // if (total < 0) { // total unknown\n // System.err.println(future.channel() + \" Transfer progress: \" + progress);\n // } else {\n // System.err.println(future.channel() + \" Transfer progress: \" + progress + \" / \" + total);\n // }\n // }\n //\n // @Override\n // public void operationComplete(ChannelProgressiveFuture future) {\n // System.err.println(future.channel() + \" Transfer complete.\");\n // }\n // });\n\n // Close connection after flush if needed, and close file after flush \n final RandomAccessFile fileToClose = fileToServe;\n lastContentFuture.addListener(new ChannelFutureListener() {\n @Override\n public void operationComplete(ChannelFuture future) {\n if (closeAfterWrite) {\n future.channel().close();\n }\n try {\n fileToClose.close();\n } catch (IOException e) {\n }\n }\n });\n\n } catch (Exception e) {\n if (fileToServe != null) {\n try {\n fileToServe.close();\n } catch (IOException e1) {\n }\n }\n throw new RuntimeException(\"Exception serving static file\", e);\n }\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n /** Serve an HTTP response (anything other than a static file). */\n private void serveHttpResponse(String reqURI, Response response, boolean isHEAD, boolean acceptEncodingGzip,\n ZonedDateTime timeNow, boolean hashTheResponse, long hashKeyMaxRemainingAgeSeconds, String hashKey,\n ChannelHandlerContext ctx) throws Exception {\n\n // Add any pending flash messages to the response, if the response is an HTML page\n if (response instanceof HTMLPageResponse) {\n // Add flash messages to response template, if any\n ArrayList<FlashMessage> flashMessages = request.getFlashMessages();\n if (flashMessages != null) {\n // Render pending flash messages into the HTML page\n ((HTMLPageResponse) response).setFlashMessages(flashMessages);\n // Clear the flash message cookie\n response.deleteCookie(Cookie.FLASH_COOKIE_NAME);\n }\n } else {\n // Store any un-displayed flash messages back in the cookie\n ArrayList<FlashMessage> flashMessages = request.getFlashMessages();\n if (flashMessages != null) {\n response.setCookie(new Cookie(Cookie.FLASH_COOKIE_NAME, \"/\",\n FlashMessage.toCookieString(flashMessages), 60));\n }\n }\n\n // Get the content of the response as a byte buffer.\n ByteBuf content;\n String contentType;\n if (response instanceof HTMLResponse) {\n // If \"?_getmodel=1\" is appended to the request URL of a request that returns an HTML response,\n // then the data model backing the response will be returned as JSON instead of rendered HTML.\n boolean isGetModelRequest = GribbitProperties.ALLOW_GET_MODEL && request.isGetModelRequest();\n content = ((HTMLResponse) response).getContent(isGetModelRequest);\n contentType = isGetModelRequest ? \"application/json;charset=utf-8\" : response.getContentType();\n } else {\n // Not a \"getmodel\" request, just get the content from the response\n content = response.getContent();\n contentType = response.getContentType();\n }\n byte[] contentBytes = content.array();\n\n // Gzip content if the configuration property is set to allow gzip, and the client supports gzip encoding,\n // and the content size is larger than 1kb, and the content type is compressible \n ByteBuf gzippedContent = null;\n if (GribbitProperties.CONTENT_GZIP && //\n acceptEncodingGzip //\n && content.readableBytes() > 1024 //\n && WebUtils.isCompressibleContentType(contentType)) {\n gzippedContent = Unpooled.buffer(/* initialCapacity = */content.readableBytes());\n // TODO: compare speed to using JZlib.GZIPOutputStream\n GZIPOutputStream gzipStream = new GZIPOutputStream(new ByteBufOutputStream(gzippedContent));\n gzipStream.write(contentBytes);\n gzipStream.close();\n }\n\n // Create a FullHttpResponse object that wraps the response status and content\n HttpResponseStatus status = response.getStatus();\n DefaultFullHttpResponse httpRes = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, //\n gzippedContent != null ? gzippedContent : content);\n httpRes.headers().set(CONTENT_TYPE, contentType);\n httpRes.headers().set(CONTENT_LENGTH,\n Integer.toString(gzippedContent != null ? gzippedContent.readableBytes() : content.readableBytes()));\n if (gzippedContent != null) {\n httpRes.headers().set(CONTENT_ENCODING, GZIP);\n }\n\n httpRes.headers().add(SERVER, GribbitServer.SERVER_IDENTIFIER);\n\n // Add an Accept-Encoding: gzip header to the response to let the client know that in future\n // it can send compressed requests. (This header is probably ignored by most clients, because\n // on initial request they don't know yet if the server can accept compressed content, but\n // there must be clients out there that look for this header and compress content on the\n // second and subsequent requests? See http://stackoverflow.com/a/1450163/3950982 )\n httpRes.headers().add(ACCEPT_ENCODING, \"gzip\");\n\n // Set date and cache headers\n setDateAndCacheHeaders(httpRes.headers(), timeNow, response.getLastModifiedEpochSeconds(),\n hashKeyMaxRemainingAgeSeconds, hashKey);\n\n // If the response needs hashing, and the response does not have an error status, then schedule the\n // content of the response for hashing, and store a mapping from the original request URI to the\n // hash URI so that future HTML responses that have src/href attributes that contain this request\n // URI will replace this request URI with the hash URI instead. This will mean the client will\n // fetch that hash URI only once until it expires in the cache, so that on subsequent requests,\n // the linked resource won't even be requested from the server.\n if (hashTheResponse && status == HttpResponseStatus.OK) {\n CacheExtension.updateHashURI(reqURI, content, response.getLastModifiedEpochSeconds());\n }\n\n // Release the content ByteBuf after last usage if gzippedContent is being used instead\n if (gzippedContent != null) {\n content.release();\n }\n\n if (isHEAD) {\n // Don't return a body for HEAD requests (but still return the content length,\n // set in the header above)\n httpRes.content().clear();\n }\n\n // Delete requested cookies in the response\n HashSet<String> cookiesToDelete = response.getCookiesToDelete();\n if (cookiesToDelete != null) {\n for (String cookieName : cookiesToDelete) {\n // Log.fine(\"Cookie to delete for req \" + reqURI + \" : \" + cookieName);\n ArrayList<Cookie> allCookiesWithName = request.getAllCookiesWithName(cookieName);\n if (allCookiesWithName != null) {\n for (Cookie cookie : allCookiesWithName) {\n // Delete all cookies with the requested name (there may be multiple cookies\n // with this name but with different paths)\n String deleteCookieStr =\n ServerCookieEncoder.encode(Cookie.deleteCookie(cookie).toNettyCookie());\n httpRes.headers().add(SET_COOKIE, deleteCookieStr);\n }\n }\n }\n }\n\n // Set requested cookies in the response\n ArrayList<Cookie> cookiesToSet = response.getCookiesToSet();\n if (cookiesToSet != null) {\n for (Cookie cookie : cookiesToSet) {\n if (cookiesToDelete != null && cookiesToDelete.contains(cookie.getName())) {\n Log.warning(\"Tried to delete and set the cookie \\\"\" + cookie.getName()\n + \"\\\" in the same response -- ignoring the set request\");\n } else {\n String setCookieStr = ServerCookieEncoder.encode(cookie.toNettyCookie());\n httpRes.headers().add(SET_COOKIE, setCookieStr);\n }\n }\n }\n\n // Add any custom headers from the Response object\n if (response.getCustomHeaders() != null) {\n for (Entry<String, String> header : response.getCustomHeaders().entrySet()) {\n httpRes.headers().add(header.getKey(), header.getValue());\n }\n }\n\n // Close the connection after serving the response if the response status is anything other than OK\n if (addKeepAliveHeader && status == HttpResponseStatus.OK) {\n httpRes.headers().add(CONNECTION, KEEP_ALIVE);\n }\n\n // closeAfterWrite = true; // FIXME: test this, it doesn't seem to work ====================================================================\n\n if (ctx.channel().isOpen()) {\n // Check Channel.isWritable() to prevent OutOfMemoryError,\n // see http://normanmaurer.me/presentations/2014-facebook-eng-netty/slides.html#10.0\n long startTime = System.currentTimeMillis();\n while (!ctx.channel().isWritable() && (System.currentTimeMillis() - startTime < 5000)) {\n // TODO: replace this spinlock (usually channel is immediately writeable;\n // is this even needed?)\n }\n if (ctx.channel().isWritable()) {\n // Write the ByteBuffer returned by httpRes.content() back into the pipeline\n // See http://normanmaurer.me/presentations/2014-facebook-eng-netty/slides.html#4.0\n ChannelFuture future = ctx.channel().writeAndFlush(httpRes);\n\n // Close the connection after the write operation is done if necessary.\n // TODO: Apache closes KeepAlive connections after a few seconds,\n // see http://en.wikipedia.org/wiki/HTTP_persistent_connection\n // TODO: implement a stale connection tracker\n if (closeAfterWrite || status != HttpResponseStatus.OK) { // FIXME: should I close the channel for redirects? (probably not...)\n future.addListener(ChannelFutureListener.CLOSE);\n }\n\n } else {\n // Tried for a period of time but could not send response; close channel\n ctx.channel().close();\n throw new RuntimeException(\"Could not send response after repeated attempts\");\n }\n } else {\n // Client already closed the connection, nothing can be sent\n // Log.info(\"Channel closed by client before response sent\");\n }\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n /** Websocket handshaker. */\n private WebSocketServerHandshaker handshaker;\n\n /** The user that was authenticated when the websocket upgrade was requested. */\n private User wsAuthenticatedUser;\n\n /** Handle a websocket frame. */\n private void handleWebsocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {\n // Check for closing frame\n if (frame instanceof CloseWebSocketFrame) {\n handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());\n return;\n }\n if (frame instanceof PingWebSocketFrame) {\n ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));\n return;\n }\n if (!(frame instanceof TextWebSocketFrame)) {\n throw new UnsupportedOperationException(String.format(\"%s frame types not supported\", frame.getClass()\n .getName()));\n }\n\n // TODO: placeholder\n String requestText = ((TextWebSocketFrame) frame).text();\n String responseText =\n requestText.toUpperCase() + \" -- \"\n + (wsAuthenticatedUser == null ? \"not logged in\" : wsAuthenticatedUser.id);\n\n ctx.channel().writeAndFlush(new TextWebSocketFrame(responseText));\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n private static void sendHttpErrorResponse(ChannelHandlerContext ctx, HttpRequest req, FullHttpResponse res) {\n // Generate an error page if response getStatus code is not OK (200).\n if (res.status().code() != 200) {\n ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8);\n res.content().writeBytes(buf);\n buf.release();\n res.headers().set(CONTENT_TYPE, \"text/plain;charset=utf-8\");\n HttpHeaderUtil.setContentLength(res, res.content().readableBytes());\n }\n ChannelFuture f = ctx.channel().writeAndFlush(res);\n if (req == null || !HttpHeaderUtil.isKeepAlive(req) || res.status().code() != 200) {\n f.addListener(ChannelFutureListener.CLOSE);\n }\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n /** Decode an HTTP message. */\n @Override\n public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {\n\n // ------------------------------------------------------------------------------\n // Handle WebSocket frames\n // ------------------------------------------------------------------------------\n\n if (msg instanceof WebSocketFrame) {\n handleWebsocketFrame(ctx, (WebSocketFrame) msg);\n return;\n }\n\n // ------------------------------------------------------------------------------\n // Decode HTTP headers\n // ------------------------------------------------------------------------------\n\n boolean requestComplete = false;\n try {\n if (msg instanceof HttpRequest) {\n HttpRequest httpReq = (HttpRequest) msg;\n\n // System.out.println(\"REQUEST: \" + httpReq.getUri());\n\n // Start a new request\n request = new Request(httpReq);\n\n // Handle expect-100-continue\n boolean expect100Continue = false;\n List<CharSequence> allExpectHeaders = httpReq.headers().getAll(EXPECT);\n for (int i = 0; i < allExpectHeaders.size(); i++) {\n String h = allExpectHeaders.get(i).toString();\n if (h.equalsIgnoreCase(\"100-continue\")) {\n expect100Continue = true;\n break;\n }\n }\n if (expect100Continue) {\n ctx.writeAndFlush(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE,\n Unpooled.EMPTY_BUFFER));\n requestComplete = true;\n return;\n }\n\n closeAfterWrite = !HttpHeaderUtil.isKeepAlive(httpReq);\n addKeepAliveHeader = !closeAfterWrite && httpReq.protocolVersion().equals(HttpVersion.HTTP_1_0);\n\n if (httpReq.method() == HttpMethod.POST) {\n // Start decoding HttpContent chunks\n destroyDecoder();\n decoder = new HttpPostRequestDecoder(factory, httpReq);\n\n } else {\n // Non-POST (probably GET) -- start handling the request\n requestComplete = true;\n }\n\n // TODO: will this return failure before all POST chunks have been received?\n if (!httpReq.decoderResult().isSuccess()) {\n sendHttpErrorResponse(ctx, httpReq, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,\n HttpResponseStatus.BAD_REQUEST));\n requestComplete = true;\n return;\n }\n }\n\n // ------------------------------------------------------------------------------\n // Decode HTTP POST body\n // ------------------------------------------------------------------------------\n\n if (msg instanceof HttpContent && decoder != null) {\n HttpContent chunk = (HttpContent) msg;\n // Offer chunk to decoder (this decreases refcount of chunk, so it doesn't have to\n // be separately released). Decoder is released after message has been handled.\n decoder.offer(chunk);\n\n try {\n while (decoder.hasNext()) {\n InterfaceHttpData data = decoder.next();\n if (data != null) {\n HttpDataType httpDataType = data.getHttpDataType();\n if (httpDataType == HttpDataType.Attribute) {\n try {\n Attribute attribute = (Attribute) data;\n request.setPostParam(attribute.getName(), attribute.getString(attribute\n .getCharset() == null ? Charset.forName(\"UTF-8\") : attribute.getCharset()));\n } finally {\n // Decrease refcount, freeing data\n data.release();\n }\n\n } else if (httpDataType == HttpDataType.FileUpload) {\n FileUpload fileUpload = (FileUpload) data;\n // TODO consider imposing size limit and returning 413 (Request Entity Too\n // Large) once the amount of data that has been sent hits the limit\n if (fileUpload.isCompleted()) {\n // Save the FileUpload object (which wraps a DiskFileUpload in /tmp).\n // Need to release this resource later.\n request.setPostFileUploadParam(fileUpload.getName(), fileUpload);\n }\n } else {\n Log.warning(\"Got unknown data chunk type: \" + httpDataType);\n }\n }\n }\n } catch (EndOfDataDecoderException e) {\n // Apparently decoder.hasNext() doesn't actually work\n }\n\n if (chunk instanceof LastHttpContent) {\n requestComplete = true;\n }\n }\n\n if (!requestComplete) {\n // Wait for more chunks.\n // (Since requestComplete is false, calling return here will not call destroyDecoder()\n // in the finally block, so it will still exist when the next chunk is received.)\n return;\n }\n\n // ------------------------------------------------------------------------------\n // Figure out how to handle HTTP request\n // ------------------------------------------------------------------------------\n\n // All POST chunks have been received (or there are no chunks); ready to start handling the request\n\n String origReqURI = request.getURI();\n\n // If this is a hash URI, look up original URI whose served resource was hashed to give this hash URI.\n // We only need to serve the resource at a hash URI once per resource per client, since resources served\n // from hash URIs are indefinitely cached in the browser.\n String hashKey = CacheExtension.getHashKey(origReqURI);\n boolean isHashURI = hashKey != null;\n String reqURI = isHashURI ? CacheExtension.getOrigURI(origReqURI) : origReqURI;\n\n InetSocketAddress requestor = (InetSocketAddress) ctx.channel().remoteAddress();\n if (requestor != null) {\n InetAddress address = requestor.getAddress();\n if (address != null) {\n request.setRequestor(address.getHostAddress());\n }\n }\n\n boolean isHEAD = request.getMethod() == HttpMethod.HEAD;\n\n // Run the GET method if HEAD is requested, just don't return a body.\n HttpMethod origReqMethod = request.getMethod();\n if (isHEAD) {\n request.setMethod(HttpMethod.GET);\n }\n\n // ------------------------------------------------------------------------------\n // Authenticate user\n // ------------------------------------------------------------------------------\n\n // The response object generated by a RestHandler\n Response response = null;\n\n // Call route handlers until one is able to handle the route,\n // or until we run out of handlers\n User user = null;\n RouteInfo authorizedRoute = null;\n ArrayList<RouteInfo> allRoutes = GribbitServer.siteResources.getAllRoutes();\n for (int i = 0, n = allRoutes.size(); i < n; i++) {\n RouteInfo route = allRoutes.get(i);\n // If the request URI matches this route path\n if (route.matches(reqURI)) {\n Class<? extends RouteHandler> handler = route.getHandler();\n\n if (!(request.getMethod() == HttpMethod.GET || request.getMethod() == HttpMethod.POST)) {\n\n // We only support GET and POST at this point\n Log.error(\"Unsupported HTTP method \" + request.getMethod().name() + \" for path \" + reqURI);\n response = new ErrorResponse(HttpResponseStatus.METHOD_NOT_ALLOWED, \"HTTP method not allowed\");\n\n } else if ((request.getMethod() == HttpMethod.GET && !route.hasGetMethod())\n || (request.getMethod() == HttpMethod.POST && !route.hasPostMethod())) {\n\n // Tried to call an HTTP method that is not defined for this route\n Log.error(\"HTTP method \" + request.getMethod().name() + \" not implemented in handler \"\n + handler.getName());\n response = new ErrorResponse(HttpResponseStatus.METHOD_NOT_ALLOWED, \"HTTP method not allowed\");\n\n } else if (RouteHandlerAuthRequired.class.isAssignableFrom(handler)) {\n\n // This handler requires authentication -- check if user is logged in\n user = User.getLoggedInUser(request);\n if (user == null) {\n\n // User is not logged in: handle request with OnUnauthorized handler instead\n response =\n getResponseForErrorHandlerRoute(GribbitServer.siteResources.getUnauthorizedRoute(),\n request, user)\n // Redirect the user back to the page they were trying to get to once they\n // do manage to log in successfully\n .setCookie(\n new Cookie(Cookie.REDIRECT_AFTER_LOGIN_COOKIE_NAME, \"/\", reqURI,\n 300));\n\n } else if (RouteHandlerAuthAndValidatedEmailRequired.class.isAssignableFrom(handler)\n && !user.emailIsValidated()) {\n\n // User is logged in, but their email address has not been validated:\n // handle request with EmailNotValidated handler instead\n response =\n getResponseForErrorHandlerRoute(\n GribbitServer.siteResources.getEmailNotValidatedRoute(), request, user);\n\n } else {\n\n // Authorization required and user logged in: OK to handle request\n // with this route\n authorizedRoute = route;\n }\n } else {\n\n // Authorization not required -- OK to handle request with this route\n authorizedRoute = route;\n }\n\n // URI matches, so don't need to search further URIs\n break;\n }\n }\n\n // ------------------------------------------------------------------------------\n // Complete websocket handshake if requested\n // ------------------------------------------------------------------------------\n\n if (response == null && authorizedRoute == null && msg instanceof HttpRequest\n // TODO: Read WS routes from class annotations\n && reqURI.endsWith(\"/websocket\")) {\n HttpRequest httpReq = (HttpRequest) msg;\n\n // Record which user was authenticated (if any) when websocket upgrade request was made.\n // TODO: Reject WS upgrade request for websockets that require authentication.\n // TODO: Also provide a means for revoking WS login.\n wsAuthenticatedUser = User.getLoggedInUser(request);\n\n WebSocketServerHandshakerFactory wsFactory =\n new WebSocketServerHandshakerFactory(GribbitServer.wsUri.toString(), null, true);\n handshaker = wsFactory.newHandshaker(httpReq);\n if (handshaker == null) {\n WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());\n } else {\n // Attempt websocket handshake, and if it succeeds, upgrade connection to websocket\n // TODO: filed bug report, handshaker.handshake should take HttpRequest, not FullHttpRequest\n DefaultFullHttpRequest fullReq =\n new DefaultFullHttpRequest(httpReq.protocolVersion(), httpReq.method(), httpReq.uri());\n fullReq.headers().add(httpReq.headers());\n handshaker.handshake(ctx.channel(), (FullHttpRequest) fullReq);\n }\n return;\n }\n\n // ------------------------------------------------------------------------------\n // Handle static file requests\n // ------------------------------------------------------------------------------\n\n // If no error has occurred so far, and no route handler matched the request URI, and this is a\n // GET request, then see if the URI points to a static file resource, and if so, serve the file.\n if (response == null && authorizedRoute == null) {\n // Static file requests can only use GET method\n if (request.getMethod() != HttpMethod.GET) {\n sendHttpErrorResponse(ctx, null, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,\n HttpResponseStatus.FORBIDDEN));\n return;\n }\n\n File staticResourceFile = GribbitServer.siteResources.getStaticResource(reqURI);\n if (staticResourceFile == null) {\n\n // Neither a route handler nor a static resource matched the request URI.\n // Return 404 Not Found.\n response =\n getResponseForErrorHandlerRoute(GribbitServer.siteResources.getNotFoundRoute(), request,\n user);\n\n } else {\n\n // A static resource matched the request URI, check last-modified timestamp\n // against the If-Modified-Since header timestamp in the request.\n long lastModifiedEpochSeconds = staticResourceFile.lastModified() / 1000;\n if (!request.cachedVersionIsOlderThan(lastModifiedEpochSeconds)) {\n // File has not been modified since it was last cached -- return Not Modified\n response = new NotModifiedResponse(lastModifiedEpochSeconds);\n\n } else {\n // If file is newer than what is in the browser cache, or is not in cache, serve the file\n serveStaticFile(reqURI, hashKey, staticResourceFile, lastModifiedEpochSeconds, ctx);\n\n Log.fine(request.getRequestor() + \"\\t\" + origReqMethod + \"\\t\" + reqURI + \"\\tfile://\"\n + staticResourceFile.getPath() + \"\\t\" + HttpResponseStatus.OK + \"\\t\"\n + (System.currentTimeMillis() - request.getReqReceivedTimeEpochMillis()) + \" msec\");\n\n // Finished request\n return;\n }\n }\n }\n\n // ------------------------------------------------------------------------------\n // Handle GET or POST requests\n // ------------------------------------------------------------------------------\n\n ZonedDateTime timeNow = null;\n\n // If an error response hasn't yet been generated and this is a (non-static-file) GET or POST request,\n // then call the get() or post() method for the route handler bound to the request URI to obtain the\n // response object.\n boolean hashTheResponse = false;\n long hashKeyRemainingAgeSeconds = 0;\n if (response == null && authorizedRoute != null) {\n\n // ----------------------------------\n // See if response should be hashed\n // ----------------------------------\n\n // For hashed *non-file* URIs, the actual last modified timestamp of dynamically-served\n // content can't be read directly, so read the last modified timestamp stored for the\n // previously hashed version in the CacheExtension class, as long as the max age of the\n // cached version hasn't been exceeded, and see if the last modified timestamp is more\n // recent than the version cached in the browser.\n //\n // The important ramification of this is that when the resource identified by the non-file\n // URI changes, the CacheExtension class must be notified of that change (including in cases\n // where the database is modified by another database client) if the modified version should\n // start being served at a new hash URI immediately, otherwise the web client connected to\n // this web server will continue to serve old resources until the max age of the cached\n // content is exceeded.\n if (isHashURI) {\n HashInfo hashInfo = CacheExtension.getHashInfo(reqURI);\n if (hashInfo != null) {\n long lastModifiedEpochSeconds = hashInfo.getLastModifiedEpochSeconds();\n timeNow = ZonedDateTime.now();\n long timeNowEpochSeconds = timeNow.toEpochSecond();\n\n long maxAgeSeconds = authorizedRoute.getMaxAgeSeconds();\n hashKeyRemainingAgeSeconds = lastModifiedEpochSeconds + maxAgeSeconds - timeNowEpochSeconds;\n\n if (maxAgeSeconds == 0) {\n // Content is not hash-cached\n hashKeyRemainingAgeSeconds = 0;\n }\n\n if (maxAgeSeconds > 0 && hashKeyRemainingAgeSeconds <= 0) {\n // Resource has expired -- call the route handler to generate a new response rather\n // than serving a Not Modified response, and schedule the response to be hashed or\n // re-hashed once the response has been generated.\n hashTheResponse = true;\n\n // Reset the expiry time at the requested number of seconds in the future\n hashKeyRemainingAgeSeconds = maxAgeSeconds;\n\n } else if (!request.cachedVersionIsOlderThan(lastModifiedEpochSeconds)) {\n // Resource has not expired in cache, but client has requested it anyway.\n // However, resource has not been modified since it was last hashed --\n // return Not Modified.\n response = new NotModifiedResponse(lastModifiedEpochSeconds);\n\n } else {\n // Resource has not expired in cache, but client has requested it anyway.\n // Resource *has* been modified since it was last hashed -- serve it the\n // normal way using the route handler, but don't hash the response, since\n // it has not expired yet.\n }\n } else {\n // There is no original URI matching this hash URI, so the hash key was stale\n // (i.e. a URI whose hashcode has been spoofed, or a very old hashcode from\n // the previous time the server was run), but we still got a valid request URI\n // by stripping away the hash code, so that is served below in the normal way.\n }\n }\n\n // If the response wasn't just set to \"Not Modified\" above, serve the request\n if (response == null) {\n\n // -----------------------------------------------------------------\n // Call the route handler for this request, generating the response\n // -----------------------------------------------------------------\n\n response = getResponseForRoute(authorizedRoute, request, user);\n\n if (response == null) {\n // Should not happen\n throw new RuntimeException(\"Didn't generate a response\");\n }\n\n }\n\n }\n if (response == null) {\n // Should not happen\n throw new RuntimeException(\"Didn't generate a response\");\n }\n\n // ------------------------------------------------------------------------------------\n // Serve an HTTP result (except in the case of static files, they were served already)\n // ------------------------------------------------------------------------------------\n\n // Turn the Response object into an HttpResponse object and serve it to the user over Netty.\n if (timeNow == null) {\n timeNow = ZonedDateTime.now();\n }\n\n // Serve the response to the client \n serveHttpResponse(reqURI, response, isHEAD, request.acceptEncodingGzip(),//\n timeNow, hashTheResponse, hashKeyRemainingAgeSeconds, hashKey, ctx);\n\n // Log the request and response\n HttpResponseStatus status = response.getStatus();\n String logMsg =\n request.getRequestor() + \"\\t\" + origReqMethod + \"\\t\" + reqURI\n + (request.getMethod() == origReqMethod ? \"\" : \"\\t\" + request.getMethod()) + \"\\t\" + status\n + \"\\t\" + (System.currentTimeMillis() - request.getReqReceivedTimeEpochMillis()) + \" msec\";\n if (status == HttpResponseStatus.OK //\n || status == HttpResponseStatus.NOT_MODIFIED //\n || status == HttpResponseStatus.FOUND //\n || (status == HttpResponseStatus.NOT_FOUND //\n && (reqURI.equals(\"favicon.ico\") || reqURI.endsWith(\"/favicon.ico\")))) {\n // Log at level \"fine\" for non-errors, or 404 for favicon\n Log.fine(logMsg);\n } else {\n // Log at level \"warning\" for errors, or 404 for non-favicon\n Log.warningWithoutCallerRef(logMsg);\n }\n\n } finally {\n if (requestComplete) {\n // Finished request -- destroy the multipart decoder and remove temporary files\n destroyDecoder();\n }\n }\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n @Override\n public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {\n // Release HTTP decoder resources, including any file uploads that were received in a POST\n // request and stored in /tmp\n destroyDecoder();\n\n if (cause instanceof NotSslRecordException) {\n ctx.channel().close();\n return;\n }\n if (\"Connection reset by peer\".equals(cause.getMessage())) {\n // (No need to log the backtrace in this case)\n // Log.info(cause.getMessage()); \n } else {\n // Log exception with backtrace\n Log.exception(\"Uncaught exception\", cause);\n }\n if (ctx.channel().isActive()) {\n sendHttpErrorResponse(ctx, null, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,\n HttpResponseStatus.INTERNAL_SERVER_ERROR));\n }\n }\n}\n\n## 内容4\n### 文件路径:\nsrc/gribbit/request/Request.java\n### 代码内容:\npackage gribbit.request;\n\nimport static io.netty.handler.codec.http.HttpHeaderNames.ACCEPT;\nimport static io.netty.handler.codec.http.HttpHeaderNames.ACCEPT_CHARSET;\nimport static io.netty.handler.codec.http.HttpHeaderNames.ACCEPT_ENCODING;\nimport static io.netty.handler.codec.http.HttpHeaderNames.ACCEPT_LANGUAGE;\nimport static io.netty.handler.codec.http.HttpHeaderNames.COOKIE;\nimport static io.netty.handler.codec.http.HttpHeaderNames.HOST;\nimport static io.netty.handler.codec.http.HttpHeaderNames.IF_MODIFIED_SINCE;\nimport static io.netty.handler.codec.http.HttpHeaderNames.ORIGIN;\nimport static io.netty.handler.codec.http.HttpHeaderNames.REFERER;\nimport static io.netty.handler.codec.http.HttpHeaderNames.USER_AGENT;\nimport gribbit.auth.Cookie;\nimport gribbit.response.flashmsg.FlashMessage;\nimport io.netty.handler.codec.http.CookieDecoder;\nimport io.netty.handler.codec.http.HttpHeaders;\nimport io.netty.handler.codec.http.HttpMethod;\nimport io.netty.handler.codec.http.HttpRequest;\nimport io.netty.handler.codec.http.QueryStringDecoder;\nimport io.netty.handler.codec.http.multipart.FileUpload;\n\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class Request {\n private long reqReceivedTimeEpochMillis;\n private HttpMethod method;\n private String requestor;\n private String host;\n private String path;\n private CharSequence accept;\n private CharSequence acceptCharset;\n private CharSequence acceptLanguage;\n private boolean acceptEncodingGzip;\n private CharSequence referer;\n private CharSequence userAgent;\n private long ifModifiedSinceEpochSecond = 0;\n\n private HashMap<String, ArrayList<Cookie>> cookieNameToCookies;\n private HashMap<String, String> postParamToValue;\n private HashMap<String, FileUpload> postParamToFileUpload;\n private Map<String, List<String>> queryParamToVals;\n\n /**\n * Header for CORS.\n * \n * See http://en.wikipedia.org/wiki/Cross-origin_resource_sharing\n **/\n private CharSequence origin;\n\n /**\n * Header for CSRF protection of AJAX requests (regular GETs and POSTs don't allow for header manipulation.)\n * \n * See https://nealpoole.com/blog/2010/11/preventing-csrf-attacks-with-ajax-and-http-headers/\n */\n private CharSequence xRequestedWith;\n\n /**\n * If set to true by appending \"?_getmodel=1\" to the URL, then return the data model backing an HTML page, not the\n * rendered page itself.\n */\n private boolean isGetModelRequest;\n\n /** Flash messages. */\n private ArrayList<FlashMessage> flashMessages;\n\n // -----------------------------------------------------------------------------------------------------\n\n public Request(HttpRequest httpReq) {\n this.reqReceivedTimeEpochMillis = System.currentTimeMillis();\n HttpHeaders headers = httpReq.headers();\n\n // Parse and decode/decrypt cookies\n for (CharSequence cookieHeader : headers.getAll(COOKIE)) {\n for (io.netty.handler.codec.http.Cookie nettyCookie : CookieDecoder.decode(cookieHeader.toString())) {\n // Log.fine(\"Cookie in request: \" + nettyCookie);\n if (this.cookieNameToCookies == null) {\n this.cookieNameToCookies = new HashMap<>();\n }\n String cookieName = nettyCookie.name();\n Cookie cookie = new Cookie(nettyCookie);\n\n // Multiple cookies may be present in the request with the same name but with different paths\n ArrayList<Cookie> cookiesWithThisName = this.cookieNameToCookies.get(cookieName);\n if (cookiesWithThisName == null) {\n this.cookieNameToCookies.put(cookieName, cookiesWithThisName = new ArrayList<>());\n }\n cookiesWithThisName.add(cookie);\n }\n }\n\n this.method = httpReq.method();\n\n CharSequence host = headers.get(HOST);\n this.host = host == null ? null : host.toString();\n\n this.xRequestedWith = headers.get(\"X-Requested-With\");\n this.accept = headers.get(ACCEPT);\n this.acceptCharset = headers.get(ACCEPT_CHARSET);\n this.acceptLanguage = headers.get(ACCEPT_LANGUAGE);\n this.origin = headers.get(ORIGIN);\n this.referer = headers.get(REFERER);\n this.userAgent = headers.get(USER_AGENT);\n\n CharSequence acceptEncoding = headers.get(ACCEPT_ENCODING);\n this.acceptEncodingGzip = acceptEncoding != null && acceptEncoding.toString().toLowerCase().contains(\"gzip\");\n\n CharSequence cacheDateHeader = headers.get(IF_MODIFIED_SINCE);\n if (cacheDateHeader != null && cacheDateHeader.length() > 0) {\n this.ifModifiedSinceEpochSecond =\n ZonedDateTime.parse(cacheDateHeader, DateTimeFormatter.RFC_1123_DATE_TIME).toEpochSecond();\n }\n\n // Decode the path.\n QueryStringDecoder decoder = new QueryStringDecoder(httpReq.uri());\n this.path = decoder.path();\n this.queryParamToVals = decoder.parameters();\n\n // Look for _getmodel=1 query parameter setting\n String getModel = this.getQueryParam(\"_getmodel\");\n if (getModel != null) {\n this.isGetModelRequest = getModel.equals(\"1\");\n // Remove _getmodel param so that user doesn't see it\n this.queryParamToVals.remove(\"_getmodel\");\n }\n\n // Get flash messages from cookie, if any\n this.flashMessages = FlashMessage.fromCookieString(getCookieValue(Cookie.FLASH_COOKIE_NAME));\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n public String getPostParam(String paramName) {\n if (postParamToValue == null) {\n return null;\n } else {\n return postParamToValue.get(paramName);\n }\n }\n\n public void setPostParam(String name, String value) {\n if (postParamToValue == null) {\n postParamToValue = new HashMap<>();\n }\n postParamToValue.put(name, value);\n }\n\n public Set<String> getPostParamNames() {\n if (postParamToValue == null) {\n return null;\n } else {\n return postParamToValue.keySet();\n }\n }\n\n public void setPostFileUploadParam(String name, FileUpload fileUpload) {\n if (postParamToFileUpload == null) {\n postParamToFileUpload = new HashMap<>();\n }\n FileUpload old = postParamToFileUpload.put(name, fileUpload);\n if (old != null) {\n // Shouldn't happen, but just in case there are two file upload params with the same\n // param name, free the first, since we're overwriting it\n old.release();\n }\n }\n\n public FileUpload getPostFileUploadParam(String name) {\n if (postParamToFileUpload == null) {\n return null;\n }\n return postParamToFileUpload.get(name);\n }\n\n public void releasePostFileUploadParams() {\n if (postParamToFileUpload != null) {\n for (FileUpload fileUpload : postParamToFileUpload.values()) {\n fileUpload.release();\n }\n postParamToFileUpload = null;\n }\n }\n\n void setPostParams(HashMap<String, String> postParamToValue) {\n this.postParamToValue = postParamToValue;\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n /** Return all URL parameters matching the given name, or null if none. */\n public List<String> getQueryParams(String paramName) {\n if (queryParamToVals == null) {\n return null;\n } else {\n return queryParamToVals.get(paramName);\n }\n }\n\n /** Return the first URL parameter matching the given name, or null if none. */\n public String getQueryParam(String paramName) {\n List<String> list = getQueryParams(paramName);\n if (list == null || list.isEmpty()) {\n return null;\n } else {\n return list.get(0);\n }\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n /** Used for sorting cookies into decreasing order of path length */\n private static final Comparator<Cookie> cookiePathLengthComparator = new Comparator<Cookie>() {\n @Override\n public int compare(Cookie o1, Cookie o2) {\n return o2.getPath().length() - o1.getPath().length();\n }\n };\n\n /**\n * Get a collection of lists of cookies -- each list in the collection consists of one or more cookies, where all\n * cookies in a list have the same name but different paths. (It is possible to receive multiple cookies with the\n * same name in a request.) Cookie lists are ordered into decreasing order of path length to conform to a \"SHOULD\"\n * clause in the HTTP header spec.\n * \n * See http://stackoverflow.com/questions/4056306/how-to-handle-multiple-cookies-with-the-same-name\n */\n public Collection<ArrayList<Cookie>> getAllCookies() {\n if (cookieNameToCookies == null) {\n return null;\n } else {\n Collection<ArrayList<Cookie>> cookieLists = cookieNameToCookies.values();\n for (ArrayList<Cookie> cookieList : cookieLists) {\n Collections.sort(cookieList, cookiePathLengthComparator);\n }\n return cookieLists;\n }\n }\n\n /**\n * Get all cookies with the given name, or null if there are no cookies with this name. (There may be multiple\n * cookies with the same name but with different paths.) The returned list is ordered into decreasing order of path\n * length to conform to a \"SHOULD\" clause in the HTTP header spec.\n * \n * See http://stackoverflow.com/questions/4056306/how-to-handle-multiple-cookies-with-the-same-name\n */\n public ArrayList<Cookie> getAllCookiesWithName(String cookieName) {\n if (cookieNameToCookies == null) {\n return null;\n } else {\n ArrayList<Cookie> cookieList = cookieNameToCookies.get(cookieName);\n if (cookieList != null) {\n Collections.sort(cookieList, cookiePathLengthComparator);\n }\n return cookieList;\n }\n }\n\n /**\n * Get a cookie by name, or null if there are no cookies with this name. If there is more than one cookie with the\n * same name, return the cookie with the longest path.\n * \n * See http://stackoverflow.com/questions/4056306/how-to-handle-multiple-cookies-with-the-same-name\n */\n public Cookie getCookie(String cookieName) {\n ArrayList<Cookie> cookieList = getAllCookiesWithName(cookieName);\n if (cookieList == null) {\n return null;\n } else {\n return cookieList.get(0);\n }\n }\n\n /**\n * Get the string value of a named cookie, or null if there are no cookies with this name. If there is more than one\n * cookie with the same name, return the value of the one with the longest path.\n * \n * See http://stackoverflow.com/questions/4056306/how-to-handle-multiple-cookies-with-the-same-name\n */\n public String getCookieValue(String cookieName) {\n Cookie cookie = getCookie(cookieName);\n if (cookie == null) {\n return null;\n } else {\n return cookie.getValue();\n }\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n /**\n * Compare timestamp in the If-Modified-Since request header, if present, to the given resource timestamp to see if\n * the resource is newer than any cached version. If the If-Modified-Since header is not set, the timestamp of the\n * cached version is assumed to be zero (the beginning of the Epoch), so this method will return true. If the passed\n * resource timestamp is zero, the resource timestamp is assumed to be invalid or unavailable, so we assume the\n * resource is not cached and return true, indicating that the cached version is out of date and should be served\n * (or served again).\n */\n public boolean cachedVersionIsOlderThan(long resourceTimestampEpochSeconds) {\n if (resourceTimestampEpochSeconds == 0) {\n // If the resource timestamp is zero, it's not valid, so don't assume this resource is cached,\n // by returning true indicating that the cached version is out of date. \n return true;\n } else {\n // Otherwise return true if the resource timestamp is later than the cached version timestamp\n // (including when the cached version timestamp is zero.)\n // Note that the HTTP If-Modified-Since header only has single-second granularity.\n return resourceTimestampEpochSeconds > ifModifiedSinceEpochSecond;\n }\n }\n\n public long getReqReceivedTimeEpochMillis() {\n return reqReceivedTimeEpochMillis;\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n /** Add a flash message (a message that will be popped up at the top of a webpage the next time a page is served. */\n public void addFlashMessage(FlashMessage flashMessage) {\n if (flashMessages == null) {\n flashMessages = new ArrayList<>();\n }\n flashMessages.add(flashMessage);\n }\n\n /** Clear the flash messages. */\n public void clearFlashMessage() {\n flashMessages = null;\n }\n\n public ArrayList<FlashMessage> getFlashMessages() {\n return flashMessages;\n }\n\n // -----------------------------------------------------------------------------------------------------------------\n\n public String getURI() {\n return path.toString();\n }\n\n public String getRequestor() {\n return requestor == null ? \"\" : requestor;\n }\n\n public void setRequestor(String requestor) {\n this.requestor = requestor;\n }\n\n public HttpMethod getMethod() {\n return method;\n }\n\n public void setMethod(HttpMethod method) {\n this.method = method;\n }\n\n public void setURI(String uri) {\n this.path = uri;\n }\n\n public CharSequence getHost() {\n return host;\n }\n\n public CharSequence getAccept() {\n return accept;\n }\n\n public CharSequence getAcceptCharset() {\n return acceptCharset;\n }\n\n public CharSequence getAcceptLanguage() {\n return acceptLanguage;\n }\n\n public boolean acceptEncodingGzip() {\n return acceptEncodingGzip;\n }\n\n public CharSequence getReferer() {\n return referer;\n }\n\n public CharSequence getUserAgent() {\n return userAgent;\n }\n\n public CharSequence getOrigin() {\n return origin;\n }\n\n public CharSequence getXRequestedWith() {\n return xRequestedWith;\n }\n\n /**\n * True if the request URL contained the query parameter \"?_getmodel=1\", in which case return the DataModel backing\n * an HTML page, and not the rendered page itself.\n */\n public boolean isGetModelRequest() {\n return isGetModelRequest;\n }\n}\n\n", "input": "", "output": "【漏洞类型】WebSocket 握手安全漏洞\n【严重性】High\n【漏洞摘要】原始代码在处理 WebSocket 协议升级请求时,未验证请求的来源和用户的身份,导致潜在的跨站 WebSocket 劫持(Cross-Site WebSocket Hijacking, CSWSH)漏洞。具体表现为,没有检查请求的 Origin 头和 CSRF 令牌,攻击者可以通过跨站请求合法用户登录后的 WebSocket 端点,从而获取用户的 Websocket 连接。\n【关键代码模式】`handleWebsocketFrame` 方法,特别是 `msg instanceof HttpRequest` 和 `reqURI.endsWith(\"/websocket\")` 的条件判断,以及 `WebSocketServerHandshakerFactory.newHandshaker` 的调用。\n【修复方法说明】通过在处理 WebSocket 握手请求时检查 Origin 头和 CSRF 令牌,修复了潜在的 CSWSH 漏洞。确保仅允许合法来源的请求进行 WebSocket 握手,同时验证用户的身份。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/AddContact.java\n### 代码内容:\nimport javax.mail.Session;\nimport javax.servlet.RequestDispatcher;\nimport javax.servlet.ServletException;\nimport javax.servlet.annotation.WebServlet;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\nimport java.io.IOException;\nimport java.sql.SQLException;\n\n/**\n * Created by chris on 09/12/14.\n */\n\n/**\n * Servlet for adding contacts\n */\n@WebServlet(\"/addcontact\")\npublic class AddContact extends HttpServlet {\n protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n //Checks whether session has timed out\n if (System.currentTimeMillis() > (request.getSession().getLastAccessedTime() + 300000)) {\n RequestDispatcher dispatcher = request.getRequestDispatcher(\"Error\"); //New Request Dispatcher\n request.setAttribute(\"error\", \"Login session timed out, please click retry to log back in\");\n request.setAttribute(\"previous\", \"index.html\");\n dispatcher.forward(request, response); //Forwards to the page\n } else {\n\n HttpSession httpSession = request.getSession(); //Get Session details\n Session session = (Session) httpSession.getAttribute(\"session\");\n String user = session.getProperties().getProperty(\"mail.user\");\n\n String forename = request.getParameter(\"firstname\");\n String surname = request.getParameter(\"secondname\");\n String email = request.getParameter(\"email\");\n\n Model m = new Model(user); //Initialize model\n\n try {\n m.addContact(forename, surname, email, user);\n httpSession.setAttribute(\"success\", \"<p id=\\\"success\\\">Contact saved successfully</p>\");\n request.getRequestDispatcher(\"addcontact.jsp\").forward(request, response);\n } catch (SQLException e) {\n\n RequestDispatcher dispatcher = request.getRequestDispatcher(\"Error\"); //New Request Dispatcher\n request.setAttribute(\"error\", e.getMessage());\n request.setAttribute(\"previous\", \"searchcontact\");\n dispatcher.forward(request, response);\n }\n }\n }\n\n}\n\n## 内容2\n### 文件路径:\nsrc/Error.java\n### 代码内容:\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.io.PrintWriter;\n\npublic class Error extends HttpServlet {\n\n /**\n * Calls doPost\n */\n protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n\n doPost(request, response);\n\n }\n\n /**\n * Gets the PrintWriter from the response and prints the HTML to show the error page\n */\n protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n\n if (System.currentTimeMillis() > (request.getSession().getLastAccessedTime() + 300000)) {\n request.setAttribute(\"error\", \"Login session timed out, please click retry to log back in\");\n request.setAttribute(\"previous\", \"index.html\");\n }\n PrintWriter out = response.getWriter(); //Gets the PrintWriter\n String back;\n String previous = (String) request.getAttribute(\"previous\");\n if (previous.equals(\"/LoginController\") || previous.equals(\"index.html\")) {\n back = \"index.html\";\n } else if (previous.equals(\"searchcontact\")) {\n back = \"contact.jsp\";\n } else {\n back = \"email.html\";\n }\n out.println(\n \"<!DOCTYPE html>\" +\n \"<html>\" +\n \"<head lang=\\\"en\\\">\" +\n \"<meta charset=\\\"UTF-8\\\">\" +\n \"<title>Error Occured</title>\" +\n \"</head>\" +\n \"<body>\" +\n \"<center>\" +\n \"<h1>Error Occurred!</h1>\" +\n \"<div>\" +\n \"<br>\" +\n \"Error: \" + request.getAttribute(\"error\") + \"<br>\" + \"<br>\" + \"<br>\" +//Gets the error message\n \"</div>\" +\n \"<div class='error-actions'>\" +\n \"<a href='\" + back + \"'>Retry</a>\" +\n \"</div>\" +\n \"</center>\" +\n \"</body>\" +\n \"</html>\"\n );\n\n }\n\n}\n\n## 内容3\n### 文件路径:\nsrc/Login.java\n### 代码内容:\nimport javax.mail.MessagingException;\nimport javax.mail.Session;\nimport javax.mail.Store;\nimport javax.servlet.RequestDispatcher;\nimport javax.servlet.ServletException;\nimport javax.servlet.annotation.WebServlet;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.util.Properties;\n\n/**\n * Created by chris on 08/12/14.\n */\n\n@WebServlet(\"/login\")\npublic class Login extends HttpServlet {\n\n public Login() {\n super();\n }\n\n protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n\n String username = request.getParameter(\"username\"); //Gets all the parameters\n String password = request.getParameter(\"password\");\n Properties prop = System.getProperties();\n\n try {\n\n prop.put(\"mail.user\", username);\n prop.put(\"mail.password\", password);\n prop.put(\"mail.smtp.auth\", \"true\");\n prop.put(\"mail.smtp.starttls.enable\", \"true\");\n prop.put(\"mail.smtp.host\", \"smtp.gmail.com\");\n prop.put(\"mail.smtp.port\", \"587\");\n prop.put(\"mail.store.protocol\", \"imaps\");\n\n Session session = Session.getDefaultInstance(prop);\n Store store = session.getStore(\"imaps\");\n store.connect(\"imap.googlemail.com\", username, password);\n\n request.getSession().setAttribute(\"session\", session);\n\n RequestDispatcher dispatcher = request.getRequestDispatcher(\"email.html\");\n dispatcher.forward(request, response);\n\n } catch (MessagingException e) {\n RequestDispatcher dispatcher = request.getRequestDispatcher(\"Error\"); //New Request Dispatcher\n request.setAttribute(\"error\", e.getMessage());\n request.setAttribute(\"previous\", request.getServletPath());\n dispatcher.forward(request, response);\n }\n }\n}\n\n## 内容4\n### 文件路径:\nsrc/Model.java\n### 代码内容:\nimport java.sql.*;\n\n/**\n * Created by chris on 08/12/14.\n */\npublic class Model {\n\n private String DBNAME = \"jdbc:postgresql://dbteach2.cs.bham.ac.uk/cxm373\";\n private String USERNAME = \"cxm373\";\n private String PASSWORD = \"computer2014\";\n private String emailUser;\n private Connection conn;\n\n public Model(String username) {\n emailUser = username;\n conn = connect();\n }\n\n public Connection connect() {\n try {\n Class.forName(\"org.postgresql.Driver\");\n } catch (ClassNotFoundException e) {\n System.out.println(\"Driver not found\");\n System.exit(0);\n }\n\n System.out.println(\"PostgreSQL driver registered.\");\n\n Connection dbConn = null;\n\n try {\n dbConn = DriverManager.getConnection(DBNAME, USERNAME, PASSWORD);\n } catch (SQLException e) {\n e.printStackTrace();\n }\n\n if (dbConn != null) {\n System.out.println(\"Connected to database successfully.\");\n } else {\n System.out.println(\"Database connection failed.\");\n }\n return dbConn;\n\n }\n\n public String search(String forename, String surname, String contactemail) throws SQLException {\n\n String query;\n if (forename.isEmpty() && surname.isEmpty()) {\n query = \"\";\n } else if(forename.isEmpty()) {\n query = \"familyname LIKE '%\" + surname + \"' and\";\n } else if(surname.isEmpty()) {\n query = \"forename LIKE '%\" + forename + \"' and \";\n } else {\n query = \"forename LIKE '%\" + forename + \"' and familyname LIKE '%\" + surname + \"' and\";\n }\n\n PreparedStatement ps = conn.prepareStatement(\"SELECT * FROM contactinfo WHERE \" + query + \" contactemailaddress = '\" + contactemail + \"'\");\n ResultSet rs = ps.executeQuery();\n StringBuilder result = new StringBuilder(\"<h3>Search results...</h3><table class=\\\"result-table\\\">\" +\n \"<tr>\" +\n \"<th>Forename</th> <th>Surname</th> <th>Email</th>\" +\n \"</tr>\");\n while(rs.next())\n\n {\n result.append(\"<tr><td>\");\n result.append(rs.getString(2));\n result.append(\"</td><td>\" + rs.getString(3));\n result.append(\"</td><td>\" + rs.getString(4) + \"</td></tr>\");\n }\n\n result.append(\"</table\");\n return result.toString();\n}\n\n public void addContact(String firstname, String surname, String email, String user) throws SQLException {\n PreparedStatement newStudent = conn.prepareStatement(\"INSERT INTO \" +\n \"contactinfo (forename, familyname, emailaddress, contactemailaddress) VALUES ('\" + firstname + \"', '\" + surname + \"', '\" + email + \"', '\" + user + \"')\");\n newStudent.execute();\n }\n}\n\n## 内容5\n### 文件路径:\nsrc/SearchContact.java\n### 代码内容:\nimport javax.mail.Session;\nimport javax.servlet.RequestDispatcher;\nimport javax.servlet.ServletException;\nimport javax.servlet.annotation.WebServlet;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\nimport java.io.IOException;\nimport java.sql.SQLException;\n\n/**\n * Created by chris on 09/12/14.\n */\n\n/**\n * Servlet for searching for contacts\n */\n@WebServlet(\"/searchcontact\")\npublic class SearchContact extends HttpServlet {\n\n protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n //Checks whether session has timed out\n if (System.currentTimeMillis() > (request.getSession().getLastAccessedTime() + 300000)) {\n RequestDispatcher dispatcher = request.getRequestDispatcher(\"Error\"); //New Request Dispatcher\n request.setAttribute(\"error\", \"Login session timed out, please click retry to log back in\");\n request.setAttribute(\"previous\", \"index.html\");\n dispatcher.forward(request, response); //Forwards to the page\n } else {\n HttpSession httpSession = request.getSession();\n Session session = (Session) httpSession.getAttribute(\"session\");\n String user = session.getProperties().getProperty(\"mail.user\");\n String searchQueryForeName = request.getParameter(\"forename\");\n String searchQuerySurName = request.getParameter(\"surname\");\n\n Model m = new Model(user);\n\n try {\n String resultTable = m.search(searchQueryForeName, searchQuerySurName, user);\n httpSession.setAttribute(\"results\", resultTable);\n httpSession.setAttribute(\"success\", \"\");\n request.getRequestDispatcher(\"contact.jsp\").forward(request, response);\n } catch (SQLException e) {\n RequestDispatcher dispatcher = request.getRequestDispatcher(\"Error\"); //New Request Dispatcher\n request.setAttribute(\"error\", e.getMessage());\n request.setAttribute(\"previous\", \"searchcontact\");\n dispatcher.forward(request, response);\n }\n }\n }\n\n protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n\n }\n\n}\n\n\n## 内容6\n### 文件路径:\nweb/addcontact.jsp\n### 代码内容:\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n<!DOCTYPE html>\n<html>\n<head>\n <title>Search for/Add contact</title>\n <link rel=\"stylesheet\" type=\"text/css\" href=\"styles.css\"/>\n <script type=\"text/javascript\" src=\"http://code.jquery.com/jquery-latest.js\"></script>\n <script type=\"text/javascript\">\n $(document).ready( function() {\n $('#success').delay(1000).fadeOut();\n });\n </script>\n</head>\n<body>\n<a id=\"back\" href=\"http://<%=request.getServerName()%>:<%=request.getServerPort()%>/email.html\">Back</a>\n<div class=\"logout-container\">\n <form action=\"logout\" method=\"post\">\n <input id=\"logout\" type=\"submit\" value=\"Log Out\"/>\n </form>\n</div>\n<section class=\"main\">\n <h1>Add a contact</h1>\n <p>Enter a forename, surname and email and click add!</p>\n <form action=\"addcontact\" method=\"post\">\n <table class=\"form-table\">\n <tr>\n <td>\n <b>Forename: </b>\n </td>\n <td>\n <input id=\"name\" name=\"firstname\" type=\"text\" placeholder=\"...\">\n </td>\n </tr>\n <tr>\n <td>\n <b>Surname: </b>\n </td>\n <td>\n <input id=\"surname\" name=\"secondname\" type=\"text\" placeholder=\"...\">\n </td>\n </tr>\n <tr>\n <td>\n <b>Email Address: </b>\n </td>\n <td>\n <input id=\"email\" name=\"email\" type=\"email\" placeholder=\"...\">\n </td>\n </tr>\n <tr>\n <td></td>\n <td>\n <input type=\"submit\" value=\"Submit\">\n <button type=\"reset\" value=\"Reset\">Reset</button>\n </td>\n </tr>\n </table>\n </form>\n <%=request.getSession().getAttribute(\"success\")%>\n</section>\n</body>\n</html>\n\n## 内容7\n### 文件路径:\nweb/contact.jsp\n### 代码内容:\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n<!DOCTYPE html>\n<html>\n<head>\n <title>Search for/Add contact</title>\n <link rel=\"stylesheet\" type=\"text/css\" href=\"styles.css\"/>\n <script type=\"text/javascript\" src=\"http://code.jquery.com/jquery-latest.js\"></script>\n <script type=\"text/javascript\">\n $( \".reset\" ).click(function() {\n document.getElementByClassName(\"result-table\").remove();\n </script>\n</head>\n<body>\n<a id=\"back\" href=\"http://<%=request.getServerName()%>:<%=request.getServerPort()%>/email.html\">Back</a>\n<div class=\"logout-container\">\n <form action=\"logout\" method=\"post\">\n <input id=\"logout\" type=\"submit\" value=\"Log Out\"/>\n </form>\n</div>\n<section class=\"main\">\n <h1>Search for a contact</h1>\n\n <p>Enter a forename and/or surname and click search!</p>\n\n <form action=\"searchcontact\" method=\"post\">\n <table class=\"form-table\">\n <tr>\n <td>\n <b>Forename: </b>\n </td>\n <td>\n <input id=\"name\" name=\"forename\" type=\"text\" placeholder=\"...\">\n </td>\n </tr>\n <tr>\n <td>\n <b>Surname: </b>\n </td>\n <td>\n <input id=\"surname\" name=\"surname\" type=\"text\" placeholder=\"...\">\n </td>\n </tr>\n <tr>\n <td></td>\n <td>\n <input type=\"submit\" value=\"Submit\">\n <button class=\"reset\" type=\"reset\" value=\"Reset\">Reset</button>\n </td>\n </tr>\n </table>\n </form>\n <%=request.getSession().getAttribute(\"results\")%>\n</section>\n</body>\n</html>\n\n", "input": "", "output": "【漏洞类型】SQL注入\n【严重性】高\n【漏洞摘要】原始代码中的`Model`类中的`search`和`addContact`方法直接将用户输入拼接到SQL查询字符串中,而没有进行任何验证或转义处理。这种做法容易导致SQL注入攻击,攻击者可以通过构造恶意输入来改变SQL查询的语义,从而获取或修改数据库中的数据。\n【关键代码模式】不使用预处理语句(PreparedStatement)直接拼接用户输入到SQL查询字符串中,例如:\n```java\nPreparedStatement ps = conn.prepareStatement(\"SELECT * FROM contactinfo WHERE \" + query + \" contactemailaddress = '\" + contactemail + \"'\");\n```\n```java\nPreparedStatement newStudent = conn.prepareStatement(\"INSERT INTO \" +\n \"contactinfo (forename, familyname, emailaddress, contactemailaddress) VALUES ('\" + firstname + \"', '\" + surname + \"', '\" + email + \"', '\" + user + \"')\");\n```\n【修复方法说明】修复方法通过使用预处理语句(PreparedStatement)来代替直接拼接用户输入,从而防止SQL注入攻击。最佳实践是始终使用预处理语句来处理用户输入,确保输入不会被恶意利用。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DH.java\n### 代码内容:\npackage org.bouncycastle.jcajce.provider.asymmetric;\n\nimport org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;\nimport org.bouncycastle.asn1.x9.X9ObjectIdentifiers;\nimport org.bouncycastle.jcajce.provider.asymmetric.dh.KeyFactorySpi;\nimport org.bouncycastle.jcajce.provider.config.ConfigurableProvider;\nimport org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;\n\npublic class DH\n{\n private static final String PREFIX = \"org.bouncycastle.jcajce.provider.asymmetric\" + \".dh.\";\n\n public static class Mappings\n extends AsymmetricAlgorithmProvider\n {\n public Mappings()\n {\n }\n\n public void configure(ConfigurableProvider provider)\n {\n provider.addAlgorithm(\"KeyPairGenerator.DH\", PREFIX + \"KeyPairGeneratorSpi\");\n provider.addAlgorithm(\"Alg.Alias.KeyPairGenerator.DIFFIEHELLMAN\", \"DH\");\n\n provider.addAlgorithm(\"KeyAgreement.DH\", PREFIX + \"KeyAgreementSpi\");\n provider.addAlgorithm(\"Alg.Alias.KeyAgreement.DIFFIEHELLMAN\", \"DH\");\n provider.addAlgorithm(\"KeyAgreement\", PKCSObjectIdentifiers.id_alg_ESDH, PREFIX + \"KeyAgreementSpi$DHwithRFC2631KDF\");\n provider.addAlgorithm(\"KeyAgreement\", PKCSObjectIdentifiers.id_alg_SSDH, PREFIX + \"KeyAgreementSpi$DHwithRFC2631KDF\");\n\n provider.addAlgorithm(\"KeyFactory.DH\", PREFIX + \"KeyFactorySpi\");\n provider.addAlgorithm(\"Alg.Alias.KeyFactory.DIFFIEHELLMAN\", \"DH\");\n\n provider.addAlgorithm(\"AlgorithmParameters.DH\", PREFIX + \"AlgorithmParametersSpi\");\n provider.addAlgorithm(\"Alg.Alias.AlgorithmParameters.DIFFIEHELLMAN\", \"DH\");\n\n provider.addAlgorithm(\"Alg.Alias.AlgorithmParameterGenerator.DIFFIEHELLMAN\", \"DH\");\n\n provider.addAlgorithm(\"AlgorithmParameterGenerator.DH\", PREFIX + \"AlgorithmParameterGeneratorSpi\");\n\n provider.addAlgorithm(\"Cipher.IES\", PREFIX + \"IESCipher$IES\");\n provider.addAlgorithm(\"Cipher.IESwithAES\", PREFIX + \"IESCipher$IESwithAES\");\n provider.addAlgorithm(\"Cipher.IESWITHAES\", PREFIX + \"IESCipher$IESwithAES\");\n provider.addAlgorithm(\"Cipher.IESWITHDESEDE\", PREFIX + \"IESCipher$IESwithDESede\");\n\n provider.addAlgorithm(\"Cipher.DHIES\", PREFIX + \"IESCipher$IES\");\n provider.addAlgorithm(\"Cipher.DHIESwithAES\", PREFIX + \"IESCipher$IESwithAES\");\n provider.addAlgorithm(\"Cipher.DHIESWITHAES\", PREFIX + \"IESCipher$IESwithAES\");\n provider.addAlgorithm(\"Cipher.DHIESWITHDESEDE\", PREFIX + \"IESCipher$IESwithDESede\");\n\n provider.addAlgorithm(\"Cipher.OLDDHIES\", PREFIX + \"IESCipher$OldIES\");\n provider.addAlgorithm(\"Cipher.OLDDHIESwithAES\", PREFIX + \"IESCipher$OldIESwithAES\");\n provider.addAlgorithm(\"Cipher.OLDDHIESWITHAES\", PREFIX + \"IESCipher$OldIESwithAES\");\n provider.addAlgorithm(\"Cipher.OLDDHIESWITHDESEDE\", PREFIX + \"IESCipher$OldIESwithDESede\");\n\n registerOid(provider, PKCSObjectIdentifiers.dhKeyAgreement, \"DH\", new KeyFactorySpi());\n registerOid(provider, X9ObjectIdentifiers.dhpublicnumber, \"DH\", new KeyFactorySpi());\n }\n }\n}\n\n## 内容2\n### 文件路径:\nprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java\n### 代码内容:\npackage org.bouncycastle.jcajce.provider.asymmetric;\n\nimport org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;\nimport org.bouncycastle.asn1.eac.EACObjectIdentifiers;\nimport org.bouncycastle.asn1.nist.NISTObjectIdentifiers;\nimport org.bouncycastle.asn1.sec.SECObjectIdentifiers;\nimport org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;\nimport org.bouncycastle.asn1.x9.X9ObjectIdentifiers;\nimport org.bouncycastle.jcajce.provider.asymmetric.ec.KeyFactorySpi;\nimport org.bouncycastle.jcajce.provider.config.ConfigurableProvider;\nimport org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;\nimport org.bouncycastle.util.Properties;\n\npublic class EC\n{\n private static final String PREFIX = \"org.bouncycastle.jcajce.provider.asymmetric\" + \".ec.\";\n\n public static class Mappings\n extends AsymmetricAlgorithmProvider\n {\n public Mappings()\n {\n }\n\n public void configure(ConfigurableProvider provider)\n {\n provider.addAlgorithm(\"AlgorithmParameters.EC\", PREFIX + \"AlgorithmParametersSpi\");\n\n provider.addAlgorithm(\"KeyAgreement.ECDH\", PREFIX + \"KeyAgreementSpi$DH\");\n provider.addAlgorithm(\"KeyAgreement.ECDHC\", PREFIX + \"KeyAgreementSpi$DHC\");\n provider.addAlgorithm(\"KeyAgreement.ECCDH\", PREFIX + \"KeyAgreementSpi$DHC\");\n\n provider.addAlgorithm(\"KeyAgreement.\" + X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme, PREFIX + \"KeyAgreementSpi$DHwithSHA1KDFAndSharedInfo\");\n provider.addAlgorithm(\"KeyAgreement.\" + X9ObjectIdentifiers.dhSinglePass_cofactorDH_sha1kdf_scheme, PREFIX + \"KeyAgreementSpi$CDHwithSHA1KDFAndSharedInfo\");\n\n provider.addAlgorithm(\"KeyAgreement.\" + SECObjectIdentifiers.dhSinglePass_stdDH_sha224kdf_scheme, PREFIX + \"KeyAgreementSpi$DHwithSHA224KDFAndSharedInfo\");\n provider.addAlgorithm(\"KeyAgreement.\" + SECObjectIdentifiers.dhSinglePass_cofactorDH_sha224kdf_scheme, PREFIX + \"KeyAgreementSpi$CDHwithSHA224KDFAndSharedInfo\");\n\n provider.addAlgorithm(\"KeyAgreement.\" + SECObjectIdentifiers.dhSinglePass_stdDH_sha256kdf_scheme, PREFIX + \"KeyAgreementSpi$DHwithSHA256KDFAndSharedInfo\");\n provider.addAlgorithm(\"KeyAgreement.\" + SECObjectIdentifiers.dhSinglePass_cofactorDH_sha256kdf_scheme, PREFIX + \"KeyAgreementSpi$CDHwithSHA256KDFAndSharedInfo\");\n\n provider.addAlgorithm(\"KeyAgreement.\" + SECObjectIdentifiers.dhSinglePass_stdDH_sha384kdf_scheme, PREFIX + \"KeyAgreementSpi$DHwithSHA384KDFAndSharedInfo\");\n provider.addAlgorithm(\"KeyAgreement.\" + SECObjectIdentifiers.dhSinglePass_cofactorDH_sha384kdf_scheme, PREFIX + \"KeyAgreementSpi$CDHwithSHA384KDFAndSharedInfo\");\n\n provider.addAlgorithm(\"KeyAgreement.\" + SECObjectIdentifiers.dhSinglePass_stdDH_sha512kdf_scheme, PREFIX + \"KeyAgreementSpi$DHwithSHA512KDFAndSharedInfo\");\n provider.addAlgorithm(\"KeyAgreement.\" + SECObjectIdentifiers.dhSinglePass_cofactorDH_sha512kdf_scheme, PREFIX + \"KeyAgreementSpi$CDHwithSHA512KDFAndSharedInfo\");\n\n provider.addAlgorithm(\"KeyAgreement.ECDHWITHSHA1KDF\", PREFIX + \"KeyAgreementSpi$DHwithSHA1KDF\");\n\n provider.addAlgorithm(\"KeyAgreement.ECCDHWITHSHA1CKDF\", PREFIX + \"KeyAgreementSpi$DHwithSHA1CKDF\");\n provider.addAlgorithm(\"KeyAgreement.ECCDHWITHSHA256CKDF\", PREFIX + \"KeyAgreementSpi$DHwithSHA256CKDF\");\n provider.addAlgorithm(\"KeyAgreement.ECCDHWITHSHA384CKDF\", PREFIX + \"KeyAgreementSpi$DHwithSHA384CKDF\");\n provider.addAlgorithm(\"KeyAgreement.ECCDHWITHSHA512CKDF\", PREFIX + \"KeyAgreementSpi$DHwithSHA512CKDF\");\n\n registerOid(provider, X9ObjectIdentifiers.id_ecPublicKey, \"EC\", new KeyFactorySpi.EC());\n\n registerOid(provider, X9ObjectIdentifiers.dhSinglePass_cofactorDH_sha1kdf_scheme, \"EC\", new KeyFactorySpi.EC());\n registerOid(provider, X9ObjectIdentifiers.mqvSinglePass_sha1kdf_scheme, \"ECMQV\", new KeyFactorySpi.ECMQV());\n\n registerOid(provider, SECObjectIdentifiers.dhSinglePass_stdDH_sha224kdf_scheme, \"EC\", new KeyFactorySpi.EC());\n registerOid(provider, SECObjectIdentifiers.dhSinglePass_cofactorDH_sha224kdf_scheme, \"EC\", new KeyFactorySpi.EC());\n\n registerOid(provider, SECObjectIdentifiers.dhSinglePass_stdDH_sha256kdf_scheme, \"EC\", new KeyFactorySpi.EC());\n registerOid(provider, SECObjectIdentifiers.dhSinglePass_cofactorDH_sha256kdf_scheme, \"EC\", new KeyFactorySpi.EC());\n\n registerOid(provider, SECObjectIdentifiers.dhSinglePass_stdDH_sha384kdf_scheme, \"EC\", new KeyFactorySpi.EC());\n registerOid(provider, SECObjectIdentifiers.dhSinglePass_cofactorDH_sha384kdf_scheme, \"EC\", new KeyFactorySpi.EC());\n\n registerOid(provider, SECObjectIdentifiers.dhSinglePass_stdDH_sha512kdf_scheme, \"EC\", new KeyFactorySpi.EC());\n registerOid(provider, SECObjectIdentifiers.dhSinglePass_cofactorDH_sha512kdf_scheme, \"EC\", new KeyFactorySpi.EC());\n\n registerOidAlgorithmParameters(provider, X9ObjectIdentifiers.id_ecPublicKey, \"EC\");\n\n registerOidAlgorithmParameters(provider, X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme, \"EC\");\n registerOidAlgorithmParameters(provider, X9ObjectIdentifiers.dhSinglePass_cofactorDH_sha1kdf_scheme, \"EC\");\n\n registerOidAlgorithmParameters(provider, SECObjectIdentifiers.dhSinglePass_stdDH_sha224kdf_scheme, \"EC\");\n registerOidAlgorithmParameters(provider, SECObjectIdentifiers.dhSinglePass_cofactorDH_sha224kdf_scheme, \"EC\");\n\n registerOidAlgorithmParameters(provider, SECObjectIdentifiers.dhSinglePass_stdDH_sha256kdf_scheme, \"EC\");\n registerOidAlgorithmParameters(provider, SECObjectIdentifiers.dhSinglePass_cofactorDH_sha256kdf_scheme, \"EC\");\n\n registerOidAlgorithmParameters(provider, SECObjectIdentifiers.dhSinglePass_stdDH_sha384kdf_scheme, \"EC\");\n registerOidAlgorithmParameters(provider, SECObjectIdentifiers.dhSinglePass_cofactorDH_sha384kdf_scheme, \"EC\");\n\n registerOidAlgorithmParameters(provider, SECObjectIdentifiers.dhSinglePass_stdDH_sha512kdf_scheme, \"EC\");\n registerOidAlgorithmParameters(provider, SECObjectIdentifiers.dhSinglePass_cofactorDH_sha512kdf_scheme, \"EC\");\n\n if (!Properties.isOverrideSet(\"org.bouncycastle.ec.disable_mqv\"))\n {\n provider.addAlgorithm(\"KeyAgreement.ECMQV\", PREFIX + \"KeyAgreementSpi$MQV\");\n\n provider.addAlgorithm(\"KeyAgreement.ECMQVWITHSHA1CKDF\", PREFIX + \"KeyAgreementSpi$MQVwithSHA1CKDF\");\n provider.addAlgorithm(\"KeyAgreement.ECMQVWITHSHA224CKDF\", PREFIX + \"KeyAgreementSpi$MQVwithSHA224CKDF\");\n provider.addAlgorithm(\"KeyAgreement.ECMQVWITHSHA256CKDF\", PREFIX + \"KeyAgreementSpi$MQVwithSHA256CKDF\");\n provider.addAlgorithm(\"KeyAgreement.ECMQVWITHSHA384CKDF\", PREFIX + \"KeyAgreementSpi$MQVwithSHA384CKDF\");\n provider.addAlgorithm(\"KeyAgreement.ECMQVWITHSHA512CKDF\", PREFIX + \"KeyAgreementSpi$MQVwithSHA512CKDF\");\n\n provider.addAlgorithm(\"KeyAgreement.\" + X9ObjectIdentifiers.mqvSinglePass_sha1kdf_scheme, PREFIX + \"KeyAgreementSpi$MQVwithSHA1KDFAndSharedInfo\");\n provider.addAlgorithm(\"KeyAgreement.\" + SECObjectIdentifiers.mqvSinglePass_sha224kdf_scheme, PREFIX + \"KeyAgreementSpi$MQVwithSHA224KDFAndSharedInfo\");\n provider.addAlgorithm(\"KeyAgreement.\" + SECObjectIdentifiers.mqvSinglePass_sha256kdf_scheme, PREFIX + \"KeyAgreementSpi$MQVwithSHA256KDFAndSharedInfo\");\n provider.addAlgorithm(\"KeyAgreement.\" + SECObjectIdentifiers.mqvSinglePass_sha384kdf_scheme, PREFIX + \"KeyAgreementSpi$MQVwithSHA384KDFAndSharedInfo\");\n provider.addAlgorithm(\"KeyAgreement.\" + SECObjectIdentifiers.mqvSinglePass_sha512kdf_scheme, PREFIX + \"KeyAgreementSpi$MQVwithSHA512KDFAndSharedInfo\");\n\n registerOid(provider, X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme, \"EC\", new KeyFactorySpi.EC());\n registerOidAlgorithmParameters(provider, X9ObjectIdentifiers.mqvSinglePass_sha1kdf_scheme, \"EC\");\n\n registerOid(provider, SECObjectIdentifiers.mqvSinglePass_sha224kdf_scheme, \"ECMQV\", new KeyFactorySpi.ECMQV());\n registerOidAlgorithmParameters(provider, SECObjectIdentifiers.mqvSinglePass_sha256kdf_scheme, \"EC\");\n\n registerOid(provider, SECObjectIdentifiers.mqvSinglePass_sha256kdf_scheme, \"ECMQV\", new KeyFactorySpi.ECMQV());\n registerOidAlgorithmParameters(provider, SECObjectIdentifiers.mqvSinglePass_sha224kdf_scheme, \"EC\");\n\n registerOid(provider, SECObjectIdentifiers.mqvSinglePass_sha384kdf_scheme, \"ECMQV\", new KeyFactorySpi.ECMQV());\n registerOidAlgorithmParameters(provider, SECObjectIdentifiers.mqvSinglePass_sha384kdf_scheme, \"EC\");\n\n registerOid(provider, SECObjectIdentifiers.mqvSinglePass_sha512kdf_scheme, \"ECMQV\", new KeyFactorySpi.ECMQV());\n registerOidAlgorithmParameters(provider, SECObjectIdentifiers.mqvSinglePass_sha512kdf_scheme, \"EC\");\n\n provider.addAlgorithm(\"KeyFactory.ECMQV\", PREFIX + \"KeyFactorySpi$ECMQV\");\n provider.addAlgorithm(\"KeyPairGenerator.ECMQV\", PREFIX + \"KeyPairGeneratorSpi$ECMQV\");\n }\n\n provider.addAlgorithm(\"KeyFactory.EC\", PREFIX + \"KeyFactorySpi$EC\");\n provider.addAlgorithm(\"KeyFactory.ECDSA\", PREFIX + \"KeyFactorySpi$ECDSA\");\n provider.addAlgorithm(\"KeyFactory.ECDH\", PREFIX + \"KeyFactorySpi$ECDH\");\n provider.addAlgorithm(\"KeyFactory.ECDHC\", PREFIX + \"KeyFactorySpi$ECDHC\");\n\n provider.addAlgorithm(\"KeyPairGenerator.EC\", PREFIX + \"KeyPairGeneratorSpi$EC\");\n provider.addAlgorithm(\"KeyPairGenerator.ECDSA\", PREFIX + \"KeyPairGeneratorSpi$ECDSA\");\n provider.addAlgorithm(\"KeyPairGenerator.ECDH\", PREFIX + \"KeyPairGeneratorSpi$ECDH\");\n provider.addAlgorithm(\"KeyPairGenerator.ECDHWITHSHA1KDF\", PREFIX + \"KeyPairGeneratorSpi$ECDH\");\n provider.addAlgorithm(\"KeyPairGenerator.ECDHC\", PREFIX + \"KeyPairGeneratorSpi$ECDHC\");\n provider.addAlgorithm(\"KeyPairGenerator.ECIES\", PREFIX + \"KeyPairGeneratorSpi$ECDH\");\n\n provider.addAlgorithm(\"Cipher.ECIES\", PREFIX + \"IESCipher$ECIES\");\n provider.addAlgorithm(\"Cipher.ECIESwithAES\", PREFIX + \"IESCipher$ECIESwithAES\");\n provider.addAlgorithm(\"Cipher.ECIESWITHAES\", PREFIX + \"IESCipher$ECIESwithAES\");\n provider.addAlgorithm(\"Cipher.ECIESwithDESEDE\", PREFIX + \"IESCipher$ECIESwithDESede\");\n provider.addAlgorithm(\"Cipher.ECIESWITHDESEDE\", PREFIX + \"IESCipher$ECIESwithDESede\");\n provider.addAlgorithm(\"Cipher.ECIESwithAES-CBC\", PREFIX + \"IESCipher$ECIESwithAESCBC\");\n provider.addAlgorithm(\"Cipher.ECIESWITHAES-CBC\", PREFIX + \"IESCipher$ECIESwithAESCBC\");\n provider.addAlgorithm(\"Cipher.ECIESwithDESEDE-CBC\", PREFIX + \"IESCipher$ECIESwithDESedeCBC\");\n provider.addAlgorithm(\"Cipher.ECIESWITHDESEDE-CBC\", PREFIX + \"IESCipher$ECIESwithDESedeCBC\");\n\n provider.addAlgorithm(\"Cipher.OldECIES\", PREFIX + \"IESCipher$OldECIES\");\n provider.addAlgorithm(\"Cipher.OldECIESwithAES\", PREFIX + \"IESCipher$OldECIESwithAES\");\n provider.addAlgorithm(\"Cipher.OldECIESWITHAES\", PREFIX + \"IESCipher$OldECIESwithAES\");\n provider.addAlgorithm(\"Cipher.OldECIESwithDESEDE\", PREFIX + \"IESCipher$OldECIESwithDESede\");\n provider.addAlgorithm(\"Cipher.OldECIESWITHDESEDE\", PREFIX + \"IESCipher$OldECIESwithDESede\");\n provider.addAlgorithm(\"Cipher.OldECIESwithAES-CBC\", PREFIX + \"IESCipher$OldECIESwithAESCBC\");\n provider.addAlgorithm(\"Cipher.OldECIESWITHAES-CBC\", PREFIX + \"IESCipher$OldECIESwithAESCBC\");\n provider.addAlgorithm(\"Cipher.OldECIESwithDESEDE-CBC\", PREFIX + \"IESCipher$OldECIESwithDESedeCBC\");\n provider.addAlgorithm(\"Cipher.OldECIESWITHDESEDE-CBC\", PREFIX + \"IESCipher$OldECIESwithDESedeCBC\");\n\n provider.addAlgorithm(\"Signature.ECDSA\", PREFIX + \"SignatureSpi$ecDSA\");\n provider.addAlgorithm(\"Signature.NONEwithECDSA\", PREFIX + \"SignatureSpi$ecDSAnone\");\n\n provider.addAlgorithm(\"Alg.Alias.Signature.SHA1withECDSA\", \"ECDSA\");\n provider.addAlgorithm(\"Alg.Alias.Signature.ECDSAwithSHA1\", \"ECDSA\");\n provider.addAlgorithm(\"Alg.Alias.Signature.SHA1WITHECDSA\", \"ECDSA\");\n provider.addAlgorithm(\"Alg.Alias.Signature.ECDSAWITHSHA1\", \"ECDSA\");\n provider.addAlgorithm(\"Alg.Alias.Signature.SHA1WithECDSA\", \"ECDSA\");\n provider.addAlgorithm(\"Alg.Alias.Signature.ECDSAWithSHA1\", \"ECDSA\");\n provider.addAlgorithm(\"Alg.Alias.Signature.1.2.840.10045.4.1\", \"ECDSA\");\n provider.addAlgorithm(\"Alg.Alias.Signature.\" + TeleTrusTObjectIdentifiers.ecSignWithSha1, \"ECDSA\");\n\n provider.addAlgorithm(\"Signature.ECDDSA\", PREFIX + \"SignatureSpi$ecDetDSA\");\n provider.addAlgorithm(\"Signature.SHA1WITHECDDSA\", PREFIX + \"SignatureSpi$ecDetDSA\");\n provider.addAlgorithm(\"Signature.SHA224WITHECDDSA\", PREFIX + \"SignatureSpi$ecDetDSA224\");\n provider.addAlgorithm(\"Signature.SHA256WITHECDDSA\", PREFIX + \"SignatureSpi$ecDetDSA256\");\n provider.addAlgorithm(\"Signature.SHA384WITHECDDSA\", PREFIX + \"SignatureSpi$ecDetDSA384\");\n provider.addAlgorithm(\"Signature.SHA512WITHECDDSA\", PREFIX + \"SignatureSpi$ecDetDSA512\");\n provider.addAlgorithm(\"Signature.SHA3-224WITHECDDSA\", PREFIX + \"SignatureSpi$ecDetDSASha3_224\");\n provider.addAlgorithm(\"Signature.SHA3-256WITHECDDSA\", PREFIX + \"SignatureSpi$ecDetDSASha3_256\");\n provider.addAlgorithm(\"Signature.SHA3-384WITHECDDSA\", PREFIX + \"SignatureSpi$ecDetDSASha3_384\");\n provider.addAlgorithm(\"Signature.SHA3-512WITHECDDSA\", PREFIX + \"SignatureSpi$ecDetDSASha3_512\");\n\n provider.addAlgorithm(\"Alg.Alias.Signature.DETECDSA\", \"ECDDSA\");\n provider.addAlgorithm(\"Alg.Alias.Signature.SHA1WITHDETECDSA\", \"SHA1WITHECDDSA\");\n provider.addAlgorithm(\"Alg.Alias.Signature.SHA224WITHDETECDSA\", \"SHA224WITHECDDSA\");\n provider.addAlgorithm(\"Alg.Alias.Signature.SHA256WITHDETECDSA\", \"SHA256WITHECDDSA\");\n provider.addAlgorithm(\"Alg.Alias.Signature.SHA384WITHDETECDSA\", \"SHA384WITHECDDSA\");\n provider.addAlgorithm(\"Alg.Alias.Signature.SHA512WITHDETECDSA\", \"SHA512WITHECDDSA\");\n\n addSignatureAlgorithm(provider, \"SHA224\", \"ECDSA\", PREFIX + \"SignatureSpi$ecDSA224\", X9ObjectIdentifiers.ecdsa_with_SHA224);\n addSignatureAlgorithm(provider, \"SHA256\", \"ECDSA\", PREFIX + \"SignatureSpi$ecDSA256\", X9ObjectIdentifiers.ecdsa_with_SHA256);\n addSignatureAlgorithm(provider, \"SHA384\", \"ECDSA\", PREFIX + \"SignatureSpi$ecDSA384\", X9ObjectIdentifiers.ecdsa_with_SHA384);\n addSignatureAlgorithm(provider, \"SHA512\", \"ECDSA\", PREFIX + \"SignatureSpi$ecDSA512\", X9ObjectIdentifiers.ecdsa_with_SHA512);\n addSignatureAlgorithm(provider, \"SHA3-224\", \"ECDSA\", PREFIX + \"SignatureSpi$ecDSASha3_224\", NISTObjectIdentifiers.id_ecdsa_with_sha3_224);\n addSignatureAlgorithm(provider, \"SHA3-256\", \"ECDSA\", PREFIX + \"SignatureSpi$ecDSASha3_256\", NISTObjectIdentifiers.id_ecdsa_with_sha3_256);\n addSignatureAlgorithm(provider, \"SHA3-384\", \"ECDSA\", PREFIX + \"SignatureSpi$ecDSASha3_384\", NISTObjectIdentifiers.id_ecdsa_with_sha3_384);\n addSignatureAlgorithm(provider, \"SHA3-512\", \"ECDSA\", PREFIX + \"SignatureSpi$ecDSASha3_512\", NISTObjectIdentifiers.id_ecdsa_with_sha3_512);\n\n addSignatureAlgorithm(provider, \"RIPEMD160\", \"ECDSA\", PREFIX + \"SignatureSpi$ecDSARipeMD160\",TeleTrusTObjectIdentifiers.ecSignWithRipemd160);\n\n provider.addAlgorithm(\"Signature.SHA1WITHECNR\", PREFIX + \"SignatureSpi$ecNR\");\n provider.addAlgorithm(\"Signature.SHA224WITHECNR\", PREFIX + \"SignatureSpi$ecNR224\");\n provider.addAlgorithm(\"Signature.SHA256WITHECNR\", PREFIX + \"SignatureSpi$ecNR256\");\n provider.addAlgorithm(\"Signature.SHA384WITHECNR\", PREFIX + \"SignatureSpi$ecNR384\");\n provider.addAlgorithm(\"Signature.SHA512WITHECNR\", PREFIX + \"SignatureSpi$ecNR512\");\n\n addSignatureAlgorithm(provider, \"SHA1\", \"CVC-ECDSA\", PREFIX + \"SignatureSpi$ecCVCDSA\", EACObjectIdentifiers.id_TA_ECDSA_SHA_1);\n addSignatureAlgorithm(provider, \"SHA224\", \"CVC-ECDSA\", PREFIX + \"SignatureSpi$ecCVCDSA224\", EACObjectIdentifiers.id_TA_ECDSA_SHA_224);\n addSignatureAlgorithm(provider, \"SHA256\", \"CVC-ECDSA\", PREFIX + \"SignatureSpi$ecCVCDSA256\", EACObjectIdentifiers.id_TA_ECDSA_SHA_256);\n addSignatureAlgorithm(provider, \"SHA384\", \"CVC-ECDSA\", PREFIX + \"SignatureSpi$ecCVCDSA384\", EACObjectIdentifiers.id_TA_ECDSA_SHA_384);\n addSignatureAlgorithm(provider, \"SHA512\", \"CVC-ECDSA\", PREFIX + \"SignatureSpi$ecCVCDSA512\", EACObjectIdentifiers.id_TA_ECDSA_SHA_512);\n\n addSignatureAlgorithm(provider, \"SHA1\", \"PLAIN-ECDSA\", PREFIX + \"SignatureSpi$ecCVCDSA\", BSIObjectIdentifiers.ecdsa_plain_SHA1);\n addSignatureAlgorithm(provider, \"SHA224\", \"PLAIN-ECDSA\", PREFIX + \"SignatureSpi$ecCVCDSA224\", BSIObjectIdentifiers.ecdsa_plain_SHA224);\n addSignatureAlgorithm(provider, \"SHA256\", \"PLAIN-ECDSA\", PREFIX + \"SignatureSpi$ecCVCDSA256\", BSIObjectIdentifiers.ecdsa_plain_SHA256);\n addSignatureAlgorithm(provider, \"SHA384\", \"PLAIN-ECDSA\", PREFIX + \"SignatureSpi$ecCVCDSA384\", BSIObjectIdentifiers.ecdsa_plain_SHA384);\n addSignatureAlgorithm(provider, \"SHA512\", \"PLAIN-ECDSA\", PREFIX + \"SignatureSpi$ecCVCDSA512\", BSIObjectIdentifiers.ecdsa_plain_SHA512);\n addSignatureAlgorithm(provider, \"RIPEMD160\", \"PLAIN-ECDSA\", PREFIX + \"SignatureSpi$ecPlainDSARP160\", BSIObjectIdentifiers.ecdsa_plain_RIPEMD160);\n }\n }\n}\n\n## 内容3\n### 文件路径:\nprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/IESCipher.java\n### 代码内容:\npackage org.bouncycastle.jcajce.provider.asymmetric.dh;\n\nimport java.io.ByteArrayOutputStream;\nimport java.security.AlgorithmParameters;\nimport java.security.InvalidAlgorithmParameterException;\nimport java.security.InvalidKeyException;\nimport java.security.Key;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.SecureRandom;\nimport java.security.spec.AlgorithmParameterSpec;\n\nimport javax.crypto.BadPaddingException;\nimport javax.crypto.Cipher;\nimport javax.crypto.CipherSpi;\nimport javax.crypto.IllegalBlockSizeException;\nimport javax.crypto.NoSuchPaddingException;\nimport javax.crypto.ShortBufferException;\nimport javax.crypto.interfaces.DHKey;\nimport javax.crypto.interfaces.DHPrivateKey;\nimport javax.crypto.interfaces.DHPublicKey;\n\nimport org.bouncycastle.crypto.BlockCipher;\nimport org.bouncycastle.crypto.InvalidCipherTextException;\nimport org.bouncycastle.crypto.KeyEncoder;\nimport org.bouncycastle.crypto.agreement.DHBasicAgreement;\nimport org.bouncycastle.crypto.digests.SHA1Digest;\nimport org.bouncycastle.crypto.engines.AESEngine;\nimport org.bouncycastle.crypto.engines.DESedeEngine;\nimport org.bouncycastle.crypto.engines.IESEngine;\nimport org.bouncycastle.crypto.engines.OldIESEngine;\nimport org.bouncycastle.crypto.generators.DHKeyPairGenerator;\nimport org.bouncycastle.crypto.generators.EphemeralKeyPairGenerator;\nimport org.bouncycastle.crypto.generators.KDF2BytesGenerator;\nimport org.bouncycastle.crypto.macs.HMac;\nimport org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;\nimport org.bouncycastle.crypto.params.AsymmetricKeyParameter;\nimport org.bouncycastle.crypto.params.DHKeyGenerationParameters;\nimport org.bouncycastle.crypto.params.DHKeyParameters;\nimport org.bouncycastle.crypto.params.DHParameters;\nimport org.bouncycastle.crypto.params.DHPublicKeyParameters;\nimport org.bouncycastle.crypto.params.IESParameters;\nimport org.bouncycastle.crypto.params.IESWithCipherParameters;\nimport org.bouncycastle.crypto.parsers.DHIESPublicKeyParser;\nimport org.bouncycastle.jcajce.provider.asymmetric.util.DHUtil;\nimport org.bouncycastle.jcajce.provider.asymmetric.util.IESUtil;\nimport org.bouncycastle.jcajce.util.BCJcaJceHelper;\nimport org.bouncycastle.jcajce.util.JcaJceHelper;\nimport org.bouncycastle.jce.interfaces.IESKey;\nimport org.bouncycastle.jce.spec.IESParameterSpec;\nimport org.bouncycastle.util.BigIntegers;\nimport org.bouncycastle.util.Strings;\n\npublic class IESCipher\n extends CipherSpi\n{\n private final JcaJceHelper helper = new BCJcaJceHelper();\n\n private IESEngine engine;\n private int state = -1;\n private ByteArrayOutputStream buffer = new ByteArrayOutputStream();\n private AlgorithmParameters engineParam = null;\n private IESParameterSpec engineSpec = null;\n private AsymmetricKeyParameter key;\n private SecureRandom random;\n private boolean dhaesMode = false;\n private AsymmetricKeyParameter otherKeyParameter = null;\n\n public IESCipher(IESEngine engine)\n {\n this.engine = engine;\n }\n\n public IESCipher(OldIESEngine engine)\n {\n this.engine = engine;\n }\n\n public int engineGetBlockSize()\n {\n if (engine.getCipher() != null)\n {\n return engine.getCipher().getBlockSize();\n }\n else\n {\n return 0;\n }\n }\n\n public int engineGetKeySize(Key key)\n {\n if (key instanceof DHKey)\n {\n return ((DHKey)key).getParams().getP().bitLength();\n }\n else\n {\n throw new IllegalArgumentException(\"not a DH key\");\n }\n }\n\n public byte[] engineGetIV()\n {\n return null;\n }\n\n public AlgorithmParameters engineGetParameters()\n {\n if (engineParam == null && engineSpec != null)\n {\n try\n {\n engineParam = helper.createAlgorithmParameters(\"IES\");\n engineParam.init(engineSpec);\n }\n catch (Exception e)\n {\n throw new RuntimeException(e.toString());\n }\n }\n\n return engineParam;\n }\n\n public void engineSetMode(String mode)\n throws NoSuchAlgorithmException\n {\n String modeName = Strings.toUpperCase(mode);\n\n if (modeName.equals(\"NONE\"))\n {\n dhaesMode = false;\n }\n else if (modeName.equals(\"DHAES\"))\n {\n dhaesMode = true;\n }\n else\n {\n throw new IllegalArgumentException(\"can't support mode \" + mode);\n }\n }\n\n public int engineGetOutputSize(int inputLen)\n {\n int len1, len2, len3;\n\n if (key == null)\n {\n throw new IllegalStateException(\"cipher not initialised\");\n }\n\n len1 = engine.getMac().getMacSize();\n\n if (otherKeyParameter == null)\n {\n len2 = 1 + 2 * (((DHKeyParameters)key).getParameters().getP().bitLength() + 7) / 8;\n }\n else\n {\n len2 = 0;\n }\n\n if (engine.getCipher() == null)\n {\n len3 = inputLen;\n }\n else if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE)\n {\n len3 = engine.getCipher().getOutputSize(inputLen);\n }\n else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE)\n {\n len3 = engine.getCipher().getOutputSize(inputLen - len1 - len2);\n }\n else\n {\n throw new IllegalStateException(\"cipher not initialised\");\n }\n\n if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE)\n {\n return buffer.size() + len1 + len2 + len3;\n }\n else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE)\n {\n return buffer.size() - len1 - len2 + len3;\n }\n else\n {\n throw new IllegalStateException(\"IESCipher not initialised\");\n }\n\n }\n\n public void engineSetPadding(String padding)\n throws NoSuchPaddingException\n {\n String paddingName = Strings.toUpperCase(padding);\n\n // TDOD: make this meaningful...\n if (paddingName.equals(\"NOPADDING\"))\n {\n\n }\n else if (paddingName.equals(\"PKCS5PADDING\") || paddingName.equals(\"PKCS7PADDING\"))\n {\n\n }\n else\n {\n throw new NoSuchPaddingException(\"padding not available with IESCipher\");\n }\n }\n\n // Initialisation methods\n\n public void engineInit(\n int opmode,\n Key key,\n AlgorithmParameters params,\n SecureRandom random)\n throws InvalidKeyException, InvalidAlgorithmParameterException\n {\n AlgorithmParameterSpec paramSpec = null;\n\n if (params != null)\n {\n try\n {\n paramSpec = params.getParameterSpec(IESParameterSpec.class);\n }\n catch (Exception e)\n {\n throw new InvalidAlgorithmParameterException(\"cannot recognise parameters: \" + e.toString());\n }\n }\n\n engineParam = params;\n engineInit(opmode, key, paramSpec, random);\n }\n\n public void engineInit(\n int opmode,\n Key key,\n AlgorithmParameterSpec engineSpec,\n SecureRandom random)\n throws InvalidAlgorithmParameterException, InvalidKeyException\n {\n // Use default parameters (including cipher key size) if none are specified\n if (engineSpec == null)\n {\n this.engineSpec = IESUtil.guessParameterSpec(engine.getCipher());\n }\n else if (engineSpec instanceof IESParameterSpec)\n {\n this.engineSpec = (IESParameterSpec)engineSpec;\n }\n else\n {\n throw new InvalidAlgorithmParameterException(\"must be passed IES parameters\");\n }\n\n // Parse the recipient's key\n if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE)\n {\n if (key instanceof DHPublicKey)\n {\n this.key = DHUtil.generatePublicKeyParameter((PublicKey)key);\n }\n else if (key instanceof IESKey)\n {\n IESKey ieKey = (IESKey)key;\n\n this.key = DHUtil.generatePublicKeyParameter(ieKey.getPublic());\n this.otherKeyParameter = DHUtil.generatePrivateKeyParameter(ieKey.getPrivate());\n }\n else\n {\n throw new InvalidKeyException(\"must be passed recipient's public DH key for encryption\");\n }\n }\n else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE)\n {\n if (key instanceof DHPrivateKey)\n {\n this.key = DHUtil.generatePrivateKeyParameter((PrivateKey)key);\n }\n else if (key instanceof IESKey)\n {\n IESKey ieKey = (IESKey)key;\n\n this.otherKeyParameter = DHUtil.generatePublicKeyParameter(ieKey.getPublic());\n this.key = DHUtil.generatePrivateKeyParameter(ieKey.getPrivate());\n }\n else\n {\n throw new InvalidKeyException(\"must be passed recipient's private DH key for decryption\");\n }\n }\n else\n {\n throw new InvalidKeyException(\"must be passed EC key\");\n }\n\n this.random = random;\n this.state = opmode;\n buffer.reset();\n\n }\n\n public void engineInit(\n int opmode,\n Key key,\n SecureRandom random)\n throws InvalidKeyException\n {\n try\n {\n engineInit(opmode, key, (AlgorithmParameterSpec)null, random);\n }\n catch (InvalidAlgorithmParameterException e)\n {\n throw new IllegalArgumentException(\"can't handle supplied parameter spec\");\n }\n\n }\n\n // Update methods - buffer the input\n\n public byte[] engineUpdate(\n byte[] input,\n int inputOffset,\n int inputLen)\n {\n buffer.write(input, inputOffset, inputLen);\n return null;\n }\n\n public int engineUpdate(\n byte[] input,\n int inputOffset,\n int inputLen,\n byte[] output,\n int outputOffset)\n {\n buffer.write(input, inputOffset, inputLen);\n return 0;\n }\n\n // Finalisation methods\n\n public byte[] engineDoFinal(\n byte[] input,\n int inputOffset,\n int inputLen)\n throws IllegalBlockSizeException, BadPaddingException\n {\n if (inputLen != 0)\n {\n buffer.write(input, inputOffset, inputLen);\n }\n\n byte[] in = buffer.toByteArray();\n buffer.reset();\n\n // Convert parameters for use in IESEngine\n IESParameters params = new IESWithCipherParameters(engineSpec.getDerivationV(),\n engineSpec.getEncodingV(),\n engineSpec.getMacKeySize(),\n engineSpec.getCipherKeySize());\n\n DHParameters dhParams = ((DHKeyParameters)key).getParameters();\n\n byte[] V;\n if (otherKeyParameter != null)\n {\n try\n {\n if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE)\n {\n engine.init(true, otherKeyParameter, key, params);\n }\n else\n {\n engine.init(false, key, otherKeyParameter, params);\n }\n return engine.processBlock(in, 0, in.length);\n }\n catch (Exception e)\n {\n throw new BadPaddingException(e.getMessage());\n }\n }\n\n if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE)\n {\n // Generate the ephemeral key pair\n DHKeyPairGenerator gen = new DHKeyPairGenerator();\n gen.init(new DHKeyGenerationParameters(random, dhParams));\n\n EphemeralKeyPairGenerator kGen = new EphemeralKeyPairGenerator(gen, new KeyEncoder()\n {\n public byte[] getEncoded(AsymmetricKeyParameter keyParameter)\n {\n byte[] Vloc = new byte[(((DHKeyParameters)keyParameter).getParameters().getP().bitLength() + 7) / 8];\n byte[] Vtmp = BigIntegers.asUnsignedByteArray(((DHPublicKeyParameters)keyParameter).getY());\n\n if (Vtmp.length > Vloc.length)\n {\n throw new IllegalArgumentException(\"Senders's public key longer than expected.\");\n }\n else\n {\n System.arraycopy(Vtmp, 0, Vloc, Vloc.length - Vtmp.length, Vtmp.length);\n }\n\n return Vloc;\n }\n });\n\n // Encrypt the buffer\n try\n {\n engine.init(key, params, kGen);\n\n return engine.processBlock(in, 0, in.length);\n }\n catch (Exception e)\n {\n throw new BadPaddingException(e.getMessage());\n }\n }\n else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE)\n {\n // Decrypt the buffer\n try\n {\n engine.init(key, params, new DHIESPublicKeyParser(((DHKeyParameters)key).getParameters()));\n\n return engine.processBlock(in, 0, in.length);\n }\n catch (InvalidCipherTextException e)\n {\n throw new BadPaddingException(e.getMessage());\n }\n }\n else\n {\n throw new IllegalStateException(\"IESCipher not initialised\");\n }\n\n }\n\n public int engineDoFinal(\n byte[] input,\n int inputOffset,\n int inputLength,\n byte[] output,\n int outputOffset)\n throws ShortBufferException, IllegalBlockSizeException, BadPaddingException\n {\n\n byte[] buf = engineDoFinal(input, inputOffset, inputLength);\n System.arraycopy(buf, 0, output, outputOffset, buf.length);\n return buf.length;\n\n }\n\n /**\n * Classes that inherit from us\n */\n\n static public class IES\n extends IESCipher\n {\n public IES()\n {\n super(new IESEngine(new DHBasicAgreement(),\n new KDF2BytesGenerator(new SHA1Digest()),\n new HMac(new SHA1Digest())));\n }\n }\n\n static public class IESwithDESede\n extends IESCipher\n {\n public IESwithDESede()\n {\n super(new IESEngine(new DHBasicAgreement(),\n new KDF2BytesGenerator(new SHA1Digest()),\n new HMac(new SHA1Digest()),\n new PaddedBufferedBlockCipher(new DESedeEngine())));\n }\n }\n\n static public class IESwithAES\n extends IESCipher\n {\n public IESwithAES()\n {\n super(new IESEngine(new DHBasicAgreement(),\n new KDF2BytesGenerator(new SHA1Digest()),\n new HMac(new SHA1Digest()),\n new PaddedBufferedBlockCipher(new AESEngine())));\n }\n }\n\n /**\n * Backwards compatibility.\n */\n static public class OldIESwithCipher\n extends IESCipher\n {\n public OldIESwithCipher(BlockCipher baseCipher)\n {\n super(new OldIESEngine(new DHBasicAgreement(),\n new KDF2BytesGenerator(new SHA1Digest()),\n new HMac(new SHA1Digest()),\n new PaddedBufferedBlockCipher(baseCipher)));\n }\n }\n\n static public class OldIES\n extends IESCipher\n {\n public OldIES()\n {\n super(new OldIESEngine(new DHBasicAgreement(),\n new KDF2BytesGenerator(new SHA1Digest()),\n new HMac(new SHA1Digest())));\n }\n }\n\n static public class OldIESwithDESede\n extends OldIESwithCipher\n {\n public OldIESwithDESede()\n {\n super(new DESedeEngine());\n }\n }\n\n static public class OldIESwithAES\n extends OldIESwithCipher\n {\n public OldIESwithAES()\n {\n super(new AESEngine());\n }\n }\n}\n\n## 内容4\n### 文件路径:\nprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/IESCipher.java\n### 代码内容:\npackage org.bouncycastle.jcajce.provider.asymmetric.ec;\n\nimport java.io.ByteArrayOutputStream;\nimport java.security.AlgorithmParameters;\nimport java.security.InvalidAlgorithmParameterException;\nimport java.security.InvalidKeyException;\nimport java.security.Key;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.SecureRandom;\nimport java.security.spec.AlgorithmParameterSpec;\n\nimport javax.crypto.BadPaddingException;\nimport javax.crypto.Cipher;\nimport javax.crypto.CipherSpi;\nimport javax.crypto.IllegalBlockSizeException;\nimport javax.crypto.NoSuchPaddingException;\nimport javax.crypto.ShortBufferException;\n\nimport org.bouncycastle.crypto.BlockCipher;\nimport org.bouncycastle.crypto.CipherParameters;\nimport org.bouncycastle.crypto.InvalidCipherTextException;\nimport org.bouncycastle.crypto.KeyEncoder;\nimport org.bouncycastle.crypto.agreement.ECDHBasicAgreement;\nimport org.bouncycastle.crypto.digests.SHA1Digest;\nimport org.bouncycastle.crypto.engines.AESEngine;\nimport org.bouncycastle.crypto.engines.DESedeEngine;\nimport org.bouncycastle.crypto.engines.IESEngine;\nimport org.bouncycastle.crypto.engines.OldIESEngine;\nimport org.bouncycastle.crypto.generators.ECKeyPairGenerator;\nimport org.bouncycastle.crypto.generators.EphemeralKeyPairGenerator;\nimport org.bouncycastle.crypto.generators.KDF2BytesGenerator;\nimport org.bouncycastle.crypto.macs.HMac;\nimport org.bouncycastle.crypto.modes.CBCBlockCipher;\nimport org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;\nimport org.bouncycastle.crypto.params.AsymmetricKeyParameter;\nimport org.bouncycastle.crypto.params.ECDomainParameters;\nimport org.bouncycastle.crypto.params.ECKeyGenerationParameters;\nimport org.bouncycastle.crypto.params.ECKeyParameters;\nimport org.bouncycastle.crypto.params.ECPublicKeyParameters;\nimport org.bouncycastle.crypto.params.IESWithCipherParameters;\nimport org.bouncycastle.crypto.params.ParametersWithIV;\nimport org.bouncycastle.crypto.parsers.ECIESPublicKeyParser;\nimport org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;\nimport org.bouncycastle.jcajce.provider.asymmetric.util.IESUtil;\nimport org.bouncycastle.jcajce.util.BCJcaJceHelper;\nimport org.bouncycastle.jcajce.util.JcaJceHelper;\nimport org.bouncycastle.jce.interfaces.ECKey;\nimport org.bouncycastle.jce.interfaces.IESKey;\nimport org.bouncycastle.jce.spec.IESParameterSpec;\nimport org.bouncycastle.util.Strings;\n\npublic class IESCipher\n extends CipherSpi\n{\n private final JcaJceHelper helper = new BCJcaJceHelper();\n\n private int ivLength;\n private IESEngine engine;\n private int state = -1;\n private ByteArrayOutputStream buffer = new ByteArrayOutputStream();\n private AlgorithmParameters engineParam = null;\n private IESParameterSpec engineSpec = null;\n private AsymmetricKeyParameter key;\n private SecureRandom random;\n private boolean dhaesMode = false;\n private AsymmetricKeyParameter otherKeyParameter = null;\n\n public IESCipher(IESEngine engine)\n {\n this.engine = engine;\n this.ivLength = 0;\n }\n\n public IESCipher(IESEngine engine, int ivLength)\n {\n this.engine = engine;\n this.ivLength = ivLength;\n }\n\n public int engineGetBlockSize()\n {\n if (engine.getCipher() != null)\n {\n return engine.getCipher().getBlockSize();\n }\n else\n {\n return 0;\n }\n }\n\n public int engineGetKeySize(Key key)\n {\n if (key instanceof ECKey)\n {\n return ((ECKey)key).getParameters().getCurve().getFieldSize();\n }\n else\n {\n throw new IllegalArgumentException(\"not an EC key\");\n }\n }\n\n public byte[] engineGetIV()\n {\n return null;\n }\n\n public AlgorithmParameters engineGetParameters()\n {\n if (engineParam == null && engineSpec != null)\n {\n try\n {\n engineParam = helper.createAlgorithmParameters(\"IES\");\n engineParam.init(engineSpec);\n }\n catch (Exception e)\n {\n throw new RuntimeException(e.toString());\n }\n }\n\n return engineParam;\n }\n\n public void engineSetMode(String mode)\n throws NoSuchAlgorithmException\n {\n String modeName = Strings.toUpperCase(mode);\n\n if (modeName.equals(\"NONE\"))\n {\n dhaesMode = false;\n }\n else if (modeName.equals(\"DHAES\"))\n {\n dhaesMode = true;\n }\n else\n {\n throw new IllegalArgumentException(\"can't support mode \" + mode);\n }\n }\n\n public int engineGetOutputSize(int inputLen)\n {\n int len1, len2, len3;\n\n if (key == null)\n {\n throw new IllegalStateException(\"cipher not initialised\");\n }\n\n len1 = engine.getMac().getMacSize();\n\n if (otherKeyParameter == null)\n {\n len2 = 2 * (((ECKeyParameters)key).getParameters().getCurve().getFieldSize() + 7) / 8;\n }\n else\n {\n len2 = 0;\n }\n\n if (engine.getCipher() == null)\n {\n len3 = inputLen;\n }\n else if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE)\n {\n len3 = engine.getCipher().getOutputSize(inputLen);\n }\n else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE)\n {\n len3 = engine.getCipher().getOutputSize(inputLen - len1 - len2);\n }\n else\n {\n throw new IllegalStateException(\"cipher not initialised\");\n }\n\n if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE)\n {\n return buffer.size() + len1 + 1 + len2 + len3;\n }\n else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE)\n {\n return buffer.size() - len1 - len2 + len3;\n }\n else\n {\n throw new IllegalStateException(\"cipher not initialised\");\n }\n\n }\n\n public void engineSetPadding(String padding)\n throws NoSuchPaddingException\n {\n String paddingName = Strings.toUpperCase(padding);\n\n // TDOD: make this meaningful...\n if (paddingName.equals(\"NOPADDING\"))\n {\n\n }\n else if (paddingName.equals(\"PKCS5PADDING\") || paddingName.equals(\"PKCS7PADDING\"))\n {\n\n }\n else\n {\n throw new NoSuchPaddingException(\"padding not available with IESCipher\");\n }\n }\n\n // Initialisation methods\n\n public void engineInit(\n int opmode,\n Key key,\n AlgorithmParameters params,\n SecureRandom random)\n throws InvalidKeyException, InvalidAlgorithmParameterException\n {\n AlgorithmParameterSpec paramSpec = null;\n\n if (params != null)\n {\n try\n {\n paramSpec = params.getParameterSpec(IESParameterSpec.class);\n }\n catch (Exception e)\n {\n throw new InvalidAlgorithmParameterException(\"cannot recognise parameters: \" + e.toString());\n }\n }\n\n engineParam = params;\n engineInit(opmode, key, paramSpec, random);\n\n }\n\n public void engineInit(\n int opmode,\n Key key,\n AlgorithmParameterSpec engineSpec,\n SecureRandom random)\n throws InvalidAlgorithmParameterException, InvalidKeyException\n {\n otherKeyParameter = null;\n\n // Use default parameters (including cipher key size) if none are specified\n if (engineSpec == null)\n {\n this.engineSpec = IESUtil.guessParameterSpec(engine.getCipher());\n }\n else if (engineSpec instanceof IESParameterSpec)\n {\n this.engineSpec = (IESParameterSpec)engineSpec;\n }\n else\n {\n throw new InvalidAlgorithmParameterException(\"must be passed IES parameters\");\n }\n\n byte[] nonce = this.engineSpec.getNonce();\n\n if (nonce != null)\n {\n if (ivLength == 0)\n {\n throw new InvalidAlgorithmParameterException(\"NONCE present in IES Parameters when none required\");\n }\n else if (nonce.length != ivLength)\n {\n throw new InvalidAlgorithmParameterException(\"NONCE in IES Parameters needs to be \" + ivLength + \" bytes long\");\n }\n }\n\n // Parse the recipient's key\n if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE)\n {\n if (key instanceof PublicKey)\n {\n this.key = ECUtils.generatePublicKeyParameter((PublicKey)key);\n }\n else if (key instanceof IESKey)\n {\n IESKey ieKey = (IESKey)key;\n\n this.key = ECUtils.generatePublicKeyParameter(ieKey.getPublic());\n this.otherKeyParameter = ECUtil.generatePrivateKeyParameter(ieKey.getPrivate());\n }\n else\n {\n throw new InvalidKeyException(\"must be passed recipient's public EC key for encryption\");\n }\n }\n else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE)\n {\n if (key instanceof PrivateKey)\n {\n this.key = ECUtil.generatePrivateKeyParameter((PrivateKey)key);\n }\n else if (key instanceof IESKey)\n {\n IESKey ieKey = (IESKey)key;\n\n this.otherKeyParameter = ECUtils.generatePublicKeyParameter(ieKey.getPublic());\n this.key = ECUtil.generatePrivateKeyParameter(ieKey.getPrivate());\n }\n else\n {\n throw new InvalidKeyException(\"must be passed recipient's private EC key for decryption\");\n }\n }\n else\n {\n throw new InvalidKeyException(\"must be passed EC key\");\n }\n\n this.random = random;\n this.state = opmode;\n buffer.reset();\n\n }\n\n public void engineInit(\n int opmode,\n Key key,\n SecureRandom random)\n throws InvalidKeyException\n {\n try\n {\n engineInit(opmode, key, (AlgorithmParameterSpec)null, random);\n }\n catch (InvalidAlgorithmParameterException e)\n {\n throw new IllegalArgumentException(\"can't handle supplied parameter spec\");\n }\n\n }\n\n // Update methods - buffer the input\n\n public byte[] engineUpdate(\n byte[] input,\n int inputOffset,\n int inputLen)\n {\n buffer.write(input, inputOffset, inputLen);\n return null;\n }\n\n public int engineUpdate(\n byte[] input,\n int inputOffset,\n int inputLen,\n byte[] output,\n int outputOffset)\n {\n buffer.write(input, inputOffset, inputLen);\n return 0;\n }\n\n // Finalisation methods\n\n public byte[] engineDoFinal(\n byte[] input,\n int inputOffset,\n int inputLen)\n throws IllegalBlockSizeException, BadPaddingException\n {\n if (inputLen != 0)\n {\n buffer.write(input, inputOffset, inputLen);\n }\n\n final byte[] in = buffer.toByteArray();\n buffer.reset();\n\n // Convert parameters for use in IESEngine\n CipherParameters params = new IESWithCipherParameters(engineSpec.getDerivationV(),\n engineSpec.getEncodingV(),\n engineSpec.getMacKeySize(),\n engineSpec.getCipherKeySize());\n\n if (engineSpec.getNonce() != null)\n {\n params = new ParametersWithIV(params, engineSpec.getNonce());\n }\n\n final ECDomainParameters ecParams = ((ECKeyParameters)key).getParameters();\n\n final byte[] V;\n\n if (otherKeyParameter != null)\n {\n try\n {\n if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE)\n {\n engine.init(true, otherKeyParameter, key, params);\n }\n else\n {\n engine.init(false, key, otherKeyParameter, params);\n }\n return engine.processBlock(in, 0, in.length);\n }\n catch (Exception e)\n {\n throw new BadPaddingException(e.getMessage());\n }\n }\n\n if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE)\n {\n // Generate the ephemeral key pair\n ECKeyPairGenerator gen = new ECKeyPairGenerator();\n gen.init(new ECKeyGenerationParameters(ecParams, random));\n\n final boolean usePointCompression = engineSpec.getPointCompression();\n EphemeralKeyPairGenerator kGen = new EphemeralKeyPairGenerator(gen, new KeyEncoder()\n {\n public byte[] getEncoded(AsymmetricKeyParameter keyParameter)\n {\n return ((ECPublicKeyParameters)keyParameter).getQ().getEncoded(usePointCompression);\n }\n });\n\n // Encrypt the buffer\n try\n {\n engine.init(key, params, kGen);\n\n return engine.processBlock(in, 0, in.length);\n }\n catch (Exception e)\n {\n throw new BadPaddingException(e.getMessage());\n }\n\n }\n else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE)\n {\n // Decrypt the buffer\n try\n {\n engine.init(key, params, new ECIESPublicKeyParser(ecParams));\n\n return engine.processBlock(in, 0, in.length);\n }\n catch (InvalidCipherTextException e)\n {\n throw new BadPaddingException(e.getMessage());\n }\n }\n else\n {\n throw new IllegalStateException(\"cipher not initialised\");\n }\n\n }\n\n public int engineDoFinal(\n byte[] input,\n int inputOffset,\n int inputLength,\n byte[] output,\n int outputOffset)\n throws ShortBufferException, IllegalBlockSizeException, BadPaddingException\n {\n\n byte[] buf = engineDoFinal(input, inputOffset, inputLength);\n System.arraycopy(buf, 0, output, outputOffset, buf.length);\n return buf.length;\n }\n\n /**\n * Classes that inherit from us\n */\n\n static public class ECIES\n extends IESCipher\n {\n public ECIES()\n {\n super(new IESEngine(new ECDHBasicAgreement(),\n new KDF2BytesGenerator(new SHA1Digest()),\n new HMac(new SHA1Digest())));\n }\n }\n\n static public class ECIESwithCipher\n extends IESCipher\n {\n public ECIESwithCipher(BlockCipher cipher)\n {\n super(new IESEngine(new ECDHBasicAgreement(),\n new KDF2BytesGenerator(new SHA1Digest()),\n new HMac(new SHA1Digest()),\n new PaddedBufferedBlockCipher(cipher)));\n }\n\n public ECIESwithCipher(BlockCipher cipher, int ivLength)\n {\n super(new IESEngine(new ECDHBasicAgreement(),\n new KDF2BytesGenerator(new SHA1Digest()),\n new HMac(new SHA1Digest()),\n new PaddedBufferedBlockCipher(cipher)), ivLength);\n }\n }\n\n static public class ECIESwithDESede\n extends ECIESwithCipher\n {\n public ECIESwithDESede()\n {\n super(new DESedeEngine());\n }\n }\n\n static public class ECIESwithAES\n extends ECIESwithCipher\n {\n public ECIESwithAES()\n {\n super(new AESEngine());\n }\n }\n\n static public class ECIESwithDESedeCBC\n extends ECIESwithCipher\n {\n public ECIESwithDESedeCBC()\n {\n super(new CBCBlockCipher(new DESedeEngine()), 8);\n }\n }\n\n static public class ECIESwithAESCBC\n extends ECIESwithCipher\n {\n public ECIESwithAESCBC()\n {\n super(new CBCBlockCipher(new AESEngine()), 16);\n }\n }\n\n /**\n * Backwards compatibility\n */\n static public class OldECIES\n extends IESCipher\n {\n public OldECIES()\n {\n super(new OldIESEngine(new ECDHBasicAgreement(),\n new KDF2BytesGenerator(new SHA1Digest()),\n new HMac(new SHA1Digest())));\n }\n }\n\n static public class OldECIESwithCipher\n extends IESCipher\n {\n public OldECIESwithCipher(BlockCipher baseCipher)\n {\n super(new OldIESEngine(new ECDHBasicAgreement(),\n new KDF2BytesGenerator(new SHA1Digest()),\n new HMac(new SHA1Digest()),\n new PaddedBufferedBlockCipher(baseCipher)));\n }\n\n public OldECIESwithCipher(BlockCipher baseCipher, int ivLength)\n {\n super(new OldIESEngine(new ECDHBasicAgreement(),\n new KDF2BytesGenerator(new SHA1Digest()),\n new HMac(new SHA1Digest()),\n new PaddedBufferedBlockCipher(baseCipher)), ivLength);\n }\n }\n\n static public class OldECIESwithDESede\n extends OldECIESwithCipher\n {\n public OldECIESwithDESede()\n {\n super(new DESedeEngine());\n }\n }\n\n static public class OldECIESwithAES\n extends OldECIESwithCipher\n {\n public OldECIESwithAES()\n {\n super(new AESEngine());\n }\n }\n\n static public class OldECIESwithDESedeCBC\n extends OldECIESwithCipher\n {\n public OldECIESwithDESedeCBC()\n {\n super(new CBCBlockCipher(new DESedeEngine()), 8);\n }\n }\n\n static public class OldECIESwithAESCBC\n extends OldECIESwithCipher\n {\n public OldECIESwithAESCBC()\n {\n super(new CBCBlockCipher(new AESEngine()), 16);\n }\n }\n}\n\n## 内容5\n### 文件路径:\nprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ies/AlgorithmParametersSpi.java\n### 代码内容:\npackage org.bouncycastle.jcajce.provider.asymmetric.ies;\n\nimport java.io.IOException;\nimport java.security.spec.AlgorithmParameterSpec;\nimport java.security.spec.InvalidParameterSpecException;\n\nimport org.bouncycastle.asn1.ASN1EncodableVector;\nimport org.bouncycastle.asn1.ASN1Encoding;\nimport org.bouncycastle.asn1.ASN1Integer;\nimport org.bouncycastle.asn1.ASN1OctetString;\nimport org.bouncycastle.asn1.ASN1Primitive;\nimport org.bouncycastle.asn1.ASN1Sequence;\nimport org.bouncycastle.asn1.ASN1TaggedObject;\nimport org.bouncycastle.asn1.DEROctetString;\nimport org.bouncycastle.asn1.DERSequence;\nimport org.bouncycastle.asn1.DERTaggedObject;\nimport org.bouncycastle.jce.spec.IESParameterSpec;\n\npublic class AlgorithmParametersSpi\n extends java.security.AlgorithmParametersSpi\n{\n protected boolean isASN1FormatString(String format)\n {\n return format == null || format.equals(\"ASN.1\");\n }\n\n protected AlgorithmParameterSpec engineGetParameterSpec(\n Class paramSpec)\n throws InvalidParameterSpecException\n {\n if (paramSpec == null)\n {\n throw new NullPointerException(\"argument to getParameterSpec must not be null\");\n }\n\n return localEngineGetParameterSpec(paramSpec);\n }\n\n IESParameterSpec currentSpec;\n\n /**\n * in the absence of a standard way of doing it this will do for\n * now...\n */\n protected byte[] engineGetEncoded()\n {\n try\n {\n ASN1EncodableVector v = new ASN1EncodableVector();\n\n if (currentSpec.getDerivationV() != null)\n {\n v.add(new DERTaggedObject(false, 0, new DEROctetString(currentSpec.getDerivationV())));\n }\n if (currentSpec.getEncodingV() != null)\n {\n v.add(new DERTaggedObject(false, 1, new DEROctetString(currentSpec.getEncodingV())));\n }\n v.add(new ASN1Integer(currentSpec.getMacKeySize()));\n\n return new DERSequence(v).getEncoded(ASN1Encoding.DER);\n }\n catch (IOException e)\n {\n throw new RuntimeException(\"Error encoding IESParameters\");\n }\n }\n\n protected byte[] engineGetEncoded(\n String format)\n {\n if (isASN1FormatString(format) || format.equalsIgnoreCase(\"X.509\"))\n {\n return engineGetEncoded();\n }\n\n return null;\n }\n\n protected AlgorithmParameterSpec localEngineGetParameterSpec(\n Class paramSpec)\n throws InvalidParameterSpecException\n {\n if (paramSpec == IESParameterSpec.class || paramSpec == AlgorithmParameterSpec.class)\n {\n return currentSpec;\n }\n\n throw new InvalidParameterSpecException(\"unknown parameter spec passed to ElGamal parameters object.\");\n }\n\n protected void engineInit(\n AlgorithmParameterSpec paramSpec)\n throws InvalidParameterSpecException\n {\n if (!(paramSpec instanceof IESParameterSpec))\n {\n throw new InvalidParameterSpecException(\"IESParameterSpec required to initialise a IES algorithm parameters object\");\n }\n\n this.currentSpec = (IESParameterSpec)paramSpec;\n }\n\n protected void engineInit(\n byte[] params)\n throws IOException\n {\n try\n {\n ASN1Sequence s = (ASN1Sequence)ASN1Primitive.fromByteArray(params);\n\n if (s.size() == 1)\n {\n this.currentSpec = new IESParameterSpec(null, null, ASN1Integer.getInstance(s.getObjectAt(0)).getValue().intValue());\n }\n else if (s.size() == 2)\n {\n ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(s.getObjectAt(0));\n\n if (tagged.getTagNo() == 0)\n {\n this.currentSpec = new IESParameterSpec(ASN1OctetString.getInstance(tagged, false).getOctets(), null, ASN1Integer.getInstance(s.getObjectAt(1)).getValue().intValue());\n }\n else\n {\n this.currentSpec = new IESParameterSpec(null, ASN1OctetString.getInstance(tagged, false).getOctets(), ASN1Integer.getInstance(s.getObjectAt(1)).getValue().intValue());\n }\n }\n else\n {\n ASN1TaggedObject tagged1 = ASN1TaggedObject.getInstance(s.getObjectAt(0));\n ASN1TaggedObject tagged2 = ASN1TaggedObject.getInstance(s.getObjectAt(1));\n\n this.currentSpec = new IESParameterSpec(ASN1OctetString.getInstance(tagged1, false).getOctets(), ASN1OctetString.getInstance(tagged2, false).getOctets(), ASN1Integer.getInstance(s.getObjectAt(2)).getValue().intValue());\n }\n }\n catch (ClassCastException e)\n {\n throw new IOException(\"Not a valid IES Parameter encoding.\");\n }\n catch (ArrayIndexOutOfBoundsException e)\n {\n throw new IOException(\"Not a valid IES Parameter encoding.\");\n }\n }\n\n protected void engineInit(\n byte[] params,\n String format)\n throws IOException\n {\n if (isASN1FormatString(format) || format.equalsIgnoreCase(\"X.509\"))\n {\n engineInit(params);\n }\n else\n {\n throw new IOException(\"Unknown parameter format \" + format);\n }\n }\n\n protected String engineToString()\n {\n return \"IES Parameters\";\n }\n}\n\n## 内容6\n### 文件路径:\nprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/IESUtil.java\n### 代码内容:\npackage org.bouncycastle.jcajce.provider.asymmetric.util;\n\nimport org.bouncycastle.crypto.BlockCipher;\nimport org.bouncycastle.crypto.BufferedBlockCipher;\nimport org.bouncycastle.jce.spec.IESParameterSpec;\n\npublic class IESUtil\n{\n public static IESParameterSpec guessParameterSpec(BufferedBlockCipher iesBlockCipher)\n {\n if (iesBlockCipher == null)\n {\n return new IESParameterSpec(null, null, 128);\n }\n else\n {\n BlockCipher underlyingCipher = iesBlockCipher.getUnderlyingCipher();\n\n if (underlyingCipher.getAlgorithmName().equals(\"DES\") ||\n underlyingCipher.getAlgorithmName().equals(\"RC2\") ||\n underlyingCipher.getAlgorithmName().equals(\"RC5-32\") ||\n underlyingCipher.getAlgorithmName().equals(\"RC5-64\"))\n {\n return new IESParameterSpec(null, null, 64, 64);\n }\n else if (underlyingCipher.getAlgorithmName().equals(\"SKIPJACK\"))\n {\n return new IESParameterSpec(null, null, 80, 80);\n }\n else if (underlyingCipher.getAlgorithmName().equals(\"GOST28147\"))\n {\n return new IESParameterSpec(null, null, 256, 256);\n }\n\n return new IESParameterSpec(null, null, 128, 128);\n }\n }\n}\n\n## 内容7\n### 文件路径:\nprov/src/main/java/org/bouncycastle/jce/spec/IESParameterSpec.java\n### 代码内容:\npackage org.bouncycastle.jce.spec;\n\nimport java.security.spec.AlgorithmParameterSpec;\n\nimport org.bouncycastle.util.Arrays;\n\n/**\n * Parameter spec for an integrated encryptor, as in IEEE P1363a\n */\npublic class IESParameterSpec\n implements AlgorithmParameterSpec\n{\n private byte[] derivation;\n private byte[] encoding;\n private int macKeySize;\n private int cipherKeySize;\n private byte[] nonce;\n private boolean usePointCompression;\n\n /**\n * Set the IES engine parameters.\n *\n * @param derivation the optional derivation vector for the KDF.\n * @param encoding the optional encoding vector for the KDF.\n * @param macKeySize the key size (in bits) for the MAC.\n */\n public IESParameterSpec(\n byte[] derivation,\n byte[] encoding,\n int macKeySize)\n {\n this(derivation, encoding, macKeySize, -1, null, false);\n }\n\n /**\n * Set the IES engine parameters.\n *\n * @param derivation the optional derivation vector for the KDF.\n * @param encoding the optional encoding vector for the KDF.\n * @param macKeySize the key size (in bits) for the MAC.\n * @param cipherKeySize the key size (in bits) for the block cipher.\n */\n public IESParameterSpec(\n byte[] derivation,\n byte[] encoding,\n int macKeySize,\n int cipherKeySize)\n {\n this(derivation, encoding, macKeySize, cipherKeySize, null, false);\n }\n\n /**\n * Set the IES engine parameters.\n *\n * @param derivation the optional derivation vector for the KDF.\n * @param encoding the optional encoding vector for the KDF.\n * @param macKeySize the key size (in bits) for the MAC.\n * @param cipherKeySize the key size (in bits) for the block cipher.\n * @param nonce an IV to use initialising the block cipher.\n */\n public IESParameterSpec(\n byte[] derivation,\n byte[] encoding,\n int macKeySize,\n int cipherKeySize,\n byte[] nonce)\n {\n this(derivation, encoding, macKeySize, cipherKeySize, nonce, false);\n }\n\n /**\n * Set the IES engine parameters.\n *\n * @param derivation the optional derivation vector for the KDF.\n * @param encoding the optional encoding vector for the KDF.\n * @param macKeySize the key size (in bits) for the MAC.\n * @param cipherKeySize the key size (in bits) for the block cipher.\n * @param nonce an IV to use initialising the block cipher.\n * @param usePointCompression whether to use EC point compression or not (false by default)\n */\n public IESParameterSpec(\n byte[] derivation,\n byte[] encoding,\n int macKeySize,\n int cipherKeySize,\n byte[] nonce,\n boolean usePointCompression)\n {\n if (derivation != null)\n {\n this.derivation = new byte[derivation.length];\n System.arraycopy(derivation, 0, this.derivation, 0, derivation.length);\n }\n else\n {\n this.derivation = null;\n }\n\n if (encoding != null)\n {\n this.encoding = new byte[encoding.length];\n System.arraycopy(encoding, 0, this.encoding, 0, encoding.length);\n }\n else\n {\n this.encoding = null;\n }\n\n this.macKeySize = macKeySize;\n this.cipherKeySize = cipherKeySize;\n this.nonce = Arrays.clone(nonce);\n this.usePointCompression = usePointCompression;\n }\n\n /**\n * return the derivation vector.\n */\n public byte[] getDerivationV()\n {\n return Arrays.clone(derivation);\n }\n\n /**\n * return the encoding vector.\n */\n public byte[] getEncodingV()\n {\n return Arrays.clone(encoding);\n }\n\n /**\n * return the key size in bits for the MAC used with the message\n */\n public int getMacKeySize()\n {\n return macKeySize;\n }\n\n /**\n * return the key size in bits for the block cipher used with the message\n */\n public int getCipherKeySize()\n {\n return cipherKeySize;\n }\n\n /**\n * Return the nonce (IV) value to be associated with message.\n *\n * @return block cipher IV for message.\n */\n public byte[] getNonce()\n {\n return Arrays.clone(nonce);\n }\n\n /**\n * Set the 'point compression' flag.\n */\n public void setPointCompression(boolean usePointCompression)\n {\n this.usePointCompression = usePointCompression;\n }\n\n /**\n * Return the 'point compression' flag.\n *\n * @return the point compression flag\n */\n public boolean getPointCompression()\n {\n return usePointCompression;\n }\n}\n", "input": "", "output": "【漏洞类型】弱加密算法支持\n【严重性】High\n【漏洞摘要】原始代码中支持了非 CBC 模式(如 ECB)的加密算法(例如 AES 和 DESede),这可能导致数据加密的安全性不足。具体来说,原始代码中通过 `addSignatureAlgorithm` 方法和 `engineDoFinal` 方法注册了多种非 CBC 模式的加密算法,这可能会导致数据在传输或存储过程中不安全。例如,在 DH.java 和 EC.java 文件中,`Cipher.IESwithAES`、`Cipher.IESWITHAES`、`Cipher.IESWITHDESEDE`、`Cipher.DHIES`、`Cipher.DHIESWITHAES` 等算法在未指定 IV(初始化向量)的情况下使用,这使得非 CBC 模式的加密算法更容易受到攻击。\n【关键代码模式】与加密算法初始化和使用相关的代码模式,包括 `engineInit` 方法和 `engineDoFinal` 方法。\n【修复方法说明】修复方法通过移除对非 CBC 模式加密算法的支持,仅保留了 AES 和 DESede 的 CBC 模式加密算法,并在初始化时生成随机的 IV。这提高了数据加密的安全性,因为 CBC 模式需要 IV 来确保安全性,而非 CBC 模式的加密算法在缺乏 IV 时容易受到攻击。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java\n### 代码内容:\npackage org.bouncycastle.jcajce.provider.asymmetric.dsa;\n\nimport java.io.IOException;\nimport java.math.BigInteger;\nimport java.security.InvalidKeyException;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.SecureRandom;\nimport java.security.SignatureException;\nimport java.security.SignatureSpi;\nimport java.security.interfaces.DSAKey;\nimport java.security.interfaces.DSAPublicKey;\nimport java.security.spec.AlgorithmParameterSpec;\n\nimport org.bouncycastle.asn1.ASN1Encoding;\nimport org.bouncycastle.asn1.ASN1Integer;\nimport org.bouncycastle.asn1.ASN1Primitive;\nimport org.bouncycastle.asn1.ASN1Sequence;\nimport org.bouncycastle.asn1.DERSequence;\nimport org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;\nimport org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;\nimport org.bouncycastle.asn1.x509.X509ObjectIdentifiers;\nimport org.bouncycastle.crypto.CipherParameters;\nimport org.bouncycastle.crypto.DSA;\nimport org.bouncycastle.crypto.Digest;\nimport org.bouncycastle.crypto.digests.NullDigest;\nimport org.bouncycastle.crypto.digests.SHA1Digest;\nimport org.bouncycastle.crypto.digests.SHA224Digest;\nimport org.bouncycastle.crypto.digests.SHA256Digest;\nimport org.bouncycastle.crypto.digests.SHA384Digest;\nimport org.bouncycastle.crypto.digests.SHA3Digest;\nimport org.bouncycastle.crypto.digests.SHA512Digest;\nimport org.bouncycastle.crypto.params.ParametersWithRandom;\nimport org.bouncycastle.crypto.signers.HMacDSAKCalculator;\n\npublic class DSASigner\n extends SignatureSpi\n implements PKCSObjectIdentifiers, X509ObjectIdentifiers\n{\n private Digest digest;\n private DSA signer;\n private SecureRandom random;\n\n protected DSASigner(\n Digest digest,\n DSA signer)\n {\n this.digest = digest;\n this.signer = signer;\n }\n\n protected void engineInitVerify(\n PublicKey publicKey)\n throws InvalidKeyException\n {\n CipherParameters param = DSAUtil.generatePublicKeyParameter(publicKey);\n\n digest.reset();\n signer.init(false, param);\n }\n\n protected void engineInitSign(\n PrivateKey privateKey,\n SecureRandom random)\n throws InvalidKeyException\n {\n this.random = random;\n engineInitSign(privateKey);\n }\n\n protected void engineInitSign(\n PrivateKey privateKey)\n throws InvalidKeyException\n {\n CipherParameters param = DSAUtil.generatePrivateKeyParameter(privateKey);\n\n if (random != null)\n {\n param = new ParametersWithRandom(param, random);\n }\n\n digest.reset();\n signer.init(true, param);\n }\n\n protected void engineUpdate(\n byte b)\n throws SignatureException\n {\n digest.update(b);\n }\n\n protected void engineUpdate(\n byte[] b,\n int off,\n int len) \n throws SignatureException\n {\n digest.update(b, off, len);\n }\n\n protected byte[] engineSign()\n throws SignatureException\n {\n byte[] hash = new byte[digest.getDigestSize()];\n\n digest.doFinal(hash, 0);\n\n try\n {\n BigInteger[] sig = signer.generateSignature(hash);\n\n return derEncode(sig[0], sig[1]);\n }\n catch (Exception e)\n {\n throw new SignatureException(e.toString());\n }\n }\n\n protected boolean engineVerify(\n byte[] sigBytes) \n throws SignatureException\n {\n byte[] hash = new byte[digest.getDigestSize()];\n\n digest.doFinal(hash, 0);\n\n BigInteger[] sig;\n\n try\n {\n sig = derDecode(sigBytes);\n }\n catch (Exception e)\n {\n throw new SignatureException(\"error decoding signature bytes.\");\n }\n\n return signer.verifySignature(hash, sig[0], sig[1]);\n }\n\n protected void engineSetParameter(\n AlgorithmParameterSpec params)\n {\n throw new UnsupportedOperationException(\"engineSetParameter unsupported\");\n }\n\n /**\n * @deprecated replaced with <a href = \"#engineSetParameter(java.security.spec.AlgorithmParameterSpec)\">\n */\n protected void engineSetParameter(\n String param,\n Object value)\n {\n throw new UnsupportedOperationException(\"engineSetParameter unsupported\");\n }\n\n /**\n * @deprecated\n */\n protected Object engineGetParameter(\n String param)\n {\n throw new UnsupportedOperationException(\"engineSetParameter unsupported\");\n }\n\n private byte[] derEncode(\n BigInteger r,\n BigInteger s)\n throws IOException\n {\n ASN1Integer[] rs = new ASN1Integer[]{ new ASN1Integer(r), new ASN1Integer(s) };\n return new DERSequence(rs).getEncoded(ASN1Encoding.DER);\n }\n\n private BigInteger[] derDecode(\n byte[] encoding)\n throws IOException\n {\n ASN1Sequence s = (ASN1Sequence)ASN1Primitive.fromByteArray(encoding);\n return new BigInteger[]{\n ((ASN1Integer)s.getObjectAt(0)).getValue(),\n ((ASN1Integer)s.getObjectAt(1)).getValue()\n };\n }\n\n static public class stdDSA\n extends DSASigner\n {\n public stdDSA()\n {\n super(new SHA1Digest(), new org.bouncycastle.crypto.signers.DSASigner());\n }\n }\n\n static public class detDSA\n extends DSASigner\n {\n public detDSA()\n {\n super(new SHA1Digest(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA1Digest())));\n }\n }\n\n static public class dsa224\n extends DSASigner\n {\n public dsa224()\n {\n super(new SHA224Digest(), new org.bouncycastle.crypto.signers.DSASigner());\n }\n }\n\n static public class detDSA224\n extends DSASigner\n {\n public detDSA224()\n {\n super(new SHA224Digest(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA224Digest())));\n }\n }\n\n static public class dsa256\n extends DSASigner\n {\n public dsa256()\n {\n super(new SHA256Digest(), new org.bouncycastle.crypto.signers.DSASigner());\n }\n }\n\n static public class detDSA256\n extends DSASigner\n {\n public detDSA256()\n {\n super(new SHA256Digest(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA256Digest())));\n }\n }\n\n static public class dsa384\n extends DSASigner\n {\n public dsa384()\n {\n super(new SHA384Digest(), new org.bouncycastle.crypto.signers.DSASigner());\n }\n }\n\n static public class detDSA384\n extends DSASigner\n {\n public detDSA384()\n {\n super(new SHA384Digest(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA384Digest())));\n }\n }\n\n static public class dsa512\n extends DSASigner\n {\n public dsa512()\n {\n super(new SHA512Digest(), new org.bouncycastle.crypto.signers.DSASigner());\n }\n }\n\n static public class detDSA512\n extends DSASigner\n {\n public detDSA512()\n {\n super(new SHA512Digest(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA512Digest())));\n }\n }\n\n static public class dsaSha3_224\n extends DSASigner\n {\n public dsaSha3_224()\n {\n super(new SHA3Digest(224), new org.bouncycastle.crypto.signers.DSASigner());\n }\n }\n\n static public class detDSASha3_224\n extends DSASigner\n {\n public detDSASha3_224()\n {\n super(new SHA3Digest(224), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA3Digest(224))));\n }\n }\n\n static public class dsaSha3_256\n extends DSASigner\n {\n public dsaSha3_256()\n {\n super(new SHA3Digest(256), new org.bouncycastle.crypto.signers.DSASigner());\n }\n }\n\n static public class detDSASha3_256\n extends DSASigner\n {\n public detDSASha3_256()\n {\n super(new SHA3Digest(256), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA3Digest(256))));\n }\n }\n\n static public class dsaSha3_384\n extends DSASigner\n {\n public dsaSha3_384()\n {\n super(new SHA3Digest(384), new org.bouncycastle.crypto.signers.DSASigner());\n }\n }\n\n static public class detDSASha3_384\n extends DSASigner\n {\n public detDSASha3_384()\n {\n super(new SHA3Digest(384), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA3Digest(384))));\n }\n }\n\n static public class dsaSha3_512\n extends DSASigner\n {\n public dsaSha3_512()\n {\n super(new SHA3Digest(512), new org.bouncycastle.crypto.signers.DSASigner());\n }\n }\n\n static public class detDSASha3_512\n extends DSASigner\n {\n public detDSASha3_512()\n {\n super(new SHA3Digest(512), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA3Digest(512))));\n }\n }\n\n static public class noneDSA\n extends DSASigner\n {\n public noneDSA()\n {\n super(new NullDigest(), new org.bouncycastle.crypto.signers.DSASigner());\n }\n }\n}\n\n", "input": "", "output": "【漏洞类型】格式化字符串漏洞\n【严重性】Medium\n【漏洞摘要】在 engineSign() 和 engineVerify() 方法中,异常处理时使用了 e.toString()。这可能导致异常信息被拼接到错误消息中,从而泄露内部实现细节或敏感信息。\n【关键代码模式】异常处理中的 e.toString(),特别是当该字符串被拼接到用户可查看的错误消息中时。\n【修复方法说明】将异常信息转换为通用错误消息,避免泄露内部实现细节。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\ncore/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java\n### 代码内容:\npackage org.bouncycastle.crypto.signers;\n\nimport java.math.BigInteger;\nimport java.security.SecureRandom;\n\nimport org.bouncycastle.crypto.CipherParameters;\nimport org.bouncycastle.crypto.DSA;\nimport org.bouncycastle.crypto.params.DSAKeyParameters;\nimport org.bouncycastle.crypto.params.DSAParameters;\nimport org.bouncycastle.crypto.params.DSAPrivateKeyParameters;\nimport org.bouncycastle.crypto.params.DSAPublicKeyParameters;\nimport org.bouncycastle.crypto.params.ParametersWithRandom;\n\n/**\n * The Digital Signature Algorithm - as described in \"Handbook of Applied\n * Cryptography\", pages 452 - 453.\n */\npublic class DSASigner\n implements DSA\n{\n private final DSAKCalculator kCalculator;\n\n private DSAKeyParameters key;\n private SecureRandom random;\n\n /**\n * Default configuration, random K values.\n */\n public DSASigner()\n {\n this.kCalculator = new RandomDSAKCalculator();\n }\n\n /**\n * Configuration with an alternate, possibly deterministic calculator of K.\n *\n * @param kCalculator a K value calculator.\n */\n public DSASigner(DSAKCalculator kCalculator)\n {\n this.kCalculator = kCalculator;\n }\n\n public void init(\n boolean forSigning,\n CipherParameters param)\n {\n SecureRandom providedRandom = null;\n\n if (forSigning)\n {\n if (param instanceof ParametersWithRandom)\n {\n ParametersWithRandom rParam = (ParametersWithRandom)param;\n\n this.key = (DSAPrivateKeyParameters)rParam.getParameters();\n providedRandom = rParam.getRandom();\n }\n else\n {\n this.key = (DSAPrivateKeyParameters)param;\n }\n }\n else\n {\n this.key = (DSAPublicKeyParameters)param;\n }\n\n this.random = initSecureRandom(forSigning && !kCalculator.isDeterministic(), providedRandom);\n }\n\n /**\n * generate a signature for the given message using the key we were\n * initialised with. For conventional DSA the message should be a SHA-1\n * hash of the message of interest.\n *\n * @param message the message that will be verified later.\n */\n public BigInteger[] generateSignature(\n byte[] message)\n {\n DSAParameters params = key.getParameters();\n BigInteger q = params.getQ();\n BigInteger m = calculateE(q, message);\n BigInteger x = ((DSAPrivateKeyParameters)key).getX();\n\n if (kCalculator.isDeterministic())\n {\n kCalculator.init(q, x, message);\n }\n else\n {\n kCalculator.init(q, random);\n }\n\n BigInteger k = kCalculator.nextK();\n\n BigInteger r = params.getG().modPow(k, params.getP()).mod(q);\n\n k = k.modInverse(q).multiply(m.add(x.multiply(r)));\n\n BigInteger s = k.mod(q);\n\n return new BigInteger[]{ r, s };\n }\n\n /**\n * return true if the value r and s represent a DSA signature for\n * the passed in message for standard DSA the message should be a\n * SHA-1 hash of the real message to be verified.\n */\n public boolean verifySignature(\n byte[] message,\n BigInteger r,\n BigInteger s)\n {\n DSAParameters params = key.getParameters();\n BigInteger q = params.getQ();\n BigInteger m = calculateE(q, message);\n BigInteger zero = BigInteger.valueOf(0);\n\n if (zero.compareTo(r) >= 0 || q.compareTo(r) <= 0)\n {\n return false;\n }\n\n if (zero.compareTo(s) >= 0 || q.compareTo(s) <= 0)\n {\n return false;\n }\n\n BigInteger w = s.modInverse(q);\n\n BigInteger u1 = m.multiply(w).mod(q);\n BigInteger u2 = r.multiply(w).mod(q);\n\n BigInteger p = params.getP();\n u1 = params.getG().modPow(u1, p);\n u2 = ((DSAPublicKeyParameters)key).getY().modPow(u2, p);\n\n BigInteger v = u1.multiply(u2).mod(p).mod(q);\n\n return v.equals(r);\n }\n\n private BigInteger calculateE(BigInteger n, byte[] message)\n {\n if (n.bitLength() >= message.length * 8)\n {\n return new BigInteger(1, message);\n }\n else\n {\n byte[] trunc = new byte[n.bitLength() / 8];\n\n System.arraycopy(message, 0, trunc, 0, trunc.length);\n\n return new BigInteger(1, trunc);\n }\n }\n\n protected SecureRandom initSecureRandom(boolean needed, SecureRandom provided)\n {\n return !needed ? null : (provided != null) ? provided : new SecureRandom();\n }\n}\n\n", "input": "", "output": "【漏洞类型】Timming Attack\n【严重性】High\n【漏洞摘要】此代码在生成DSA签名时存在潜在的TIMING ATTACK漏洞。具体表现为在generateSignature方法中,计算r的值时直接使用了k和x,这可能导致攻击者通过测量计算时间来推断出k的值,从而增加私钥泄露的风险。根本原因是代码中缺乏对k和x的随机化处理,这使得签名生成过程的时间可能受到k和x的值的影响。\n【关键代码模式】\n- generateSignature方法中的BigInteger r = params.getG().modPow(k, params.getP()).mod(q); 这一行代码直接使用了k和x进行计算。\n- calculateE方法和verifySignature方法中的BigInteger m = calculateE(q, message); 这一行代码处理了消息的哈希值,但没有涉及加密或签名过程中的随机化。\n【修复方法说明】修复方法通过在generateSignature方法中计算r的值时加入随机化处理,解决了TIMING ATTACK漏洞。最佳实践是确保在加密和签名过程中使用随机值,以避免通过时间测量推断出私钥。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nslf4j-ext/src/main/java/org/slf4j/ext/EventData.java\n### 代码内容:\npackage org.slf4j.ext;\n\nimport java.io.Serializable;\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.beans.XMLDecoder;\nimport java.beans.XMLEncoder;\nimport java.beans.ExceptionListener;\n\n/**\n * Base class for Event Data. Event Data contains data to be logged about an\n * event. Users may extend this class for each EventType they want to log.\n * \n * @author Ralph Goers\n */\npublic class EventData implements Serializable {\n\n private static final long serialVersionUID = 153270778642103985L;\n\n private Map<String, Object> eventData = new HashMap<String, Object>();\n public static final String EVENT_MESSAGE = \"EventMessage\";\n public static final String EVENT_TYPE = \"EventType\";\n public static final String EVENT_DATETIME = \"EventDateTime\";\n public static final String EVENT_ID = \"EventId\";\n\n /**\n * Default Constructor\n */\n public EventData() {\n }\n\n /**\n * Constructor to create event data from a Map.\n * \n * @param map\n * The event data.\n */\n public EventData(Map<String, Object> map) {\n eventData.putAll(map);\n }\n\n /**\n * Construct from a serialized form of the Map containing the RequestInfo\n * elements\n * \n * @param xml\n * The serialized form of the RequestInfo Map.\n */\n @SuppressWarnings(\"unchecked\")\n public EventData(String xml) {\n ByteArrayInputStream bais = new ByteArrayInputStream(xml.getBytes());\n try {\n XMLDecoder decoder = new XMLDecoder(bais);\n this.eventData = (Map<String, Object>) decoder.readObject();\n } catch (Exception e) {\n throw new EventException(\"Error decoding \" + xml, e);\n }\n }\n\n /**\n * Serialize all the EventData items into an XML representation.\n * \n * @return an XML String containing all the EventData items.\n */\n public String toXML() {\n return toXML(eventData);\n }\n\n /**\n * Serialize all the EventData items into an XML representation.\n * \n * @param map the Map to transform\n * @return an XML String containing all the EventData items.\n */\n public static String toXML(Map<String, Object> map) {\n ByteArrayOutputStream baos = new ByteArrayOutputStream();\n try {\n XMLEncoder encoder = new XMLEncoder(baos);\n encoder.setExceptionListener(new ExceptionListener() {\n public void exceptionThrown(Exception exception) {\n exception.printStackTrace();\n }\n });\n encoder.writeObject(map);\n encoder.close();\n return baos.toString();\n } catch (Exception e) {\n e.printStackTrace();\n return null;\n }\n }\n\n /**\n * Retrieve the event identifier.\n * \n * @return The event identifier\n */\n public String getEventId() {\n return (String) this.eventData.get(EVENT_ID);\n }\n\n /**\n * Set the event identifier.\n * \n * @param eventId\n * The event identifier.\n */\n public void setEventId(String eventId) {\n if (eventId == null) {\n throw new IllegalArgumentException(\"eventId cannot be null\");\n }\n this.eventData.put(EVENT_ID, eventId);\n }\n\n /**\n * Retrieve the message text associated with this event, if any.\n * \n * @return The message text associated with this event or null if there is\n * none.\n */\n public String getMessage() {\n return (String) this.eventData.get(EVENT_MESSAGE);\n }\n\n /**\n * Set the message text associated with this event.\n * \n * @param message\n * The message text.\n */\n public void setMessage(String message) {\n this.eventData.put(EVENT_MESSAGE, message);\n }\n\n /**\n * Retrieve the date and time the event occurred.\n * \n * @return The Date associated with the event.\n */\n public Date getEventDateTime() {\n return (Date) this.eventData.get(EVENT_DATETIME);\n }\n\n /**\n * Set the date and time the event occurred in case it is not the same as when\n * the event was logged.\n * \n * @param eventDateTime\n * The event Date.\n */\n public void setEventDateTime(Date eventDateTime) {\n this.eventData.put(EVENT_DATETIME, eventDateTime);\n }\n\n /**\n * Set the type of event that occurred.\n * \n * @param eventType\n * The type of the event.\n */\n public void setEventType(String eventType) {\n this.eventData.put(EVENT_TYPE, eventType);\n }\n\n /**\n * Retrieve the type of the event.\n * \n * @return The event type.\n */\n public String getEventType() {\n return (String) this.eventData.get(EVENT_TYPE);\n }\n\n /**\n * Add arbitrary attributes about the event.\n * \n * @param name\n * The attribute's key.\n * @param obj\n * The data associated with the key.\n */\n public void put(String name, Serializable obj) {\n this.eventData.put(name, obj);\n }\n\n /**\n * Retrieve an event attribute.\n * \n * @param name\n * The attribute's key.\n * @return The value associated with the key or null if the key is not\n * present.\n */\n public Serializable get(String name) {\n return (Serializable) this.eventData.get(name);\n }\n\n /**\n * Populate the event data from a Map.\n * \n * @param data\n * The Map to copy.\n */\n public void putAll(Map<String, Object> data) {\n this.eventData.putAll(data);\n }\n\n /**\n * Returns the number of attributes in the EventData.\n * \n * @return the number of attributes in the EventData.\n */\n public int getSize() {\n return this.eventData.size();\n }\n\n /**\n * Returns an Iterator over all the entries in the EventData.\n * \n * @return an Iterator that can be used to access all the event attributes.\n */\n public Iterator<Map.Entry<String, Object>> getEntrySetIterator() {\n return this.eventData.entrySet().iterator();\n }\n\n /**\n * Retrieve all the attributes in the EventData as a Map. Changes to this map\n * will be reflected in the EventData.\n * \n * @return The Map of attributes in this EventData instance.\n */\n public Map<String, Object> getEventMap() {\n return this.eventData;\n }\n\n /**\n * Convert the EventData to a String.\n * \n * @return The EventData as a String.\n */\n @Override\n public String toString() {\n return toXML();\n }\n\n /**\n * Compare two EventData objects for equality.\n * \n * @param o\n * The Object to compare.\n * @return true if the objects are the same instance or contain all the same\n * keys and their values.\n */\n @SuppressWarnings(\"unchecked\")\n @Override\n public boolean equals(Object o) {\n if (this == o) {\n return true;\n }\n if (!(o instanceof EventData || o instanceof Map)) {\n return false;\n }\n Map<String, Object> map = (o instanceof EventData) ? ((EventData) o).getEventMap() : (Map<String, Object>) o;\n\n return this.eventData.equals(map);\n }\n\n /**\n * Compute the hashCode for this EventData instance.\n * \n * @return The hashcode for this EventData instance.\n */\n @Override\n public int hashCode() {\n return this.eventData.hashCode();\n }\n}\n", "input": "", "output": "【漏洞类型】反序列化漏洞\n【严重性】High\n【漏洞摘要】此代码存在反序列化漏洞,位于`EventData`类的构造函数中。当使用`XMLDecoder`从XML字符串中反序列化`Map`时,攻击者可以通过构造恶意XML输入来执行任意代码。`XMLDecoder`类在反序列化过程中会调用对象的`readObject`方法,如果攻击者能够控制输入的XML内容,他们可以插入恶意代码,导致远程代码执行(RCE)。\n【关键代码模式】`XMLDecoder`, `decodeObject`, 反序列化未知来源的XML数据。\n【修复方法说明】修复方法通过标注`EventData`类为`@deprecated`,表示该类将被移除而不再使用,从而避免了反序列化漏洞的风险。最佳实践是避免使用`XMLDecoder`等不安全的反序列化机制,或者在使用时严格验证和过滤输入数据。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nspring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/notify/DingTalkNotifier.java\n### 代码内容:\npackage de.codecentric.boot.admin.server.notify;\n\nimport java.net.URLEncoder;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.annotation.Nullable;\nimport javax.crypto.Mac;\nimport javax.crypto.spec.SecretKeySpec;\n\nimport org.apache.commons.codec.binary.Base64;\nimport org.springframework.context.expression.MapAccessor;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ParserContext;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.http.HttpEntity;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.client.RestTemplate;\nimport reactor.core.publisher.Mono;\n\nimport de.codecentric.boot.admin.server.domain.entities.Instance;\nimport de.codecentric.boot.admin.server.domain.entities.InstanceRepository;\nimport de.codecentric.boot.admin.server.domain.events.InstanceEvent;\n\n/**\n * Notifier submitting events to DingTalk.\n *\n * @author Mask\n */\npublic class DingTalkNotifier extends AbstractStatusChangeNotifier {\n\n\tprivate static final String DEFAULT_MESSAGE = \"#{instance.registration.name} #{instance.id} is #{event.statusInfo.status}\";\n\n\tprivate final SpelExpressionParser parser = new SpelExpressionParser();\n\n\tprivate RestTemplate restTemplate;\n\n\t/**\n\t * Webhook URI for the DingTalk API.\n\t */\n\tprivate String webhookUrl;\n\n\t/**\n\t * Secret for DingTalk.\n\t */\n\t@Nullable\n\tprivate String secret;\n\n\tprivate Expression message;\n\n\tpublic DingTalkNotifier(InstanceRepository repository, RestTemplate restTemplate) {\n\t\tsuper(repository);\n\t\tthis.restTemplate = restTemplate;\n\t\tthis.message = parser.parseExpression(DEFAULT_MESSAGE, ParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n\t@Override\n\tprotected Mono<Void> doNotify(InstanceEvent event, Instance instance) {\n\t\treturn Mono\n\t\t\t\t.fromRunnable(() -> restTemplate.postForEntity(buildUrl(), createMessage(event, instance), Void.class));\n\t}\n\n\tprivate String buildUrl() {\n\t\tLong timestamp = System.currentTimeMillis();\n\t\treturn String.format(\"%s×tamp=%s&sign=%s\", webhookUrl, timestamp, getSign(timestamp));\n\t}\n\n\tprotected Object createMessage(InstanceEvent event, Instance instance) {\n\t\tMap<String, Object> messageJson = new HashMap<>();\n\t\tmessageJson.put(\"msgtype\", \"text\");\n\n\t\tMap<String, Object> content = new HashMap<>();\n\t\tcontent.put(\"content\", getText(event, instance));\n\t\tmessageJson.put(\"text\", content);\n\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setContentType(MediaType.APPLICATION_JSON);\n\t\treturn new HttpEntity<>(messageJson, headers);\n\t}\n\n\tprivate Object getText(InstanceEvent event, Instance instance) {\n\t\tMap<String, Object> root = new HashMap<>();\n\t\troot.put(\"event\", event);\n\t\troot.put(\"instance\", instance);\n\t\troot.put(\"lastStatus\", getLastStatus(event.getInstance()));\n\t\tStandardEvaluationContext context = new StandardEvaluationContext(root);\n\t\tcontext.addPropertyAccessor(new MapAccessor());\n\t\treturn message.getValue(context, String.class);\n\t}\n\n\tprivate String getSign(Long timestamp) {\n\t\ttry {\n\t\t\tString stringToSign = timestamp + \"\\n\" + secret;\n\t\t\tMac mac = Mac.getInstance(\"HmacSHA256\");\n\t\t\tmac.init(new SecretKeySpec(secret.getBytes(\"UTF-8\"), \"HmacSHA256\"));\n\t\t\tbyte[] signData = mac.doFinal(stringToSign.getBytes(\"UTF-8\"));\n\t\t\treturn URLEncoder.encode(new String(Base64.encodeBase64(signData)), \"UTF-8\");\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tex.printStackTrace();\n\t\t}\n\t\treturn \"\";\n\t}\n\n\tpublic void setRestTemplate(RestTemplate restTemplate) {\n\t\tthis.restTemplate = restTemplate;\n\t}\n\n\tpublic String getWebhookUrl() {\n\t\treturn webhookUrl;\n\t}\n\n\tpublic void setWebhookUrl(String webhookUrl) {\n\t\tthis.webhookUrl = webhookUrl;\n\t}\n\n\t@Nullable\n\tpublic String getSecret() {\n\t\treturn secret;\n\t}\n\n\tpublic void setSecret(@Nullable String secret) {\n\t\tthis.secret = secret;\n\t}\n\n\tpublic String getMessage() {\n\t\treturn message.getExpressionString();\n\t}\n\n\tpublic void setMessage(String message) {\n\t\tthis.message = parser.parseExpression(message, ParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n}\n\n## 内容2\n### 文件路径:\nspring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/notify/DiscordNotifier.java\n### 代码内容:\npackage de.codecentric.boot.admin.server.notify;\n\nimport java.net.URI;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.annotation.Nullable;\n\nimport org.springframework.context.expression.MapAccessor;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ParserContext;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.http.HttpEntity;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.client.RestTemplate;\nimport reactor.core.publisher.Mono;\n\nimport de.codecentric.boot.admin.server.domain.entities.Instance;\nimport de.codecentric.boot.admin.server.domain.entities.InstanceRepository;\nimport de.codecentric.boot.admin.server.domain.events.InstanceEvent;\n\n/**\n * Notifier submitting events to Discord by webhooks.\n *\n * @author Movitz Sunar\n * @see <a href=\n * \"https://discordapp.com/developers/docs/resources/webhook#execute-webhook\">https://discordapp.com/developers/docs/resources/webhook#execute-webhook</a>\n */\npublic class DiscordNotifier extends AbstractStatusChangeNotifier {\n\n\tprivate static final String DEFAULT_MESSAGE = \"*#{instance.registration.name}* (#{instance.id}) is *#{event.statusInfo.status}*\";\n\n\tprivate final SpelExpressionParser parser = new SpelExpressionParser();\n\n\tprivate RestTemplate restTemplate;\n\n\tprivate Expression message;\n\n\t/**\n\t * Webhook URI for the Discord API (i.e.\n\t * https://discordapp.com/api/webhooks/{webhook.id}/{webhook.token})\n\t */\n\t@Nullable\n\tprivate URI webhookUrl;\n\n\t/**\n\t * If the message is a text to speech message. False by default.\n\t */\n\tprivate boolean tts = false;\n\n\t/**\n\t * Optional username. Default is set in Discord.\n\t */\n\t@Nullable\n\tprivate String username;\n\n\t/**\n\t * Optional URL to avatar.\n\t */\n\t@Nullable\n\tprivate String avatarUrl;\n\n\tpublic DiscordNotifier(InstanceRepository repository, RestTemplate restTemplate) {\n\t\tsuper(repository);\n\t\tthis.restTemplate = restTemplate;\n\t\tthis.message = parser.parseExpression(DEFAULT_MESSAGE, ParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n\t@Override\n\tprotected Mono<Void> doNotify(InstanceEvent event, Instance instance) {\n\t\tif (webhookUrl == null) {\n\t\t\treturn Mono.error(new IllegalStateException(\"'webhookUrl' must not be null.\"));\n\t\t}\n\t\treturn Mono.fromRunnable(\n\t\t\t\t() -> restTemplate.postForEntity(webhookUrl, createDiscordNotification(event, instance), Void.class));\n\t}\n\n\tprotected Object createDiscordNotification(InstanceEvent event, Instance instance) {\n\t\tMap<String, Object> body = new HashMap<>();\n\t\tbody.put(\"content\", createContent(event, instance));\n\t\tbody.put(\"tts\", tts);\n\n\t\tif (avatarUrl != null) {\n\t\t\tbody.put(\"avatar_url\", avatarUrl);\n\t\t}\n\t\tif (username != null) {\n\t\t\tbody.put(\"username\", username);\n\t\t}\n\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setContentType(MediaType.APPLICATION_JSON);\n\t\theaders.add(HttpHeaders.USER_AGENT, \"RestTemplate\");\n\t\treturn new HttpEntity<>(body, headers);\n\t}\n\n\t@Nullable\n\tprotected String createContent(InstanceEvent event, Instance instance) {\n\t\tMap<String, Object> root = new HashMap<>();\n\t\troot.put(\"event\", event);\n\t\troot.put(\"instance\", instance);\n\t\troot.put(\"lastStatus\", getLastStatus(event.getInstance()));\n\t\tStandardEvaluationContext context = new StandardEvaluationContext(root);\n\t\tcontext.addPropertyAccessor(new MapAccessor());\n\t\treturn message.getValue(context, String.class);\n\t}\n\n\t@Nullable\n\tpublic URI getWebhookUrl() {\n\t\treturn webhookUrl;\n\t}\n\n\tpublic void setWebhookUrl(@Nullable URI webhookUrl) {\n\t\tthis.webhookUrl = webhookUrl;\n\t}\n\n\tpublic boolean isTts() {\n\t\treturn tts;\n\t}\n\n\tpublic void setTts(boolean tts) {\n\t\tthis.tts = tts;\n\t}\n\n\t@Nullable\n\tpublic String getUsername() {\n\t\treturn username;\n\t}\n\n\tpublic void setUsername(@Nullable String username) {\n\t\tthis.username = username;\n\t}\n\n\t@Nullable\n\tpublic String getAvatarUrl() {\n\t\treturn avatarUrl;\n\t}\n\n\tpublic void setAvatarUrl(@Nullable String avatarUrl) {\n\t\tthis.avatarUrl = avatarUrl;\n\t}\n\n\tpublic String getMessage() {\n\t\treturn message.getExpressionString();\n\t}\n\n\tpublic void setMessage(String message) {\n\t\tthis.message = parser.parseExpression(message, ParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n\tpublic void setRestTemplate(RestTemplate restTemplate) {\n\t\tthis.restTemplate = restTemplate;\n\t}\n\n}\n\n## 内容3\n### 文件路径:\nspring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/notify/HipchatNotifier.java\n### 代码内容:\npackage de.codecentric.boot.admin.server.notify;\n\nimport java.net.URI;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.annotation.Nullable;\n\nimport org.springframework.context.expression.MapAccessor;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ParserContext;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.http.HttpEntity;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.client.RestTemplate;\nimport reactor.core.publisher.Mono;\n\nimport de.codecentric.boot.admin.server.domain.entities.Instance;\nimport de.codecentric.boot.admin.server.domain.entities.InstanceRepository;\nimport de.codecentric.boot.admin.server.domain.events.InstanceEvent;\nimport de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent;\nimport de.codecentric.boot.admin.server.domain.values.StatusInfo;\n\n/**\n * Notifier submitting events to HipChat.\n *\n * @author Jamie Brown\n */\npublic class HipchatNotifier extends AbstractStatusChangeNotifier {\n\n\tprivate static final String DEFAULT_DESCRIPTION = \"<strong>#{instance.registration.name}</strong>/#{instance.id} is <strong>#{event.statusInfo.status}</strong>\";\n\n\tprivate final SpelExpressionParser parser = new SpelExpressionParser();\n\n\tprivate RestTemplate restTemplate;\n\n\t/**\n\t * Base URL for HipChat API (i.e. https://ACCOUNT_NAME.hipchat.com/v2\n\t */\n\t@Nullable\n\tprivate URI url;\n\n\t/**\n\t * API token that has access to notify in the room\n\t */\n\t@Nullable\n\tprivate String authToken;\n\n\t/**\n\t * Id of the room to notify\n\t */\n\t@Nullable\n\tprivate String roomId;\n\n\t/**\n\t * TRUE will cause OS notification, FALSE will only notify to room\n\t */\n\tprivate boolean notify = false;\n\n\t/**\n\t * Trigger description. SpEL template using event as root;\n\t */\n\tprivate Expression description;\n\n\tpublic HipchatNotifier(InstanceRepository repository, RestTemplate restTemplate) {\n\t\tsuper(repository);\n\t\tthis.restTemplate = restTemplate;\n\t\tthis.description = parser.parseExpression(DEFAULT_DESCRIPTION, ParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n\t@Override\n\tprotected Mono<Void> doNotify(InstanceEvent event, Instance instance) {\n\t\treturn Mono.fromRunnable(\n\t\t\t\t() -> restTemplate.postForEntity(buildUrl(), createHipChatNotification(event, instance), Void.class));\n\t}\n\n\tprotected String buildUrl() {\n\t\tif (url == null) {\n\t\t\tthrow new IllegalStateException(\"'url' must not be null.\");\n\t\t}\n\t\treturn String.format(\"%s/room/%s/notification?auth_token=%s\", url.toString(), roomId, authToken);\n\t}\n\n\tprotected HttpEntity<Map<String, Object>> createHipChatNotification(InstanceEvent event, Instance instance) {\n\t\tMap<String, Object> body = new HashMap<>();\n\t\tbody.put(\"color\", getColor(event));\n\t\tbody.put(\"message\", getMessage(event, instance));\n\t\tbody.put(\"notify\", getNotify());\n\t\tbody.put(\"message_format\", \"html\");\n\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setContentType(MediaType.APPLICATION_JSON);\n\t\treturn new HttpEntity<>(body, headers);\n\t}\n\n\tprotected boolean getNotify() {\n\t\treturn notify;\n\t}\n\n\t@Nullable\n\tprotected String getMessage(InstanceEvent event, Instance instance) {\n\t\tMap<String, Object> root = new HashMap<>();\n\t\troot.put(\"event\", event);\n\t\troot.put(\"instance\", instance);\n\t\troot.put(\"lastStatus\", getLastStatus(event.getInstance()));\n\t\tStandardEvaluationContext context = new StandardEvaluationContext(root);\n\t\tcontext.addPropertyAccessor(new MapAccessor());\n\t\treturn description.getValue(context, String.class);\n\t}\n\n\tprotected String getColor(InstanceEvent event) {\n\t\tif (event instanceof InstanceStatusChangedEvent) {\n\t\t\treturn StatusInfo.STATUS_UP.equals(((InstanceStatusChangedEvent) event).getStatusInfo().getStatus())\n\t\t\t\t\t? \"green\" : \"red\";\n\t\t}\n\t\telse {\n\t\t\treturn \"gray\";\n\t\t}\n\t}\n\n\tpublic void setUrl(@Nullable URI url) {\n\t\tthis.url = url;\n\t}\n\n\t@Nullable\n\tpublic URI getUrl() {\n\t\treturn url;\n\t}\n\n\tpublic void setAuthToken(@Nullable String authToken) {\n\t\tthis.authToken = authToken;\n\t}\n\n\t@Nullable\n\tpublic String getAuthToken() {\n\t\treturn authToken;\n\t}\n\n\tpublic void setRoomId(@Nullable String roomId) {\n\t\tthis.roomId = roomId;\n\t}\n\n\t@Nullable\n\tpublic String getRoomId() {\n\t\treturn roomId;\n\t}\n\n\tpublic void setNotify(boolean notify) {\n\t\tthis.notify = notify;\n\t}\n\n\tpublic boolean isNotify() {\n\t\treturn notify;\n\t}\n\n\tpublic void setDescription(String description) {\n\t\tthis.description = parser.parseExpression(description, ParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n\tpublic String getDescription() {\n\t\treturn description.getExpressionString();\n\t}\n\n\tpublic void setRestTemplate(RestTemplate restTemplate) {\n\t\tthis.restTemplate = restTemplate;\n\t}\n\n}\n\n## 内容4\n### 文件路径:\nspring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/notify/LetsChatNotifier.java\n### 代码内容:\npackage de.codecentric.boot.admin.server.notify;\n\nimport java.net.URI;\nimport java.nio.charset.StandardCharsets;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.annotation.Nullable;\n\nimport org.springframework.context.expression.MapAccessor;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ParserContext;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.http.HttpEntity;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.util.Base64Utils;\nimport org.springframework.web.client.RestTemplate;\nimport reactor.core.publisher.Mono;\n\nimport de.codecentric.boot.admin.server.domain.entities.Instance;\nimport de.codecentric.boot.admin.server.domain.entities.InstanceRepository;\nimport de.codecentric.boot.admin.server.domain.events.InstanceEvent;\n\n/**\n * Notifier submitting events to let´s Chat.\n *\n * @author Rico Pahlisch\n */\npublic class LetsChatNotifier extends AbstractStatusChangeNotifier {\n\n\tprivate static final String DEFAULT_MESSAGE = \"*#{instance.registration.name}* (#{instance.id}) is *#{event.statusInfo.status}*\";\n\n\tprivate final SpelExpressionParser parser = new SpelExpressionParser();\n\n\tprivate RestTemplate restTemplate;\n\n\t/**\n\t * Host URL for Let´s Chat\n\t */\n\t@Nullable\n\tprivate URI url;\n\n\t/**\n\t * Name of the room\n\t */\n\t@Nullable\n\tprivate String room;\n\n\t/**\n\t * Token for the Let´s chat API\n\t */\n\t@Nullable\n\tprivate String token;\n\n\t/**\n\t * username which sends notification\n\t */\n\tprivate String username = \"Spring Boot Admin\";\n\n\t/**\n\t * Message template. SpEL template using event as root\n\t */\n\tprivate Expression message;\n\n\tpublic LetsChatNotifier(InstanceRepository repository, RestTemplate restTemplate) {\n\t\tsuper(repository);\n\t\tthis.restTemplate = restTemplate;\n\t\tthis.message = parser.parseExpression(DEFAULT_MESSAGE, ParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n\t@Override\n\tprotected Mono<Void> doNotify(InstanceEvent event, Instance instance) {\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setContentType(MediaType.APPLICATION_JSON);\n\t\t// Let's Chat requiers the token as basic username, the password can be an\n\t\t// arbitrary string.\n\t\tString auth = Base64Utils\n\t\t\t\t.encodeToString(String.format(\"%s:%s\", token, username).getBytes(StandardCharsets.UTF_8));\n\t\theaders.add(HttpHeaders.AUTHORIZATION, String.format(\"Basic %s\", auth));\n\t\treturn Mono.fromRunnable(() -> restTemplate.exchange(createUrl(), HttpMethod.POST,\n\t\t\t\tnew HttpEntity<>(createMessage(event, instance), headers), Void.class));\n\t}\n\n\tprivate URI createUrl() {\n\t\tif (url == null) {\n\t\t\tthrow new IllegalStateException(\"'url' must not be null.\");\n\t\t}\n\t\treturn URI.create(String.format(\"%s/rooms/%s/messages\", url, room));\n\t}\n\n\tprotected Object createMessage(InstanceEvent event, Instance instance) {\n\t\tMap<String, String> messageJson = new HashMap<>();\n\t\tmessageJson.put(\"text\", getText(event, instance));\n\t\treturn messageJson;\n\t}\n\n\t@Nullable\n\tprotected String getText(InstanceEvent event, Instance instance) {\n\t\tMap<String, Object> root = new HashMap<>();\n\t\troot.put(\"event\", event);\n\t\troot.put(\"instance\", instance);\n\t\troot.put(\"lastStatus\", getLastStatus(event.getInstance()));\n\t\tStandardEvaluationContext context = new StandardEvaluationContext(root);\n\t\tcontext.addPropertyAccessor(new MapAccessor());\n\t\treturn message.getValue(context, String.class);\n\t}\n\n\tpublic void setRestTemplate(RestTemplate restTemplate) {\n\t\tthis.restTemplate = restTemplate;\n\t}\n\n\tpublic void setUrl(@Nullable URI url) {\n\t\tthis.url = url;\n\t}\n\n\t@Nullable\n\tpublic URI getUrl() {\n\t\treturn url;\n\t}\n\n\tpublic void setUsername(String username) {\n\t\tthis.username = username;\n\t}\n\n\tpublic String getUsername() {\n\t\treturn username;\n\t}\n\n\tpublic void setRoom(@Nullable String room) {\n\t\tthis.room = room;\n\t}\n\n\t@Nullable\n\tpublic String getRoom() {\n\t\treturn room;\n\t}\n\n\tpublic void setToken(@Nullable String token) {\n\t\tthis.token = token;\n\t}\n\n\t@Nullable\n\tpublic String getToken() {\n\t\treturn token;\n\t}\n\n\tpublic void setMessage(String message) {\n\t\tthis.message = parser.parseExpression(message, ParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n\tpublic String getMessage() {\n\t\treturn message.getExpressionString();\n\t}\n\n}\n\n## 内容5\n### 文件路径:\nspring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/notify/MicrosoftTeamsNotifier.java\n### 代码内容:\npackage de.codecentric.boot.admin.server.notify;\n\nimport java.net.URI;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport javax.annotation.Nullable;\n\nimport lombok.Builder;\nimport lombok.Data;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.expression.MapAccessor;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ParserContext;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.http.HttpEntity;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.client.RestTemplate;\nimport reactor.core.publisher.Mono;\n\nimport de.codecentric.boot.admin.server.domain.entities.Instance;\nimport de.codecentric.boot.admin.server.domain.entities.InstanceRepository;\nimport de.codecentric.boot.admin.server.domain.events.InstanceDeregisteredEvent;\nimport de.codecentric.boot.admin.server.domain.events.InstanceEvent;\nimport de.codecentric.boot.admin.server.domain.events.InstanceRegisteredEvent;\nimport de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent;\n\nimport static java.util.Collections.singletonList;\n\npublic class MicrosoftTeamsNotifier extends AbstractStatusChangeNotifier {\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(MicrosoftTeamsNotifier.class);\n\n\tprivate static final String STATUS_KEY = \"Status\";\n\n\tprivate static final String SERVICE_URL_KEY = \"Service URL\";\n\n\tprivate static final String HEALTH_URL_KEY = \"Health URL\";\n\n\tprivate static final String MANAGEMENT_URL_KEY = \"Management URL\";\n\n\tprivate static final String SOURCE_KEY = \"Source\";\n\n\tprivate static final String DEFAULT_THEME_COLOR_EXPRESSION = \"#{event.type == 'STATUS_CHANGED' ? (event.statusInfo.status=='UP' ? '6db33f' : 'b32d36') : '439fe0'}\";\n\n\tprivate static final String DEFAULT_DEREGISTER_ACTIVITY_SUBTITLE_EXPRESSION = \"#{instance.registration.name} with id #{instance.id} has de-registered from Spring Boot Admin\";\n\n\tprivate static final String DEFAULT_REGISTER_ACTIVITY_SUBTITLE_EXPRESSION = \"#{instance.registration.name} with id #{instance.id} has registered with Spring Boot Admin\";\n\n\tprivate static final String DEFAULT_STATUS_ACTIVITY_SUBTITLE_EXPRESSION = \"#{instance.registration.name} with id #{instance.id} changed status from #{lastStatus} to #{event.statusInfo.status}\";\n\n\tprivate final SpelExpressionParser parser = new SpelExpressionParser();\n\n\tprivate RestTemplate restTemplate;\n\n\t/**\n\t * Webhook url for Microsoft Teams Channel Webhook connector (i.e.\n\t * https://outlook.office.com/webhook/{webhook-id})\n\t */\n\t@Nullable\n\tprivate URI webhookUrl;\n\n\t/**\n\t * Theme Color is the color of the accent on the message that appears in Microsoft\n\t * Teams. Default is Spring Green\n\t */\n\tprivate Expression themeColor;\n\n\t/**\n\t * Message will be used as title of the Activity section of the Teams message when an\n\t * app de-registers.\n\t */\n\tprivate Expression deregisterActivitySubtitle;\n\n\t/**\n\t * Message will be used as title of the Activity section of the Teams message when an\n\t * app registers\n\t */\n\tprivate Expression registerActivitySubtitle;\n\n\t/**\n\t * Message will be used as title of the Activity section of the Teams message when an\n\t * app changes status\n\t */\n\tprivate Expression statusActivitySubtitle;\n\n\t/**\n\t * Title of the Teams message when an app de-registers\n\t */\n\tprivate String deRegisteredTitle = \"De-Registered\";\n\n\t/**\n\t * Title of the Teams message when an app registers\n\t */\n\tprivate String registeredTitle = \"Registered\";\n\n\t/**\n\t * Title of the Teams message when an app changes status\n\t */\n\tprivate String statusChangedTitle = \"Status Changed\";\n\n\t/**\n\t * Summary section of every Teams message originating from Spring Boot Admin\n\t */\n\tprivate String messageSummary = \"Spring Boot Admin Notification\";\n\n\tpublic MicrosoftTeamsNotifier(InstanceRepository repository, RestTemplate restTemplate) {\n\t\tsuper(repository);\n\t\tthis.restTemplate = restTemplate;\n\t\tthis.themeColor = parser.parseExpression(DEFAULT_THEME_COLOR_EXPRESSION, ParserContext.TEMPLATE_EXPRESSION);\n\t\tthis.deregisterActivitySubtitle = parser.parseExpression(DEFAULT_DEREGISTER_ACTIVITY_SUBTITLE_EXPRESSION,\n\t\t\t\tParserContext.TEMPLATE_EXPRESSION);\n\t\tthis.registerActivitySubtitle = parser.parseExpression(DEFAULT_REGISTER_ACTIVITY_SUBTITLE_EXPRESSION,\n\t\t\t\tParserContext.TEMPLATE_EXPRESSION);\n\t\tthis.statusActivitySubtitle = parser.parseExpression(DEFAULT_STATUS_ACTIVITY_SUBTITLE_EXPRESSION,\n\t\t\t\tParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n\t@Override\n\tprotected Mono<Void> doNotify(InstanceEvent event, Instance instance) {\n\t\tMessage message;\n\t\tStandardEvaluationContext context = createEvaluationContext(event, instance);\n\t\tif (event instanceof InstanceRegisteredEvent) {\n\t\t\tmessage = getRegisteredMessage(instance, context);\n\t\t}\n\t\telse if (event instanceof InstanceDeregisteredEvent) {\n\t\t\tmessage = getDeregisteredMessage(instance, context);\n\t\t}\n\t\telse if (event instanceof InstanceStatusChangedEvent) {\n\t\t\tmessage = getStatusChangedMessage(instance, context);\n\t\t}\n\t\telse {\n\t\t\treturn Mono.empty();\n\t\t}\n\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setContentType(MediaType.APPLICATION_JSON);\n\n\t\tif (webhookUrl == null) {\n\t\t\treturn Mono.error(new IllegalStateException(\"'webhookUrl' must not be null.\"));\n\t\t}\n\n\t\treturn Mono.fromRunnable(() -> this.restTemplate.postForEntity(webhookUrl,\n\t\t\t\tnew HttpEntity<Object>(message, headers), Void.class));\n\t}\n\n\t@Override\n\tprotected boolean shouldNotify(InstanceEvent event, Instance instance) {\n\t\treturn event instanceof InstanceRegisteredEvent || event instanceof InstanceDeregisteredEvent\n\t\t\t\t|| super.shouldNotify(event, instance);\n\t}\n\n\tprotected Message getDeregisteredMessage(Instance instance, StandardEvaluationContext context) {\n\t\tString activitySubtitle = evaluateExpression(context, deregisterActivitySubtitle);\n\t\treturn createMessage(instance, deRegisteredTitle, activitySubtitle, context);\n\t}\n\n\tprotected Message getRegisteredMessage(Instance instance, StandardEvaluationContext context) {\n\t\tString activitySubtitle = evaluateExpression(context, registerActivitySubtitle);\n\t\treturn createMessage(instance, registeredTitle, activitySubtitle, context);\n\t}\n\n\tprotected Message getStatusChangedMessage(Instance instance, StandardEvaluationContext context) {\n\t\tString activitySubtitle = evaluateExpression(context, statusActivitySubtitle);\n\t\treturn createMessage(instance, statusChangedTitle, activitySubtitle, context);\n\t}\n\n\tprotected Message createMessage(Instance instance, String registeredTitle, String activitySubtitle,\n\t\t\tStandardEvaluationContext context) {\n\t\tList<Fact> facts = new ArrayList<>();\n\t\tfacts.add(new Fact(STATUS_KEY, instance.getStatusInfo().getStatus()));\n\t\tfacts.add(new Fact(SERVICE_URL_KEY, instance.getRegistration().getServiceUrl()));\n\t\tfacts.add(new Fact(HEALTH_URL_KEY, instance.getRegistration().getHealthUrl()));\n\t\tfacts.add(new Fact(MANAGEMENT_URL_KEY, instance.getRegistration().getManagementUrl()));\n\t\tfacts.add(new Fact(SOURCE_KEY, instance.getRegistration().getSource()));\n\n\t\tSection section = Section.builder().activityTitle(instance.getRegistration().getName())\n\t\t\t\t.activitySubtitle(activitySubtitle).facts(facts).build();\n\n\t\treturn Message.builder().title(registeredTitle).summary(messageSummary)\n\t\t\t\t.themeColor(evaluateExpression(context, themeColor)).sections(singletonList(section)).build();\n\t}\n\n\tprotected String evaluateExpression(StandardEvaluationContext context, Expression expression) {\n\t\treturn Objects.requireNonNull(expression.getValue(context, String.class));\n\t}\n\n\tprotected StandardEvaluationContext createEvaluationContext(InstanceEvent event, Instance instance) {\n\t\tMap<String, Object> root = new HashMap<>();\n\t\troot.put(\"event\", event);\n\t\troot.put(\"instance\", instance);\n\t\troot.put(\"lastStatus\", getLastStatus(event.getInstance()));\n\t\tStandardEvaluationContext context = new StandardEvaluationContext(root);\n\t\tcontext.addPropertyAccessor(new MapAccessor());\n\t\treturn context;\n\t}\n\n\t@Nullable\n\tpublic URI getWebhookUrl() {\n\t\treturn webhookUrl;\n\t}\n\n\tpublic void setWebhookUrl(@Nullable URI webhookUrl) {\n\t\tthis.webhookUrl = webhookUrl;\n\t}\n\n\tpublic String getThemeColor() {\n\t\treturn themeColor.getExpressionString();\n\t}\n\n\tpublic void setThemeColor(String themeColor) {\n\t\tthis.themeColor = parser.parseExpression(themeColor, ParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n\tpublic String getDeregisterActivitySubtitle() {\n\t\treturn deregisterActivitySubtitle.getExpressionString();\n\t}\n\n\tpublic void setDeregisterActivitySubtitle(String deregisterActivitySubtitle) {\n\t\tthis.deregisterActivitySubtitle = parser.parseExpression(deregisterActivitySubtitle,\n\t\t\t\tParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n\tpublic String getRegisterActivitySubtitle() {\n\t\treturn registerActivitySubtitle.getExpressionString();\n\t}\n\n\tpublic void setRegisterActivitySubtitle(String registerActivitySubtitle) {\n\t\tthis.registerActivitySubtitle = parser.parseExpression(registerActivitySubtitle,\n\t\t\t\tParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n\tpublic String getStatusActivitySubtitle() {\n\t\treturn statusActivitySubtitle.getExpressionString();\n\t}\n\n\tpublic void setStatusActivitySubtitle(String statusActivitySubtitle) {\n\t\tthis.statusActivitySubtitle = parser.parseExpression(statusActivitySubtitle, ParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n\tpublic String getDeRegisteredTitle() {\n\t\treturn deRegisteredTitle;\n\t}\n\n\tpublic void setDeRegisteredTitle(String deRegisteredTitle) {\n\t\tthis.deRegisteredTitle = deRegisteredTitle;\n\t}\n\n\tpublic String getRegisteredTitle() {\n\t\treturn registeredTitle;\n\t}\n\n\tpublic void setRegisteredTitle(String registeredTitle) {\n\t\tthis.registeredTitle = registeredTitle;\n\t}\n\n\tpublic String getStatusChangedTitle() {\n\t\treturn statusChangedTitle;\n\t}\n\n\tpublic void setStatusChangedTitle(String statusChangedTitle) {\n\t\tthis.statusChangedTitle = statusChangedTitle;\n\t}\n\n\tpublic String getMessageSummary() {\n\t\treturn messageSummary;\n\t}\n\n\tpublic void setMessageSummary(String messageSummary) {\n\t\tthis.messageSummary = messageSummary;\n\t}\n\n\tpublic void setRestTemplate(RestTemplate restTemplate) {\n\t\tthis.restTemplate = restTemplate;\n\t}\n\n\t@Data\n\t@Builder\n\tpublic static class Message {\n\n\t\tprivate final String summary;\n\n\t\tprivate final String themeColor;\n\n\t\tprivate final String title;\n\n\t\t@Builder.Default\n\t\tprivate final List<Section> sections = new ArrayList<>();\n\n\t}\n\n\t@Data\n\t@Builder\n\tpublic static class Section {\n\n\t\tprivate final String activityTitle;\n\n\t\tprivate final String activitySubtitle;\n\n\t\t@Builder.Default\n\t\tprivate final List<Fact> facts = new ArrayList<>();\n\n\t}\n\n\t@Data\n\tpublic static class Fact {\n\n\t\tprivate final String name;\n\n\t\t@Nullable\n\t\tprivate final String value;\n\n\t}\n\n}\n\n## 内容6\n### 文件路径:\nspring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/notify/OpsGenieNotifier.java\n### 代码内容:\npackage de.codecentric.boot.admin.server.notify;\n\nimport java.net.URI;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.annotation.Nullable;\n\nimport org.springframework.context.expression.MapAccessor;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ParserContext;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.http.HttpEntity;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.client.RestTemplate;\nimport reactor.core.publisher.Mono;\n\nimport de.codecentric.boot.admin.server.domain.entities.Instance;\nimport de.codecentric.boot.admin.server.domain.entities.InstanceRepository;\nimport de.codecentric.boot.admin.server.domain.events.InstanceEvent;\nimport de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent;\nimport de.codecentric.boot.admin.server.domain.values.StatusInfo;\n\n/**\n * Notifier submitting events to opsgenie.com.\n *\n * @author Fernando Sure\n */\npublic class OpsGenieNotifier extends AbstractStatusChangeNotifier {\n\n\tprivate static final URI DEFAULT_URI = URI.create(\"https://api.opsgenie.com/v2/alerts\");\n\n\tprivate static final String DEFAULT_MESSAGE = \"#{instance.registration.name}/#{instance.id} is #{instance.statusInfo.status}\";\n\n\tprivate final SpelExpressionParser parser = new SpelExpressionParser();\n\n\tprivate RestTemplate restTemplate;\n\n\t/**\n\t * BASE URL for OpsGenie API\n\t */\n\tprivate URI url = DEFAULT_URI;\n\n\t/**\n\t * Integration ApiKey\n\t */\n\t@Nullable\n\tprivate String apiKey;\n\n\t/**\n\t * Comma separated list of actions that can be executed.\n\t */\n\t@Nullable\n\tprivate String actions;\n\n\t/**\n\t * Field to specify source of alert. By default, it will be assigned to IP address of\n\t * incoming request\n\t */\n\t@Nullable\n\tprivate String source;\n\n\t/**\n\t * Comma separated list of labels attached to the alert\n\t */\n\t@Nullable\n\tprivate String tags;\n\n\t/**\n\t * The entity the alert is related to.\n\t */\n\t@Nullable\n\tprivate String entity;\n\n\t/**\n\t * Default owner of the execution. If user is not specified, the system becomes owner\n\t * of the execution.\n\t */\n\t@Nullable\n\tprivate String user;\n\n\t/**\n\t * Trigger description. SpEL template using event as root;\n\t */\n\tprivate Expression description;\n\n\tpublic OpsGenieNotifier(InstanceRepository repository, RestTemplate restTemplate) {\n\t\tsuper(repository);\n\t\tthis.restTemplate = restTemplate;\n\t\tthis.description = parser.parseExpression(DEFAULT_MESSAGE, ParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n\t@Override\n\tprotected Mono<Void> doNotify(InstanceEvent event, Instance instance) {\n\t\treturn Mono.fromRunnable(() -> restTemplate.exchange(buildUrl(event, instance), HttpMethod.POST,\n\t\t\t\tcreateRequest(event, instance), Void.class));\n\t}\n\n\tprotected String buildUrl(InstanceEvent event, Instance instance) {\n\t\tif ((event instanceof InstanceStatusChangedEvent)\n\t\t\t\t&& (StatusInfo.STATUS_UP.equals(((InstanceStatusChangedEvent) event).getStatusInfo().getStatus()))) {\n\t\t\treturn String.format(\"%s/%s/close\", url.toString(), generateAlias(instance));\n\t\t}\n\t\treturn url.toString();\n\t}\n\n\tprotected HttpEntity<?> createRequest(InstanceEvent event, Instance instance) {\n\t\tMap<String, Object> body = new HashMap<>();\n\n\t\tif (user != null) {\n\t\t\tbody.put(\"user\", user);\n\t\t}\n\t\tif (source != null) {\n\t\t\tbody.put(\"source\", source);\n\t\t}\n\n\t\tif (event instanceof InstanceStatusChangedEvent\n\t\t\t\t&& !StatusInfo.STATUS_UP.equals(((InstanceStatusChangedEvent) event).getStatusInfo().getStatus())) {\n\n\t\t\tbody.put(\"message\", getMessage(event, instance));\n\t\t\tbody.put(\"alias\", generateAlias(instance));\n\t\t\tbody.put(\"description\", getDescription(event, instance));\n\t\t\tif (actions != null) {\n\t\t\t\tbody.put(\"actions\", actions);\n\t\t\t}\n\t\t\tif (tags != null) {\n\t\t\t\tbody.put(\"tags\", tags);\n\t\t\t}\n\t\t\tif (entity != null) {\n\t\t\t\tbody.put(\"entity\", entity);\n\t\t\t}\n\n\t\t\tMap<String, Object> details = new HashMap<>();\n\t\t\tdetails.put(\"type\", \"link\");\n\t\t\tdetails.put(\"href\", instance.getRegistration().getHealthUrl());\n\t\t\tdetails.put(\"text\", \"Instance health-endpoint\");\n\t\t\tbody.put(\"details\", details);\n\t\t}\n\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setContentType(MediaType.APPLICATION_JSON);\n\t\theaders.set(HttpHeaders.AUTHORIZATION, \"GenieKey \" + apiKey);\n\t\treturn new HttpEntity<>(body, headers);\n\t}\n\n\tprotected String generateAlias(Instance instance) {\n\t\treturn instance.getRegistration().getName() + \"_\" + instance.getId();\n\t}\n\n\t@Nullable\n\tprotected String getMessage(InstanceEvent event, Instance instance) {\n\t\tMap<String, Object> root = new HashMap<>();\n\t\troot.put(\"event\", event);\n\t\troot.put(\"instance\", instance);\n\t\troot.put(\"lastStatus\", getLastStatus(event.getInstance()));\n\t\tStandardEvaluationContext context = new StandardEvaluationContext(root);\n\t\tcontext.addPropertyAccessor(new MapAccessor());\n\t\treturn description.getValue(context, String.class);\n\t}\n\n\tprotected String getDescription(InstanceEvent event, Instance instance) {\n\t\treturn String.format(\"Instance %s (%s) went from %s to %s\", instance.getRegistration().getName(),\n\t\t\t\tinstance.getId(), getLastStatus(instance.getId()),\n\t\t\t\t((InstanceStatusChangedEvent) event).getStatusInfo().getStatus());\n\t}\n\n\tpublic void setApiKey(@Nullable String apiKey) {\n\t\tthis.apiKey = apiKey;\n\t}\n\n\t@Nullable\n\tpublic String getApiKey() {\n\t\treturn apiKey;\n\t}\n\n\tpublic void setDescription(String description) {\n\t\tthis.description = parser.parseExpression(description, ParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n\tpublic String getMessage() {\n\t\treturn description.getExpressionString();\n\t}\n\n\tpublic void setRestTemplate(RestTemplate restTemplate) {\n\t\tthis.restTemplate = restTemplate;\n\t}\n\n\t@Nullable\n\tpublic String getActions() {\n\t\treturn actions;\n\t}\n\n\tpublic void setActions(@Nullable String actions) {\n\t\tthis.actions = actions;\n\t}\n\n\t@Nullable\n\tpublic String getSource() {\n\t\treturn source;\n\t}\n\n\tpublic void setSource(@Nullable String source) {\n\t\tthis.source = source;\n\t}\n\n\t@Nullable\n\tpublic String getTags() {\n\t\treturn tags;\n\t}\n\n\tpublic void setTags(@Nullable String tags) {\n\t\tthis.tags = tags;\n\t}\n\n\t@Nullable\n\tpublic String getEntity() {\n\t\treturn entity;\n\t}\n\n\tpublic void setEntity(@Nullable String entity) {\n\t\tthis.entity = entity;\n\t}\n\n\t@Nullable\n\tpublic String getUser() {\n\t\treturn user;\n\t}\n\n\tpublic void setUser(@Nullable String user) {\n\t\tthis.user = user;\n\t}\n\n\tpublic URI getUrl() {\n\t\treturn url;\n\t}\n\n\tpublic void setUrl(URI url) {\n\t\tthis.url = url;\n\t}\n\n}\n\n## 内容7\n### 文件路径:\nspring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/notify/PagerdutyNotifier.java\n### 代码内容:\npackage de.codecentric.boot.admin.server.notify;\n\nimport java.net.URI;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.annotation.Nullable;\n\nimport org.springframework.context.expression.MapAccessor;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ParserContext;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.web.client.RestTemplate;\nimport reactor.core.publisher.Mono;\n\nimport de.codecentric.boot.admin.server.domain.entities.Instance;\nimport de.codecentric.boot.admin.server.domain.entities.InstanceRepository;\nimport de.codecentric.boot.admin.server.domain.events.InstanceEvent;\nimport de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent;\n\nimport static java.util.Collections.singletonList;\n\n/**\n * Notifier submitting events to Pagerduty.\n *\n * @author Johannes Edmeier\n */\npublic class PagerdutyNotifier extends AbstractStatusChangeNotifier {\n\n\tpublic static final URI DEFAULT_URI = URI\n\t\t\t.create(\"https://events.pagerduty.com/generic/2010-04-15/create_event.json\");\n\n\tprivate static final String DEFAULT_DESCRIPTION = \"#{instance.registration.name}/#{instance.id} is #{instance.statusInfo.status}\";\n\n\tprivate final SpelExpressionParser parser = new SpelExpressionParser();\n\n\tprivate RestTemplate restTemplate;\n\n\t/**\n\t * URI for pagerduty-REST-API\n\t */\n\tprivate URI url = DEFAULT_URI;\n\n\t/**\n\t * Service-Key for pagerduty-REST-API\n\t */\n\t@Nullable\n\tprivate String serviceKey;\n\n\t/**\n\t * Client for pagerduty-REST-API\n\t */\n\t@Nullable\n\tprivate String client;\n\n\t/**\n\t * Client-url for pagerduty-REST-API\n\t */\n\t@Nullable\n\tprivate URI clientUrl;\n\n\t/**\n\t * Trigger description. SpEL template using event as root;\n\t */\n\tprivate Expression description;\n\n\tpublic PagerdutyNotifier(InstanceRepository repository, RestTemplate restTemplate) {\n\t\tsuper(repository);\n\t\tthis.restTemplate = restTemplate;\n\t\tthis.description = parser.parseExpression(DEFAULT_DESCRIPTION, ParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n\t@Override\n\tprotected Mono<Void> doNotify(InstanceEvent event, Instance instance) {\n\t\treturn Mono\n\t\t\t\t.fromRunnable(() -> restTemplate.postForEntity(url, createPagerdutyEvent(event, instance), Void.class));\n\t}\n\n\tprotected Map<String, Object> createPagerdutyEvent(InstanceEvent event, Instance instance) {\n\t\tMap<String, Object> result = new HashMap<>();\n\t\tresult.put(\"service_key\", serviceKey);\n\t\tresult.put(\"incident_key\", instance.getRegistration().getName() + \"/\" + event.getInstance());\n\t\tresult.put(\"description\", getDescription(event, instance));\n\n\t\tMap<String, Object> details = getDetails(event);\n\t\tresult.put(\"details\", details);\n\n\t\tif (event instanceof InstanceStatusChangedEvent) {\n\t\t\tif (\"UP\".equals(((InstanceStatusChangedEvent) event).getStatusInfo().getStatus())) {\n\t\t\t\tresult.put(\"event_type\", \"resolve\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\tresult.put(\"event_type\", \"trigger\");\n\t\t\t\tif (client != null) {\n\t\t\t\t\tresult.put(\"client\", client);\n\t\t\t\t}\n\t\t\t\tif (clientUrl != null) {\n\t\t\t\t\tresult.put(\"client_url\", clientUrl);\n\t\t\t\t}\n\n\t\t\t\tMap<String, Object> context = new HashMap<>();\n\t\t\t\tcontext.put(\"type\", \"link\");\n\t\t\t\tcontext.put(\"href\", instance.getRegistration().getHealthUrl());\n\t\t\t\tcontext.put(\"text\", \"Application health-endpoint\");\n\t\t\t\tresult.put(\"contexts\", singletonList(context));\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Nullable\n\tprotected String getDescription(InstanceEvent event, Instance instance) {\n\t\tMap<String, Object> root = new HashMap<>();\n\t\troot.put(\"event\", event);\n\t\troot.put(\"instance\", instance);\n\t\troot.put(\"lastStatus\", getLastStatus(event.getInstance()));\n\t\tStandardEvaluationContext context = new StandardEvaluationContext(root);\n\t\tcontext.addPropertyAccessor(new MapAccessor());\n\n\t\treturn description.getValue(context, String.class);\n\t}\n\n\tprotected Map<String, Object> getDetails(InstanceEvent event) {\n\t\tMap<String, Object> details = new HashMap<>();\n\t\tif (event instanceof InstanceStatusChangedEvent) {\n\t\t\tdetails.put(\"from\", this.getLastStatus(event.getInstance()));\n\t\t\tdetails.put(\"to\", ((InstanceStatusChangedEvent) event).getStatusInfo());\n\t\t}\n\t\treturn details;\n\t}\n\n\tpublic void setUrl(URI url) {\n\t\tthis.url = url;\n\t}\n\n\tpublic URI getUrl() {\n\t\treturn url;\n\t}\n\n\tpublic void setClient(@Nullable String client) {\n\t\tthis.client = client;\n\t}\n\n\t@Nullable\n\tpublic String getClient() {\n\t\treturn client;\n\t}\n\n\tpublic void setClientUrl(@Nullable URI clientUrl) {\n\t\tthis.clientUrl = clientUrl;\n\t}\n\n\t@Nullable\n\tpublic URI getClientUrl() {\n\t\treturn clientUrl;\n\t}\n\n\tpublic void setServiceKey(@Nullable String serviceKey) {\n\t\tthis.serviceKey = serviceKey;\n\t}\n\n\t@Nullable\n\tpublic String getServiceKey() {\n\t\treturn serviceKey;\n\t}\n\n\tpublic void setDescription(String description) {\n\t\tthis.description = parser.parseExpression(description, ParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n\tpublic String getDescription() {\n\t\treturn description.getExpressionString();\n\t}\n\n\tpublic void setRestTemplate(RestTemplate restTemplate) {\n\t\tthis.restTemplate = restTemplate;\n\t}\n\n}\n\n## 内容8\n### 文件路径:\nspring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/notify/SlackNotifier.java\n### 代码内容:\npackage de.codecentric.boot.admin.server.notify;\n\nimport java.net.URI;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.annotation.Nullable;\n\nimport org.springframework.context.expression.MapAccessor;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ParserContext;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.http.HttpEntity;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.client.RestTemplate;\nimport reactor.core.publisher.Mono;\n\nimport de.codecentric.boot.admin.server.domain.entities.Instance;\nimport de.codecentric.boot.admin.server.domain.entities.InstanceRepository;\nimport de.codecentric.boot.admin.server.domain.events.InstanceEvent;\nimport de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent;\nimport de.codecentric.boot.admin.server.domain.values.StatusInfo;\n\n/**\n * Notifier submitting events to Slack.\n *\n * @author Artur Dobosiewicz\n */\npublic class SlackNotifier extends AbstractStatusChangeNotifier {\n\n\tprivate static final String DEFAULT_MESSAGE = \"*#{instance.registration.name}* (#{instance.id}) is *#{event.statusInfo.status}*\";\n\n\tprivate final SpelExpressionParser parser = new SpelExpressionParser();\n\n\tprivate RestTemplate restTemplate;\n\n\t/**\n\t * Webhook url for Slack API (i.e. https://hooks.slack.com/services/xxx)\n\t */\n\t@Nullable\n\tprivate URI webhookUrl;\n\n\t/**\n\t * Optional channel name without # sign (i.e. somechannel)\n\t */\n\t@Nullable\n\tprivate String channel;\n\n\t/**\n\t * Optional emoji icon without colons (i.e. my-emoji)\n\t */\n\t@Nullable\n\tprivate String icon;\n\n\t/**\n\t * Optional username which sends notification\n\t */\n\t@Nullable\n\tprivate String username = \"Spring Boot Admin\";\n\n\t/**\n\t * Message formatted using Slack markups. SpEL template using event as root\n\t */\n\tprivate Expression message;\n\n\tpublic SlackNotifier(InstanceRepository repository, RestTemplate restTemplate) {\n\t\tsuper(repository);\n\t\tthis.restTemplate = restTemplate;\n\t\tthis.message = parser.parseExpression(DEFAULT_MESSAGE, ParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n\t@Override\n\tprotected Mono<Void> doNotify(InstanceEvent event, Instance instance) {\n\t\tif (webhookUrl == null) {\n\t\t\treturn Mono.error(new IllegalStateException(\"'webhookUrl' must not be null.\"));\n\t\t}\n\t\treturn Mono\n\t\t\t\t.fromRunnable(() -> restTemplate.postForEntity(webhookUrl, createMessage(event, instance), Void.class));\n\t}\n\n\tpublic void setRestTemplate(RestTemplate restTemplate) {\n\t\tthis.restTemplate = restTemplate;\n\t}\n\n\tprotected Object createMessage(InstanceEvent event, Instance instance) {\n\t\tMap<String, Object> messageJson = new HashMap<>();\n\t\tmessageJson.put(\"username\", username);\n\t\tif (icon != null) {\n\t\t\tmessageJson.put(\"icon_emoji\", \":\" + icon + \":\");\n\t\t}\n\t\tif (channel != null) {\n\t\t\tmessageJson.put(\"channel\", channel);\n\t\t}\n\n\t\tMap<String, Object> attachments = new HashMap<>();\n\t\tattachments.put(\"text\", getText(event, instance));\n\t\tattachments.put(\"color\", getColor(event));\n\t\tattachments.put(\"mrkdwn_in\", Collections.singletonList(\"text\"));\n\t\tmessageJson.put(\"attachments\", Collections.singletonList(attachments));\n\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setContentType(MediaType.APPLICATION_JSON);\n\t\treturn new HttpEntity<>(messageJson, headers);\n\t}\n\n\t@Nullable\n\tprotected String getText(InstanceEvent event, Instance instance) {\n\t\tMap<String, Object> root = new HashMap<>();\n\t\troot.put(\"event\", event);\n\t\troot.put(\"instance\", instance);\n\t\troot.put(\"lastStatus\", getLastStatus(event.getInstance()));\n\t\tStandardEvaluationContext context = new StandardEvaluationContext(root);\n\t\tcontext.addPropertyAccessor(new MapAccessor());\n\t\treturn message.getValue(context, String.class);\n\t}\n\n\tprotected String getColor(InstanceEvent event) {\n\t\tif (event instanceof InstanceStatusChangedEvent) {\n\t\t\treturn StatusInfo.STATUS_UP.equals(((InstanceStatusChangedEvent) event).getStatusInfo().getStatus())\n\t\t\t\t\t? \"good\" : \"danger\";\n\t\t}\n\t\telse {\n\t\t\treturn \"#439FE0\";\n\t\t}\n\t}\n\n\t@Nullable\n\tpublic URI getWebhookUrl() {\n\t\treturn webhookUrl;\n\t}\n\n\tpublic void setWebhookUrl(@Nullable URI webhookUrl) {\n\t\tthis.webhookUrl = webhookUrl;\n\t}\n\n\t@Nullable\n\tpublic String getChannel() {\n\t\treturn channel;\n\t}\n\n\tpublic void setChannel(@Nullable String channel) {\n\t\tthis.channel = channel;\n\t}\n\n\t@Nullable\n\tpublic String getIcon() {\n\t\treturn icon;\n\t}\n\n\tpublic void setIcon(@Nullable String icon) {\n\t\tthis.icon = icon;\n\t}\n\n\t@Nullable\n\tpublic String getUsername() {\n\t\treturn username;\n\t}\n\n\tpublic void setUsername(@Nullable String username) {\n\t\tthis.username = username;\n\t}\n\n\tpublic String getMessage() {\n\t\treturn message.getExpressionString();\n\t}\n\n\tpublic void setMessage(String message) {\n\t\tthis.message = parser.parseExpression(message, ParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n}\n\n## 内容9\n### 文件路径:\nspring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/notify/TelegramNotifier.java\n### 代码内容:\npackage de.codecentric.boot.admin.server.notify;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.annotation.Nullable;\n\nimport org.springframework.context.expression.MapAccessor;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ParserContext;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.web.client.RestTemplate;\nimport reactor.core.publisher.Mono;\n\nimport de.codecentric.boot.admin.server.domain.entities.Instance;\nimport de.codecentric.boot.admin.server.domain.entities.InstanceRepository;\nimport de.codecentric.boot.admin.server.domain.events.InstanceEvent;\n\n/**\n * Notifier submitting events to Telegram.\n */\npublic class TelegramNotifier extends AbstractStatusChangeNotifier {\n\n\tprivate static final String DEFAULT_MESSAGE = \"<strong>#{instance.registration.name}</strong>/#{instance.id} is <strong>#{event.statusInfo.status}</strong>\";\n\n\tprivate final SpelExpressionParser parser = new SpelExpressionParser();\n\n\tprivate RestTemplate restTemplate;\n\n\t/**\n\t * base url for telegram (i.e. https://api.telegram.org)\n\t */\n\tprivate String apiUrl = \"https://api.telegram.org\";\n\n\t/**\n\t * Unique identifier for the target chat or username of the target channel\n\t */\n\t@Nullable\n\tprivate String chatId;\n\n\t/**\n\t * The token identifiying und authorizing your Telegram bot (e.g.\n\t * `123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11`)\n\t */\n\t@Nullable\n\tprivate String authToken;\n\n\t/**\n\t * Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width\n\t * text or inline URLs in your bot's message.\n\t */\n\tprivate String parseMode = \"HTML\";\n\n\t/**\n\t * If true users will receive a notification with no sound.\n\t */\n\tprivate boolean disableNotify = false;\n\n\tprivate Expression message;\n\n\tpublic TelegramNotifier(InstanceRepository repository, RestTemplate restTemplate) {\n\t\tsuper(repository);\n\t\tthis.restTemplate = restTemplate;\n\t\tthis.message = parser.parseExpression(DEFAULT_MESSAGE, ParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n\t@Override\n\tprotected Mono<Void> doNotify(InstanceEvent event, Instance instance) {\n\t\treturn Mono\n\t\t\t\t.fromRunnable(() -> restTemplate.getForObject(buildUrl(), Void.class, createMessage(event, instance)));\n\t}\n\n\tprotected String buildUrl() {\n\t\treturn String.format(\"%s/bot%s/sendmessage?chat_id={chat_id}&text={text}&parse_mode={parse_mode}\"\n\t\t\t\t+ \"&disable_notification={disable_notification}\", this.apiUrl, this.authToken);\n\t}\n\n\tprivate Map<String, Object> createMessage(InstanceEvent event, Instance instance) {\n\t\tMap<String, Object> parameters = new HashMap<>();\n\t\tparameters.put(\"chat_id\", this.chatId);\n\t\tparameters.put(\"parse_mode\", this.parseMode);\n\t\tparameters.put(\"disable_notification\", this.disableNotify);\n\t\tparameters.put(\"text\", getText(event, instance));\n\t\treturn parameters;\n\t}\n\n\t@Nullable\n\tprotected String getText(InstanceEvent event, Instance instance) {\n\t\tMap<String, Object> root = new HashMap<>();\n\t\troot.put(\"event\", event);\n\t\troot.put(\"instance\", instance);\n\t\troot.put(\"lastStatus\", getLastStatus(event.getInstance()));\n\t\tStandardEvaluationContext context = new StandardEvaluationContext(root);\n\t\tcontext.addPropertyAccessor(new MapAccessor());\n\t\treturn message.getValue(context, String.class);\n\t}\n\n\tpublic void setRestTemplate(RestTemplate restTemplate) {\n\t\tthis.restTemplate = restTemplate;\n\t}\n\n\tpublic String getApiUrl() {\n\t\treturn apiUrl;\n\t}\n\n\tpublic void setApiUrl(String apiUrl) {\n\t\tthis.apiUrl = apiUrl;\n\t}\n\n\t@Nullable\n\tpublic String getChatId() {\n\t\treturn chatId;\n\t}\n\n\tpublic void setChatId(@Nullable String chatId) {\n\t\tthis.chatId = chatId;\n\t}\n\n\t@Nullable\n\tpublic String getAuthToken() {\n\t\treturn authToken;\n\t}\n\n\tpublic void setAuthToken(@Nullable String authToken) {\n\t\tthis.authToken = authToken;\n\t}\n\n\tpublic boolean isDisableNotify() {\n\t\treturn disableNotify;\n\t}\n\n\tpublic void setDisableNotify(boolean disableNotify) {\n\t\tthis.disableNotify = disableNotify;\n\t}\n\n\tpublic String getParseMode() {\n\t\treturn parseMode;\n\t}\n\n\tpublic void setParseMode(String parseMode) {\n\t\tthis.parseMode = parseMode;\n\t}\n\n\tpublic void setMessage(String message) {\n\t\tthis.message = parser.parseExpression(message, ParserContext.TEMPLATE_EXPRESSION);\n\t}\n\n}\n\n", "input": "", "output": "【漏洞类型】表达式注入漏洞\n【严重性】High\n【漏洞摘要】这些代码中存在表达式注入漏洞,因为它们使用Spring Expression Language (SpEL)来解析用户输入的消息模板。如果攻击者能够控制消息模板的内容,他们可以注入恶意的SpEL表达式,从而执行任意代码。例如,在`DingTalkNotifier`类中,`message`表达式是从`DEFAULT_MESSAGE`解析而来的,而在`DiscordNotifier`类中,`message`表达式是从用户输入的`message`属性解析而来的。\n【关键代码模式】`SpelExpressionParser.parseExpression`、`StandardEvaluationContext`、`MapAccessor`、`Message`、`Expression`、`description`。\n【修复方法说明】修复方法通过将`StandardEvaluationContext`替换为`SimpleEvaluationContext`,并使用`DataBindingPropertyAccessor`来限制表达式的执行范围,从而防止潜在的代码注入攻击。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/main/java/com/jflyfox/modules/front/controller/PersonController.java\n### 代码内容:\npackage com.jflyfox.modules.front.controller;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.jfinal.aop.Before;\nimport com.jfinal.plugin.activerecord.Db;\nimport com.jfinal.plugin.activerecord.Page;\nimport com.jfinal.plugin.activerecord.Record;\nimport com.jflyfox.component.base.BaseProjectController;\nimport com.jflyfox.component.beelt.BeeltFunctions;\nimport com.jflyfox.component.util.JFlyFoxUtils;\nimport com.jflyfox.jfinal.base.Paginator;\nimport com.jflyfox.jfinal.component.annotation.ControllerBind;\nimport com.jflyfox.modules.CommonController;\nimport com.jflyfox.modules.admin.article.ArticleConstant;\nimport com.jflyfox.modules.admin.article.TbArticle;\nimport com.jflyfox.modules.admin.comment.CommentService;\nimport com.jflyfox.modules.admin.tags.TbTags;\nimport com.jflyfox.modules.front.interceptor.FrontInterceptor;\nimport com.jflyfox.modules.front.service.FrontCacheService;\nimport com.jflyfox.system.user.SysUser;\nimport com.jflyfox.system.user.UserCache;\nimport com.jflyfox.util.DateUtils;\nimport com.jflyfox.util.StrUtils;\nimport com.jflyfox.util.extend.HtmlUtils;\n\n/**\n * 个人信息\n * \n * 2015年3月10日 下午5:36:22 flyfox 369191470@qq.com\n */\n@ControllerBind(controllerKey = \"/front/person\")\npublic class PersonController extends BaseProjectController {\n\n\tpublic static final String path = \"/person/\";\n\n\t/**\n\t * 个人信息\n\t */\n\t@Before(FrontInterceptor.class)\n\tpublic void index() {\n\t\tSysUser user = (SysUser) getSessionUser();\n\t\tif (user == null) {\n\t\t\tredirect(CommonController.firstPage);\n\t\t\treturn;\n\t\t}\n\n\t\t// 活动目录\n\t\tsetAttr(\"folders_selected\", \"personhome\");\n\n\t\tsetAttr(\"user\", user);\n\n\t\t// 数据列表,只查询展示的和类型为11,12的\n\t\tPage<TbArticle> articles = TbArticle.dao.paginate(getPaginator(), \"select t.* \", //\n\t\t\t\t\" from tb_article t\" //\n\t\t\t\t\t\t+ \" left join tb_folder tf on tf.id = t.folder_id \" //\n\t\t\t\t\t\t+ \" where \" + getPublicWhere() //\n\t\t\t\t\t\t+ \" and t.create_id = ? and tf.site_id = ? \" //\n\t\t\t\t\t\t+ \" order by t.sort,t.create_time desc\", user.getUserid(), getSessionSite().getSiteId());\n\t\tsetAttr(\"page\", articles);\n\n\t\t// 显示50个标签\n\t\tif (articles.getTotalRow() > 0) {\n\t\t\tPage<TbTags> taglist = new FrontCacheService().getTagsByFolder(new Paginator(1, 50), articles.getList()\n\t\t\t\t\t.get(0).getFolderId());\n\t\t\tsetAttr(\"taglist\", taglist.getList());\n\t\t} else {\n\t\t\tPage<TbTags> taglist = tags();\n\t\t\tsetAttr(\"taglist\", taglist.getList());\n\t\t}\n\n\t\tcommon(user);\n\n\t\trenderAuto(path + \"workspace.html\");\n\t}\n\n\t/**\n\t * 个人信息\n\t */\n\t@Before(FrontInterceptor.class)\n\tpublic void article() {\n\t\tSysUser user = (SysUser) getSessionUser();\n\t\tif (user == null) {\n\t\t\tredirect(CommonController.firstPage);\n\t\t\treturn;\n\t\t}\n\n\t\t// 活动目录\n\t\tsetAttr(\"folders_selected\", \"personhome\");\n\n\t\tsetAttr(\"user\", user);\n\n\t\t// 数据列表,只查询展示的和类型为11,12的\n\t\tPage<TbArticle> articles = TbArticle.dao.paginate(getPaginator(), \"select t.* \", //\n\t\t\t\t\" from tb_article t\" + \" left join tb_folder tf on tf.id = t.folder_id \" //\n\t\t\t\t\t\t+ \" where \" + getPublicWhere() //\n\t\t\t\t\t\t+ \" and t.create_id = ? and tf.site_id = ? \" //\n\t\t\t\t\t\t+ \" order by t.sort,t.create_time desc\", user.getUserid(), getSessionSite().getSiteId());\n\t\tsetAttr(\"page\", articles);\n\n\t\t// 显示50个标签\n\t\tif (articles.getTotalRow() > 0) {\n\t\t\tPage<TbTags> taglist = new FrontCacheService().getTagsByFolder(new Paginator(1, 50), articles.getList()\n\t\t\t\t\t.get(0).getFolderId());\n\t\t\tsetAttr(\"taglist\", taglist.getList());\n\t\t} else {\n\t\t\tPage<TbTags> taglist = tags();\n\t\t\tsetAttr(\"taglist\", taglist.getList());\n\t\t}\n\n\t\tcommon(user);\n\n\t\trenderAuto(path + \"article.html\");\n\t}\n\n\t/**\n\t * 我喜欢的文章\n\t */\n\t@Before(FrontInterceptor.class)\n\tpublic void articlelike() {\n\t\tSysUser user = (SysUser) getSessionUser();\n\t\tif (user == null) {\n\t\t\tredirect(CommonController.firstPage);\n\t\t\treturn;\n\t\t}\n\n\t\t// 活动目录\n\t\tsetAttr(\"folders_selected\", \"personhome\");\n\n\t\tsetAttr(\"user\", user);\n\n\t\t// 数据列表,只查询展示的和类型为11,12的\n\t\tPage<TbArticle> articles = TbArticle.dao.paginate(getPaginator(), \"select t.* \", //\n\t\t\t\t\" from tb_article t \" //\n\t\t\t\t\t\t+ \" left join tb_folder tf on tf.id = t.folder_id \" //\n\t\t\t\t\t\t+ \" left join tb_articlelike al on al.article_id = t.id\" + \" where \" + getPublicWhere() //\n\t\t\t\t\t\t+ \" and al.create_id = ? and tf.site_id = ? \" //\n\t\t\t\t\t\t+ \" order by t.sort,t.create_time desc\", user.getUserid(), getSessionSite().getSiteId());\n\t\tsetAttr(\"page\", articles);\n\n\t\t// 显示50个标签\n\t\tif (articles.getTotalRow() > 0) {\n\t\t\tPage<TbTags> taglist = new FrontCacheService().getTagsByFolder(new Paginator(1, 50), articles.getList()\n\t\t\t\t\t.get(0).getFolderId());\n\t\t\tsetAttr(\"taglist\", taglist.getList());\n\t\t} else {\n\t\t\tPage<TbTags> taglist = tags();\n\t\t\tsetAttr(\"taglist\", taglist.getList());\n\t\t}\n\n\t\tcommon(user);\n\n\t\trenderAuto(path + \"articlelike.html\");\n\t}\n\n\t/**\n\t * 列表公共方法,展示文章和喜欢文章数量以及SEO title\n\t * \n\t * 2015年8月16日 下午8:48:35 flyfox 369191470@qq.com\n\t * \n\t * @param user\n\t */\n\tprivate void common(SysUser user) {\n\t\t// 文章数量和喜欢数量\n\t\tRecord record = Db.findFirst(\"select count(t.id) as artcount,count(al.id) as artlikecount from tb_article t\" //\n\t\t\t\t+ \" left join tb_folder tf on tf.id = t.folder_id \" //\n\t\t\t\t+ \" left join (select * from tb_articlelike where create_id = ? ) al on al.article_id = t.id\"\n\t\t\t\t+ \" where \" + getPublicWhere() //\n\t\t\t\t+ \" and t.create_id = ? and tf.site_id = ? \" //\n\t\t\t\t+ \" order by t.sort,t.create_time desc\" //\n\t\t, user.getUserid(), user.getUserid(), getSessionSite().getSiteId());\n\t\tsetAttr(\"userarticle\", record.get(\"artcount\"));\n\t\tsetAttr(\"userlike\", record.get(\"artlikecount\"));\n\n\t\t// title展示\n\t\tsetAttr(JFlyFoxUtils.TITLE_ATTR, \"空间 - \" + BeeltFunctions.getUserName(user.getUserid()) + \" - \"\n\t\t\t\t+ getAttr(JFlyFoxUtils.TITLE_ATTR));\n\t}\n\n\t/**\n\t * 跳转到发布博文页面\n\t * \n\t * 2015年6月17日 下午9:53:04 flyfox 369191470@qq.com\n\t */\n\t@Before(FrontInterceptor.class)\n\tpublic void newblog() {\n\t\tSysUser user = (SysUser) getSessionUser();\n\t\tif (user == null) {\n\t\t\tredirect(CommonController.firstPage);\n\t\t\treturn;\n\t\t}\n\n\t\tsetAttr(\"user\", user);\n\n\t\t// 活动目录\n\t\tsetAttr(\"folders_selected\", \"personhome\");\n\n\t\trenderAuto(path + \"new_blog.html\");\n\t}\n\n\t/**\n\t * 跳转到编辑博文页面\n\t * \n\t * 2015年6月17日 下午9:53:04 flyfox 369191470@qq.com\n\t */\n\t@Before(FrontInterceptor.class)\n\tpublic void editblog() {\n\t\tSysUser user = (SysUser) getSessionUser();\n\t\tif (user == null) {\n\t\t\tredirect(CommonController.firstPage);\n\t\t\treturn;\n\t\t}\n\n\t\tsetAttr(\"user\", user);\n\n\t\t// 活动目录\n\t\tsetAttr(\"folders_selected\", \"personhome\");\n\n\t\tTbArticle model = TbArticle.dao.findById(getParaToInt());\n\t\tsetAttr(\"model\", model);\n\t\t// 不是自己的文章也想修改,总有不怀好意的人哦\n\t\tif (model.getCreateId() != user.getUserid()) {\n\t\t\tSystem.err.println(\"####userid(\" + user.getUserid() + \")非法编辑内容\");\n\t\t\tredirect(CommonController.firstPage);\n\t\t\treturn;\n\t\t}\n\n\t\t// 设置标签\n\t\tString tags = Db.findFirst(\"select group_concat(tagname) tags \" //\n\t\t\t\t+ \" from tb_tags where article_id = ? order by id\", model.getInt(\"id\")).getStr(\"tags\");\n\t\tsetAttr(\"tags\", tags);\n\n\t\trenderAuto(path + \"edit_blog.html\");\n\t}\n\n\t/**\n\t * 保存博文\n\t * \n\t * 2015年6月17日 下午10:12:18 flyfox 369191470@qq.com\n\t */\n\tpublic void saveblog() {\n\t\tJSONObject json = new JSONObject();\n\t\tjson.put(\"status\", 2);// 失败\n\n\t\tSysUser user = (SysUser) getSessionUser();\n\t\tif (user == null) {\n\t\t\tjson.put(\"msg\", \"没有登录,不能提交博文!\");\n\t\t\trenderJson(json.toJSONString());\n\t\t\treturn;\n\t\t}\n\n\t\tInteger pid = getParaToInt();\n\t\tTbArticle model = getModel(TbArticle.class);\n\t\t// 验证题目,内容\n\t\tString content = model.getContent();\n\t\tString title = model.getTitle();\n\t\tString tags = getPara(\"tags\");\n\t\t// 删除侵入脚本\n\t\tcontent = JFlyFoxUtils.delScriptTag(content);\n\t\ttitle = HtmlUtils.delHTMLTag(title);\n\t\ttags = HtmlUtils.delHTMLTag(tags);\n\n\t\t// 这里没有必要提示太精准~因为前台有验证~绕过的都不是好人哦\n\t\tif (content == null || HtmlUtils.delHTMLTag(content).length() > 2000 //\n\t\t\t\t|| title == null || title.length() > 200 //\n\t\t\t\t|| tags == null || tags.length() > 200 //\n\t\t) {\n\t\t\tjson.put(\"msg\", \"博客信息错误,请输入正确数据!\");\n\t\t\trenderJson(json.toJSONString());\n\t\t\treturn;\n\t\t}\n\n\t\tmodel.setUpdateTime(getNow());\n\t\tif (pid != null && pid > 0) { // 更新\n\t\t\t// 管理员或者自己才能修改\n\t\t\tif (!isAdmin(user) //\n\t\t\t\t\t&& model.getCreateId().intValue() != user.getUserid().intValue()) {\n\t\t\t\tjson.put(\"msg\", \"你没有权限修改博文!\");\n\t\t\t\trenderJson(json.toJSONString());\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tmodel.update();\n\t\t} else { // 新增\n\t\t\tmodel.remove(\"id\");\n\t\t\tif (model.getFolderId() == null || model.getFolderId() <= 0) {\n\t\t\t\tmodel.setFolderId(JFlyFoxUtils.MENU_BLOG); // 博文目录\n\t\t\t}\n\t\t\tmodel.setStatus(\"1\"); // 显示\n\t\t\tmodel.setType(11);\n\t\t\tmodel.setIsComment(1); // 能评论\n\t\t\tmodel.setIsRecommend(2);// 不推荐\n\t\t\tmodel.setSort(20); // 排序\n\t\t\tmodel.set(\"approve_status\", ArticleConstant.APPROVE_STATUS_PASS); // 需要审核改为update\n\t\t\tmodel.setPublishTime(DateUtils.getNow(\"yyyy-MM-dd\")); // 发布时间\n\t\t\tmodel.setPublishUser(user.getUserName()); // 发布人\n\t\t\tmodel.setCreateId(getSessionUser().getUserid());\n\t\t\tmodel.setCreateTime(getNow());\n\t\t\tmodel.save();\n\t\t}\n\n\t\t// 保存tags\n\t\tDb.update(\" delete from tb_tags where article_id = ?\", model.getInt(\"id\"));\n\t\tif (StrUtils.isNotEmpty(tags)) {\n\t\t\tString[] tagsArr = tags.split(\",\");\n\t\t\tfor (int i = 0; i < tagsArr.length; i++) {\n\t\t\t\tString tagname = tagsArr[i];\n\t\t\t\t// 最多5个\n\t\t\t\tif (i >= 5) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (StrUtils.isEmpty(tagname)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tTbTags tbTags = new TbTags();\n\t\t\t\ttbTags.put(\"tagname\", tagname);\n\t\t\t\ttbTags.put(\"article_id\", model.getInt(\"id\"));\n\t\t\t\ttbTags.put(\"create_id\", getSessionUser().getUserid());\n\t\t\t\ttbTags.put(\"create_time\", getNow());\n\t\t\t\ttbTags.save();\n\n\t\t\t}\n\t\t}\n\n\t\tjson.put(\"status\", 1);// 成功\n\t\trenderJson(json.toJSONString());\n\t}\n\n\t/**\n\t * 跳转到编辑博文页面\n\t * \n\t * 2015年6月17日 下午9:53:04 flyfox 369191470@qq.com\n\t */\n\t@Before(FrontInterceptor.class)\n\tpublic void delblog() {\n\t\tSysUser user = (SysUser) getSessionUser();\n\t\tInteger id = getParaToInt();\n\t\tif (user == null || id == null) {\n\t\t\tredirect(CommonController.firstPage);\n\t\t\treturn;\n\t\t}\n\n\t\tTbArticle model = TbArticle.dao.findById(getParaToInt());\n\t\tsetAttr(\"model\", model);\n\t\t// 不是自己的文章也想修改,总有不怀好意的人哦\n\t\tif (model.getCreateId() != user.getUserid()) {\n\t\t\tSystem.err.println(\"####userid(\" + user.getUserid() + \")非法编辑内容\");\n\t\t\tredirect(CommonController.firstPage);\n\t\t\treturn;\n\t\t}\n\n\t\t// 删除评论~\n\t\tnew CommentService().deleteComment(id);\n\t\t// 删除文章\n\t\tTbArticle.dao.deleteById(id);\n\t\tredirect(\"/front/person\");\n\t}\n\n\t/**\n\t * 个人信息编辑\n\t */\n\t@Before(FrontInterceptor.class)\n\tpublic void profile() {\n\t\tSysUser user = (SysUser) getSessionUser();\n\t\tif (user == null) {\n\t\t\tredirect(CommonController.firstPage);\n\t\t\treturn;\n\t\t}\n\n\t\tsetAttr(\"model\", user);\n\n\t\t// TODO 用户信息展示\n\t\tsetAttr(\"user\", user);\n\n\t\t// 活动目录\n\t\tsetAttr(\"folders_selected\", \"person\");\n\n\t\trenderAuto(path + \"show_person.html\");\n\t}\n\n\t/**\n\t * 个人信息保存\n\t */\n\tpublic void save() {\n\t\tJSONObject json = new JSONObject();\n\t\tjson.put(\"status\", 2);// 失败\n\n\t\tSysUser user = (SysUser) getSessionUser();\n\t\tint userid = user.getInt(\"userid\");\n\t\tSysUser model = getModel(SysUser.class);\n\n\t\tif (userid != model.getInt(\"userid\")) {\n\t\t\tjson.put(\"msg\", \"提交数据错误!\");\n\t\t\trenderJson(json.toJSONString());\n\t\t\treturn;\n\t\t}\n\n\t\t// 第三方用户不需要密码\n\t\tif (user.getInt(\"usertype\") != 4) {\n\t\t\tString oldPassword = getPara(\"old_password\");\n\t\t\tString newPassword = getPara(\"new_password\");\n\t\t\tString newPassword2 = getPara(\"new_password2\");\n\t\t\tif (!user.getStr(\"password\").equals(JFlyFoxUtils.passwordEncrypt(oldPassword))) {\n\t\t\t\tjson.put(\"msg\", \"密码错误!\");\n\t\t\t\trenderJson(json.toJSONString());\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (StrUtils.isNotEmpty(newPassword) && !newPassword.equals(newPassword2)) {\n\t\t\t\tjson.put(\"msg\", \"两次新密码不一致!\");\n\t\t\t\trenderJson(json.toJSONString());\n\t\t\t\treturn;\n\t\t\t} else if (StrUtils.isNotEmpty(newPassword)) { // 输入密码并且一直\n\t\t\t\tmodel.set(\"password\", JFlyFoxUtils.passwordEncrypt(newPassword));\n\t\t\t}\n\t\t}\n\n\t\tif (StrUtils.isNotEmpty(model.getStr(\"email\")) && model.getStr(\"email\").indexOf(\"@\") < 0) {\n\t\t\tjson.put(\"msg\", \"email格式错误!\");\n\t\t\trenderJson(json.toJSONString());\n\t\t\treturn;\n\t\t}\n\n\t\tmodel.update();\n\t\tUserCache.init(); // 设置缓存\n\t\tSysUser newUser = SysUser.dao.findById(userid);\n\t\tsetSessionUser(newUser); // 设置session\n\t\tjson.put(\"status\", 1);// 成功\n\n\t\trenderJson(json.toJSONString());\n\t}\n\n\t/**\n\t * 查看文章某用户发布文章\n\t * \n\t * 2015年2月26日 下午1:46:14 flyfox 369191470@qq.com\n\t */\n\t@Before(FrontInterceptor.class)\n\tpublic void view() {\n\t\tInteger userid = getParaToInt();\n\n\t\tSysUser session_user = (SysUser) getSessionUser();\n\t\tif (session_user != null && session_user.getUserid() == userid) {\n\t\t\t// 如果是自己,就跳到自己的空间\n\t\t\tredirect(\"/front/person\");\n\t\t\treturn;\n\t\t}\n\n\t\t// 活动目录\n\t\tsetAttr(\"folders_selected\", 0);\n\n\t\tSysUser user = UserCache.getUser(userid);\n\t\tsetAttr(\"user\", user);\n\n\t\t// 数据列表,只查询展示的和类型为11,12的\n\t\tPage<TbArticle> articles = TbArticle.dao.paginate(getPaginator(), \"select t.* \", //\n\t\t\t\t\" from tb_article t \" //\n\t\t\t\t\t\t+ \" left join tb_folder tf on tf.id = t.folder_id \" //\n\t\t\t\t\t\t+ \" where \" + getPublicWhere() //\n\t\t\t\t\t\t+ \" and t.create_id = ? and tf.site_id = ? \" //\n\t\t\t\t\t\t+ \" order by t.sort,t.create_time desc\", userid, getSessionSite().getSiteId());\n\t\tsetAttr(\"page\", articles);\n\n\t\t// 显示50个标签\n\t\tif (articles.getTotalRow() > 0) {\n\t\t\tPage<TbTags> taglist = new FrontCacheService().getTagsByFolder(new Paginator(1, 50), articles.getList()\n\t\t\t\t\t.get(0).getFolderId());\n\t\t\tsetAttr(\"taglist\", taglist.getList());\n\t\t} else {\n\t\t\tPage<TbTags> taglist = tags();\n\t\t\tsetAttr(\"taglist\", taglist.getList());\n\t\t}\n\n\t\t// title展示\n\t\tsetAttr(JFlyFoxUtils.TITLE_ATTR, BeeltFunctions.getUserName(userid) + \"的空间 - \" + getAttr(JFlyFoxUtils.TITLE_ATTR));\n\n\t\trenderAuto(path + \"view_person.html\");\n\n\t}\n\n\tprotected Page<TbTags> tags() {\n\t\treturn new FrontCacheService().getTags(new Paginator(1, 50), getSessionSite().getSiteId());\n\t}\n}\n\n", "input": "", "output": "【漏洞类型】跨站脚本攻击(XSS)\n\n【严重性】High\n\n【漏洞摘要】此代码存在跨站脚本攻击(XSS)漏洞。在`saveblog`方法中,用户输入的内容(如`content`、`title`和`tags`)被清理了HTML和脚本标签,但这些清理后的值并没有被重新赋值给模型对象。因此,虽然用户输入被清理了,但由于模型对象的属性没有更新,原始用户输入仍然可能被渲染到页面中,导致XSS攻击。具体问题行包括:\n```java\ncontent = JFlyFoxUtils.delScriptTag(content);\ntitle = HtmlUtils.delHTMLTag(title);\ntags = HtmlUtils.delHTMLTag(tags);\n```\n根本原因在于用户输入的清理操作没有正确地更新到模型对象中,导致可能的XSS漏洞。\n\n【关键代码模式】与此漏洞相关的关键代码模式包括:\n- `HtmlUtils.delHTMLTag` 和 `JFlyFoxUtils.delScriptTag` 这样的HTML和脚本清理函数。\n- 模型对象的属性(如`content`、`title`、`tags`)在更新前没有被正确地赋值。\n\n【修复方法说明】修复方法通过将清理后的值重新赋值给模型对象,确保用户输入在展示之前已经被完全清理,从而避免XSS攻击。具体修复代码如下:\n```java\nmodel.setContent(content);\nmodel.setTitle(title);\n```\n最佳实践包括在所有用户输入的展示前进行严格的HTML和脚本清理,并确保清理后的值被正确地更新到模型对象或数据库中。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/main/java/org/torpedoquery/jpa/internal/conditions/ConditionBuilder.java\n### 代码内容:\npackage org.torpedoquery.jpa.internal.conditions;\n\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.torpedoquery.core.QueryBuilder;\nimport org.torpedoquery.jpa.ComparableFunction;\nimport org.torpedoquery.jpa.Function;\nimport org.torpedoquery.jpa.OnGoingCollectionCondition;\nimport org.torpedoquery.jpa.OnGoingComparableCondition;\nimport org.torpedoquery.jpa.OnGoingLikeCondition;\nimport org.torpedoquery.jpa.OnGoingLogicalCondition;\nimport org.torpedoquery.jpa.OnGoingStringCondition;\nimport org.torpedoquery.jpa.Query;\nimport org.torpedoquery.jpa.internal.Condition;\nimport org.torpedoquery.jpa.internal.Parameter;\nimport org.torpedoquery.jpa.internal.Selector;\nimport org.torpedoquery.jpa.internal.conditions.LikeCondition.Type;\nimport org.torpedoquery.jpa.internal.selectors.NotSelector;\nimport org.torpedoquery.jpa.internal.selectors.SizeSelector;\npublic class ConditionBuilder<T> implements OnGoingComparableCondition<T>, OnGoingStringCondition<T>,\n\t\tOnGoingLikeCondition, OnGoingCollectionCondition<T>, Condition, Serializable {\n\tprivate Selector selector;\n\tprivate final LogicalCondition logicalCondition;\n\tprivate Condition condition;\n\tprivate boolean notLike;\n\n\t/**\n\t * <p>\n\t * Constructor for ConditionBuilder.\n\t * </p>\n\t *\n\t * @param builder\n\t * a {@link org.torpedoquery.core.QueryBuilder} object.\n\t * @param selector\n\t * a {@link org.torpedoquery.jpa.internal.Selector} object.\n\t */\n\tpublic ConditionBuilder(QueryBuilder<T> builder, Selector<?> selector) {\n\t\tthis.logicalCondition = new LogicalCondition(builder, this);\n\t\tthis.selector = selector;\n\t}\n\n\t/**\n\t * <p>\n\t * Constructor for ConditionBuilder.\n\t * </p>\n\t *\n\t * @param logicalCondition\n\t * a\n\t * {@link org.torpedoquery.jpa.internal.conditions.LogicalCondition}\n\t * object.\n\t * @param selector\n\t * a {@link org.torpedoquery.jpa.internal.Selector} object.\n\t */\n\tpublic ConditionBuilder(LogicalCondition logicalCondition, Selector<?> selector) {\n\t\tthis.logicalCondition = logicalCondition;\n\t\tthis.selector = selector;\n\t}\n\n\t/**\n\t * <p>\n\t * Getter for the field <code>logicalCondition</code>.\n\t * </p>\n\t *\n\t * @return a\n\t * {@link org.torpedoquery.jpa.internal.conditions.LogicalCondition}\n\t * object.\n\t */\n\tpublic LogicalCondition getLogicalCondition() {\n\t\treturn logicalCondition;\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition eq(T value) {\n\t\tCondition conditionLocal = new EqualCondition<T>(selector, selector.generateParameter(value));\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition eq(Class<? extends T> value) {\n\t\tCondition conditionLocal = new EqualPolymorphicCondition<T>(selector, value);\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition neq(T value) {\n\t\tCondition conditionLocal = new NotEqualCondition<T>(selector, selector.generateParameter(value));\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition neq(Class<? extends T> value) {\n\t\tCondition conditionLocal = new NotEqualPolymorphicCondition<T>(selector, value);\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition lt(T value) {\n\t\tCondition conditionLocal = new LtCondition<T>(selector, selector.generateParameter(value));\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition lte(T value) {\n\t\tCondition conditionLocal = new LteCondition<T>(selector, selector.generateParameter(value));\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition gt(T value) {\n\t\tCondition conditionLocal = new GtCondition<T>(selector, selector.generateParameter(value));\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition gte(T value) {\n\t\tCondition conditionLocal = new GteCondition<T>(selector, selector.generateParameter(value));\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition isNull() {\n\t\tCondition conditionLocal = new IsNullCondition(selector);\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition isNotNull() {\n\t\tCondition conditionLocal = new IsNotNullCondition(selector);\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition in(T... values) {\n\t\treturn in(Arrays.asList(values));\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition in(Collection<T> values) {\n\t\tCondition conditionLocal = new InCondition<T>(selector, selector.generateParameter(values));\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition in(Query<T> query) {\n\t\tCondition conditionLocal = new InSubQueryCondition<T>(selector, (QueryBuilder) query);\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition notIn(T... values) {\n\t\treturn notIn(Arrays.asList(values));\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition notIn(Collection<T> values) {\n\t\treturn getOnGoingLogicalCondition(new NotInCondition<T>(selector, selector.generateParameter(values)));\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition notIn(Query<T> subQuery) {\n\t\treturn getOnGoingLogicalCondition(new NotInSubQueryCondition<T>(selector, (QueryBuilder) subQuery));\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic String createQueryFragment(AtomicInteger incrementor) {\n\t\tif (condition != null) {\n\t\t\treturn condition.createQueryFragment(incrementor);\n\t\t} else {\n\t\t\treturn \"\";\n\t\t}\n\n\t}\n\n\tprivate OnGoingLogicalCondition getOnGoingLogicalCondition(Condition condition) {\n\t\tthis.condition = condition;\n\t\treturn logicalCondition;\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic List<Parameter> getParameters() {\n\t\tif (condition != null) {\n\t\t\treturn condition.getParameters();\n\t\t} else {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLikeCondition like() {\n\t\treturn this;\n\t}\n\t\n\t@Override\n\tpublic OnGoingLogicalCondition like(String likeValue) {\n\t\treturn getOnGoingLogicalCondition(createLike(LikeCondition.Type.UNKNOW, likeValue));\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLikeCondition notLike() {\n\t\tnotLike = true;\n\t\treturn this;\n\t}\n\t\n\t@Override\n\tpublic OnGoingLogicalCondition notLike(String notLikeValue) {\n\t\tnotLike = true;\n\t\treturn getOnGoingLogicalCondition(createLike(LikeCondition.Type.UNKNOW, notLikeValue));\n\t}\n\n\tprivate LikeCondition createLike(Type type, String toMatch) {\n\t\tif (notLike) {\n\t\t\treturn new NotLikeCondition(type, selector, toMatch);\n\t\t} else {\n\t\t\treturn new LikeCondition(type, selector, toMatch);\n\t\t}\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition any(String toMatch) {\n\t\treturn getOnGoingLogicalCondition(createLike(LikeCondition.Type.ANY, toMatch));\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition startsWith(String toMatch) {\n\t\treturn getOnGoingLogicalCondition(createLike(LikeCondition.Type.STARTSWITH, toMatch));\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition endsWith(String toMatch) {\n\t\treturn getOnGoingLogicalCondition(createLike(LikeCondition.Type.ENDSWITH, toMatch));\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition isEmpty() {\n\t\treturn getOnGoingLogicalCondition(new IsEmptyCondition(selector));\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition isNotEmpty() {\n\t\treturn getOnGoingLogicalCondition(new IsNotEmptyCondition(selector));\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition memberOf(T member) {\n\t\treturn getOnGoingLogicalCondition(new MemberOfCondition<>(selector, selector.generateParameter(member)));\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingComparableCondition<Integer> size() {\n\t\tselector = new SizeSelector(selector);\n\t\treturn (OnGoingComparableCondition<Integer>) this;\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition lt(ComparableFunction<T> value) {\n\t\tCondition conditionLocal = new LtCondition<T>(selector, selector.generateParameter(value));\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition lte(ComparableFunction<T> value) {\n\t\tCondition conditionLocal = new LteCondition<T>(selector, selector.generateParameter(value));\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition gt(ComparableFunction<T> value) {\n\t\tCondition conditionLocal = new GtCondition<T>(selector, selector.generateParameter(value));\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition gte(ComparableFunction<T> value) {\n\t\tCondition conditionLocal = new GteCondition<T>(selector, selector.generateParameter(value));\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition eq(Function<T> value) {\n\t\tCondition conditionLocal = new EqualCondition<T>(selector, selector.generateParameter(value));\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition neq(Function<T> value) {\n\t\tCondition conditionLocal = new NotEqualCondition<T>(selector, selector.generateParameter(value));\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition between(T from, T to) {\n\t\tCondition conditionLocal = new BetweenCondition<T>(selector,\n\t\t\t\tArrays.asList(selector.generateParameter(from), selector.generateParameter(to)));\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition notBetween(T from, T to) {\n\t\tCondition conditionLocal = new BetweenCondition<T>(new NotSelector(selector),\n\t\t\t\tArrays.asList(selector.generateParameter(from), selector.generateParameter(to)));\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition between(ComparableFunction<T> from, ComparableFunction<T> to) {\n\t\tCondition conditionLocal = new BetweenCondition<T>(selector,\n\t\t\t\tArrays.asList(selector.generateParameter(from), selector.generateParameter(to)));\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic OnGoingLogicalCondition notBetween(ComparableFunction<T> from, ComparableFunction<T> to) {\n\t\tCondition conditionLocal = new BetweenCondition<T>(new NotSelector(selector),\n\t\t\t\tArrays.asList(selector.generateParameter(from), selector.generateParameter(to)));\n\t\treturn getOnGoingLogicalCondition(conditionLocal);\n\t}\n\t\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic <T> QueryBuilder<T> getBuilder() {\n\t\treturn logicalCondition.getBuilder();\n\t}\n\t\n}\n\n## 内容2\n### 文件路径:\nsrc/main/java/org/torpedoquery/jpa/internal/conditions/LikeCondition.java\n### 代码内容:\npackage org.torpedoquery.jpa.internal.conditions;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.torpedoquery.jpa.internal.Condition;\nimport org.torpedoquery.jpa.internal.Parameter;\nimport org.torpedoquery.jpa.internal.Selector;\n\npublic class LikeCondition implements Condition {\n\n\tpublic static enum Type {\n\t\tANY {\n\t\t\t@Override\n\t\t\tpublic String wrap(String toMatch) {\n\t\t\t\treturn \"%\" + toMatch + \"%\";\n\t\t\t}\n\t\t},\n\t\tSTARTSWITH {\n\t\t\t@Override\n\t\t\tpublic String wrap(String toMatch) {\n\t\t\t\treturn toMatch + \"%\";\n\t\t\t}\n\t\t},\n\t\tENDSWITH {\n\t\t\t@Override\n\t\t\tpublic String wrap(String toMatch) {\n\t\t\t\treturn \"%\" + toMatch;\n\t\t\t}\n\t\t},\n\t\tUNKNOW {\n\t\t\t@Override\n\t\t\tpublic String wrap(String toMatch) {\n\t\t\t\treturn toMatch;\n\t\t\t}\n\t\t};\n\n\t\tpublic abstract String wrap(String toMatch);\n\t}\n\n\tprivate final String toMatch;\n\tprivate final Type type;\n\tprivate final Selector selector;\n\n\t/**\n\t * <p>\n\t * Constructor for LikeCondition.\n\t * </p>\n\t *\n\t * @param type\n\t * a\n\t * {@link org.torpedoquery.jpa.internal.conditions.LikeCondition.Type}\n\t * object.\n\t * @param selector\n\t * a {@link org.torpedoquery.jpa.internal.Selector} object.\n\t * @param toMatch\n\t * a {@link java.lang.String} object.\n\t */\n\tpublic LikeCondition(Type type, Selector selector, String toMatch) {\n\t\tthis.type = type;\n\t\tthis.selector = selector;\n\t\tthis.toMatch = toMatch;\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic String createQueryFragment(AtomicInteger incrementor) {\n\t\treturn selector.createQueryFragment(incrementor) + \" \" + getLike() + \" '\" + type.wrap(toMatch) + \"' \";\n\t}\n\n\t/**\n\t * <p>\n\t * getLike.\n\t * </p>\n\t *\n\t * @return a {@link java.lang.String} object.\n\t */\n\tprotected String getLike() {\n\t\treturn \"like\";\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tpublic List<Parameter> getParameters() {\n\t\treturn Collections.emptyList();\n\t}\n\n}\n\n## 内容3\n### 文件路径:\nsrc/main/java/org/torpedoquery/jpa/internal/conditions/NotLikeCondition.java\n### 代码内容:\npackage org.torpedoquery.jpa.internal.conditions;\n\nimport org.torpedoquery.jpa.internal.Selector;\npublic class NotLikeCondition extends LikeCondition {\n\n\t/**\n\t * <p>Constructor for NotLikeCondition.</p>\n\t *\n\t * @param type a Type object.\n\t * @param selector a {@link org.torpedoquery.jpa.internal.Selector} object.\n\t * @param toMatch a {@link java.lang.String} object.\n\t */\n\tpublic NotLikeCondition(Type type, Selector selector, String toMatch) {\n\t\tsuper(type, selector, toMatch);\n\t}\n\n\t/** {@inheritDoc} */\n\t@Override\n\tprotected String getLike() {\n\t\treturn \"not \" + super.getLike();\n\t}\n\n}\n\n", "input": "", "output": "【漏洞类型】SQL注入漏洞\n【严重性】High\n【漏洞摘要】原始代码中存在SQL注入漏洞,具体表现为在`LikeCondition`类的`createQueryFragment`方法中,直接在SQL查询字符串中拼接了未经过处理的`toMatch`字符串。这使得攻击者可以通过构造特定的输入,插入恶意的SQL代码,从而操纵查询结果或执行其他恶意操作。\n【关键代码模式】\n- `createQueryFragment`方法中的字符串拼接操作。\n- `LikeCondition`类的`type.wrap(toMatch)`方法。\n- `NotLikeCondition`类的构造函数和`getLike`方法。\n【修复方法说明】修复方法通过将`toMatch`字符串包装在`Parameter`对象中,并在`LikeCondition`类中使用`super.createQueryFragment(incrementor)`来生成查询片段,从而避免了直接在SQL查询字符串中拼接未经过处理的输入。这遵循了使用参数化查询的最佳实践,防止了SQL注入漏洞的产生。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/main/java/org/apache/sling/engine/impl/Config.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\npackage org.apache.sling.engine.impl;\n\nimport org.osgi.service.metatype.annotations.AttributeDefinition;\nimport org.osgi.service.metatype.annotations.ObjectClassDefinition;\n\n/**\n * The OSGi configuration for the main servlet. This configuration is actually\n * used by various components throughout the bundle.\n */\n@ObjectClassDefinition(name =\"Apache Sling Main Servlet\",\n description=\"Main processor of the Sling framework controlling all \" +\n \"aspects of processing requests inside of Sling, namely authentication, \" +\n \"resource resolution, servlet/script resolution and execution of servlets \" +\n \"and scripts.\")\npublic @interface Config {\n\n String PID = \"org.apache.sling.engine.impl.SlingMainServlet\";\n\n /**\n * The default value for the number of recursive inclusions for a single\n * instance of this class (value is 50).\n */\n int DEFAULT_MAX_INCLUSION_COUNTER = 50;\n\n /**\n * The default value for the number of calls to the\n * {@link #service(SlingHttpServletRequest, SlingHttpServletResponse)}\n * method for a single instance of this class (value is 1000).\n */\n int DEFAULT_MAX_CALL_COUNTER = 1000;\n\n @AttributeDefinition(name = \"Number of Calls per Request\",\n description = \"Defines the maximum number of Servlet and Script \" +\n \"calls while processing a single client request. This number should be high \" +\n \"enough to not limit request processing artificially. On the other hand it \" +\n \"should not be too high to allow the mechanism to limit the resources required \" +\n \"to process a request in case of errors. The default value is 1000.\")\n int sling_max_calls() default DEFAULT_MAX_CALL_COUNTER;\n\n @AttributeDefinition(name = \"Recursion Depth\",\n description = \"The maximum number of recursive Servlet and \" +\n \"Script calls while processing a single client request. This number should not \" +\n \"be too high, otherwise StackOverflowErrors may occurr in case of erroneous \" +\n \"scripts and servlets. The default value is 50. \")\n int sling_max_inclusions() default DEFAULT_MAX_INCLUSION_COUNTER;\n\n @AttributeDefinition(name = \"Allow the HTTP TRACE method\",\n description = \"If set to true, the HTTP TRACE method will be \" +\n \"enabled. By default the HTTP TRACE methods is disabled as it can be used in \" +\n \"Cross Site Scripting attacks on HTTP servers.\")\n boolean sling_trace_allow() default false;\n\n @AttributeDefinition(name = \"Number of Requests to Record\",\n description = \"Defines the number of requests that \" +\n \"internally recorded for display on the \\\"Recent Requests\\\" Web Console page. If \" +\n \"this value is less than or equal to zero, no requests are internally kept. The \" +\n \"default value is 20. \")\n int sling_max_record_requests() default 20;\n\n @AttributeDefinition(name = \"Recorded Request Path Patterns\",\n description = \"One or more regular expressions which \" +\n \"limit the requests which are stored by the \\\"Recent Requests\\\" Web Console page.\")\n String[] sling_store_pattern_requests();\n\n @AttributeDefinition(name = \"Server Info\",\n description = \"The server info returned by Sling. If this field is left empty, Sling generates a default into.\")\n String sling_serverinfo();\n\n @AttributeDefinition(name = \"Additional response headers\",\n description = \"Provides mappings for additional response headers \"\n + \"Each entry is of the form 'bundleId [ \\\":\\\" responseHeaderName ] \\\"=\\\" responseHeaderValue'\")\n String[] sling_additional_response_headers() default {\"X-Content-Type-Options=nosniff\", \"X-Frame-Options=SAMEORIGIN\"};\n\n @AttributeDefinition(name = \"Servlet Name\", description = \"Optional name for the Sling main servlet registered by this component\")\n String servlet_name();\n}\n## 内容2\n### 文件路径:\nsrc/main/java/org/apache/sling/engine/impl/SlingHttpServletRequestImpl.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\npackage org.apache.sling.engine.impl;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.io.UnsupportedEncodingException;\nimport java.security.Principal;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.ResourceBundle;\n\nimport javax.servlet.RequestDispatcher;\nimport javax.servlet.ServletInputStream;\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletRequestWrapper;\nimport javax.servlet.http.Part;\n\nimport org.apache.sling.api.SlingHttpServletRequest;\nimport org.apache.sling.api.request.RequestDispatcherOptions;\nimport org.apache.sling.api.request.RequestParameter;\nimport org.apache.sling.api.request.RequestParameterMap;\nimport org.apache.sling.api.request.RequestPathInfo;\nimport org.apache.sling.api.request.RequestProgressTracker;\nimport org.apache.sling.api.resource.Resource;\nimport org.apache.sling.api.resource.ResourceResolver;\nimport org.apache.sling.engine.impl.helper.NullResourceBundle;\nimport org.apache.sling.engine.impl.parameters.ParameterSupport;\nimport org.apache.sling.engine.impl.request.ContentData;\nimport org.apache.sling.engine.impl.request.RequestData;\nimport org.apache.sling.engine.impl.request.SlingRequestDispatcher;\nimport org.osgi.service.http.HttpContext;\nimport org.osgi.service.useradmin.Authorization;\n\npublic class SlingHttpServletRequestImpl extends HttpServletRequestWrapper implements\n SlingHttpServletRequest {\n\n private final RequestData requestData;\n private final String pathInfo;\n private String responseContentType;\n\n public SlingHttpServletRequestImpl(RequestData requestData,\n HttpServletRequest servletRequest) {\n super(servletRequest);\n this.requestData = requestData;\n\n // prepare the pathInfo property\n String pathInfo = servletRequest.getServletPath();\n if (servletRequest.getPathInfo() != null) {\n pathInfo = pathInfo.concat(servletRequest.getPathInfo());\n }\n this.pathInfo = pathInfo;\n }\n\n /**\n * @return the requestData\n */\n public final RequestData getRequestData() {\n return this.requestData;\n }\n\n //---------- Adaptable interface\n\n @Override\n public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {\n return getRequestData().getSlingRequestProcessor().adaptTo(this, type);\n }\n\n //---------- SlingHttpServletRequest interface\n\n ParameterSupport getParameterSupport() {\n return this.getRequestData().getParameterSupport();\n }\n\n @Override\n public Resource getResource() {\n final ContentData cd = getRequestData().getContentData();\n return (cd == null) ? null : cd.getResource();\n }\n\n @Override\n public ResourceResolver getResourceResolver() {\n return getRequestData().getResourceResolver();\n }\n\n @Override\n public RequestProgressTracker getRequestProgressTracker() {\n return getRequestData().getRequestProgressTracker();\n }\n\n /**\n * Returns <code>null</code> if <code>resource</code> is <code>null</code>.\n */\n @Override\n public RequestDispatcher getRequestDispatcher(Resource resource) {\n return getRequestDispatcher(resource, null);\n }\n\n /**\n * Returns <code>null</code> if <code>resource</code> is <code>null</code>.\n */\n @Override\n public RequestDispatcher getRequestDispatcher(Resource resource,\n RequestDispatcherOptions options) {\n return (resource != null) ? new SlingRequestDispatcher(resource, options) : null;\n }\n\n /**\n * Returns <code>null</code> if <code>path</code> is <code>null</code>.\n */\n @Override\n public RequestDispatcher getRequestDispatcher(String path) {\n return getRequestDispatcher(path, null);\n }\n\n /**\n * Returns <code>null</code> if <code>path</code> is <code>null</code>.\n */\n @Override\n public RequestDispatcher getRequestDispatcher(String path,\n RequestDispatcherOptions options) {\n return (path != null) ? new SlingRequestDispatcher(path, options) : null;\n }\n\n /**\n * @see javax.servlet.ServletRequestWrapper#getParameter(java.lang.String)\n */\n @Override\n public String getParameter(String name) {\n return this.getParameterSupport().getParameter(name);\n }\n\n /**\n * @see javax.servlet.ServletRequestWrapper#getParameterMap()\n */\n @Override\n public Map<String, String[]> getParameterMap() {\n return this.getParameterSupport().getParameterMap();\n }\n\n /**\n * @see javax.servlet.ServletRequestWrapper#getParameterNames()\n */\n @Override\n public Enumeration<String> getParameterNames() {\n return this.getParameterSupport().getParameterNames();\n }\n\n /**\n * @see javax.servlet.ServletRequestWrapper#getParameterValues(java.lang.String)\n */\n @Override\n public String[] getParameterValues(String name) {\n return this.getParameterSupport().getParameterValues(name);\n }\n\n /**\n * @see org.apache.sling.api.SlingHttpServletRequest#getRequestParameter(java.lang.String)\n */\n @Override\n public RequestParameter getRequestParameter(String name) {\n return this.getParameterSupport().getRequestParameter(name);\n }\n\n /**\n * @see org.apache.sling.api.SlingHttpServletRequest#getRequestParameters(java.lang.String)\n */\n @Override\n public RequestParameter[] getRequestParameters(String name) {\n return this.getParameterSupport().getRequestParameters(name);\n }\n\n /**\n * @see org.apache.sling.api.SlingHttpServletRequest#getRequestParameterMap()\n */\n @Override\n public RequestParameterMap getRequestParameterMap() {\n return this.getParameterSupport().getRequestParameterMap();\n }\n\n /**\n * @see org.apache.sling.api.SlingHttpServletRequest#getRequestParameterList()\n */\n @Override\n public List<RequestParameter> getRequestParameterList() {\n return this.getParameterSupport().getRequestParameterList();\n }\n\n /**\n * @see org.apache.sling.api.SlingHttpServletRequest#getCookie(java.lang.String)\n */\n @Override\n public Cookie getCookie(String name) {\n Cookie[] cookies = getCookies();\n\n if (cookies != null) {\n for (int i = 0; i < cookies.length; i++) {\n if (cookies[i].getName().equals(name)) {\n return cookies[i];\n }\n }\n }\n\n return null;\n }\n\n /**\n * @see org.apache.sling.api.SlingHttpServletRequest#getRequestPathInfo()\n */\n @Override\n public RequestPathInfo getRequestPathInfo() {\n final ContentData cd = getRequestData().getContentData();\n return (cd == null) ? null : cd.getRequestPathInfo();\n }\n\n /**\n * @see org.apache.sling.api.SlingHttpServletRequest#getResourceBundle(java.util.Locale)\n */\n @Override\n public ResourceBundle getResourceBundle(Locale locale) {\n return getResourceBundle(null, locale);\n }\n\n /**\n * @see org.apache.sling.api.SlingHttpServletRequest#getResourceBundle(String, Locale)\n */\n @Override\n public ResourceBundle getResourceBundle(String baseName, Locale locale) {\n if (locale == null) {\n locale = getLocale();\n }\n\n return new NullResourceBundle(locale);\n }\n\n /**\n * @see org.apache.sling.api.SlingHttpServletRequest#getResponseContentType()\n */\n @Override\n public String getResponseContentType() {\n if (responseContentType == null) {\n final String ext = getRequestPathInfo().getExtension();\n if ( ext != null ) {\n responseContentType = this.requestData.getSlingRequestProcessor().getMimeType(\"dummy.\".concat(ext));\n }\n }\n return responseContentType;\n }\n\n /**\n * @see org.apache.sling.api.SlingHttpServletRequest#getResponseContentTypes()\n */\n @Override\n public Enumeration<String> getResponseContentTypes() {\n final String singleType = getResponseContentType();\n if (singleType != null) {\n return Collections.enumeration(Collections.singleton(singleType));\n }\n\n return Collections.emptyEnumeration();\n }\n\n /**\n * @see javax.servlet.ServletRequestWrapper#getInputStream()\n */\n @Override\n public ServletInputStream getInputStream() throws IOException {\n return this.getRequestData().getInputStream();\n }\n\n /**\n * @see javax.servlet.ServletRequestWrapper#getReader()\n */\n @Override\n public BufferedReader getReader() throws UnsupportedEncodingException,\n IOException {\n return this.getRequestData().getReader();\n }\n\n /**\n * @see javax.servlet.http.HttpServletRequestWrapper#getUserPrincipal()\n */\n @Override\n public Principal getUserPrincipal() {\n Principal principal = getResourceResolver().adaptTo(Principal.class);\n if (principal != null) {\n return principal;\n }\n //fallback to the userid\n String remoteUser = getRemoteUser();\n return (remoteUser != null) ? new UserPrincipal(remoteUser) : null;\n }\n\n /**\n * @see javax.servlet.http.HttpServletRequestWrapper#isUserInRole(String)\n */\n @Override\n public boolean isUserInRole(String role) {\n Object authorization = getAttribute(HttpContext.AUTHORIZATION);\n return (authorization instanceof Authorization)\n ? ((Authorization) authorization).hasRole(role)\n : false;\n }\n\n /**\n * Always returns the empty string since the actual servlet registered with\n * the servlet container (the HttpService actually) is registered as if\n * the servlet path is \"/*\".\n */\n @Override\n public String getServletPath() {\n return \"\";\n }\n\n /**\n * Returns the part of the request URL without the leading servlet context\n * path.\n */\n @Override\n public String getPathInfo() {\n return pathInfo;\n }\n\n public Part getPart(String name) {\n return (Part) this.getParameterSupport().getPart(name);\n }\n\n @SuppressWarnings(\"unchecked\")\n public Collection<Part> getParts() {\n return (Collection<Part>) this.getParameterSupport().getParts();\n }\n\n /**\n * A <code>UserPrincipal</code> ...\n */\n private static class UserPrincipal implements Principal, Serializable {\n\n private final String name;\n\n /**\n * Creates a <code>UserPrincipal</code> with the given name.\n *\n * @param name the name of this principal\n * @throws IllegalArgumentException if <code>name</code> is <code>null</code>.\n */\n public UserPrincipal(String name) throws IllegalArgumentException {\n if (name == null) {\n throw new IllegalArgumentException(\"name can not be null\");\n }\n this.name = name;\n }\n\n @Override\n public String toString() {\n return (\"UserPrincipal: \" + name);\n }\n\n @Override\n public boolean equals(Object obj) {\n if (this == obj) {\n return true;\n }\n if (obj instanceof UserPrincipal) {\n UserPrincipal other = (UserPrincipal) obj;\n return name.equals(other.name);\n }\n return false;\n }\n\n @Override\n public int hashCode() {\n return name.hashCode();\n }\n\n //------------------------------------------------------------< Principal >\n /**\n * {@inheritDoc}\n */\n @Override\n public String getName() {\n return name;\n }\n }\n\n}\n\n## 内容3\n### 文件路径:\nsrc/main/java/org/apache/sling/engine/impl/SlingRequestProcessorImpl.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\npackage org.apache.sling.engine.impl;\n\nimport static org.apache.sling.api.SlingConstants.ERROR_SERVLET_NAME;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.io.OutputStreamWriter;\nimport java.io.PrintWriter;\nimport java.io.Writer;\nimport java.security.AccessControlException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.Servlet;\nimport javax.servlet.ServletContext;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.sling.api.SlingConstants;\nimport org.apache.sling.api.SlingException;\nimport org.apache.sling.api.SlingHttpServletRequest;\nimport org.apache.sling.api.SlingHttpServletResponse;\nimport org.apache.sling.api.SlingServletException;\nimport org.apache.sling.api.adapter.AdapterManager;\nimport org.apache.sling.api.request.RequestPathInfo;\nimport org.apache.sling.api.resource.Resource;\nimport org.apache.sling.api.resource.ResourceNotFoundException;\nimport org.apache.sling.api.resource.ResourceResolver;\nimport org.apache.sling.api.servlets.ServletResolver;\nimport org.apache.sling.api.wrappers.SlingHttpServletResponseWrapper;\nimport org.apache.sling.commons.mime.MimeTypeService;\nimport org.apache.sling.engine.SlingRequestProcessor;\nimport org.apache.sling.engine.impl.debug.RequestInfoProviderImpl;\nimport org.apache.sling.engine.impl.filter.ErrorFilterChainStatus;\nimport org.apache.sling.engine.impl.filter.ErrorFilterChainThrowable;\nimport org.apache.sling.engine.impl.filter.FilterHandle;\nimport org.apache.sling.engine.impl.filter.RequestSlingFilterChain;\nimport org.apache.sling.engine.impl.filter.ServletFilterManager;\nimport org.apache.sling.engine.impl.filter.ServletFilterManager.FilterChainType;\nimport org.apache.sling.engine.impl.helper.SlingServletContext;\nimport org.apache.sling.engine.impl.filter.SlingComponentFilterChain;\nimport org.apache.sling.engine.impl.parameters.ParameterSupport;\nimport org.apache.sling.engine.impl.request.ContentData;\nimport org.apache.sling.engine.impl.request.RequestData;\nimport org.apache.sling.engine.servlets.ErrorHandler;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Modified;\nimport org.osgi.service.component.annotations.Reference;\nimport org.osgi.service.component.annotations.ReferenceCardinality;\nimport org.osgi.service.component.annotations.ReferencePolicy;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n@Component(service = {SlingRequestProcessor.class, SlingRequestProcessorImpl.class},\n configurationPid = Config.PID)\npublic class SlingRequestProcessorImpl implements SlingRequestProcessor {\n\n /** default log */\n private final Logger log = LoggerFactory.getLogger(SlingRequestProcessorImpl.class);\n\n @Reference\n private ServletResolver servletResolver;\n\n @Reference\n private ServletFilterManager filterManager;\n\n @Reference\n private RequestProcessorMBeanImpl mbean;\n\n @Reference(target = SlingServletContext.TARGET)\n private ServletContext slingServletContext;\n\n @Reference(cardinality=ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC)\n private volatile AdapterManager adapterManager;\n\n /**\n * Resolves MIME types\n *\n * @see #getMimeType(String)\n */\n @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL)\n private volatile MimeTypeService mimeTypeService;\n\n private final DefaultErrorHandler errorHandler = new DefaultErrorHandler();\n\n private volatile int maxCallCounter = Config.DEFAULT_MAX_CALL_COUNTER;\n\n private volatile int maxInclusionCounter = Config.DEFAULT_MAX_INCLUSION_COUNTER;\n\n private volatile List<StaticResponseHeader> additionalResponseHeaders = Collections.emptyList();\n\n @Activate\n public void activate(final Config config) {\n this.errorHandler.setServerInfo(this.slingServletContext.getServerInfo());\n this.modified(config);\n }\n\n @Modified\n public void modified(final Config config) {\n final List<StaticResponseHeader> mappings = new ArrayList<>();\n final String[] props = config.sling_additional_response_headers();\n if ( props != null ) {\n for (final String prop : props) {\n if (prop != null && prop.trim().length() > 0 ) {\n try {\n final StaticResponseHeader mapping = new StaticResponseHeader(prop.trim());\n mappings.add(mapping);\n } catch (final IllegalArgumentException iae) {\n log.info(\"configure: Ignoring '{}': {}\", prop, iae.getMessage());\n }\n }\n }\n }\n this.additionalResponseHeaders = mappings;\n\n // configure the request limits\n this.maxInclusionCounter = config.sling_max_inclusions();\n this.maxCallCounter = config.sling_max_calls();\n }\n\n @Reference(name = \"ErrorHandler\", cardinality=ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC, unbind = \"unsetErrorHandler\")\n void setErrorHandler(final ErrorHandler errorHandler) {\n this.errorHandler.setDelegate(errorHandler);\n }\n\n void unsetErrorHandler(final ErrorHandler errorHandler) {\n this.errorHandler.setDelegate(null);\n }\n \n public int getMaxCallCounter() {\n return maxCallCounter;\n }\n\n public int getMaxIncludeCounter() {\n return maxInclusionCounter;\n }\n\n public List<StaticResponseHeader> getAdditionalResponseHeaders() {\n return this.additionalResponseHeaders;\n }\n\n public <Type> Type adaptTo(Object object, Class<Type> type) {\n final AdapterManager adapterManager = this.adapterManager;\n if (adapterManager != null) {\n return adapterManager.getAdapter(object, type);\n }\n\n // no adapter manager, nothing to adapt to\n return null;\n }\n\n /**\n * This method is directly called by the Sling main servlet.\n */\n public void doProcessRequest(final HttpServletRequest servletRequest,\n final HttpServletResponse servletResponse,\n final ResourceResolver resourceResolver) throws IOException {\n final ServletResolver sr = this.servletResolver;\n\n // check that we have all required services\n if (resourceResolver == null || sr == null) {\n // Dependencies are missing\n // In this case we must not use the Sling error handling infrastructure but\n // just return a 503 status response handled by the servlet container environment\n\n final int status = HttpServletResponse.SC_SERVICE_UNAVAILABLE;\n String errorMessage = \"Required service missing (\";\n if ( resourceResolver == null ) {\n errorMessage = errorMessage.concat(\"ResourceResolver\");\n if ( sr == null ) {\n errorMessage = errorMessage.concat(\", \");\n }\n }\n if ( sr == null ) {\n errorMessage = errorMessage.concat(\"ServletResolver\");\n }\n log.debug(\"{}), cannot service requests, sending status {}\", errorMessage, status);\n servletResponse.sendError(status, errorMessage);\n return;\n }\n\n // setting the Sling request and response\n final RequestData requestData = new RequestData(this, servletRequest, servletResponse);\n final SlingHttpServletRequest request = requestData.getSlingRequest();\n final SlingHttpServletResponse response = requestData.getSlingResponse();\n\n final boolean isInclude = request.getAttribute(SlingConstants.ATTR_INCLUDE_CONTEXT_PATH) != null;\n try {\n // initialize the request data - resolve resource and servlet\n final Resource resource = requestData.initResource(resourceResolver);\n requestData.initServlet(resource, sr);\n\n final FilterHandle[] filters = filterManager.getFilters(FilterChainType.REQUEST);\n final FilterChain processor = new RequestSlingFilterChain(this, filters);\n\n request.getRequestProgressTracker().log(\"Applying \".concat(FilterChainType.REQUEST.name()).concat(\"filters\"));\n\n processor.doFilter(request, response);\n\n } catch ( final SlingHttpServletResponseImpl.WriterAlreadyClosedException wace ) {\n // this is an exception case, log as error\n log.error(\"Writer has already been closed.\", wace);\n\n } catch (final ResourceNotFoundException rnfe) {\n // send this exception as a 404 status\n log.debug(\"service: Resource {} not found\", rnfe.getResource());\n\n if ( isInclude ) {\n throw rnfe;\n }\n handleError(HttpServletResponse.SC_NOT_FOUND, rnfe.getMessage(), request, response);\n\n } catch (final SlingException se) {\n\n if ( isInclude ) {\n throw se;\n }\n // if we have request data and a non-null active servlet name\n // we assume, that this is the name of the causing servlet\n if (requestData.getActiveServletName() != null) {\n request.setAttribute(ERROR_SERVLET_NAME,\n requestData.getActiveServletName());\n }\n\n // send this exception as is (albeit unwrapping and wrapped\n // exception.\n Throwable t = se;\n while ( t instanceof SlingException && t.getCause() != null ) {\n t = t.getCause();\n }\n log.error(\"service: Uncaught SlingException \", t);\n handleError(t, request, response);\n\n } catch (final AccessControlException ace) {\n\n if ( isInclude ) {\n throw ace;\n }\n // SLING-319 if anything goes wrong, send 403/FORBIDDEN\n log.debug(\n \"service: Authenticated user {} does not have enough rights to executed requested action\",\n request.getRemoteUser());\n handleError(HttpServletResponse.SC_FORBIDDEN, null, request,\n response);\n\n } catch (final IOException ioe) {\n\n // forward IOException up the call chain to properly handle it\n throw ioe;\n\n } catch (final Throwable t) {\n\n if ( isInclude ) {\n if ( t instanceof RuntimeException ) {\n throw (RuntimeException)t;\n }\n if ( t instanceof Error ) {\n throw (Error)t;\n }\n throw new SlingException(t.getMessage(), t);\n }\n\n // if we have request data and a non-null active servlet name\n // we assume, that this is the name of the causing servlet\n if (requestData.getActiveServletName() != null) {\n request.setAttribute(ERROR_SERVLET_NAME,\n requestData.getActiveServletName());\n }\n\n log.error(\"service: Uncaught Throwable\", t);\n handleError(t, request, response);\n\n } finally {\n // record the request for the web console and info provider\n RequestInfoProviderImpl.recordRequest(request);\n\n if (mbean != null) {\n mbean.addRequestData(requestData);\n }\n }\n }\n\n // ---------- SlingRequestProcessor interface\n\n /**\n * @see org.apache.sling.engine.SlingRequestProcessor#processRequest(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.apache.sling.api.resource.ResourceResolver)\n */\n @Override\n public void processRequest(final HttpServletRequest servletRequest,\n final HttpServletResponse servletResponse,\n final ResourceResolver resourceResolver) throws IOException {\n // set the marker for the parameter support\n final Object oldValue = servletRequest.getAttribute(ParameterSupport.MARKER_IS_SERVICE_PROCESSING);\n servletRequest.setAttribute(ParameterSupport.MARKER_IS_SERVICE_PROCESSING, Boolean.TRUE);\n try {\n this.doProcessRequest(servletRequest, servletResponse, resourceResolver);\n } finally {\n // restore the old value\n if ( oldValue != null ) {\n servletRequest.setAttribute(ParameterSupport.MARKER_IS_SERVICE_PROCESSING, oldValue);\n } else {\n servletRequest.removeAttribute(ParameterSupport.MARKER_IS_SERVICE_PROCESSING);\n }\n }\n }\n\n /**\n * Renders the component defined by the RequestData's current ComponentData\n * instance after calling all filters of the given\n * {@link org.apache.sling.engine.impl.filter.ServletFilterManager.FilterChainType\n * filterChainType}.\n *\n * @param request\n * @param response\n * @param filterChainType\n * @throws IOException\n * @throws ServletException\n */\n\n public void processComponent(SlingHttpServletRequest request,\n SlingHttpServletResponse response,\n final FilterChainType filterChainType) throws IOException,\n ServletException {\n\n final FilterHandle filters[] = filterManager.getFilters(filterChainType);\n\n FilterChain processor = new SlingComponentFilterChain(filters);\n request.getRequestProgressTracker().log(\n \"Applying \" + filterChainType + \"filters\");\n processor.doFilter(request, response);\n }\n\n // ---------- Generic Content Request processor ----------------------------\n\n /**\n * Dispatches the request on behalf of the\n * {@link org.apache.sling.engine.impl.request.SlingRequestDispatcher}.\n * @param request The request\n * @param response The response\n * @param resource The resource\n * @param resolvedURL Request path info\n * @param include Is this an include (or forward) ?\n * @param protectHeadersOnInclude Should the headers be protected on include?\n */\n public void dispatchRequest(final ServletRequest request,\n final ServletResponse response, final Resource resource,\n final RequestPathInfo resolvedURL, \n final boolean include,\n final boolean protectHeadersOnInclude) throws IOException, ServletException {\n\n // we need a SlingHttpServletRequest/SlingHttpServletResponse tupel\n // to continue\n final SlingHttpServletRequest cRequest = RequestData.toSlingHttpServletRequest(request);\n final SlingHttpServletResponse cResponse = RequestData.toSlingHttpServletResponse(response);\n\n // get the request data (and btw check the correct type)\n final RequestData requestData = RequestData.getRequestData(cRequest);\n final ContentData oldContentData = requestData.getContentData();\n final ContentData contentData = requestData.setContent(resource, resolvedURL);\n\n try {\n // resolve the servlet\n Servlet servlet = servletResolver.resolveServlet(cRequest);\n contentData.setServlet(servlet);\n\n FilterChainType type = include\n ? FilterChainType.INCLUDE\n : FilterChainType.FORWARD;\n\n processComponent(cRequest, include && protectHeadersOnInclude ? new IncludeResponseWrapper(cResponse) : cResponse, type);\n } finally {\n requestData.resetContent(oldContentData);\n }\n }\n\n // ---------- Error Handling with Filters\n\n private void handleError(final FilterChain errorFilterChain,\n final SlingHttpServletRequest request,\n final SlingHttpServletResponse response) \n throws IOException {\n request.getRequestProgressTracker().log(\"Applying \" + FilterChainType.ERROR + \" filters\");\n\n try {\n // wrap the response ensuring getWriter will fall back to wrapping\n // the response output stream if reset does not reset this\n errorFilterChain.doFilter(request, new ErrorResponseWrapper(response));\n } catch (final ServletException se) {\n throw new SlingServletException(se);\n }\n }\n\n void handleError(final int status, final String message,\n final SlingHttpServletRequest request,\n final SlingHttpServletResponse response) throws IOException {\n final FilterHandle[] filters = filterManager.getFilters(FilterChainType.ERROR);\n final FilterChain processor = new ErrorFilterChainStatus(filters, errorHandler, status, message);\n this.handleError(processor, request, response);\n }\n\n private void handleError(final Throwable throwable,\n final SlingHttpServletRequest request,\n final SlingHttpServletResponse response) throws IOException {\n final FilterHandle[] filters = filterManager.getFilters(FilterChainType.ERROR);\n final FilterChain processor = new ErrorFilterChainThrowable(filters, errorHandler, throwable);\n this.handleError(processor, request, response);\n }\n\n private static class ErrorResponseWrapper extends\n SlingHttpServletResponseWrapper {\n\n private PrintWriter writer;\n\n public ErrorResponseWrapper(SlingHttpServletResponse wrappedResponse) {\n super(wrappedResponse);\n }\n\n @Override\n public PrintWriter getWriter() throws IOException {\n if (writer == null) {\n try {\n writer = super.getWriter();\n } catch (IllegalStateException ise) {\n // resetting the response did not reset the output channel\n // status and we have to create a writer based on the output\n // stream using the character encoding already set on the\n // response, defaulting to ISO-8859-1\n OutputStream out = getOutputStream();\n String encoding = getCharacterEncoding();\n if (encoding == null) {\n encoding = \"ISO-8859-1\";\n setCharacterEncoding(encoding);\n }\n Writer w = new OutputStreamWriter(out, encoding);\n writer = new PrintWriter(w);\n }\n }\n return writer;\n }\n\n /**\n * Flush the writer if the {@link #getWriter()} method was called\n * to potentially wrap an OuputStream still existing in the response.\n */\n @Override\n public void flushBuffer() throws IOException {\n if (writer != null) {\n writer.flush();\n }\n super.flushBuffer();\n }\n }\n\n /**\n * Returns the MIME type as resolved by the <code>MimeTypeService</code> or\n * <code>null</code> if the service is not available.\n */\n public String getMimeType(final String name) {\n MimeTypeService mtservice = mimeTypeService;\n if (mtservice != null) {\n return mtservice.getMimeType(name);\n }\n\n log.debug(\n \"getMimeType: MimeTypeService not available, cannot resolve mime type for {}\",\n name);\n return null;\n }\n}\n\n## 内容4\n### 文件路径:\nsrc/main/java/org/apache/sling/engine/impl/helper/SlingServletContext.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\npackage org.apache.sling.engine.impl.helper;\n\nimport java.io.InputStream;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.Collections;\nimport java.util.Dictionary;\nimport java.util.Enumeration;\nimport java.util.EventListener;\nimport java.util.Hashtable;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.servlet.Filter;\nimport javax.servlet.FilterRegistration;\nimport javax.servlet.RequestDispatcher;\nimport javax.servlet.Servlet;\nimport javax.servlet.ServletContext;\nimport javax.servlet.ServletContextEvent;\nimport javax.servlet.ServletContextListener;\nimport javax.servlet.ServletRegistration;\nimport javax.servlet.ServletRegistration.Dynamic;\nimport javax.servlet.SessionCookieConfig;\nimport javax.servlet.SessionTrackingMode;\nimport javax.servlet.descriptor.JspConfigDescriptor;\n\nimport org.apache.sling.engine.impl.Config;\nimport org.apache.sling.engine.impl.ProductInfoProvider;\nimport org.apache.sling.engine.impl.SlingHttpContext;\nimport org.apache.sling.engine.impl.SlingMainServlet;\nimport org.apache.sling.engine.impl.request.SlingRequestDispatcher;\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.ServiceRegistration;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Modified;\nimport org.osgi.service.component.annotations.Reference;\nimport org.osgi.service.http.whiteboard.HttpWhiteboardConstants;\nimport org.osgi.service.http.whiteboard.propertytypes.HttpWhiteboardContextSelect;\nimport org.osgi.service.http.whiteboard.propertytypes.HttpWhiteboardListener;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * The <code>SlingServletContext</code> class is the <code>ServletContext</code>\n * which is registered as a service usable by servlets and helpers inside Sling.\n * Most methods just call into the servlet context in which the\n * {@link SlingMainServlet} is running.\n * <dl>\n * <dt><b>MIME Type Mapping</b></dt>\n * <dd>Just forwards to the servlet context of the {@link SlingMainServlet} for\n * MIME type mapping.</dd>\n * <dt><b>Resources</b></dt>\n * <dd>This class provides access to the resources in the web application by\n * means of the respective resource accessor methods. These are not the same\n * resources as available through the <code>ResourceResolver</code>.</dd>\n * <dt><b>Request Dispatcher</b></dt>\n * <dd>The {@link #getRequestDispatcher(String)} method returns a\n * {@link SlingRequestDispatcher} which may dispatch a request inside sling\n * without going through the servlet container. The\n * {@link #getNamedDispatcher(String)} method returns a servlet container\n * request dispatcher which always goes through the servlet container.</dd>\n * <dt><b>Parameters and Attributes</b></dt>\n * <dd>Initialization parameters and context attributes are shared with the\n * servlet context in which the {@link SlingMainServlet} is running.</dd>\n * <dt><b>Logging</b></dt>\n * <dd>Logging is diverted to a logger whose name is the fully qualified name of\n * this class.</dd>\n * </dl>\n * <p>\n * This class implements the Servlet API 3.0 {@code ServletContext} interface.\n */\n@Component(service = ServletContextListener.class,\n configurationPid = Config.PID)\n@HttpWhiteboardContextSelect(\"(\" + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + \"=\" + SlingHttpContext.SERVLET_CONTEXT_NAME + \")\")\n@HttpWhiteboardListener\npublic class SlingServletContext implements ServletContext, ServletContextListener {\n\n public static final String TARGET = \"(name=\" + SlingHttpContext.SERVLET_CONTEXT_NAME + \")\";\n\n /** default log */\n private final Logger log = LoggerFactory.getLogger(getClass());\n\n private final ProductInfoProvider productInfoProvider;\n\n private final BundleContext bundleContext;\n\n /**\n * The server information to report in the {@link #getServerInfo()} method.\n * By default this is just the {@link #PRODUCT_NAME} (same as\n * {@link #productInfo}. During {@link #activate(BundleContext, Map, Config)}\n * activation} the field is updated with the full {@link #productInfo} value\n * as well as the operating system and java version it is running on.\n * Finally during servlet initialization the product information from the\n * servlet container's server info is added to the comment section.\n */\n private volatile String serverInfo;\n\n private volatile String configuredServerInfo;\n\n private volatile ServletContext servletContext;\n\n private volatile ServiceRegistration<ServletContext> registration;\n\n @Activate\n public SlingServletContext(final Config config, \n final BundleContext bundleContext,\n @Reference final ProductInfoProvider infoProvider) {\n this.bundleContext = bundleContext;\n this.productInfoProvider = infoProvider;\n this.setup(config);\n }\n\n @Modified\n protected void modified(final Config config) {\n setup(config);\n }\n\n private void setup(final Config config) {\n if (config.sling_serverinfo() != null && !config.sling_serverinfo().isEmpty()) {\n this.configuredServerInfo = config.sling_serverinfo();\n } else {\n this.configuredServerInfo = null;\n }\n\n this.setServerInfo();\n }\n\n /**\n * Sets up the server info to be returned for the\n * <code>ServletContext.getServerInfo()</code> method for servlets and\n * filters deployed inside Sling. The {@link SlingRequestProcessor} instance\n * is also updated with the server information.\n * <p>\n * This server info is either configured through an OSGi configuration or\n * it is made up of the following components:\n * <ol>\n * <li>The {@link #productInfo} field as the primary product information</li>\n * <li>The primary product information of the servlet container into which\n * the Sling Main Servlet is deployed. If the servlet has not yet been\n * deployed this will show as <i>unregistered</i>. If the servlet container\n * does not provide a server info this will show as <i>unknown</i>.</li>\n * <li>The name and version of the Java VM as reported by the\n * <code>java.vm.name</code> and <code>java.vm.version</code> system\n * properties</li>\n * <li>The name, version, and architecture of the OS platform as reported by\n * the <code>os.name</code>, <code>os.version</code>, and\n * <code>os.arch</code> system properties</li>\n * </ol>\n */\n private void setServerInfo() {\n if ( this.configuredServerInfo != null ) {\n this.serverInfo = this.configuredServerInfo;\n } else {\n final String containerProductInfo;\n if (getServletContext() == null) {\n containerProductInfo = \"unregistered\";\n } else {\n final String containerInfo = getServletContext().getServerInfo();\n if (containerInfo != null && containerInfo.length() > 0) {\n int lbrace = containerInfo.indexOf('(');\n if (lbrace < 0) {\n lbrace = containerInfo.length();\n }\n containerProductInfo = containerInfo.substring(0, lbrace).trim();\n } else {\n containerProductInfo = \"unknown\";\n }\n }\n\n this.serverInfo = String.format(\"%s (%s, %s %s, %s %s %s)\",\n this.productInfoProvider.getProductInfo(), containerProductInfo,\n System.getProperty(\"java.vm.name\"),\n System.getProperty(\"java.version\"), System.getProperty(\"os.name\"),\n System.getProperty(\"os.version\"), System.getProperty(\"os.arch\"));\n }\n }\n\n @Override\n public void contextDestroyed(final ServletContextEvent sce) {\n synchronized ( this ) {\n this.servletContext = null;\n this.setServerInfo();\n if ( this.registration != null ) {\n this.registration.unregister();\n this.registration = null; \n }\n }\n }\n\n @Override\n public void contextInitialized(final ServletContextEvent sce) {\n this.servletContext = sce.getServletContext();\n this.setServerInfo();\n // async registreation\n final Thread thread = new Thread(\"SlingServletContext registration\") {\n @Override\n public void run() {\n synchronized (SlingServletContext.this) {\n if ( servletContext != null ) {\n final Dictionary<String, Object> props = new Hashtable<String, Object>();\n props.put(\"name\", SlingHttpContext.SERVLET_CONTEXT_NAME); // property to identify this context\n registration = bundleContext.registerService(ServletContext.class, SlingServletContext.this, props); \n }\n }\n }\n };\n thread.setDaemon(true);\n thread.start();\n }\n\n /**\n * Returns the name of the servlet context in which Sling is configured.\n * This method calls on the <code>ServletContext</code> in which the\n * {@link SlingMainServlet} is running.\n */\n @Override\n public String getServletContextName() {\n ServletContext delegatee = getServletContext();\n if (delegatee != null) {\n return delegatee.getServletContextName();\n }\n\n return null;\n }\n\n /** Returns the context path of the web application. (Servlet API 2.5) */\n @Override\n public String getContextPath() {\n ServletContext delegatee = getServletContext();\n if (delegatee != null) {\n return delegatee.getContextPath();\n }\n\n return null;\n }\n\n /**\n * Returns the init-param of the servlet context in which Sling is\n * configured. This method calls on the <code>ServletContext</code> in\n * which the {@link SlingMainServlet} is running.\n */\n @Override\n public String getInitParameter(String name) {\n ServletContext delegatee = getServletContext();\n if (delegatee != null) {\n return delegatee.getInitParameter(name);\n }\n\n return null;\n }\n\n /**\n * Returns the names of the init-params of the servlet context in which\n * Sling is configured. This method calls on the <code>ServletContext</code>\n * in which the {@link SlingMainServlet} is running.\n */\n @Override\n public Enumeration<String> getInitParameterNames() {\n ServletContext delegatee = getServletContext();\n if (delegatee != null) {\n return delegatee.getInitParameterNames();\n }\n\n return null;\n }\n\n // ---------- attributes ---------------------------------------------------\n\n /**\n * Returns the named servlet context attribute. This method calls on the\n * <code>ServletContext</code> in which the {@link SlingMainServlet} is\n * running.\n */\n @Override\n public Object getAttribute(String name) {\n ServletContext delegatee = getServletContext();\n if (delegatee != null) {\n return delegatee.getAttribute(name);\n }\n\n return null;\n }\n\n /**\n * Returns the names of all servlet context attributes. This method calls on\n * the <code>ServletContext</code> in which the {@link SlingMainServlet}\n * is running.\n */\n @Override\n public Enumeration<String> getAttributeNames() {\n ServletContext delegatee = getServletContext();\n if (delegatee != null) {\n return delegatee.getAttributeNames();\n }\n\n return Collections.enumeration(Collections.<String>emptyList());\n }\n\n /**\n * Removes the named servlet context attribute. This method calls on the\n * <code>ServletContext</code> in which the {@link SlingMainServlet} is\n * running.\n */\n @Override\n public void removeAttribute(String name) {\n ServletContext delegatee = getServletContext();\n if (delegatee != null) {\n delegatee.removeAttribute(name);\n }\n }\n\n /**\n * Sets the name servlet context attribute to the requested value. This\n * method calls on the <code>ServletContext</code> in which the\n * {@link SlingMainServlet} is running.\n */\n @Override\n public void setAttribute(String name, Object object) {\n ServletContext delegatee = getServletContext();\n if (delegatee != null) {\n delegatee.setAttribute(name, object);\n }\n }\n\n // ---------- Servlet Container information --------------------------------\n\n /**\n * Returns the Sling server info string. This is not the same server info\n * string as returned by the servlet context in which Sling is configured.\n */\n @Override\n public String getServerInfo() {\n return this.serverInfo;\n }\n\n /**\n * Returns the major version number of the Servlet API supported by the\n * servlet container in which Sling is running. This method calls on the\n * <code>ServletContext</code> in which the {@link SlingMainServlet} is\n * running.\n */\n @Override\n public int getMajorVersion() {\n ServletContext delegatee = getServletContext();\n if (delegatee != null) {\n return delegatee.getMajorVersion();\n }\n\n return 3; // hard coded major version as fall back\n }\n\n /**\n * Returns the minor version number of the Servlet API supported by the\n * servlet container in which Sling is running. This method calls on the\n * <code>ServletContext</code> in which the {@link SlingMainServlet} is\n * running.\n */\n @Override\n public int getMinorVersion() {\n ServletContext delegatee = getServletContext();\n if (delegatee != null) {\n return delegatee.getMinorVersion();\n }\n\n return 0; // hard coded minor version as fall back\n }\n\n // ---------- MIME type mapping --------------------------------------------\n\n /**\n * Returns a MIME type for the extension of the given file name. This method\n * calls on the <code>ServletContext</code> in which the\n * {@link SlingMainServlet} is running.\n */\n @Override\n public String getMimeType(String file) {\n ServletContext delegatee = getServletContext();\n if (delegatee != null) {\n return delegatee.getMimeType(file);\n }\n\n return null;\n }\n\n // ---------- Request Dispatcher -------------------------------------------\n\n /**\n * Returns a {@link SlingRequestDispatcher} for the given path if not\n * <code>null</code>. Otherwise <code>null</code> is returned.\n */\n @Override\n public RequestDispatcher getRequestDispatcher(String path) {\n // return no dispatcher if content is null\n if (path == null) {\n log.error(\"getRequestDispatcher: No path, cannot create request dispatcher\");\n return null;\n }\n\n return new SlingRequestDispatcher(path, null);\n }\n\n /**\n * Returns a servlet container request dispatcher for the named servlet.\n * This method calls on the <code>ServletContext</code> in which the\n * {@link SlingMainServlet} is running.\n */\n @Override\n public RequestDispatcher getNamedDispatcher(String name) {\n ServletContext delegatee = getServletContext();\n if (delegatee != null) {\n return delegatee.getNamedDispatcher(name);\n }\n\n return null;\n }\n\n // ---------- Resource Access ----------------------------------------------\n\n /**\n * Returns the URI for the given path. This method calls on the\n * <code>ServletContext</code> in which the {@link SlingMainServlet} is\n * running.\n */\n @Override\n public URL getResource(String path) throws MalformedURLException {\n ServletContext delegatee = getServletContext();\n if (delegatee != null) {\n return delegatee.getResource(path);\n }\n\n return null;\n }\n\n /**\n * Returns an input stream to the given path. This method calls on the\n * <code>ServletContext</code> in which the {@link SlingMainServlet} is\n * running.\n */\n @Override\n public InputStream getResourceAsStream(String path) {\n ServletContext delegatee = getServletContext();\n if (delegatee != null) {\n return delegatee.getResourceAsStream(path);\n }\n\n return null;\n }\n\n /**\n * Returns a set of names for path entries considered children of the given\n * path. This method calls on the <code>ServletContext</code> in which the\n * {@link SlingMainServlet} is running.\n */\n @Override\n public Set<String> getResourcePaths(String parentPath) {\n ServletContext delegatee = getServletContext();\n if (delegatee != null) {\n return delegatee.getResourcePaths(parentPath);\n }\n\n return null;\n }\n\n /**\n * Returns the real file inside the web application to which the given path\n * maps or <code>null</code> if no such file exists. This method calls on\n * the <code>ServletContext</code> in which the {@link SlingMainServlet}\n * is running.\n */\n @Override\n public String getRealPath(String path) {\n ServletContext delegatee = getServletContext();\n if (delegatee != null) {\n return delegatee.getRealPath(path);\n }\n\n return null;\n }\n\n // ---------- logging ------------------------------------------------------\n\n /** Logs the message and optional throwable at error level to the logger */\n @Override\n public void log(String message, Throwable throwable) {\n log.error(message, throwable);\n }\n\n /** Logs the message at info level to the logger */\n @Override\n public void log(String message) {\n log.info(message);\n }\n\n /** Logs the message and optional exception at error level to the logger */\n @Override\n @Deprecated\n public void log(Exception exception, String message) {\n log(message, exception);\n }\n\n // ---------- foreign Servlets ---------------------------------------------\n\n /**\n * Returns the servlet context from the servlet container in which sling is\n * running. This method calls on the <code>ServletContext</code> in which\n * the {@link SlingMainServlet} is running.\n */\n @Override\n public ServletContext getContext(String uripath) {\n ServletContext delegatee = getServletContext();\n if (delegatee != null) {\n ServletContext otherContext = delegatee.getContext(uripath);\n if (otherContext != null && otherContext != delegatee) {\n return wrapServletContext(otherContext);\n }\n }\n\n return null;\n }\n\n /** Returns <code>null</code> as defined in Servlet API 2.4 */\n @Override\n @Deprecated\n public Servlet getServlet(String name) {\n return null;\n }\n\n /** Returns an empty enumeration as defined in Servlet API 2.4 */\n @Override\n @Deprecated\n public Enumeration<String> getServletNames() {\n return Collections.enumeration(Collections.<String>emptyList());\n }\n\n /** Returns an empty enumeration as defined in Servlet API 2.4 */\n @Override\n @Deprecated\n public Enumeration<Servlet> getServlets() {\n return Collections.enumeration(Collections.<Servlet>emptyList());\n }\n\n @Override\n public int getEffectiveMajorVersion() {\n ServletContext delegatee = getServletContext();\n if (delegatee != null) {\n return delegatee.getEffectiveMajorVersion();\n }\n return 3;\n }\n\n @Override\n public int getEffectiveMinorVersion() {\n ServletContext delegatee = getServletContext();\n if (delegatee != null) {\n return delegatee.getEffectiveMinorVersion();\n }\n return 0;\n }\n\n @Override\n public boolean setInitParameter(String name, String value) {\n // only supported in ServletContextListener.contextInitialized or\n // ServletContainerInitializer.onStartuo\n throw new IllegalStateException();\n }\n\n @Override\n public SessionCookieConfig getSessionCookieConfig() {\n // result in NPE if context is not set anymore\n return getServletContext().getSessionCookieConfig();\n }\n\n @Override\n public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes) {\n // only supported in ServletContextListener.contextInitialized or\n // ServletContainerInitializer.onStartuo\n throw new IllegalStateException();\n }\n\n @Override\n public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {\n // result in NPE if context is not set anymore\n return getServletContext().getDefaultSessionTrackingModes();\n }\n\n @Override\n public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {\n // result in NPE if context is not set anymore\n return getServletContext().getEffectiveSessionTrackingModes();\n }\n\n @Override\n public JspConfigDescriptor getJspConfigDescriptor() {\n // result in NPE if context is not set anymore\n return getServletContext().getJspConfigDescriptor();\n }\n\n @Override\n public ClassLoader getClassLoader() {\n // we don't allow access to any class loader here since we are\n // running in the OSGi Framework and we don't want code to fiddle\n // with class laoders obtained from the ServletContext\n throw new SecurityException();\n }\n\n @Override\n public void declareRoles(String... roleNames) {\n // only supported in ServletContextListener.contextInitialized or\n // ServletContainerInitializer.onStartuo\n throw new IllegalStateException();\n }\n\n // Servlet API 3.0, Section 4.4 Configuration methods\n\n @Override\n public Dynamic addServlet(String servletName, String className) {\n // only supported in ServletContextListener.contextInitialized or\n // ServletContainerInitializer.onStartuo\n throw new IllegalStateException();\n }\n\n @Override\n public Dynamic addServlet(String servletName, Servlet servlet) {\n // only supported in ServletContextListener.contextInitialized or\n // ServletContainerInitializer.onStartuo\n throw new IllegalStateException();\n }\n\n @Override\n public Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass) {\n // only supported in ServletContextListener.contextInitialized or\n // ServletContainerInitializer.onStartuo\n throw new IllegalStateException();\n }\n\n @Override\n public <T extends Servlet> T createServlet(Class<T> clazz) {\n // only supported in ServletContextListener.contextInitialized or\n // ServletContainerInitializer.onStartuo\n throw new IllegalStateException();\n }\n\n @Override\n public ServletRegistration getServletRegistration(String servletName) {\n // only supported in ServletContextListener.contextInitialized or\n // ServletContainerInitializer.onStartuo\n throw new IllegalStateException();\n }\n\n @Override\n public Map<String, ? extends ServletRegistration> getServletRegistrations() {\n // only supported in ServletContextListener.contextInitialized or\n // ServletContainerInitializer.onStartuo\n throw new IllegalStateException();\n }\n\n @Override\n public javax.servlet.FilterRegistration.Dynamic addFilter(String filterName, String className) {\n // only supported in ServletContextListener.contextInitialized or\n // ServletContainerInitializer.onStartuo\n throw new IllegalStateException();\n }\n\n @Override\n public javax.servlet.FilterRegistration.Dynamic addFilter(String filterName, Filter filter) {\n // only supported in ServletContextListener.contextInitialized or\n // ServletContainerInitializer.onStartuo\n throw new IllegalStateException();\n }\n\n @Override\n public javax.servlet.FilterRegistration.Dynamic addFilter(String filterName, Class<? extends Filter> filterClass) {\n // only supported in ServletContextListener.contextInitialized or\n // ServletContainerInitializer.onStartuo\n throw new IllegalStateException();\n }\n\n @Override\n public <T extends Filter> T createFilter(Class<T> clazz) {\n // only supported in ServletContextListener.contextInitialized or\n // ServletContainerInitializer.onStartuo\n throw new IllegalStateException();\n }\n\n @Override\n public FilterRegistration getFilterRegistration(String filterName) {\n // only supported in ServletContextListener.contextInitialized or\n // ServletContainerInitializer.onStartuo\n throw new IllegalStateException();\n }\n\n @Override\n public Map<String, ? extends FilterRegistration> getFilterRegistrations() {\n // only supported in ServletContextListener.contextInitialized or\n // ServletContainerInitializer.onStartuo\n throw new IllegalStateException();\n }\n\n @Override\n public void addListener(String className) {\n // only supported in ServletContextListener.contextInitialized or\n // ServletContainerInitializer.onStartuo\n throw new IllegalStateException();\n }\n\n @Override\n public <T extends EventListener> void addListener(T t) {\n // only supported in ServletContextListener.contextInitialized or\n // ServletContainerInitializer.onStartuo\n throw new IllegalStateException();\n }\n\n @Override\n public void addListener(Class<? extends EventListener> listenerClass) {\n // only supported in ServletContextListener.contextInitialized or\n // ServletContainerInitializer.onStartuo\n throw new IllegalStateException();\n }\n\n @Override\n public <T extends EventListener> T createListener(Class<T> clazz) {\n // only supported in ServletContextListener.contextInitialized or\n // ServletContainerInitializer.onStartuo\n throw new IllegalStateException();\n }\n\n @Override\n public String getVirtualServerName() {\n return getServletContext().getVirtualServerName();\n }\n\n // ---------- internal -----------------------------------------------------\n\n /**\n * Returns the real servlet context of the servlet container in which the\n * Sling Servlet is running.\n *\n * @return the servlet context\n */\n protected ServletContext getServletContext() {\n return this.servletContext;\n }\n\n protected ServletContext wrapServletContext(final ServletContext context) {\n return new ExternalServletContextWrapper(context);\n }\n}\n\n## 内容5\n### 文件路径:\nsrc/main/java/org/apache/sling/engine/impl/request/RequestData.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\npackage org.apache.sling.engine.impl.request;\n\nimport static org.apache.sling.api.SlingConstants.SLING_CURRENT_SERVLET_NAME;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport javax.servlet.Servlet;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletInputStream;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletRequestWrapper;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.ServletResponseWrapper;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.sling.api.SlingHttpServletRequest;\nimport org.apache.sling.api.SlingHttpServletResponse;\nimport org.apache.sling.api.request.RecursionTooDeepException;\nimport org.apache.sling.api.request.RequestPathInfo;\nimport org.apache.sling.api.request.RequestProgressTracker;\nimport org.apache.sling.api.request.RequestUtil;\nimport org.apache.sling.api.request.TooManyCallsException;\nimport org.apache.sling.api.request.builder.Builders;\nimport org.apache.sling.api.resource.Resource;\nimport org.apache.sling.api.resource.ResourceResolver;\nimport org.apache.sling.api.servlets.ServletResolver;\nimport org.apache.sling.api.wrappers.SlingHttpServletRequestWrapper;\nimport org.apache.sling.api.wrappers.SlingHttpServletResponseWrapper;\nimport org.apache.sling.engine.impl.SlingHttpServletRequestImpl;\nimport org.apache.sling.engine.impl.SlingHttpServletResponseImpl;\nimport org.apache.sling.engine.impl.SlingMainServlet;\nimport org.apache.sling.engine.impl.SlingRequestProcessorImpl;\nimport org.apache.sling.engine.impl.adapter.SlingServletRequestAdapter;\nimport org.apache.sling.engine.impl.adapter.SlingServletResponseAdapter;\nimport org.apache.sling.engine.impl.parameters.ParameterSupport;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * The <code>RequestData</code> class provides access to objects which are set\n * on a Servlet Request wide basis such as the repository session, the\n * persistence manager, etc.\n *\n * The setup order is:\n * <ol>\n * <li>Invoke constructor</li>\n * <li>Invoke initResource()</li>\n * <li>Invoke initServlet()</li>\n * </ol>\n * @see ContentData\n */\npublic class RequestData {\n\n /** Logger - use static as a new object is created with every request */\n private static final Logger log = LoggerFactory.getLogger(RequestData.class);\n\n /**\n * The name of the request attribute providing the resource addressed by the\n * request URL.\n */\n public static final String REQUEST_RESOURCE_PATH_ATTR = \"$$sling.request.resource$$\";\n\n /**\n * The name of the request attribute to override the max call number (-1 for infinite or integer value).\n */\n private static String REQUEST_MAX_CALL_OVERRIDE = \"sling.max.calls\";\n\n /** The request processor used for request dispatching and other stuff */\n private final SlingRequestProcessorImpl slingRequestProcessor;\n\n private final long startTimestamp;\n\n /** The original servlet Servlet Request Object */\n private final HttpServletRequest servletRequest;\n\n /** The original servlet Servlet Response object */\n private final HttpServletResponse servletResponse;\n\n /** The original servlet Servlet Request Object */\n private final SlingHttpServletRequest slingRequest;\n\n /** The original servlet Servlet Response object */\n private final SlingHttpServletResponse slingResponse;\n\n /** The parameter support class */\n private ParameterSupport parameterSupport;\n\n private ResourceResolver resourceResolver;\n\n private final RequestProgressTracker requestProgressTracker;\n\n /** the current ContentData */\n private ContentData currentContentData;\n\n /**\n * the number of servlets called by\n * {@link #service(SlingHttpServletRequest, SlingHttpServletResponse)}\n */\n private int servletCallCounter;\n\n /**\n * The name of the currently active serlvet.\n *\n * @see #setActiveServletName(String)\n * @see #getActiveServletName()\n */\n private String activeServletName;\n\n /**\n * Recursion depth\n */\n private int recursionDepth;\n\n /**\n * The peak value for the recursion depth.\n */\n private int peakRecusionDepth;\n\n /**\n * Prevent traversal using '/../' or '/..' even if '[' or '}' is used in-between\n */\n private static final Set<Character> SKIPPED_TRAVERSAL_CHARS = new HashSet<>();\n static {\n SKIPPED_TRAVERSAL_CHARS.add('[');\n SKIPPED_TRAVERSAL_CHARS.add('}');\n }\n\n public RequestData(SlingRequestProcessorImpl slingRequestProcessor,\n HttpServletRequest request, HttpServletResponse response) {\n this.startTimestamp = System.currentTimeMillis();\n\n this.slingRequestProcessor = slingRequestProcessor;\n\n this.servletRequest = request;\n this.servletResponse = response;\n\n this.slingRequest = new SlingHttpServletRequestImpl(this, this.servletRequest);\n\n this.slingResponse = new SlingHttpServletResponseImpl(this, this.servletResponse);\n\n // Use tracker from SlingHttpServletRequest\n if ( request instanceof SlingHttpServletRequest ) {\n this.requestProgressTracker = ((SlingHttpServletRequest)request).getRequestProgressTracker();\n } else {\n // Getting the RequestProgressTracker from the request attributes like\n // this should not be generally used, it's just a way to pass it from\n // its creation point to here, so it's made available via\n // the Sling request's getRequestProgressTracker method.\n final Object o = request.getAttribute(RequestProgressTracker.class.getName());\n if (o instanceof RequestProgressTracker) {\n this.requestProgressTracker = (RequestProgressTracker)o;\n } else {\n log.warn(\"RequestProgressTracker not found in request attributes\");\n this.requestProgressTracker = Builders.newRequestProgressTracker();\n this.requestProgressTracker.log(\"Method={0}, PathInfo={1}\", request.getMethod(), request.getPathInfo());\n }\n }\n }\n\n public Resource initResource(ResourceResolver resourceResolver) {\n // keep the resource resolver for request processing\n this.resourceResolver = resourceResolver;\n\n // resolve the resource\n requestProgressTracker.startTimer(\"ResourceResolution\");\n final SlingHttpServletRequest request = getSlingRequest();\n\n StringBuffer requestURL = servletRequest.getRequestURL();\n String path = request.getPathInfo();\n if (requestURL.indexOf(\";\") > -1 && !path.contains(\";\")) {\n try {\n final URL rUrl = new URL(requestURL.toString());\n final String prefix = request.getContextPath().concat(request.getServletPath());\n path = rUrl.getPath().substring(prefix.length());\n } catch ( final MalformedURLException e) {\n // ignore\n }\n }\n\n Resource resource = resourceResolver.resolve(request, path);\n if (request.getAttribute(REQUEST_RESOURCE_PATH_ATTR) == null) {\n request.setAttribute(REQUEST_RESOURCE_PATH_ATTR, resource.getPath());\n }\n requestProgressTracker.logTimer(\"ResourceResolution\",\n \"URI={0} resolves to Resource={1}\",\n getServletRequest().getRequestURI(), resource);\n return resource;\n }\n\n public void initServlet(final Resource resource,\n final ServletResolver sr) {\n // the resource and the request path info, will never be null\n RequestPathInfo requestPathInfo = new SlingRequestPathInfo(resource);\n ContentData contentData = setContent(resource, requestPathInfo);\n\n\t requestProgressTracker.log(\"Resource Path Info: {0}\", requestPathInfo);\n\n // finally resolve the servlet for the resource\n requestProgressTracker.startTimer(\"ServletResolution\");\n Servlet servlet = sr.resolveServlet(slingRequest);\n requestProgressTracker.logTimer(\"ServletResolution\",\n \"URI={0} handled by Servlet={1}\",\n getServletRequest().getRequestURI(), (servlet == null ? \"-none-\" : RequestUtil.getServletName(servlet)));\n contentData.setServlet(servlet);\n }\n\n public SlingRequestProcessorImpl getSlingRequestProcessor() {\n return slingRequestProcessor;\n }\n\n public HttpServletRequest getServletRequest() {\n return servletRequest;\n }\n\n public HttpServletResponse getServletResponse() {\n return servletResponse;\n }\n\n public SlingHttpServletRequest getSlingRequest() {\n return slingRequest;\n }\n\n public SlingHttpServletResponse getSlingResponse() {\n return slingResponse;\n }\n\n // ---------- Request Helper\n\n /**\n * Unwraps the ServletRequest to a SlingHttpServletRequest.\n *\n * @param request the request\n * @return the unwrapped request\n * @throws IllegalArgumentException If the <code>request</code> is not a\n * <code>SlingHttpServletRequest</code> and not a\n * <code>ServletRequestWrapper</code> wrapping a\n * <code>SlingHttpServletRequest</code>.\n */\n public static SlingHttpServletRequest unwrap(ServletRequest request) {\n\n // early check for most cases\n if (request instanceof SlingHttpServletRequest) {\n return (SlingHttpServletRequest) request;\n }\n\n // unwrap wrappers\n while (request instanceof ServletRequestWrapper) {\n request = ((ServletRequestWrapper) request).getRequest();\n\n // immediate termination if we found one\n if (request instanceof SlingHttpServletRequest) {\n return (SlingHttpServletRequest) request;\n }\n }\n\n // if we unwrapped everything and did not find a\n // SlingHttpServletRequest, we lost\n throw new IllegalArgumentException(\n \"ServletRequest not wrapping SlingHttpServletRequest\");\n }\n\n /**\n * Unwraps the SlingHttpServletRequest to a SlingHttpServletRequestImpl\n *\n * @param request the request\n * @return the unwrapped request\n * @throws IllegalArgumentException If <code>request</code> is not a\n * <code>SlingHttpServletRequestImpl</code> and not\n * <code>SlingHttpServletRequestWrapper</code> wrapping a\n * <code>SlingHttpServletRequestImpl</code>.\n */\n public static SlingHttpServletRequestImpl unwrap(\n SlingHttpServletRequest request) {\n while (request instanceof SlingHttpServletRequestWrapper) {\n request = ((SlingHttpServletRequestWrapper) request).getSlingRequest();\n }\n\n if (request instanceof SlingHttpServletRequestImpl) {\n return (SlingHttpServletRequestImpl) request;\n }\n\n throw new IllegalArgumentException(\n \"SlingHttpServletRequest not of correct type\");\n }\n\n /**\n * Unwraps the ServletRequest to a SlingHttpServletRequest.\n *\n * @param response the response\n * @return the unwrapped response\n * @throws IllegalArgumentException If the <code>response</code> is not a\n * <code>SlingHttpServletResponse</code> and not a\n * <code>ServletResponseWrapper</code> wrapping a\n * <code>SlingHttpServletResponse</code>.\n */\n public static SlingHttpServletResponse unwrap(ServletResponse response) {\n\n // early check for most cases\n if (response instanceof SlingHttpServletResponse) {\n return (SlingHttpServletResponse) response;\n }\n\n // unwrap wrappers\n while (response instanceof ServletResponseWrapper) {\n response = ((ServletResponseWrapper) response).getResponse();\n\n // immediate termination if we found one\n if (response instanceof SlingHttpServletResponse) {\n return (SlingHttpServletResponse) response;\n }\n }\n\n // if we unwrapped everything and did not find a\n // SlingHttpServletResponse, we lost\n throw new IllegalArgumentException(\n \"ServletResponse not wrapping SlingHttpServletResponse\");\n }\n\n /**\n * Unwraps a SlingHttpServletResponse to a SlingHttpServletResponseImpl\n *\n * @param response the response\n * @return the unwrapped response\n * @throws IllegalArgumentException If <code>response</code> is not a\n * <code>SlingHttpServletResponseImpl</code> and not\n * <code>SlingHttpServletResponseWrapper</code> wrapping a\n * <code>SlingHttpServletResponseImpl</code>.\n */\n public static SlingHttpServletResponseImpl unwrap(\n SlingHttpServletResponse response) {\n while (response instanceof SlingHttpServletResponseWrapper) {\n response = ((SlingHttpServletResponseWrapper) response).getSlingResponse();\n }\n\n if (response instanceof SlingHttpServletResponseImpl) {\n return (SlingHttpServletResponseImpl) response;\n }\n\n throw new IllegalArgumentException(\n \"SlingHttpServletResponse not of correct type\");\n }\n\n /**\n * @param request the request\n * @return the request data\n * @throws IllegalArgumentException If the <code>request</code> is not a\n * <code>SlingHttpServletRequest</code> and not a\n * <code>ServletRequestWrapper</code> wrapping a\n * <code>SlingHttpServletRequest</code>.\n */\n public static RequestData getRequestData(ServletRequest request) {\n return unwrap(unwrap(request)).getRequestData();\n }\n\n /**\n * @param request the request\n * @return the request data\n * @throws IllegalArgumentException If <code>request</code> is not a\n * <code>SlingHttpServletRequestImpl</code> and not\n * <code>SlingHttpServletRequestWrapper</code> wrapping a\n * <code>SlingHttpServletRequestImpl</code>.\n */\n public static RequestData getRequestData(SlingHttpServletRequest request) {\n return unwrap(request).getRequestData();\n }\n\n /**\n * @param request the request\n * @return the sling http servlet request\n * @throws IllegalArgumentException if <code>request</code> is not a\n * <code>HttpServletRequest</code> of if <code>request</code>\n * is not backed by <code>SlingHttpServletRequestImpl</code>.\n */\n public static SlingHttpServletRequest toSlingHttpServletRequest(\n ServletRequest request) {\n\n // unwrap to SlingHttpServletRequest, may throw if no\n // SlingHttpServletRequest is wrapped in request\n SlingHttpServletRequest cRequest = unwrap(request);\n\n // ensure the SlingHttpServletRequest is backed by\n // SlingHttpServletRequestImpl\n RequestData.unwrap(cRequest);\n\n // if the request is not wrapper at all, we are done\n if (cRequest == request) {\n return cRequest;\n }\n\n // ensure the request is a HTTP request\n if (!(request instanceof HttpServletRequest)) {\n throw new IllegalArgumentException(\"Request is not an HTTP request\");\n }\n\n // otherwise, we create a new response wrapping the servlet response\n // and unwrapped component response\n return new SlingServletRequestAdapter(cRequest,\n (HttpServletRequest) request);\n }\n\n /**\n * @param response the response\n * @return the sling http servlet response\n * @throws IllegalArgumentException if <code>response</code> is not a\n * <code>HttpServletResponse</code> of if\n * <code>response</code> is not backed by\n * <code>SlingHttpServletResponseImpl</code>.\n */\n public static SlingHttpServletResponse toSlingHttpServletResponse(\n ServletResponse response) {\n\n // unwrap to SlingHttpServletResponse\n SlingHttpServletResponse cResponse = unwrap(response);\n\n // if the servlet response is actually the SlingHttpServletResponse, we\n // are done\n if (cResponse == response) {\n return cResponse;\n }\n\n // ensure the response is a HTTP response\n if (!(response instanceof HttpServletResponse)) {\n throw new IllegalArgumentException(\n \"Response is not an HTTP response\");\n }\n\n // otherwise, we create a new response wrapping the servlet response\n // and unwrapped component response\n return new SlingServletResponseAdapter(cResponse,\n (HttpServletResponse) response);\n }\n\n /**\n * Helper method to call the servlet for the current content data. If the\n * current content data has no servlet, <em>NOT_FOUND</em> (404) error is\n * sent and the method terminates.\n * <p>\n * If the the servlet exists, the\n * {@link org.apache.sling.api.SlingConstants#SLING_CURRENT_SERVLET_NAME} request attribute is set\n * to the name of that servlet and that servlet name is also set as the\n * {@link #setActiveServletName(String) currently active servlet}. After\n * the termination of the servlet (normal or throwing a Throwable) the\n * request attribute is reset to the previous value. The name of the\n * currently active servlet is only reset to the previous value if the\n * servlet terminates normally. In case of a Throwable, the active servlet\n * name is not reset and indicates which servlet caused the potential abort\n * of the request.\n *\n * @param request The request object for the service method\n * @param response The response object for the service method\n * @throws IOException May be thrown by the servlet's service method\n * @throws ServletException May be thrown by the servlet's service method\n */\n public static void service(SlingHttpServletRequest request,\n SlingHttpServletResponse response) throws IOException,\n ServletException {\n\n if (!isValidRequest(request.getRequestPathInfo().getResourcePath(), request.getRequestPathInfo().getSelectors())) {\n response.sendError(HttpServletResponse.SC_BAD_REQUEST,\n \"Malformed request syntax\");\n return;\n }\n\n RequestData requestData = RequestData.getRequestData(request);\n Servlet servlet = requestData.getContentData().getServlet();\n if (servlet == null) {\n\n response.sendError(HttpServletResponse.SC_NOT_FOUND,\n \"No Servlet to handle request\");\n\n } else {\n\n String name = RequestUtil.getServletName(servlet);\n\n // verify the number of service calls in this request\n if (requestData.hasServletMaxCallCount(request)) {\n throw new TooManyCallsException(name);\n }\n\n // replace the current servlet name in the request\n Object oldValue = request.getAttribute(SLING_CURRENT_SERVLET_NAME);\n request.setAttribute(SLING_CURRENT_SERVLET_NAME, name);\n\n // setup the tracker for this service call\n String timerName = name + \"#\" + requestData.servletCallCounter;\n requestData.servletCallCounter++;\n requestData.getRequestProgressTracker().startTimer(timerName);\n\n try {\n\n String callerServlet = requestData.setActiveServletName(name);\n\n servlet.service(request, response);\n\n requestData.setActiveServletName(callerServlet);\n\n } finally {\n\n request.setAttribute(SLING_CURRENT_SERVLET_NAME, oldValue);\n\n requestData.getRequestProgressTracker().logTimer(timerName);\n\n }\n }\n }\n\n /*\n * Don't allow path segments that contain only dots or a mix of dots and %5B.\n * Additionally, check that we didn't have an empty selector from a dot replacement.\n */\n static boolean isValidRequest(String resourcePath, String... selectors) {\n for (String selector : selectors) {\n if (selector.trim().isEmpty()) {\n return false;\n }\n }\n return resourcePath == null || !traversesParentPath(resourcePath);\n }\n\n // ---------- Content inclusion stacking -----------------------------------\n\n public ContentData setContent(final Resource resource,\n final RequestPathInfo requestPathInfo) {\n if ( this.recursionDepth >= this.slingRequestProcessor.getMaxIncludeCounter()) {\n throw new RecursionTooDeepException(requestPathInfo.getResourcePath());\n }\n this.recursionDepth++;\n if (this.recursionDepth > this.peakRecusionDepth) {\n this.peakRecusionDepth = this.recursionDepth;\n }\n currentContentData = new ContentData(resource, requestPathInfo);\n return currentContentData;\n }\n\n public void resetContent(final ContentData data) {\n this.recursionDepth--;\n currentContentData = data;\n }\n\n public ContentData getContentData() {\n return currentContentData;\n }\n\n public ResourceResolver getResourceResolver() {\n return resourceResolver;\n }\n\n public RequestProgressTracker getRequestProgressTracker() {\n return requestProgressTracker;\n }\n\n public int getPeakRecusionDepth() {\n return peakRecusionDepth;\n }\n\n public int getServletCallCount() {\n return servletCallCounter;\n }\n\n /**\n * Returns {@code true} if the number of {@code RequestDispatcher.include}\n * calls has been reached within the given request. That maximum number may\n * either be defined by the {@link #REQUEST_MAX_CALL_OVERRIDE} request\n * attribute or the {@link SlingMainServlet#PROP_MAX_CALL_COUNTER}\n * configuration of the {@link SlingMainServlet}.\n *\n * @param request The request to check\n * @return {@code true} if the maximum number of calls has been reached (or\n * surpassed)\n */\n private boolean hasServletMaxCallCount(final ServletRequest request) {\n // verify the number of service calls in this request\n log.debug(\"Servlet call counter : {}\", getServletCallCount());\n\n // max number of calls can be overriden with a request attribute (-1 for\n // infinite or integer value)\n int maxCallCounter = this.slingRequestProcessor.getMaxCallCounter();\n Object reqMaxOverride = request.getAttribute(REQUEST_MAX_CALL_OVERRIDE);\n if (reqMaxOverride instanceof Number) {\n maxCallCounter = ((Number) reqMaxOverride).intValue();\n }\n\n return (maxCallCounter >= 0) && getServletCallCount() >= maxCallCounter;\n }\n\n public long getElapsedTimeMsec() {\n return System.currentTimeMillis() - startTimestamp;\n }\n\n /**\n * Sets the name of the currently active servlet and returns the name of the\n * previously active servlet.\n *\n * @param servletName the servlet name\n * @return the previous servlet name\n */\n public String setActiveServletName(String servletName) {\n String old = activeServletName;\n activeServletName = servletName;\n return old;\n }\n\n /**\n * Returns the name of the currently active servlet. If this name is not\n * <code>null</code> at the end of request processing, more precisly in\n * the case of an uncaught <code>Throwable</code> at the end of request\n * processing, this is the name of the servlet causing the uncaught\n * <code>Throwable</code>.\n *\n * @return the current servlet name\n */\n public String getActiveServletName() {\n return activeServletName;\n }\n\n // ---------- Parameter support -------------------------------------------\n\n public ServletInputStream getInputStream() throws IOException {\n if (parameterSupport != null && parameterSupport.requestDataUsed()) {\n throw new IllegalStateException(\n \"Request Data has already been read\");\n }\n\n // may throw IllegalStateException if the reader has already been\n // acquired\n return getServletRequest().getInputStream();\n }\n\n public BufferedReader getReader() throws UnsupportedEncodingException,\n IOException {\n if (parameterSupport != null && parameterSupport.requestDataUsed()) {\n throw new IllegalStateException(\n \"Request Data has already been read\");\n }\n\n // may throw IllegalStateException if the input stream has already been\n // acquired\n return getServletRequest().getReader();\n }\n\n public ParameterSupport getParameterSupport() {\n if (parameterSupport == null) {\n parameterSupport = ParameterSupport.getInstance(getServletRequest());\n }\n\n return parameterSupport;\n }\n\n /*\n * Traverses the path segment wise and checks\n * if there is any path with only dots (\".\")\n * skipping SKIPPED_TRAVERSAL_CHARS characters in segment.\n */\n private static boolean traversesParentPath(String path) {\n int index = 0;\n while (index < path.length()) {\n int charCount = 0;\n int dotCount = 0;\n // count dots (\".\") and total chars in each path segment (between two '/')\n while (index < path.length() && path.charAt(index) != '/') {\n char c = path.charAt(index);\n if (!SKIPPED_TRAVERSAL_CHARS.contains(c)) {\n if (c == '.') {\n dotCount++;\n }\n charCount++;\n }\n index++;\n }\n // if all chars are dots (\".\")\n // path is traversing the parent directory\n if (charCount > 1 && dotCount == charCount) {\n return true;\n }\n index++;\n }\n return false;\n }\n\n}\n\n## 内容6\n### 文件路径:\nsrc/main/java/org/apache/sling/engine/impl/request/SlingRequestDispatcher.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\npackage org.apache.sling.engine.impl.request;\n\nimport java.io.IOException;\n\nimport javax.servlet.RequestDispatcher;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.sling.api.SlingConstants;\nimport org.apache.sling.api.SlingHttpServletRequest;\nimport org.apache.sling.api.request.RequestDispatcherOptions;\nimport org.apache.sling.api.request.RequestProgressTracker;\nimport org.apache.sling.api.resource.Resource;\nimport org.apache.sling.api.resource.ResourceWrapper;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class SlingRequestDispatcher implements RequestDispatcher {\n\n /** default log */\n private final Logger log = LoggerFactory.getLogger(getClass());\n\n private Resource resource;\n\n private RequestDispatcherOptions options;\n\n private String path;\n\n public SlingRequestDispatcher(String path, RequestDispatcherOptions options) {\n this.path = path;\n this.options = options;\n this.resource = null;\n }\n\n public SlingRequestDispatcher(Resource resource,\n RequestDispatcherOptions options) {\n\n this.resource = resource;\n this.options = options;\n this.path = resource.getPath();\n }\n\n @Override\n public void include(ServletRequest request, ServletResponse sResponse)\n throws ServletException, IOException {\n\n // guard access to the request and content data: If the request is\n // not (wrapping) a SlingHttpServletRequest, accessing the request Data\n // throws an IllegalArgumentException and we cannot continue\n final ContentData cd;\n try {\n cd = RequestData.getRequestData(request).getContentData();\n } catch (IllegalArgumentException iae) {\n throw new ServletException(iae.getMessage());\n }\n\n // ClassCastException is not expected here because we operate in\n // HTTP requests only (it cannot be excluded though if some client\n // code uses a ServletRequestWrapper rather than an\n // HttpServletRequestWrapper ...)\n final HttpServletRequest hRequest = (HttpServletRequest) request;\n\n // set the inclusion request attributes from the current request\n final Object v1 = setAttribute(request,\n SlingConstants.ATTR_REQUEST_CONTENT, cd.getResource());\n final Object v2 = setAttribute(request,\n SlingConstants.ATTR_REQUEST_SERVLET, cd.getServlet());\n final Object v3 = setAttribute(request,\n SlingConstants.ATTR_REQUEST_PATH_INFO, cd.getRequestPathInfo());\n final Object v4 = setAttribute(request,\n SlingConstants.ATTR_INCLUDE_CONTEXT_PATH, hRequest.getContextPath());\n final Object v5 = setAttribute(request,\n SlingConstants.ATTR_INCLUDE_PATH_INFO, hRequest.getPathInfo());\n final Object v6 = setAttribute(request,\n SlingConstants.ATTR_INCLUDE_QUERY_STRING, hRequest.getQueryString());\n final Object v7 = setAttribute(request,\n SlingConstants.ATTR_INCLUDE_REQUEST_URI, hRequest.getRequestURI());\n final Object v8 = setAttribute(request,\n SlingConstants.ATTR_INCLUDE_SERVLET_PATH, hRequest.getServletPath());\n\n try {\n\n dispatch(request, sResponse, true);\n\n } finally {\n\n // reset inclusion request attributes to previous values\n request.setAttribute(SlingConstants.ATTR_REQUEST_CONTENT, v1);\n request.setAttribute(SlingConstants.ATTR_REQUEST_SERVLET, v2);\n request.setAttribute(SlingConstants.ATTR_REQUEST_PATH_INFO, v3);\n request.setAttribute(SlingConstants.ATTR_INCLUDE_CONTEXT_PATH, v4);\n request.setAttribute(SlingConstants.ATTR_INCLUDE_PATH_INFO, v5);\n request.setAttribute(SlingConstants.ATTR_INCLUDE_QUERY_STRING, v6);\n request.setAttribute(SlingConstants.ATTR_INCLUDE_REQUEST_URI, v7);\n request.setAttribute(SlingConstants.ATTR_INCLUDE_SERVLET_PATH, v8);\n\n }\n }\n\n @Override\n public void forward(ServletRequest request, ServletResponse response)\n throws ServletException, IOException {\n\n // fail forwarding if the response has already been committed\n if (response.isCommitted()) {\n throw new IllegalStateException(\"Response already committed\");\n }\n\n // reset the response, will throw an IllegalStateException\n // if already committed, which will not be the case because\n // we already tested for this condition\n response.resetBuffer();\n\n // ensure inclusion information attributes are not set\n request.removeAttribute(SlingConstants.ATTR_REQUEST_CONTENT);\n request.removeAttribute(SlingConstants.ATTR_REQUEST_SERVLET);\n request.removeAttribute(SlingConstants.ATTR_REQUEST_PATH_INFO);\n request.removeAttribute(SlingConstants.ATTR_INCLUDE_CONTEXT_PATH);\n request.removeAttribute(SlingConstants.ATTR_INCLUDE_PATH_INFO);\n request.removeAttribute(SlingConstants.ATTR_INCLUDE_QUERY_STRING);\n request.removeAttribute(SlingConstants.ATTR_INCLUDE_REQUEST_URI);\n request.removeAttribute(SlingConstants.ATTR_INCLUDE_SERVLET_PATH);\n\n // now just include as normal\n dispatch(request, response, false);\n\n // finally, we would have to ensure the response is committed\n // and closed. Let's just flush the buffer and thus commit the\n // response for now\n response.flushBuffer();\n }\n\n private String getAbsolutePath(SlingHttpServletRequest request, String path) {\n // path is already absolute\n if (path.startsWith(\"/\")) {\n return path;\n }\n\n // get parent of current request\n String uri = request.getResource().getPath();\n int lastSlash = uri.lastIndexOf('/');\n if (lastSlash >= 0) {\n uri = uri.substring(0, lastSlash);\n }\n\n // append relative path to parent\n return uri + '/' + path;\n }\n\n /**\n * Dispatch the request\n * @param request The request\n * @param response The response\n * @param include Is this an include (or forward)\n */\n private void dispatch(final ServletRequest request, final ServletResponse response,\n final boolean include) throws ServletException, IOException {\n SlingHttpServletRequest cRequest = RequestData.unwrap(request);\n RequestData rd = RequestData.getRequestData(cRequest);\n String absPath = getAbsolutePath(cRequest, path);\n RequestProgressTracker requestProgressTracker = cRequest.getRequestProgressTracker();\n\n // if the response is not an HttpServletResponse, fail gracefully not\n // doing anything\n if (!(response instanceof HttpServletResponse)) {\n log.error(\"include: Failed to include {}, response has wrong type\",\n absPath);\n return;\n }\n\n if (resource == null) {\n String timerName = \"resolveIncludedResource(\" + absPath + \")\";\n requestProgressTracker.startTimer(timerName);\n\n // resolve the absolute path in the resource resolver, using\n // only those parts of the path as if it would be request path\n resource = cRequest.getResourceResolver().resolve(absPath);\n\n // if the resource could not be resolved, fail gracefully\n if (resource == null) {\n log.error(\n \"include: Could not resolve {} to a resource, not including\",\n absPath);\n return;\n }\n\n requestProgressTracker.logTimer(timerName,\n \"path={0} resolves to Resource={1}\",\n absPath, resource);\n }\n\n // ensure request path info and optional merges\n SlingRequestPathInfo info = getMergedRequestPathInfo(cRequest);\n requestProgressTracker.log(\n \"Including resource {0} ({1})\", resource, info);\n final boolean protectHeaders = this.options != null ? this.options.isProtectHeadersOnInclude() : false;\n rd.getSlingRequestProcessor().dispatchRequest(request, response, resource,\n info, include, protectHeaders);\n }\n\n /**\n * Returns a {@link SlingRequestPathInfo} object to use to select the\n * servlet or script to call to render the resource to be dispatched to.\n * <p>\n * <b>Note:</b> If this request dispatcher has been created with resource\n * type overwriting request dispatcher options, the resource to dispatch to\n * may be wrapped with a {@link TypeOverwritingResourceWrapper} as a result\n * of calling this method.\n */\n private SlingRequestPathInfo getMergedRequestPathInfo(\n final SlingHttpServletRequest cRequest) {\n SlingRequestPathInfo info = new SlingRequestPathInfo(resource);\n info = info.merge(cRequest.getRequestPathInfo());\n\n // merge request dispatcher options and resource type overwrite\n if (options != null) {\n info = info.merge(options);\n\n // ensure overwritten resource type\n String rtOverwrite = options.getForceResourceType();\n if (rtOverwrite != null\n && !rtOverwrite.equals(resource.getResourceType())) {\n resource = new TypeOverwritingResourceWrapper(resource,\n rtOverwrite);\n }\n }\n\n return info;\n }\n\n private Object setAttribute(final ServletRequest request,\n final String name, final Object value) {\n final Object oldValue = request.getAttribute(name);\n request.setAttribute(name, value);\n return oldValue;\n }\n\n private static class TypeOverwritingResourceWrapper extends ResourceWrapper {\n\n private final String resourceType;\n\n TypeOverwritingResourceWrapper(Resource delegatee, String resourceType) {\n super(delegatee);\n this.resourceType = resourceType;\n }\n\n @Override\n public String getResourceType() {\n return resourceType;\n }\n\n /**\n * Overwrite this here because the wrapped resource will return null as\n * a super type instead of the resource type overwritten here\n */\n @Override\n public String getResourceSuperType() {\n return null;\n }\n\n @Override\n public boolean isResourceType(final String resourceType) {\n return this.getResourceResolver().isResourceType(this, resourceType);\n }\n\n }\n}\n\n", "input": "", "output": "【漏洞类型】路径遍历漏洞\n【严重性】High\n【漏洞摘要】代码中存在路径遍历漏洞,根源在于`SlingRequestDispatcher`类的`dispatch`方法中对`absPath`的处理不充分。具体表现为,如果`path`参数包含特定的字符(如“..”或“%5B”),仍然可能解析出上级目录的资源,从而导致路径遍历攻击。问题行在`SlingRequestDispatcher.java`文件的`getAbsolutePath`方法中:\n```java\nreturn uri + '/' + path;\n```\n【关键代码模式】与路径遍历漏洞相关的关键代码模式、函数或关键词包括:\n- `getAbsolutePath`方法中对`path`参数的处理。\n- `dispatch`方法中使用`absPath`来解析资源。\n- `SlingRequestDispatcher`类的构造方法,接收`path`参数。\n- `RequestDispatcher`接口的`include`和`forward`方法。\n- `HttpServletRequest`类的`getContextPath`和`getServletPath`方法。\n\n【修复方法说明】修复方法通过增加`protectHeadersOnInclude`和`checkContentTypeOnInclude`两个配置选项,以及创建新的响应包装器`IncludeNoContentTypeOverrideResponseWrapper`,在`dispatchRequest`方法中对`RequestDispatcher.include`操作进行保护,防止头部和内容类型的更改,从而解决路径遍历漏洞问题。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nxwiki-platform-core/xwiki-platform-bridge/pom.xml\n### 代码内容:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n-->\n\n<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/maven-v4_0_0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n <parent>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-core</artifactId>\n <version>15.2-SNAPSHOT</version>\n </parent>\n <artifactId>xwiki-platform-bridge</artifactId>\n <name>XWiki Platform - Bridge</name>\n <packaging>jar</packaging>\n <description>Temporary bridge between new components and the old core, until the old core is completely split into components</description>\n <properties>\n <xwiki.jacoco.instructionRatio>0.39</xwiki.jacoco.instructionRatio>\n <!-- Name to display by the Extension Manager -->\n <xwiki.extension.name>Model Bridge API</xwiki.extension.name>\n </properties>\n <dependencies>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-model-api</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-component-api</artifactId>\n <version>${commons.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.rendering</groupId>\n <artifactId>xwiki-rendering-api</artifactId>\n <version>${rendering.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-observation-api</artifactId>\n <version>${commons.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-security-authorization-api</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-user-api</artifactId>\n <version>${project.version}</version>\n </dependency>\n <!-- Test Dependencies -->\n <dependency>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-tool-test-component</artifactId>\n <version>${commons.version}</version>\n <scope>test</scope>\n </dependency>\n </dependencies>\n</project>\n\n## 内容2\n### 文件路径:\nxwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-api/src/main/java/org/xwiki/icon/IconException.java\n### 代码内容:\n/*\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n */\npackage org.xwiki.icon;\n\n/**\n * Exception relating to icon manipulations.\n *\n * @since 6.2M1\n * @version $Id$\n */\npublic class IconException extends Exception\n{\n /**\n * Constructor.\n *\n * @param message message to store in the exception\n * @param source source of the error\n */\n public IconException(String message, Throwable source)\n {\n super(message, source);\n }\n}\n\n## 内容3\n### 文件路径:\nxwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-api/src/main/java/org/xwiki/icon/IconSet.java\n### 代码内容:\n/*\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n */\npackage org.xwiki.icon;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * A collection of icons, with some properties to display them.\n *\n * @version $Id$\n * @since 6.2M1\n */\npublic class IconSet\n{\n private String name;\n\n private Map<String, Icon> iconMap = new HashMap<>();\n\n private String css;\n\n private String ssx;\n\n private String jsx;\n\n private String renderWiki;\n\n private String renderHTML;\n\n private String iconUrl;\n\n private String iconCssClass;\n\n private IconType type;\n\n /**\n * Constructor.\n *\n * @param name name of the icon set\n */\n public IconSet(String name)\n {\n this.name = name;\n }\n\n /**\n * @param name name of the icon to get\n * @return the icon corresponding to the given name\n */\n public Icon getIcon(String name)\n {\n return iconMap.get(name);\n }\n\n /**\n * Add an icon to the icon set.\n *\n * @param name name of the icon\n * @param icon the icon to add\n */\n public void addIcon(String name, Icon icon)\n {\n iconMap.put(name, icon);\n }\n\n /**\n * @return the name of the icon set\n */\n public String getName()\n {\n return name;\n }\n\n /**\n * Set the name of the icon set.\n *\n * @param name the name to set\n */\n public void setName(String name)\n {\n this.name = name;\n }\n\n /**\n * @return the URL of a CSS file to enable to display this icon set properly, or null if it is not necessary\n */\n public String getCss()\n {\n return css;\n }\n\n /**\n * Set the URL of a CSS file to enable to display this icon set properly.\n *\n * @param css URL of the CSS file (it can contains velocity code).\n */\n public void setCss(String css)\n {\n this.css = css;\n }\n\n /**\n * @return the name of a SSX document to enable to display this icon set properly, or null if it is not necessary\n */\n public String getSsx()\n {\n return ssx;\n }\n\n /**\n * Set the page name of a SSX document to enable to display the icon set properly.\n *\n * @param ssx the SSX document name\n */\n public void setSsx(String ssx)\n {\n this.ssx = ssx;\n }\n\n /**\n * @return the name of a JSX document to enable to display this icon set properly, or null if it is not necessary\n */\n public String getJsx()\n {\n return jsx;\n }\n\n /**\n * Set the page name of a JSX document to enable to display the icon set properly.\n *\n * @param jsx the JSX document name\n */\n public void setJsx(String jsx)\n {\n this.jsx = jsx;\n }\n\n /**\n * @return the wiki code (containing velocity), to display an icon from this set.\n */\n public String getRenderWiki()\n {\n return renderWiki;\n }\n\n /**\n * Set the wiki code (containing velocity) to display an icon from this set.\n *\n * @param renderWiki wiki code to set\n */\n public void setRenderWiki(String renderWiki)\n {\n this.renderWiki = renderWiki;\n }\n\n /**\n * @return the HTML code (containing velocity) to display an icon from this set\n */\n public String getRenderHTML()\n {\n return renderHTML;\n }\n\n /**\n * Set the HTML code (containing velocity) to display an icon from this set.\n *\n * @param renderHTML the HTML code to set\n */\n public void setRenderHTML(String renderHTML)\n {\n this.renderHTML = renderHTML;\n }\n\n /**\n * @return the icon url\n * @since 10.6RC1\n */\n public String getIconUrl()\n {\n return iconUrl;\n }\n\n /**\n * Set the url of the icon.\n *\n * @param iconUrl the icon url\n * @since 10.6RC1\n */\n public void setIconUrl(String iconUrl)\n {\n this.iconUrl = iconUrl;\n }\n\n /**\n * @return the icon css class\n * @since 10.6RC1\n */\n public String getIconCssClass()\n {\n return iconCssClass;\n }\n\n /**\n * Set the css class of the icon.\n *\n * @param iconCssClass the icon css class\n * @since 10.6RC1\n */\n public void setIconCssClass(String iconCssClass)\n {\n this.iconCssClass = iconCssClass;\n }\n\n /**\n * @return the type of icons that contains this set\n */\n public IconType getType()\n {\n return type;\n }\n\n /**\n * Set the type of icons that contains this set.\n *\n * @param type type to set\n */\n public void setType(IconType type)\n {\n this.type = type;\n }\n\n /**\n * @return the list of names of all icons\n * @since 6.4M1\n */\n public List<String> getIconNames()\n {\n return new ArrayList<>(iconMap.keySet());\n }\n\n /**\n * Checks if the provided icon name exists in the icon set.\n *\n * @param iconName an icon name (for instance, {@code add})\n * @return {@code true} if the icon name exists in the icon set, {@code false} otherwise\n * @since 13.4RC1\n */\n public boolean hasIcon(String iconName)\n {\n return this.iconMap.containsKey(iconName);\n }\n}\n\n## 内容4\n### 文件路径:\nxwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-default/pom.xml\n### 代码内容:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n-->\n\n<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/maven-v4_0_0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n <parent>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-icon</artifactId>\n <version>15.2-SNAPSHOT</version>\n </parent>\n <artifactId>xwiki-platform-icon-default</artifactId>\n <name>XWiki Platform - Icon - Default implementation</name>\n <description>XWiki Platform - Icon - Default implementation</description>\n <properties>\n <xwiki.jacoco.instructionRatio>0.97</xwiki.jacoco.instructionRatio>\n </properties>\n <dependencies>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-icon-api</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-oldcore</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-bridge</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-skin-api</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-wiki-api</artifactId>\n <version>${project.version}</version>\n </dependency>\n\n <!-- Test Dependencies -->\n <dependency>\n <groupId>javax.servlet</groupId>\n <artifactId>javax.servlet-api</artifactId>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-tool-test-component</artifactId>\n <version>${commons.version}</version>\n <scope>test</scope>\n </dependency>\n <!-- Test component list dependency to skinx. -->\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-skin-skinx</artifactId>\n <version>${project.version}</version>\n <scope>test</scope>\n </dependency>\n </dependencies>\n <build>\n <plugins>\n <plugin>\n <groupId>org.apache.maven.plugins</groupId>\n <artifactId>maven-jar-plugin</artifactId>\n <executions>\n <execution>\n <id>test-jar</id>\n <goals>\n <goal>test-jar</goal>\n </goals>\n <configuration>\n <includes>\n <include>**/DefaultIconManagerComponentList.class</include>\n </includes>\n </configuration>\n </execution>\n </executions>\n </plugin>\n </plugins>\n </build>\n</project>\n\n## 内容5\n### 文件路径:\nxwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-default/src/main/java/org/xwiki/icon/internal/DefaultIconSetLoader.java\n### 代码内容:\n/*\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n */\npackage org.xwiki.icon.internal;\n\nimport java.io.IOException;\nimport java.io.Reader;\nimport java.io.StringReader;\nimport java.util.Properties;\n\nimport javax.inject.Inject;\nimport javax.inject.Singleton;\n\nimport org.xwiki.bridge.DocumentAccessBridge;\nimport org.xwiki.bridge.DocumentModelBridge;\nimport org.xwiki.component.annotation.Component;\nimport org.xwiki.icon.Icon;\nimport org.xwiki.icon.IconException;\nimport org.xwiki.icon.IconSet;\nimport org.xwiki.icon.IconSetLoader;\nimport org.xwiki.icon.IconType;\nimport org.xwiki.model.reference.DocumentReference;\nimport org.xwiki.wiki.descriptor.WikiDescriptorManager;\n\n/**\n * Default implementation of {@link org.xwiki.icon.IconSetLoader}.\n *\n * @version $Id$\n * @since 6.2M1\n */\n@Component\n@Singleton\npublic class DefaultIconSetLoader implements IconSetLoader\n{\n private static final String CSS_PROPERTY_NAME = \"xwiki.iconset.css\";\n\n private static final String SSX_PROPERTY_NAME = \"xwiki.iconset.ssx\";\n\n private static final String JSX_PROPERTY_NAME = \"xwiki.iconset.jsx\";\n\n private static final String RENDER_WIKI_PROPERTY_NAME = \"xwiki.iconset.render.wiki\";\n\n private static final String RENDER_HTML_PROPERTY_NAME = \"xwiki.iconset.render.html\";\n\n private static final String ICON_TYPE_PROPERTY_NAME = \"xwiki.iconset.type\";\n\n private static final String ICON_URL_PROPERTY_NAME = \"xwiki.iconset.icon.url\";\n\n private static final String ICON_CSS_CLASS_PROPERTY_NAME = \"xwiki.iconset.icon.cssClass\";\n\n private static final String ERROR_MSG = \"Failed to load the IconSet [%s].\";\n\n @Inject\n private DocumentAccessBridge documentAccessBridge;\n\n @Inject\n private WikiDescriptorManager wikiDescriptorManager;\n\n @Override\n public IconSet loadIconSet(DocumentReference iconSetReference) throws IconException\n {\n try {\n // Get the document\n DocumentModelBridge doc = documentAccessBridge.getDocumentInstance(iconSetReference);\n String content = doc.getContent();\n // The name of the icon set is stored in the IconThemesCode.IconThemeClass XObject of the document\n DocumentReference iconClassRef = new DocumentReference(wikiDescriptorManager.getCurrentWikiId(),\n \"IconThemesCode\", \"IconThemeClass\");\n String name = (String) documentAccessBridge.getProperty(iconSetReference, iconClassRef, \"name\");\n // Load the icon set\n return loadIconSet(new StringReader(content), name);\n } catch (Exception e) {\n throw new IconException(String.format(ERROR_MSG, iconSetReference), e);\n }\n }\n\n @Override\n public IconSet loadIconSet(Reader input, String name) throws IconException\n {\n IconSet iconSet = new IconSet(name);\n Properties properties = new Properties();\n try {\n properties.load(input);\n } catch (IOException e) {\n throw new IconException(String.format(ERROR_MSG, name), e);\n }\n\n properties.stringPropertyNames().forEach(key -> setIconSetProperty(iconSet, key, properties.getProperty(key)));\n\n return iconSet;\n }\n\n private void setIconSetProperty(IconSet iconSet, String key, String value)\n {\n if (CSS_PROPERTY_NAME.equals(key)) {\n iconSet.setCss(value);\n } else if (SSX_PROPERTY_NAME.equals(key)) {\n iconSet.setSsx(value);\n } else if (JSX_PROPERTY_NAME.equals(key)) {\n iconSet.setJsx(value);\n } else if (RENDER_WIKI_PROPERTY_NAME.equals(key)) {\n iconSet.setRenderWiki(value);\n } else if (RENDER_HTML_PROPERTY_NAME.equals(key)) {\n iconSet.setRenderHTML(value);\n } else if (ICON_TYPE_PROPERTY_NAME.equals(key)) {\n iconSet.setType(IconType.valueOf(value.toUpperCase()));\n } else if (ICON_URL_PROPERTY_NAME.equals(key)) {\n iconSet.setIconUrl(value);\n } else if (ICON_CSS_CLASS_PROPERTY_NAME.equals(key)) {\n iconSet.setIconCssClass(value);\n } else {\n Icon icon = new Icon();\n icon.setValue(value);\n iconSet.addIcon(key, icon);\n }\n }\n}\n\n## 内容6\n### 文件路径:\nxwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-default/src/main/java/org/xwiki/icon/internal/DefaultIconSetManager.java\n### 代码内容:\n/*\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n */\npackage org.xwiki.icon.internal;\n\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.util.List;\n\nimport javax.inject.Inject;\nimport javax.inject.Named;\nimport javax.inject.Provider;\nimport javax.inject.Singleton;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.xwiki.bridge.DocumentAccessBridge;\nimport org.xwiki.component.annotation.Component;\nimport org.xwiki.configuration.ConfigurationSource;\nimport org.xwiki.icon.IconException;\nimport org.xwiki.icon.IconSet;\nimport org.xwiki.icon.IconSetCache;\nimport org.xwiki.icon.IconSetLoader;\nimport org.xwiki.icon.IconSetManager;\nimport org.xwiki.icon.internal.context.IconSetContext;\nimport org.xwiki.model.reference.DocumentReference;\nimport org.xwiki.model.reference.DocumentReferenceResolver;\nimport org.xwiki.query.Query;\nimport org.xwiki.query.QueryException;\nimport org.xwiki.query.QueryManager;\nimport org.xwiki.wiki.descriptor.WikiDescriptorManager;\n\nimport com.xpn.xwiki.XWiki;\nimport com.xpn.xwiki.XWikiContext;\n\n/**\n * Default implementation of {@link org.xwiki.icon.IconSetManager}.\n *\n * @since 6.2M1\n * @version $Id$\n */\n@Component\n@Singleton\npublic class DefaultIconSetManager implements IconSetManager\n{\n private static final String DEFAULT_ICONSET_NAME = \"default\";\n\n @Inject\n private Provider<XWikiContext> xcontextProvider;\n\n @Inject\n @Named(\"current\")\n private DocumentReferenceResolver<String> documentReferenceResolver;\n\n @Inject\n private DocumentAccessBridge documentAccessBridge;\n\n @Inject\n private IconSetCache iconSetCache;\n\n @Inject\n private IconSetLoader iconSetLoader;\n\n @Inject\n private IconSetContext iconSetContext;\n\n @Inject\n private QueryManager queryManager;\n\n @Inject\n private WikiDescriptorManager wikiDescriptorManager;\n \n @Inject\n @Named(\"all\")\n private ConfigurationSource configurationSource;\n\n @Override\n public IconSet getCurrentIconSet() throws IconException\n {\n // Check the context\n IconSet iconSet = this.iconSetContext.getIconSet();\n if (iconSet != null) {\n return iconSet;\n }\n\n // Get the current icon theme\n String iconTheme = this.configurationSource.getProperty(\"iconTheme\");\n\n // Get the icon set\n DocumentReference iconThemeDocRef = documentReferenceResolver.resolve(iconTheme);\n if (!StringUtils.isBlank(iconTheme) && exists(iconThemeDocRef)) {\n iconSet = iconSetCache.get(iconThemeDocRef);\n if (iconSet == null) {\n // lazy loading\n iconSet = iconSetLoader.loadIconSet(iconThemeDocRef);\n iconSetCache.put(iconThemeDocRef, iconSet);\n iconSetCache.put(iconSet.getName(), wikiDescriptorManager.getCurrentWikiId(), iconSet);\n }\n }\n\n return iconSet;\n }\n\n private boolean exists(DocumentReference iconThemeDocRef) throws IconException\n {\n try {\n return this.documentAccessBridge.exists(iconThemeDocRef);\n } catch (Exception e) {\n throw new IconException(\"Failed to check if the icon theme age exist\", e);\n }\n }\n\n @Override\n public IconSet getDefaultIconSet() throws IconException\n {\n XWikiContext xcontext = xcontextProvider.get();\n XWiki xwiki = xcontext.getWiki();\n\n IconSet iconSet = iconSetCache.get(DEFAULT_ICONSET_NAME);\n if (iconSet == null) {\n // lazy loading\n try (InputStreamReader reader =\n new InputStreamReader(xwiki.getResourceAsStream(\"/resources/icons/default.iconset\"))) {\n iconSet = iconSetLoader.loadIconSet(reader, DEFAULT_ICONSET_NAME);\n iconSetCache.put(DEFAULT_ICONSET_NAME, iconSet);\n } catch (IOException e) {\n throw new IconException(\"Failed to load the current default icon set resource.\", e);\n } catch (IconException e) {\n throw new IconException(\"Failed to get the current default icon set.\", e);\n }\n }\n\n return iconSet;\n }\n\n @Override\n public IconSet getIconSet(String name) throws IconException\n {\n // Special case: the default icon theme\n if (DEFAULT_ICONSET_NAME.equals(name)) {\n return getDefaultIconSet();\n }\n\n // Get the icon set from the cache\n IconSet iconSet = iconSetCache.get(name, wikiDescriptorManager.getCurrentWikiId());\n\n // Load it if it is not loaded yet\n if (iconSet == null) {\n try {\n // Search by name\n String xwql = \"FROM doc.object(IconThemesCode.IconThemeClass) obj WHERE obj.name = :name\";\n Query query = queryManager.createQuery(xwql, Query.XWQL);\n query.bindValue(\"name\", name);\n List<String> results = query.execute();\n if (results.isEmpty()) {\n return null;\n }\n\n // Get the first result\n String docName = results.get(0);\n DocumentReference docRef = documentReferenceResolver.resolve(docName);\n\n // Load the icon theme\n iconSet = iconSetLoader.loadIconSet(docRef);\n\n // Put it in the cache\n iconSetCache.put(docRef, iconSet);\n iconSetCache.put(name, wikiDescriptorManager.getCurrentWikiId(), iconSet);\n } catch (QueryException e) {\n throw new IconException(String.format(\"Failed to load the icon set [%s].\", name), e);\n }\n }\n\n // Return the icon set\n return iconSet;\n }\n\n @Override\n public List<String> getIconSetNames() throws IconException\n {\n try {\n String xwql = \"SELECT obj.name FROM Document doc, doc.object(IconThemesCode.IconThemeClass) obj \"\n + \"ORDER BY obj.name\";\n Query query = queryManager.createQuery(xwql, Query.XWQL);\n return query.execute();\n } catch (QueryException e) {\n throw new IconException(\"Failed to get the name of all icon sets.\", e);\n }\n }\n}\n\n## 内容7\n### 文件路径:\nxwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-default/src/main/java/org/xwiki/icon/internal/VelocityRenderer.java\n### 代码内容:\n/*\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n */\npackage org.xwiki.icon.internal;\n\nimport java.io.StringWriter;\n\nimport javax.inject.Inject;\nimport javax.inject.Singleton;\n\nimport org.apache.velocity.VelocityContext;\nimport org.xwiki.component.annotation.Component;\nimport org.xwiki.icon.IconException;\nimport org.xwiki.logging.LoggerConfiguration;\nimport org.xwiki.velocity.VelocityEngine;\nimport org.xwiki.velocity.VelocityManager;\nimport org.xwiki.velocity.XWikiVelocityContext;\nimport org.xwiki.velocity.XWikiVelocityException;\n\n/**\n * Internal helper to render safely any velocity code.\n *\n * @version $Id$\n * @since 6.4M1\n */\n@Component(roles = VelocityRenderer.class)\n@Singleton\npublic class VelocityRenderer\n{\n @Inject\n private VelocityManager velocityManager;\n\n @Inject\n private LoggerConfiguration loggerConfiguration;\n\n /**\n * Render a velocity code without messing with the document context and namespace.\n * @param code code to render\n * @return the rendered code\n * @throws IconException if problem occurs\n */\n public String render(String code) throws IconException\n {\n // The macro namespace to use by the velocity engine, see afterwards.\n String namespace = \"IconVelocityRenderer_\" + Thread.currentThread().getId();\n\n // Create the output writer\n StringWriter output = new StringWriter();\n\n VelocityEngine engine = null;\n try {\n // Get the velocity engine\n engine = velocityManager.getVelocityEngine();\n\n // Use a new macro namespace to prevent the code redefining existing macro.\n // We use the thread name to have a unique id.\n engine.startedUsingMacroNamespace(namespace);\n\n // Create a new VelocityContext to prevent the code creating variables in the current context.\n // See https://jira.xwiki.org/browse/XWIKI-11400.\n // We set the current context as inner context of the new one to be able to read existing variables.\n // See https://jira.xwiki.org/browse/XWIKI-11426.\n VelocityContext context = new XWikiVelocityContext(velocityManager.getVelocityContext(),\n this.loggerConfiguration.isDeprecatedLogEnabled());\n\n // Render the code\n if (engine.evaluate(context, output, \"DefaultIconRenderer\", code)) {\n return output.toString();\n } else {\n // I don't know how to check the velocity runtime log\n throw new IconException(\"Failed to render the icon. See the Velocity runtime log.\", null);\n }\n } catch (XWikiVelocityException e) {\n throw new IconException(\"Failed to render the icon.\", e);\n } finally {\n // Do not forget to close the macro namespace we have created previously\n if (engine != null) {\n engine.stoppedUsingMacroNamespace(namespace);\n }\n }\n }\n}\n\n## 内容8\n### 文件路径:\nxwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-xwiki/src/main/java/org/xwiki/rendering/script/RenderingScriptService.java\n### 代码内容:\n/*\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n */\npackage org.xwiki.rendering.script;\n\nimport java.io.StringReader;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\n\nimport javax.inject.Inject;\nimport javax.inject.Named;\nimport javax.inject.Provider;\nimport javax.inject.Singleton;\n\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.slf4j.Logger;\nimport org.xwiki.component.annotation.Component;\nimport org.xwiki.component.manager.ComponentLookupException;\nimport org.xwiki.component.manager.ComponentManager;\nimport org.xwiki.rendering.block.Block;\nimport org.xwiki.rendering.block.XDOM;\nimport org.xwiki.rendering.configuration.ExtendedRenderingConfiguration;\nimport org.xwiki.rendering.configuration.RenderingConfiguration;\nimport org.xwiki.rendering.macro.MacroCategoryManager;\nimport org.xwiki.rendering.macro.MacroId;\nimport org.xwiki.rendering.macro.MacroIdFactory;\nimport org.xwiki.rendering.macro.MacroLookupException;\nimport org.xwiki.rendering.macro.MacroManager;\nimport org.xwiki.rendering.macro.descriptor.MacroDescriptor;\nimport org.xwiki.rendering.parser.ParseException;\nimport org.xwiki.rendering.parser.Parser;\nimport org.xwiki.rendering.renderer.BlockRenderer;\nimport org.xwiki.rendering.renderer.PrintRendererFactory;\nimport org.xwiki.rendering.renderer.printer.DefaultWikiPrinter;\nimport org.xwiki.rendering.renderer.printer.WikiPrinter;\nimport org.xwiki.rendering.syntax.Syntax;\nimport org.xwiki.script.service.ScriptService;\nimport org.xwiki.stability.Unstable;\n\n/**\n * Provides Rendering-specific Scripting APIs.\n *\n * @version $Id$\n * @since 2.3M1\n */\n@Component\n@Named(\"rendering\")\n@Singleton\npublic class RenderingScriptService implements ScriptService\n{\n /**\n * Used to lookup parsers and renderers to discover available syntaxes.\n */\n @Inject\n @Named(\"context\")\n private Provider<ComponentManager> componentManagerProvider;\n\n @Inject\n private Logger logger;\n\n @Inject\n private RenderingConfiguration baseConfiguration;\n\n @Inject\n private ExtendedRenderingConfiguration extendedConfiguration;\n\n @Inject\n private MacroManager macroManager;\n\n @Inject\n private MacroCategoryManager macroCategoryManager;\n \n @Inject\n private MacroIdFactory macroIdFactory;\n\n /**\n * @return the list of syntaxes for which a Parser is available\n */\n public List<Syntax> getAvailableParserSyntaxes()\n {\n List<Syntax> syntaxes = new ArrayList<>();\n try {\n for (Parser parser : this.componentManagerProvider.get().<Parser>getInstanceList(Parser.class)) {\n syntaxes.add(parser.getSyntax());\n }\n } catch (ComponentLookupException e) {\n // Failed to lookup parsers, consider there are no syntaxes available\n this.logger.error(\"Failed to lookup parsers\", e);\n }\n\n return syntaxes;\n }\n\n /**\n * @return the list of syntaxes for which a Renderer is available\n */\n public List<Syntax> getAvailableRendererSyntaxes()\n {\n List<Syntax> syntaxes = new ArrayList<>();\n try {\n List<PrintRendererFactory> factories =\n this.componentManagerProvider.get().getInstanceList(PrintRendererFactory.class);\n for (PrintRendererFactory factory : factories) {\n syntaxes.add(factory.getSyntax());\n }\n } catch (ComponentLookupException e) {\n // Failed to lookup renderers, consider there are no syntaxes available\n this.logger.error(\"Failed to lookup renderers\", e);\n }\n\n return syntaxes;\n }\n\n /**\n * @return the names of Transformations that are configured in the Rendering Configuration and which are used by the\n * Transformation Manager when running all transformations\n */\n public List<String> getDefaultTransformationNames()\n {\n return this.baseConfiguration.getTransformationNames();\n }\n\n /**\n * Parses a text written in the passed syntax.\n *\n * @param text the text to parse\n * @param syntaxId the id of the syntax in which the text is written in\n * @return the XDOM representing the AST of the parsed text or null if an error occurred\n * @since 3.2M3\n */\n public XDOM parse(String text, String syntaxId)\n {\n XDOM result;\n try {\n Parser parser = this.componentManagerProvider.get().getInstance(Parser.class, syntaxId);\n result = parser.parse(new StringReader(text));\n } catch (Exception e) {\n result = null;\n }\n return result;\n }\n\n /**\n * Render a list of Blocks into the passed syntax.\n *\n * @param block the block to render\n * @param outputSyntaxId the syntax in which to render the blocks\n * @return the string representing the passed blocks in the passed syntax or null if an error occurred\n * @since 3.2M3\n */\n public String render(Block block, String outputSyntaxId)\n {\n String result;\n WikiPrinter printer = new DefaultWikiPrinter();\n try {\n BlockRenderer renderer =\n this.componentManagerProvider.get().getInstance(BlockRenderer.class, outputSyntaxId);\n renderer.render(block, printer);\n result = printer.toString();\n } catch (Exception e) {\n result = null;\n }\n return result;\n }\n\n /**\n * Converts a Syntax specified as a String into a proper Syntax object.\n *\n * @param syntaxId the syntax as a string (eg \"xwiki/2.0\", \"html/4.01\", etc)\n * @return the proper Syntax object representing the passed syntax\n */\n public Syntax resolveSyntax(String syntaxId)\n {\n Syntax syntax;\n try {\n syntax = Syntax.valueOf(syntaxId);\n } catch (ParseException exception) {\n syntax = null;\n }\n return syntax;\n }\n\n /**\n * Escapes a give text using the escaping method specific to the given syntax.\n * <p>\n * One example of escaping method is using escape characters like {@code ~} for the {@link Syntax#XWIKI_2_1} syntax\n * on all or just some characters of the given text.\n * <p>\n * The current implementation only escapes XWiki 1.0, 2.0 and 2.1 syntaxes.\n *\n * @param content the text to escape\n * @param syntax the syntax to escape the content in (e.g. {@link Syntax#XWIKI_1_0}, {@link Syntax#XWIKI_2_0},\n * {@link Syntax#XWIKI_2_1}, etc.). This is the syntax where the output will be used and not necessarily\n * the same syntax of the input content\n * @return the escaped text or {@code null} if the given content or the given syntax are {@code null}, or if the\n * syntax is not supported\n * @since 7.1M1\n */\n public String escape(String content, Syntax syntax)\n {\n if (content == null || syntax == null) {\n return null;\n }\n\n // Determine the escape character for the syntax.\n char escapeChar;\n try {\n escapeChar = getEscapeCharacter(syntax);\n } catch (Exception e) {\n // We don`t know how to proceed, so we just return null.\n return null;\n }\n\n // Since we prefix all characters, the result size will be double the input's, so we can just use char[].\n char[] result = new char[content.length() * 2];\n\n // Escape the content.\n for (int i = 0; i < content.length(); i++) {\n result[2 * i] = escapeChar;\n result[2 * i + 1] = content.charAt(i);\n }\n\n return String.valueOf(result);\n }\n\n /**\n * @return the list of Rendering Syntaxes that are configured for the current wiki (i.e. that are proposed to the\n * user when editing wiki pages). These are input Syntaxes only (i.e. Syntaxes having a Parser for them).\n * @since 8.2M1\n */\n public List<Syntax> getConfiguredSyntaxes()\n {\n return this.extendedConfiguration.getConfiguredSyntaxes();\n }\n\n /**\n * @return the list of Rendering Syntaxes that are disabled for the current wiki (i.e. that should not be proposed\n * to the user when editing wiki pages)\n * @since 8.2M1\n */\n public List<Syntax> getDisabledSyntaxes()\n {\n return this.extendedConfiguration.getDisabledSyntaxes();\n }\n\n /**\n * @param syntax the syntax for which to return the list of Macro descriptors\n * @return the macro descriptors for the macros registered and available to the passed syntax\n * @throws MacroLookupException if a macro component descriptor cannot be loaded\n * @since 9.7RC1\n */\n public List<MacroDescriptor> getMacroDescriptors(Syntax syntax) throws MacroLookupException\n {\n List<MacroDescriptor> macroDescriptors = new ArrayList<>();\n for (MacroId id : this.macroManager.getMacroIds(syntax)) {\n macroDescriptors.add(this.macroManager.getMacro(id).getDescriptor());\n }\n return macroDescriptors;\n }\n\n /**\n * @param macroIdAsString a string representing a macro id\n * @return the resolved macro id or {@code null} if resolving the given string fails\n * @since 10.10RC1\n */\n public MacroId resolveMacroId(String macroIdAsString)\n {\n try {\n return this.macroIdFactory.createMacroId(macroIdAsString);\n } catch (ParseException e) {\n this.logger.warn(\"Failed to resolve macro id [{}]. Root cause is: [{}]\", macroIdAsString,\n ExceptionUtils.getRootCauseMessage(e));\n return null;\n }\n }\n\n /**\n * @param macroId the macro id\n * @return the descriptor of the specified macro if it exists, {@code null} otherwise\n * @since 10.10RC1\n */\n public MacroDescriptor getMacroDescriptor(MacroId macroId)\n {\n if (this.macroManager.exists(macroId)) {\n try {\n return this.macroManager.getMacro(macroId).getDescriptor();\n } catch (MacroLookupException e) {\n // Shouldn't happen normally.\n }\n }\n return null;\n }\n\n /**\n * Return the list of categories of a given macro.\n *\n * @param macroId the macro id\n * @return the list of categories of the macro\n * @since 14.6RC1\n */\n @Unstable\n public Set<String> getMacroCategories(MacroId macroId)\n {\n return this.macroCategoryManager.getMacroCategories(macroId);\n }\n\n /**\n * @return the set of hidden macro categories\n * @since 14.8RC1\n */\n @Unstable\n public Set<String> getHiddenMacroCategories()\n {\n return this.macroCategoryManager.getHiddenCategories();\n }\n\n private char getEscapeCharacter(Syntax syntax) throws IllegalArgumentException\n {\n if (Syntax.XWIKI_1_0.equals(syntax)) {\n return '\\\\';\n } else if (Syntax.XWIKI_2_0.equals(syntax) || Syntax.XWIKI_2_1.equals(syntax)) {\n return '~';\n }\n\n throw new IllegalArgumentException(String.format(\"Escaping is not supported for Syntax [%s]\", syntax));\n }\n}\n\n", "input": "", "output": "【漏洞类型】未授权访问\n【严重性】Medium\n【漏洞摘要】在`DefaultIconSetLoader`类中,`loadIconSet`方法从文档中加载图标集,但未验证文档的作者是否具有执行脚本的权利。这可能导致未授权用户通过操控文档内容来执行恶意脚本。\n\n【关键代码模式】`loadIconSet`方法,特别是以下代码行:\n```java\nDocumentModelBridge doc = documentAccessBridge.getDocumentInstance(iconSetReference);\nString content = doc.getContent();\n```\n以及`VelocityRenderer`类的`render`方法中,未对上下文文档进行权限检查的代码:\n```java\nreturn velocityRenderer.render(contentToParse.toString());\n```\n\n【修复方法说明】修复方法通过引入`AuthorizationManager`和`UserReferenceSerializer`来检查文档作者的脚本执行权限,并在`VelocityRenderer`中引入了`DocumentReference`参数,以确保渲染代码时使用文档的上下文和作者权限。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nxwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/XWikiServletResponse.java\n### 代码内容:\n/*\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n */\npackage com.xpn.xwiki.web;\n\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.net.URL;\nimport java.util.Collection;\nimport java.util.Locale;\nimport java.util.regex.Pattern;\n\nimport javax.servlet.ServletOutputStream;\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.xwiki.url.URLSecurityManager;\n\npublic class XWikiServletResponse implements XWikiResponse\n{\n private static final Logger LOGGER = LoggerFactory.getLogger(XWikiServletResponse.class);\n private static final Pattern ABSOLUTE_URL_PATTERN = Pattern.compile(\"[a-z0-9]+://.*\");\n\n private HttpServletResponse response;\n\n public XWikiServletResponse(HttpServletResponse response)\n {\n this.response = response;\n }\n\n @Override\n public int getStatus()\n {\n return this.response.getStatus();\n }\n\n @Override\n public HttpServletResponse getHttpServletResponse()\n {\n return this.response;\n }\n\n @Override\n public void sendRedirect(String redirect) throws IOException\n {\n if (StringUtils.isBlank(redirect)) {\n // Nowhere to go to\n return;\n }\n if (StringUtils.containsAny(redirect, '\\r', '\\n')) {\n LOGGER.warn(\"Possible HTTP Response Splitting attack, attempting to redirect to [{}]\", redirect);\n return;\n }\n\n // check for trusted domains, only if the given location is an absolute URL.\n if (ABSOLUTE_URL_PATTERN.matcher(redirect).matches()) {\n if (!getURLSecurityManager().isDomainTrusted(new URL(redirect))) {\n LOGGER.warn(\n \"Possible phishing attack, attempting to redirect to [{}], this request has been blocked. \"\n + \"If the request was legitimate, add the domain related to this request in the list \"\n + \"of trusted domains in the configuration: it can be configured in xwiki.properties in \"\n + \"url.trustedDomains.\", redirect);\n return;\n }\n }\n this.response.sendRedirect(redirect);\n }\n\n private URLSecurityManager getURLSecurityManager()\n {\n return Utils.getComponent(URLSecurityManager.class);\n }\n\n @Override\n public void setContentType(String type)\n {\n this.response.setContentType(type);\n }\n\n @Override\n public void setBufferSize(int i)\n {\n this.response.setBufferSize(i);\n }\n\n @Override\n public int getBufferSize()\n {\n return this.response.getBufferSize();\n }\n\n @Override\n public void flushBuffer() throws IOException\n {\n this.response.flushBuffer();\n }\n\n @Override\n public void resetBuffer()\n {\n this.response.resetBuffer();\n }\n\n @Override\n public boolean isCommitted()\n {\n return this.response.isCommitted();\n }\n\n @Override\n public void reset()\n {\n this.response.reset();\n }\n\n @Override\n public void setContentLength(int length)\n {\n this.response.setContentLength(length);\n }\n\n @Override\n public void setContentLengthLong(long len)\n {\n this.response.setContentLengthLong(len);\n }\n\n @Override\n public String getCharacterEncoding()\n {\n return this.response.getCharacterEncoding();\n }\n\n @Override\n public ServletOutputStream getOutputStream() throws IOException\n {\n return this.response.getOutputStream();\n }\n\n @Override\n public PrintWriter getWriter() throws IOException\n {\n return this.response.getWriter();\n }\n\n @Override\n public void setCharacterEncoding(String s)\n {\n this.response.setCharacterEncoding(s);\n }\n\n @Override\n public void addCookie(Cookie cookie)\n {\n this.response.addCookie(cookie);\n }\n\n public void addCookie(String cookieName, String cookieValue, int age)\n {\n Cookie cookie = new Cookie(cookieName, cookieValue);\n cookie.setVersion(1);\n cookie.setMaxAge(age);\n this.response.addCookie(cookie);\n }\n\n /**\n * Remove a cookie.\n *\n * @param request The servlet request needed to find the cookie to remove\n * @param cookieName The name of the cookie that must be removed.\n */\n @Override\n public void removeCookie(String cookieName, XWikiRequest request)\n {\n Cookie cookie = request.getCookie(cookieName);\n if (cookie != null) {\n cookie.setMaxAge(0);\n cookie.setPath(cookie.getPath());\n addCookie(cookie);\n }\n }\n\n @Override\n public void setLocale(Locale locale)\n {\n this.response.setLocale(locale);\n }\n\n @Override\n public Locale getLocale()\n {\n return this.response.getLocale();\n }\n\n @Override\n public void setDateHeader(String name, long value)\n {\n this.response.setDateHeader(name, value);\n }\n\n @Override\n public void setIntHeader(String name, int value)\n {\n this.response.setIntHeader(name, value);\n }\n\n @Override\n public void setHeader(String name, String value)\n {\n this.response.setHeader(name, value);\n }\n\n @Override\n public void addHeader(String name, String value)\n {\n this.response.addHeader(name, value);\n }\n\n @Override\n public void addDateHeader(String name, long value)\n {\n this.response.addDateHeader(name, value);\n }\n\n @Override\n public void addIntHeader(String name, int value)\n {\n this.response.addIntHeader(name, value);\n }\n\n @Override\n public void setStatus(int i)\n {\n this.response.setStatus(i);\n }\n\n /**\n * @deprecated\n */\n @Override\n @Deprecated\n public void setStatus(int i, String s)\n {\n this.response.setStatus(i, s);\n }\n\n @Override\n public boolean containsHeader(String name)\n {\n return this.response.containsHeader(name);\n }\n\n @Override\n public String encodeURL(String s)\n {\n return this.response.encodeURL(s);\n }\n\n @Override\n public String encodeRedirectURL(String s)\n {\n return this.response.encodeRedirectURL(s);\n }\n\n /**\n * @deprecated\n */\n @Override\n @Deprecated\n public String encodeUrl(String s)\n {\n return this.response.encodeUrl(s);\n }\n\n /**\n * @deprecated\n */\n @Override\n @Deprecated\n public String encodeRedirectUrl(String s)\n {\n return this.response.encodeRedirectUrl(s);\n }\n\n @Override\n public void sendError(int i, String s) throws IOException\n {\n this.response.sendError(i, s);\n }\n\n @Override\n public void sendError(int i) throws IOException\n {\n this.response.sendError(i);\n }\n\n @Override\n public String getContentType()\n {\n return this.response.getContentType();\n }\n\n @Override\n public String getHeader(String s)\n {\n return this.response.getHeader(s);\n }\n\n @Override\n public Collection<String> getHeaders(String s)\n {\n return this.response.getHeaders(s);\n }\n\n @Override\n public Collection<String> getHeaderNames()\n {\n return this.response.getHeaderNames();\n }\n}\n\n", "input": "", "output": "【漏洞类型】HTTP Response Splitting\n【严重性】Medium\n【漏洞摘要】HTTP Response Splitting是一种注入攻击,攻击者通过在URL中插入特殊字符,可以在响应头中插入额外的HTTP头,从而控制响应内容。原始代码中,sendRedirect方法仅检查了URL中是否包含换行符('\\r'和'\\n'),但未处理其他可能导致响应分割的字符,如'%0D'(回车符)和'%0A'(换行符)。这使得攻击者有可能通过编码的特殊字符绕过检查。\n【关键代码模式】sendRedirect(String redirect), StringUtils.containsAny(redirect, '\\r', '\\n')\n【修复方法说明】修复方法通过更新正则表达式ABSOLUTE_URL_PATTERN来匹配更多的URL模式,同时添加了一个新的检查,用于检测URL是否以\"//\"开头,这可能是尝试利用HTTP协议的相对URL重定向进行攻击的迹象。修复还加强了日志记录,提醒管理员注意潜在的钓鱼攻击。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/main/java/com/github/jlangch/venice/impl/util/io/LoadPaths.java\n### 代码内容:\npackage com.github.jlangch.venice.impl.util.io;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport com.github.jlangch.venice.VncException;\nimport com.github.jlangch.venice.impl.util.io.zip.ZipFileSystemUtil;\nimport com.github.jlangch.venice.javainterop.ILoadPaths;\n\npublic class LoadPaths implements ILoadPaths {\n\n private LoadPaths(\n final List<File> paths,\n final boolean unlimitedAccess\n ) {\n if (paths != null) {\n this.paths.addAll(paths);\n }\n this.unlimitedAccess = unlimitedAccess;\n }\n\n public static LoadPaths of(\n final List<File> paths,\n final boolean unlimitedAccess\n ) {\n if (paths == null || paths.isEmpty()) {\n return new LoadPaths(null, unlimitedAccess);\n }\n else {\n final List<File> savePaths = new ArrayList<>();\n\n for(File p : paths) {\n if (p != null) {\n if (p.isFile() || p.isDirectory()) {\n savePaths.add(canonical(p.getAbsoluteFile()));\n }\n else {\n // skip silently\n }\n }\n }\n\n return new LoadPaths(savePaths, unlimitedAccess);\n }\n }\n\n @Override\n public String loadVeniceFile(final File file) {\n if (file == null) {\n return null;\n }\n else {\n final String path = file.getPath();\n\n final String vncFile = path.endsWith(\".venice\") ? path : path + \".venice\";\n\n final ByteBuffer data = load(new File(vncFile));\n\n return data == null\n ? null\n : new String(data.array(), getCharset(\"UTF-8\"));\n }\n }\n\n @Override\n public ByteBuffer loadBinaryResource(final File file) {\n return load(file);\n }\n\n @Override\n public String loadTextResource(final File file, final String encoding) {\n final ByteBuffer data = load(file);\n\n return data == null\n ? null\n : new String(data.array(), getCharset(encoding));\n }\n\n @Override\n public List<File> getPaths() {\n return Collections.unmodifiableList(paths);\n }\n\n @Override\n public boolean isOnLoadPath(final File file) {\n if (file == null) {\n throw new IllegalArgumentException(\"A file must not be null\");\n }\n else if (unlimitedAccess) {\n return true;\n }\n else {\n final File f = canonical(file);\n final File dir = f.getParentFile();\n\n // check load paths\n for(File p : paths) {\n if (p.isDirectory()) {\n if (dir.equals(p)) return true;\n }\n else if (p.isFile()) {\n if (f.equals(p)) return true;\n }\n }\n\n return false;\n }\n }\n\n @Override\n public boolean isUnlimitedAccess() {\n return unlimitedAccess;\n }\n\n private ByteBuffer load(final File file) {\n final ByteBuffer dataFromLoadPath = paths.stream()\n .map(p -> loadFromLoadPath(p, file))\n .filter(d -> d != null)\n .findFirst()\n .orElse(null);\n\n if (dataFromLoadPath != null) {\n return dataFromLoadPath;\n }\n else if (unlimitedAccess && file.isFile()) {\n return loadFile(file);\n }\n else {\n return null;\n }\n }\n\n private ByteBuffer loadFromLoadPath(\n final File loadPath,\n final File file\n ) {\n if (loadPath.getName().endsWith(\".zip\")) {\n return loadFileFromZip(loadPath, file);\n }\n else if (loadPath.isDirectory()) {\n return loadFileFromDir(loadPath, file);\n }\n else if (loadPath.isFile()) {\n final File f = canonical(file);\n if (loadPath.equals(f)) {\n try {\n return ByteBuffer.wrap(Files.readAllBytes(f.toPath()));\n }\n catch(IOException ex) {\n return null;\n }\n }\n else {\n return null;\n }\n }\n else {\n return null;\n }\n }\n\n private ByteBuffer loadFileFromZip(\n final File zip,\n final File file\n ) {\n if (zip.exists()) {\n try {\n return ZipFileSystemUtil\n .loadBinaryFileFromZip(zip, file)\n .getValue();\n }\n catch(Exception ex) {\n return null;\n }\n }\n else {\n return null;\n }\n }\n\n private ByteBuffer loadFileFromDir(final File loadPath, final File file) {\n try {\n if (file.isAbsolute()) {\n return isFileWithinDirectory(loadPath, file)\n ? loadFile(file)\n : null;\n }\n else {\n final File f = new File(loadPath, file.getPath());\n return f.isFile()\n ? loadFile(new File(loadPath, file.getPath()))\n : null;\n }\n }\n catch (Exception ex) {\n throw new VncException(\n String.format(\"Failed to load file '%s'\", file.getPath()),\n ex);\n }\n }\n\n private static File canonical(final File file) {\n try {\n return file.getCanonicalFile();\n }\n catch(IOException ex) {\n throw new VncException(\n String.format(\n \"The file '%s' can not be converted to a canonical path!\",\n file.getPath()),\n ex);\n }\n }\n\n private ByteBuffer loadFile(final File file) {\n try {\n return ByteBuffer.wrap(Files.readAllBytes(file.toPath()));\n }\n catch(IOException ex) {\n return null;\n }\n }\n\n private boolean isFileWithinDirectory(\n final File dir,\n final File file\n ) throws IOException {\n final File dir_ = dir.getAbsoluteFile();\n if (dir_.isDirectory()) {\n final File fl = new File(dir_, file.getPath());\n if (fl.isFile()) {\n if (fl.getCanonicalFile().toPath().startsWith(dir_.getCanonicalFile().toPath())) {\n // Prevent accessing files outside the load-path.\n // E.g.: ../../coffee\n return true;\n }\n }\n }\n\n return false;\n }\n\n private Charset getCharset(final String encoding) {\n return encoding == null || encoding.isEmpty()\n ? Charset.defaultCharset()\n : Charset.forName(encoding);\n }\n\n // a list of existing canonical paths\n private final List<File> paths = new ArrayList<>();\n private final boolean unlimitedAccess;\n}\n\n", "input": "", "output": "【漏洞类型】路径遍历漏洞\n\n【严重性】Medium\n\n【漏洞摘要】此代码存在路径遍历漏洞,允许攻击者通过构造特殊的文件路径来访问或读取不应被访问的文件。具体表现为在方法`isFileWithinDirectory`中,使用`fl.getCanonicalPath().startsWith(dir_.getCanonicalPath())`来检查文件是否在指定目录下。由于`getCanonicalPath()`可能抛出`IOException`,在某些情况下,攻击者可以利用这一点来绕过安全检查。此外,`loadFileFromDir`方法中对相对路径的处理不当,可能导致路径遍历漏洞。\n\n【关键代码模式】\n- `isFileWithinDirectory`方法中的`fl.getCanonicalPath().startsWith(dir_.getCanonicalPath())`检查。\n- `loadFileFromDir`方法中的`if (file.isAbsolute())`和`if (fl.isFile())`检查。\n- `canonical`方法中的`file.getCanonicalFile()`调用。\n- `loadFile`方法中的`Files.readAllBytes(file.toPath())`调用。\n\n【修复方法说明】\n修复方法通过将`fl.getCanonicalPath().startsWith(dir_.getCanonicalPath())`替换为`fl.getCanonicalFile().toPath().startsWith(dir_.getCanonicalFile().toPath())`来确保路径检查的准确性。此外,改进了`loadFileFromDir`方法中对相对路径的处理,以防止路径遍历漏洞。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/main/java/org/sitenv/vocabularies/configuration/CodeValidatorApiConfiguration.java\n### 代码内容:\npackage org.sitenv.vocabularies.configuration;\n\nimport org.sitenv.vocabularies.loader.VocabularyLoadRunner;\nimport org.sitenv.vocabularies.loader.VocabularyLoaderFactory;\nimport org.sitenv.vocabularies.validation.NodeValidatorFactory;\nimport org.sitenv.vocabularies.validation.dto.GlobalCodeValidatorResults;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.ServiceLocatorFactoryBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.support.PropertySourcesPlaceholderConfigurer;\nimport org.springframework.core.env.Environment;\nimport org.springframework.data.jpa.repository.config.EnableJpaRepositories;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.orm.hibernate4.HibernateExceptionTranslator;\nimport org.springframework.orm.jpa.JpaTransactionManager;\nimport org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;\nimport org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;\nimport org.springframework.oxm.jaxb.Jaxb2Marshaller;\nimport org.springframework.transaction.PlatformTransactionManager;\n\nimport javax.persistence.EntityManagerFactory;\nimport javax.sql.DataSource;\nimport javax.xml.XMLConstants;\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.parsers.ParserConfigurationException;\nimport javax.xml.xpath.XPathFactory;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\n\n/**\n * Created by Brian on 2/5/2016.\n */\n@Configuration\n@ComponentScan(\"org.sitenv.vocabularies\")\n@EnableJpaRepositories(\"org.sitenv.vocabularies.validation.repositories\")\npublic class CodeValidatorApiConfiguration {\n\n @Bean\n public EntityManagerFactory entityManagerFactory() {\n HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();\n vendorAdapter.setGenerateDdl(false);\n vendorAdapter.setShowSql(true);\n LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();\n factory.setJpaVendorAdapter(vendorAdapter);\n factory.setPackagesToScan(\"org.sitenv.vocabularies.validation.entities\");\n Properties jpaProperties = new Properties();\n jpaProperties.put(\"hibernate.hbm2ddl.auto\", \"none\");\n jpaProperties.put(\"hibernate.dialect\", \"org.hibernate.dialect.HSQLDialect\");\n jpaProperties.put(\"hibernate.format_sql\", \"true\");\n jpaProperties.put(\"hibernate.show_sql\", \"false\");\n factory.setDataSource(dataSource());\n factory.setJpaProperties(jpaProperties);\n factory.afterPropertiesSet();\n return factory.getObject();\n }\n\n @Bean\n public PlatformTransactionManager transactionManager() {\n JpaTransactionManager txManager = new JpaTransactionManager();\n txManager.setEntityManagerFactory(entityManagerFactory());\n return txManager;\n }\n\n @Bean\n public HibernateExceptionTranslator hibernateExceptionTranslator() {\n return new HibernateExceptionTranslator();\n }\n\n @Bean\n public DataSource dataSource() {\n return new EmbeddedDatabaseBuilder()\n .setType(EmbeddedDatabaseType.HSQL)\n .addScript(\"classpath:schema.sql\")\n .build();\n }\n\n @Bean\n public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {\n PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();\n propertySourcesPlaceholderConfigurer.setLocalOverride(true);\n return propertySourcesPlaceholderConfigurer;\n }\n\n @Bean\n public ServiceLocatorFactoryBean vocabularyLoaderFactoryServiceLocatorFactoryBean() {\n ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();\n bean.setServiceLocatorInterface(VocabularyLoaderFactory.class);\n return bean;\n }\n\n @Bean\n public VocabularyLoaderFactory vocabularyLoaderFactory() {\n return (VocabularyLoaderFactory) vocabularyLoaderFactoryServiceLocatorFactoryBean().getObject();\n }\n\n @Bean\n public ServiceLocatorFactoryBean vocabularyValidatorFactoryServiceLocatorFactoryBean() {\n ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();\n bean.setServiceLocatorInterface(NodeValidatorFactory.class);\n return bean;\n }\n\n @Bean\n public NodeValidatorFactory vocabularyValidatorFactory() {\n return (NodeValidatorFactory) vocabularyValidatorFactoryServiceLocatorFactoryBean().getObject();\n }\n\n @Autowired\n @Bean\n VocabularyLoadRunner vocabularyLoadRunner(final Environment environment, final VocabularyLoaderFactory vocabularyLoaderFactory, final DataSource dataSource){\n VocabularyLoadRunner vocabularyLoadRunner = null;\n String localCodeRepositoryDir = environment.getProperty(\"vocabulary.localCodeRepositoryDir\");\n String localValueSetRepositoryDir = environment.getProperty(\"vocabulary.localValueSetRepositoryDir\");\n vocabularyLoadRunner = new VocabularyLoadRunner();\n System.out.println(\"LOADING VOCABULARY DATABASES FROM THE FOLLOWING RESOURCES: VALUESETS - \" + localValueSetRepositoryDir + \" CODES - \" + localCodeRepositoryDir);\n vocabularyLoadRunner.setCodeDirectory(localCodeRepositoryDir);\n vocabularyLoadRunner.setValueSetDirectory(localValueSetRepositoryDir);\n vocabularyLoadRunner.setDataSource(dataSource);\n vocabularyLoadRunner.setVocabularyLoaderFactory(vocabularyLoaderFactory);\n return vocabularyLoadRunner;\n }\n\n @Bean\n public static List<ConfiguredExpression> vocabularyValidationConfigurations(ValidationConfigurationLoader configurationLoader){\n return configurationLoader.getConfigurations().getExpressions();\n }\n\n @Bean\n public DocumentBuilder documentBuilder() throws ParserConfigurationException {\n DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(\"com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl\", ClassLoader.getSystemClassLoader());\n domFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, \"\");\n domFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, \"\");\n domFactory.setFeature(\"http://apache.org/xml/features/disallow-doctype-decl\", true);\n domFactory.setFeature(\"http://xml.org/sax/features/external-general-entities\", false);\n domFactory.setFeature(\"http://xml.org/sax/features/external-parameter-entities\", false);\n domFactory.setFeature(\"http://apache.org/xml/features/nonvalidating/load-external-dtd\", false);\n domFactory.setXIncludeAware(false);\n domFactory.setExpandEntityReferences(false);\n domFactory.setNamespaceAware(true);\n return domFactory.newDocumentBuilder();\n }\n\n @Bean\n public XPathFactory xPathFactory(){\n return XPathFactory.newInstance();\n }\n\n @Autowired\n @Bean\n public ValidationConfigurationLoader validationConfigurationLoader(final Environment environment){\n ValidationConfigurationLoader validationConfigurationLoader = new ValidationConfigurationLoader();\n validationConfigurationLoader.setValidationConfigurationFilePath(environment.getProperty(\"referenceccda.configFile\"));\n validationConfigurationLoader.setUnmarshaller(castorMarshaller());\n return validationConfigurationLoader;\n }\n\n @Bean\n public static Jaxb2Marshaller castorMarshaller() {\n Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();\n jaxb2Marshaller.setPackagesToScan(\"org.sitenv.vocabularies.configuration\");\n Map<String,Object> map = new HashMap<>();\n map.put(\"jaxb.formatted.output\", true);\n jaxb2Marshaller.setMarshallerProperties(map);\n return jaxb2Marshaller;\n }\n \n @Bean\n public static GlobalCodeValidatorResults globalCodeValidatorResults() { \t\n \treturn new GlobalCodeValidatorResults();\n }\n}\n\n", "input": "", "output": "【漏洞类型】XML 外部实体 (XXE) 漏洞\n【严重性】High\n【漏洞摘要】XXE 漏洞允许攻击者通过 XML 输入注入恶意的外部实体,从而可能导致拒绝服务 (DoS) 攻击、信息泄露或执行任意代码的风险。在原始代码中,`DocumentBuilderFactory.newInstance()` 方法创建了一个 `DocumentBuilderFactory` 实例,但未禁用外部实体处理。具体来说,`documentBuilder()` 方法中未设置 `domFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, \"\");` 和 `domFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, \"\");`,也没有禁用其他与外部实体处理相关的特性。\n【关键代码模式】`DocumentBuilderFactory.newInstance()`、`domFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, \"\");`、`domFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, \"\");`、`domFactory.setFeature(\"http://apache.org/xml/features/disallow-doctype-decl\", true)`、`domFactory.setFeature(\"http://xml.org/sax/features/external-general-entities\", false)`、`domFactory.setFeature(\"http://xml.org/sax/features/external-parameter-entities\", false)`、`domFactory.setFeature(\"http://apache.org/xml/features/nonvalidating/load-external-dtd\", false)`、`domFactory.setXIncludeAware(false)`、`domFactory.setExpandEntityReferences(false)`。\n【修复方法说明】修复方法通过指定 `DocumentBuilderFactory` 的实现类并禁用外部实体处理特性来解决问题。具体来说,`documentBuilder()` 方法中添加了 `domFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, \"\");` 和 `domFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, \"\");`,以及禁用了其他与外部实体处理相关的特性。这遵循了 OWASP 的最佳实践,即在解析 XML 时禁用外部实体处理,以防止 XXE 攻击。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/main/java/org/owasp/validator/html/scan/AntiSamyDOMScanner.java\n### 代码内容:\npackage org.owasp.validator.html.scan;\n\nimport org.apache.batik.css.parser.ParseException;\nimport org.apache.xerces.dom.DocumentImpl;\nimport net.sourceforge.htmlunit.cyberneko.parsers.DOMFragmentParser;\nimport org.owasp.validator.css.CssScanner;\nimport org.owasp.validator.html.CleanResults;\nimport org.owasp.validator.html.Policy;\nimport org.owasp.validator.html.PolicyException;\nimport org.owasp.validator.html.ScanException;\nimport org.owasp.validator.html.model.Attribute;\nimport org.owasp.validator.html.model.Tag;\nimport org.owasp.validator.html.util.ErrorMessageUtil;\nimport org.owasp.validator.html.util.HTMLEntityEncoder;\nimport org.w3c.dom.Comment;\nimport org.w3c.dom.DOMException;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.DocumentFragment;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.NamedNodeMap;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\nimport org.w3c.dom.ProcessingInstruction;\nimport org.w3c.dom.Text;\nimport org.xml.sax.InputSource;\nimport org.xml.sax.SAXException;\nimport org.xml.sax.SAXNotRecognizedException;\nimport org.xml.sax.SAXNotSupportedException;\n\nimport java.io.IOException;\nimport java.io.StringReader;\nimport java.io.StringWriter;\nimport java.util.List;\nimport java.util.Queue;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ConcurrentLinkedQueue;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * This is where the magic lives. All the scanning/filtration logic resides\n * here, but it should not be called directly. All scanning should be done\n * through an <code>AntiSamy.scan()</code> method.\n * \n * @author Arshan Dabirsiaghi\n */\npublic class AntiSamyDOMScanner extends AbstractAntiSamyScanner {\n private Document document = new DocumentImpl();\n private DocumentFragment dom = document.createDocumentFragment();\n private CleanResults results = null;\n private static final int maxDepth = 250;\n private static final Pattern invalidXmlCharacters =\n Pattern.compile(\"[\\\\u0000-\\\\u001F\\\\uD800-\\\\uDFFF\\\\uFFFE-\\\\uFFFF&&[^\\\\u0009\\\\u000A\\\\u000D]]\");\n private static final Pattern conditionalDirectives = Pattern.compile(\"<?!?\\\\[\\\\s*(?:end)?if[^]]*\\\\]>?\");\n\n private static final Queue<CachedItem> cachedItems = new ConcurrentLinkedQueue<CachedItem>();\n\n static class CachedItem {\n private final DOMFragmentParser parser;\n private final Matcher invalidXmlCharMatcher = invalidXmlCharacters.matcher(\"\");\n\n CachedItem() throws SAXNotSupportedException, SAXNotRecognizedException {\n this.parser = getDomParser();\n }\n\n DOMFragmentParser getDomFragmentParser() {\n return parser;\n }\n }\n\n public AntiSamyDOMScanner(Policy policy) {\n super(policy);\n }\n\n /* UnusedDeclaration TODO Investigate */\n public AntiSamyDOMScanner() throws PolicyException {\n super();\n }\n\n /**\n * This is where the magic lives.\n *\n * @param html A String whose contents we want to scan.\n * @return A <code>CleanResults</code> object with an\n * <code>XMLDocumentFragment</code> object and its String\n * representation, as well as some scan statistics.\n * @throws ScanException When there is a problem encountered\n\t * while scanning the HTML.\n\t */\n @Override\n public CleanResults scan(String html) throws ScanException {\n\n if (html == null) {\n throw new ScanException(new NullPointerException(\"Null html input\"));\n }\n\n errorMessages.clear();\n int maxInputSize = policy.getMaxInputSize();\n\n if (maxInputSize < html.length()) {\n addError(ErrorMessageUtil.ERROR_INPUT_SIZE, new Object[]{html.length(), maxInputSize});\n throw new ScanException(errorMessages.get(0));\n }\n\n isNofollowAnchors = policy.isNofollowAnchors();\n isNoopenerAndNoreferrerAnchors = policy.isNoopenerAndNoreferrerAnchors();\n isValidateParamAsEmbed = policy.isValidateParamAsEmbed();\n\n long startOfScan = System.currentTimeMillis();\n\n try {\n\n CachedItem cachedItem;\n cachedItem = cachedItems.poll();\n if (cachedItem == null){\n cachedItem = new CachedItem();\n }\n\n /*\n * We have to replace any invalid XML characters to prevent NekoHTML\n * from breaking when it gets passed encodings like %21.\n */\n\n html = stripNonValidXMLCharacters(html, cachedItem.invalidXmlCharMatcher);\n\n /*\n * First thing we do is call the HTML cleaner (\"NekoHTML\") on it\n * with the appropriate options. We choose not to omit tags due to\n * the fallibility of our own listing in the ever changing world of\n * W3C.\n */\n\n DOMFragmentParser parser = cachedItem.getDomFragmentParser();\n\n try {\n parser.parse(new InputSource(new StringReader(html)), dom);\n } catch (Exception e) {\n throw new ScanException(e);\n }\n\n processChildren(dom, 0);\n\n /*\n * Serialize the output and then return the resulting DOM object and\n * its string representation.\n */\n\n final String trimmedHtml = html;\n\n StringWriter out = new StringWriter();\n\n @SuppressWarnings(\"deprecation\")\n org.apache.xml.serialize.OutputFormat format = getOutputFormat();\n\n //noinspection deprecation\n org.apache.xml.serialize.HTMLSerializer serializer = getHTMLSerializer(out, format);\n serializer.serialize(dom);\n\n /*\n * Get the String out of the StringWriter and rip out the XML\n * declaration if the Policy says we should.\n */\n final String trimmed = trim( trimmedHtml, out.getBuffer().toString() );\n\n Callable<String> cleanHtml = new Callable<String>() {\n public String call() throws Exception {\n return trimmed;\n }\n };\n\n /*\n * Return the DOM object as well as string HTML.\n */\n results = new CleanResults(startOfScan, cleanHtml, dom, errorMessages);\n\n cachedItems.add( cachedItem);\n return results;\n\n } catch (SAXException | IOException e) {\n throw new ScanException(e);\n }\n\n }\n\n static DOMFragmentParser getDomParser()\n throws SAXNotRecognizedException, SAXNotSupportedException {\n DOMFragmentParser parser = new DOMFragmentParser();\n parser.setProperty(\"http://cyberneko.org/html/properties/names/elems\", \"lower\");\n\n parser.setFeature(\"http://cyberneko.org/html/features/scanner/style/strip-cdata-delims\", false);\n parser.setFeature(\"http://cyberneko.org/html/features/scanner/cdata-sections\", true);\n\n try {\n parser.setFeature(\"http://cyberneko.org/html/features/enforce-strict-attribute-names\", true);\n } catch (SAXNotRecognizedException se) {\n // this indicates that the patched nekohtml is not on the\n // classpath\n }\n return parser;\n }\n\n /**\n * The workhorse of the scanner. Recursively scans document elements\n * according to the policy. This should be called implicitly through the\n * AntiSamy.scan() method.\n *\n * @param node The node to validate.\n */\n private void recursiveValidateTag(final Node node, int currentStackDepth) throws ScanException {\n\n currentStackDepth++;\n\n if(currentStackDepth > maxDepth) {\n throw new ScanException(\"Too many nested tags\");\n }\n\n if (node instanceof Comment) {\n processCommentNode(node);\n return;\n }\n\n boolean isElement = node instanceof Element;\n NodeList eleChildNodes = node.getChildNodes();\n if (isElement && eleChildNodes.getLength() == 0) {\n if (removeDisallowedEmpty(node)){\n return;\n }\n }\n\n if (node instanceof Text && Node.CDATA_SECTION_NODE == node.getNodeType()) {\n stripCData(node);\n return;\n }\n\n if (node instanceof ProcessingInstruction) {\n removePI(node);\n }\n\n if (!isElement) {\n return;\n }\n\n final Element ele = (Element) node;\n final Node parentNode = ele.getParentNode();\n\n final String tagName = ele.getNodeName();\n final String tagNameLowerCase = tagName.toLowerCase();\n Tag tagRule = policy.getTagByLowercaseName(tagNameLowerCase);\n\n /*\n * If <param> and no policy and isValidateParamAsEmbed and policy in\n * place for <embed> and <embed> policy is to validate, use custom\n * policy to get the tag through to the validator.\n */\n Tag embedTag = policy.getEmbedTag();\n boolean masqueradingParam = isMasqueradingParam(tagRule, embedTag, tagNameLowerCase);\n if (masqueradingParam){\n tagRule = Constants.BASIC_PARAM_TAG_RULE;\n }\n\n if ((tagRule == null && policy.isEncodeUnknownTag()) || (tagRule != null && tagRule.isAction( \"encode\"))) {\n encodeTag(currentStackDepth, ele, tagName, eleChildNodes);\n } else if (tagRule == null || tagRule.isAction( Policy.ACTION_FILTER)) {\n actionFilter(currentStackDepth, ele, tagName, tagRule, eleChildNodes);\n } else if (tagRule.isAction( Policy.ACTION_VALIDATE)) {\n actionValidate(currentStackDepth, ele, parentNode, tagName, tagNameLowerCase, tagRule, masqueradingParam, embedTag, eleChildNodes);\n } else if (tagRule.isAction( Policy.ACTION_TRUNCATE)) {\n actionTruncate(ele, tagName, eleChildNodes);\n } else {\n /*\n * If we reached this that means that the tag's action is \"remove\",\n * which means to remove the tag (including its contents).\n */\n addError(ErrorMessageUtil.ERROR_TAG_DISALLOWED, new Object[]{HTMLEntityEncoder.htmlEntityEncode(tagName)});\n removeNode(ele);\n }\n }\n\n private boolean isMasqueradingParam(Tag tagRule, Tag embedTag, String tagNameLowerCase){\n if (tagRule == null && isValidateParamAsEmbed && \"param\".equals(tagNameLowerCase)) {\n return embedTag != null && embedTag.isAction(Policy.ACTION_VALIDATE);\n }\n return false;\n }\n\n private void encodeTag(int currentStackDepth, Element ele, String tagName, NodeList eleChildNodes) throws ScanException {\n addError(ErrorMessageUtil.ERROR_TAG_ENCODED, new Object[]{HTMLEntityEncoder.htmlEntityEncode(tagName)});\n processChildren(eleChildNodes, currentStackDepth);\n\n /*\n * Transform the tag to text, HTML-encode it and promote the\n * children. The tag will be kept in the fragment as one or two text\n * Nodes located before and after the children; representing how the\n * tag used to wrap them.\n */\n\n encodeAndPromoteChildren(ele);\n }\n\n private void actionFilter(int currentStackDepth, Element ele, String tagName, Tag tag, NodeList eleChildNodes) throws ScanException {\n if (tag == null) {\n addError(ErrorMessageUtil.ERROR_TAG_NOT_IN_POLICY, new Object[]{HTMLEntityEncoder.htmlEntityEncode(tagName)});\n } else {\n addError(ErrorMessageUtil.ERROR_TAG_FILTERED, new Object[]{HTMLEntityEncoder.htmlEntityEncode(tagName)});\n }\n\n processChildren(eleChildNodes, currentStackDepth);\n promoteChildren(ele);\n }\n\n private void actionValidate(int currentStackDepth, Element ele, Node parentNode, String tagName, String tagNameLowerCase, Tag tag, boolean masqueradingParam, Tag embedTag, NodeList eleChildNodes) throws ScanException {\n /*\n * If doing <param> as <embed>, now is the time to convert it.\n */\n String nameValue = null;\n if (masqueradingParam) {\n nameValue = ele.getAttribute(\"name\");\n if (nameValue != null && !\"\".equals(nameValue)) {\n String valueValue = ele.getAttribute(\"value\");\n ele.setAttribute(nameValue, valueValue);\n ele.removeAttribute(\"name\");\n ele.removeAttribute(\"value\");\n tag = embedTag;\n }\n }\n\n /*\n * Check to see if it's a <style> tag. We have to special case this\n * tag so we can hand it off to the custom style sheet validating\n * parser.\n */\n\n if (\"style\".equals(tagNameLowerCase) && policy.getStyleTag() != null) {\n if (processStyleTag(ele, parentNode)) return;\n }\n\n /*\n * Go through the attributes in the tainted tag and validate them\n * against the values we have for them.\n *\n * If we don't have a rule for the attribute we remove the\n * attribute.\n */\n\n if (processAttributes(ele, tagName, tag, currentStackDepth)) return; // can't process any more if we\n\n if (\"a\".equals(tagNameLowerCase)) {\n boolean addNofollow = isNofollowAnchors;\n boolean addNoopenerAndNoreferrer = false;\n\n if (isNoopenerAndNoreferrerAnchors) {\n Node targetAttribute = ele.getAttributes().getNamedItem(\"target\");\n if (targetAttribute != null && targetAttribute.getNodeValue().equalsIgnoreCase(\"_blank\")) {\n addNoopenerAndNoreferrer = true;\n }\n }\n\n Node relAttribute = ele.getAttributes().getNamedItem(\"rel\");\n String relValue = Attribute.mergeRelValuesInAnchor(addNofollow, addNoopenerAndNoreferrer, relAttribute == null ? \"\" : relAttribute.getNodeValue());\n if (!relValue.isEmpty()){\n ele.setAttribute(\"rel\", relValue.trim());\n }\n }\n\n processChildren(eleChildNodes, currentStackDepth);\n\n /*\n * If we have been dealing with a <param> that has been converted to\n * an <embed>, convert it back\n */\n if (masqueradingParam && nameValue != null && !\"\".equals(nameValue)) {\n String valueValue = ele.getAttribute(nameValue);\n ele.setAttribute(\"name\", nameValue);\n ele.setAttribute(\"value\", valueValue);\n ele.removeAttribute(nameValue);\n }\n }\n\n private boolean processStyleTag(Element ele, Node parentNode) {\n /*\n * Invoke the css parser on this element.\n */\n CssScanner styleScanner = new CssScanner(policy, messages, policy.isEmbedStyleSheets());\n\n try {\n if (ele.getChildNodes().getLength() > 0) {\n StringBuffer toScan = new StringBuffer();\n\n for (int i = 0; i < ele.getChildNodes().getLength(); i++) {\n Node childNode = ele.getChildNodes().item(i);\n if (toScan.length() > 0) {\n toScan.append(\"\\n\");\n }\n toScan.append(childNode.getTextContent());\n }\n\n CleanResults cr = styleScanner.scanStyleSheet(toScan.toString(), policy.getMaxInputSize());\n errorMessages.addAll(cr.getErrorMessages());\n\n /*\n * If IE gets an empty style tag, i.e. <style/> it will\n * break all CSS on the page. I wish I was kidding. So,\n * if after validation no CSS properties are left, we\n * would normally be left with an empty style tag and\n * break all CSS. To prevent that, we have this check.\n */\n\n String cleanHTML = cr.getCleanHTML();\n cleanHTML = cleanHTML == null || cleanHTML.equals(\"\") ? \"/* */\" : cleanHTML;\n\n ele.getFirstChild().setNodeValue(cleanHTML);\n /*\n * Remove every other node after cleaning CSS, there will\n * be only one node in the end, as it always should have.\n */\n for (int i = 1; i < ele.getChildNodes().getLength(); i++) {\n Node childNode = ele.getChildNodes().item(i);\n ele.removeChild(childNode);\n }\n }\n\n } catch (DOMException | ScanException | ParseException | NumberFormatException e) {\n\n /*\n * ParseException shouldn't be possible anymore, but we'll leave it\n * here because I (Arshan) am hilariously dumb sometimes.\n * Batik can throw NumberFormatExceptions (see bug #48).\n */\n\n addError(ErrorMessageUtil.ERROR_CSS_TAG_MALFORMED, new Object[]{HTMLEntityEncoder.htmlEntityEncode(ele.getFirstChild().getNodeValue())});\n parentNode.removeChild(ele);\n return true;\n }\n return false;\n }\n\n private void actionTruncate(Element ele, String tagName, NodeList eleChildNodes) {\n /*\n * Remove all attributes. This is for tags like i, b, u, etc. Purely\n * formatting without any need for attributes. It also removes any\n * children.\n */\n\n NamedNodeMap nnmap = ele.getAttributes();\n while (nnmap.getLength() > 0) {\n addError(ErrorMessageUtil.ERROR_ATTRIBUTE_NOT_IN_POLICY,\n new Object[]{tagName, HTMLEntityEncoder.htmlEntityEncode(nnmap.item(0).getNodeName())});\n ele.removeAttribute(nnmap.item(0).getNodeName());\n }\n\n int i = 0;\n int j = 0;\n int length = eleChildNodes.getLength();\n\n while (i < length) {\n Node nodeToRemove = eleChildNodes.item(j);\n if (nodeToRemove.getNodeType() != Node.TEXT_NODE) {\n ele.removeChild(nodeToRemove);\n } else {\n j++;\n }\n i++;\n }\n }\n\n private boolean processAttributes(Element ele, String tagName, Tag tag, int currentStackDepth) throws ScanException {\n Node attribute;\n\n NamedNodeMap attributes = ele.getAttributes();\n for (int currentAttributeIndex = 0; currentAttributeIndex < attributes.getLength(); currentAttributeIndex++) {\n\n attribute = attributes.item(currentAttributeIndex);\n\n String name = attribute.getNodeName();\n String value = attribute.getNodeValue();\n\n Attribute attr = tag.getAttributeByName(name.toLowerCase());\n\n /*\n * If we there isn't an attribute by that name in our policy\n * check to see if it's a globally defined attribute. Validate\n * against that if so.\n */\n if (attr == null) {\n attr = policy.getGlobalAttributeByName(name);\n if (attr == null && policy.isAllowDynamicAttributes()) {\n // not a global attribute, perhaps it is a dynamic attribute, if allowed\n attr = policy.getDynamicAttributeByName(name);\n }\n }\n\n /*\n * We have to special case the \"style\" attribute since it's\n * validated quite differently.\n */\n if (\"style\".equals(name.toLowerCase()) && attr != null) {\n\n /*\n * Invoke the CSS parser on this element.\n */\n CssScanner styleScanner = new CssScanner(policy, messages, false);\n\n try {\n CleanResults cr = styleScanner.scanInlineStyle(value, tagName, policy.getMaxInputSize());\n attribute.setNodeValue(cr.getCleanHTML());\n List<String> cssScanErrorMessages = cr.getErrorMessages();\n errorMessages.addAll(cssScanErrorMessages);\n\n } catch (DOMException | ScanException e) {\n\n addError(ErrorMessageUtil.ERROR_CSS_ATTRIBUTE_MALFORMED,\n new Object[]{tagName, HTMLEntityEncoder.htmlEntityEncode(ele.getNodeValue())});\n ele.removeAttribute(attribute.getNodeName());\n currentAttributeIndex--;\n }\n\n } else {\n\n if (attr != null) {\n\n // See if attribute is invalid\n if (!(attr.containsAllowedValue( value.toLowerCase()) ||\n (attr.matchesAllowedExpression( value ))) ) {\n\n /*\n * Attribute is NOT valid, so: Document transgression and perform the\n * \"onInvalid\" action. The default action is to\n * strip the attribute and leave the rest intact.\n */\n\n String onInvalidAction = attr.getOnInvalid();\n\n if (\"removeTag\".equals(onInvalidAction)) {\n\n /*\n * Remove the tag and its contents.\n */\n\n removeNode(ele);\n\n addError(ErrorMessageUtil.ERROR_ATTRIBUTE_INVALID_REMOVED,\n new Object[]{tagName, HTMLEntityEncoder.htmlEntityEncode(name), HTMLEntityEncoder.htmlEntityEncode(value)});\n return true;\n\n } else if (\"filterTag\".equals(onInvalidAction)) {\n\n /*\n * Remove the attribute and keep the rest of the tag.\n */\n\n processChildren(ele, currentStackDepth);\n promoteChildren(ele);\n addError(ErrorMessageUtil.ERROR_ATTRIBUTE_CAUSE_FILTER,\n new Object[]{tagName, HTMLEntityEncoder.htmlEntityEncode(name), HTMLEntityEncoder.htmlEntityEncode(value)});\n return true;\n } else if (\"encodeTag\".equals(onInvalidAction)) {\n\n /*\n * Remove the attribute and keep the rest of the tag.\n */\n\n processChildren(ele, currentStackDepth);\n encodeAndPromoteChildren(ele);\n addError(ErrorMessageUtil.ERROR_ATTRIBUTE_CAUSE_ENCODE,\n new Object[]{tagName, HTMLEntityEncoder.htmlEntityEncode(name), HTMLEntityEncoder.htmlEntityEncode(value)});\n return true;\n } else {\n\n /*\n * onInvalidAction = \"removeAttribute\"\n */\n\n ele.removeAttribute(attribute.getNodeName());\n currentAttributeIndex--;\n addError(ErrorMessageUtil.ERROR_ATTRIBUTE_INVALID,\n new Object[]{tagName, HTMLEntityEncoder.htmlEntityEncode(name), HTMLEntityEncoder.htmlEntityEncode(value)});\n \n }\n }\n\n } else {\n /*\n * the attribute they specified isn't in our policy\n * - remove it (whitelisting!)\n */\n\n addError(ErrorMessageUtil.ERROR_ATTRIBUTE_NOT_IN_POLICY,\n new Object[]{tagName, HTMLEntityEncoder.htmlEntityEncode(name), HTMLEntityEncoder.htmlEntityEncode(value)});\n ele.removeAttribute(attribute.getNodeName());\n currentAttributeIndex--;\n\n } // end if attribute is found in policy file\n\n } // end while loop through attributes\n\n } // loop through each attribute\n return false;\n }\n\n private void processChildren(Node ele, int currentStackDepth) throws ScanException {\n processChildren(ele.getChildNodes(), currentStackDepth);\n }\n\n private void processChildren(NodeList childNodes, int currentStackDepth ) throws ScanException {\n Node tmp;\n for (int i = 0; i < childNodes.getLength(); i++) {\n\n tmp = childNodes.item(i);\n recursiveValidateTag(tmp, currentStackDepth);\n\n /*\n * This indicates the node was removed/failed validation.\n */\n if (tmp.getParentNode() == null) {\n i--;\n }\n }\n }\n\n private void removePI(Node node) {\n addError(ErrorMessageUtil.ERROR_PI_FOUND, new Object[]{HTMLEntityEncoder.htmlEntityEncode(node.getTextContent())});\n removeNode(node);\n }\n\n private void stripCData(Node node) {\n addError(ErrorMessageUtil.ERROR_CDATA_FOUND, new Object[]{HTMLEntityEncoder.htmlEntityEncode(node.getTextContent())});\n Node text = document.createTextNode(node.getTextContent());\n node.getParentNode().insertBefore(text, node);\n node.getParentNode().removeChild(node);\n }\n\n private void processCommentNode(Node node) {\n if (!policy.isPreserveComments()) {\n node.getParentNode().removeChild(node);\n } else {\n String value = ((Comment) node).getData();\n // Strip conditional directives regardless of the\n // PRESERVE_COMMENTS setting.\n if (value != null) {\n ((Comment) node).setData(conditionalDirectives.matcher(value).replaceAll(\"\"));\n }\n }\n }\n\n private boolean removeDisallowedEmpty(Node node){\n String tagName = node.getNodeName();\n\n if (!isAllowedEmptyTag(tagName)) {\n /*\n * Wasn't in the list of allowed elements, so we'll nuke it.\n */\n addError(ErrorMessageUtil.ERROR_TAG_EMPTY, new Object[]{HTMLEntityEncoder.htmlEntityEncode(node.getNodeName())});\n removeNode(node);\n return true;\n }\n return false;\n }\n\n private void removeNode(Node node) {\n\t\tNode parent = node.getParentNode();\n\t\tparent.removeChild(node);\n\t\tString tagName = parent.getNodeName();\n\t\tif(\tparent instanceof Element && \n\t\t\tparent.getChildNodes().getLength() == 0 && \n\t\t\t!isAllowedEmptyTag(tagName)) {\n\t\t\tremoveNode(parent);\n\t\t}\n\t}\n\n\tprivate boolean isAllowedEmptyTag(String tagName) {\n return \"head\".equals(tagName ) || policy.getAllowedEmptyTags().matches(tagName);\n\t}\n\t\n /**\n * Used to promote the children of a parent to accomplish the \"filterTag\" action.\n *\n * @param ele The Element we want to filter.\n */\n private void promoteChildren(Element ele) {\n promoteChildren(ele, ele.getChildNodes());\n }\n\n private void promoteChildren(Element ele, NodeList eleChildNodes) {\n\n Node parent = ele.getParentNode();\n\n while (eleChildNodes.getLength() > 0) {\n Node node = ele.removeChild(eleChildNodes.item(0));\n parent.insertBefore(node, ele);\n }\n\n if (parent != null) {\n removeNode(ele);\n }\n }\n\n /**\n * This method was borrowed from Mark McLaren, to whom I owe much beer.\n *\n * This method ensures that the output has only valid XML unicode characters\n * as specified by the XML 1.0 standard. For reference, please see <a\n * href=\"http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char\">the\n * standard</a>. This method will return an empty String if the input is\n * null or empty.\n *\n * @param in The String whose non-valid characters we want to remove.\n * @param invalidXmlCharsMatcher The reusable regex matcher\n * @return The in String, stripped of non-valid characters.\n */\n private String stripNonValidXMLCharacters(String in, Matcher invalidXmlCharsMatcher) {\n\n if (in == null || (\"\".equals(in))) {\n return \"\"; // vacancy test.\n }\n invalidXmlCharsMatcher.reset(in);\n return invalidXmlCharsMatcher.matches() ? invalidXmlCharsMatcher.replaceAll(\"\") : in;\n }\n\n /**\n * Transform the element to text, HTML-encode it and promote the children.\n * The element will be kept in the fragment as one or two text Nodes located\n * before and after the children; representing how the tag used to wrap\n * them. If the element didn't have any children then only one text Node is\n * created representing an empty element.\n *\n * @param ele Element to be encoded\n */\n private void encodeAndPromoteChildren(Element ele) {\n Node parent = ele.getParentNode();\n String tagName = ele.getTagName();\n Node openingTag = parent.getOwnerDocument().createTextNode(toString(ele));\n parent.insertBefore(openingTag, ele);\n if (ele.hasChildNodes()) {\n Node closingTag = parent.getOwnerDocument().createTextNode(\"</\" + tagName + \">\");\n parent.insertBefore(closingTag, ele.getNextSibling());\n }\n promoteChildren(ele);\n }\n\n /**\n * Returns a text version of the passed element\n *\n * @param ele Element to be converted\n * @return String representation of the element\n */\n private String toString(Element ele) {\n StringBuilder eleAsString = new StringBuilder(\"<\" + ele.getNodeName());\n NamedNodeMap attributes = ele.getAttributes();\n Node attribute;\n for (int i = 0; i < attributes.getLength(); i++) {\n attribute = attributes.item(i);\n\n String name = attribute.getNodeName();\n String value = attribute.getNodeValue();\n\n eleAsString.append(\" \");\n eleAsString.append(HTMLEntityEncoder.htmlEntityEncode(name));\n eleAsString.append(\"=\\\"\");\n eleAsString.append(HTMLEntityEncoder.htmlEntityEncode(value));\n eleAsString.append(\"\\\"\");\n }\n if (ele.hasChildNodes()) {\n eleAsString.append(\">\");\n } else {\n eleAsString.append(\"/>\");\n }\n return eleAsString.toString();\n }\n\n @Override\n public CleanResults getResults() {\n return results;\n }\n}\n\n", "input": "", "output": "【漏洞类型】DOM遍历和操作不当\n【严重性】Medium\n【漏洞摘要】在处理`<style>`标签子节点时,代码通过从前往后遍历子节点列表并删除它们来清理CSS内容。这种迭代方式在删除子节点后会导致列表长度变化,从而可能跳过某些应被删除的节点。这可能导致部分未清理的CSS内容保留在最终的HTML输出中,进而可能被恶意用户利用。\n【关键代码模式】`processStyleTag`函数中的`for`循环用于删除子节点,但循环方向是从前往后。\n\n```java\n// 原始代码\nfor (int i = 1; i < ele.getChildNodes().getLength(); i++) {\n Node childNode = ele.getChildNodes().item(i);\n ele.removeChild(childNode);\n}\n```\n\n【修复方法说明】修复方法将循环方向改为从后往前,以确保在删除子节点时不会跳过任何节点。\n\n```java\n// 修复后的代码\nfor (int i = childNodesCount - 1; i >= 1; i--) {\n Node childNode = ele.getChildNodes().item(i);\n ele.removeChild(childNode);\n}\n```\n\n**最佳实践或安全编码技术**:\n- 在遍历和修改节点列表时,应从后往前遍历,以避免在删除节点后导致的索引偏移问题。\n- 在处理HTML和CSS时,确保所有子节点都被正确清理和验证,以防止潜在的安全风险。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/main/java/org/zeroturnaround/zip/ZipUtil.java\n### 代码内容:\npackage org.zeroturnaround.zip;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedOutputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.nio.charset.Charset;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.zip.Deflater;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\nimport java.util.zip.ZipInputStream;\nimport java.util.zip.ZipOutputStream;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.zeroturnaround.zip.commons.FileUtils;\nimport org.zeroturnaround.zip.commons.FilenameUtils;\nimport org.zeroturnaround.zip.commons.IOUtils;\nimport org.zeroturnaround.zip.transform.ZipEntryTransformer;\nimport org.zeroturnaround.zip.transform.ZipEntryTransformerEntry;\n\n/**\n * ZIP file manipulation utilities.\n *\n * @author Rein Raudjärv\n * @author Innokenty Shuvalov\n *\n * @see #containsEntry(File, String)\n * @see #unpackEntry(File, String)\n * @see #unpack(File, File)\n * @see #pack(File, File)\n */\npublic final class ZipUtil {\n\n private static final String PATH_SEPARATOR = \"/\";\n\n /** Default compression level */\n public static final int DEFAULT_COMPRESSION_LEVEL = Deflater.DEFAULT_COMPRESSION;\n\n // Use / instead of . to work around an issue with Maven Shade Plugin\n private static final Logger log = LoggerFactory.getLogger(\"org/zeroturnaround/zip/ZipUtil\".replace('/', '.')); // NOSONAR\n\n private ZipUtil() {\n }\n\n /* Extracting single entries from ZIP files. */\n\n /**\n * Checks if the ZIP file contains the given entry.\n *\n * @param zip\n * ZIP file.\n * @param name\n * entry name.\n * @return <code>true</code> if the ZIP file contains the given entry.\n */\n public static boolean containsEntry(File zip, String name) {\n ZipFile zf = null;\n try {\n zf = new ZipFile(zip);\n return zf.getEntry(name) != null;\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n finally {\n closeQuietly(zf);\n }\n }\n\n /**\n * Returns the compression method of a given entry of the ZIP file.\n *\n * @param zip\n * ZIP file.\n * @param name\n * entry name.\n * @return Returns <code>ZipEntry.STORED</code>, <code>ZipEntry.DEFLATED</code> or -1 if\n * the ZIP file does not contain the given entry.\n * @deprecated The compression level cannot be retrieved. This method exists only to ensure backwards compatibility with ZipUtil version 1.9, which returned the compression\n * method, not the level.\n */\n @Deprecated\n public static int getCompressionLevelOfEntry(File zip, String name) {\n return getCompressionMethodOfEntry(zip, name);\n }\n\n /**\n * Returns the compression method of a given entry of the ZIP file.\n *\n * @param zip\n * ZIP file.\n * @param name\n * entry name.\n * @return Returns <code>ZipEntry.STORED</code>, <code>ZipEntry.DEFLATED</code> or -1 if\n * the ZIP file does not contain the given entry.\n */\n public static int getCompressionMethodOfEntry(File zip, String name) {\n ZipFile zf = null;\n try {\n zf = new ZipFile(zip);\n ZipEntry zipEntry = zf.getEntry(name);\n if (zipEntry == null) {\n return -1;\n }\n return zipEntry.getMethod();\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n finally {\n closeQuietly(zf);\n }\n }\n\n /**\n * Checks if the ZIP file contains any of the given entries.\n *\n * @param zip\n * ZIP file.\n * @param names\n * entry names.\n * @return <code>true</code> if the ZIP file contains any of the given\n * entries.\n */\n public static boolean containsAnyEntry(File zip, String[] names) {\n ZipFile zf = null;\n try {\n zf = new ZipFile(zip);\n for (int i = 0; i < names.length; i++) {\n if (zf.getEntry(names[i]) != null) {\n return true;\n }\n }\n return false;\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n finally {\n closeQuietly(zf);\n }\n }\n\n /**\n * Unpacks a single entry from a ZIP file.\n *\n * @param zip\n * ZIP file.\n * @param name\n * entry name.\n * @return contents of the entry or <code>null</code> if it was not found.\n */\n public static byte[] unpackEntry(File zip, String name) {\n ZipFile zf = null;\n try {\n zf = new ZipFile(zip);\n return doUnpackEntry(zf, name);\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n finally {\n closeQuietly(zf);\n }\n }\n\n /**\n * Unpacks a single entry from a ZIP file.\n *\n * @param zip\n * ZIP file.\n * @param name\n * entry name.\n *\n * @param charset\n * charset to be used to process the zip\n *\n * @return contents of the entry or <code>null</code> if it was not found.\n */\n public static byte[] unpackEntry(File zip, String name, Charset charset) {\n ZipFile zf = null;\n try {\n if (charset != null) {\n zf = new ZipFile(zip, charset);\n }\n else {\n zf = new ZipFile(zip);\n }\n return doUnpackEntry(zf, name);\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n finally {\n closeQuietly(zf);\n }\n }\n\n /**\n * Unpacks a single entry from a ZIP file.\n *\n * @param zf\n * ZIP file.\n * @param name\n * entry name.\n * @return contents of the entry or <code>null</code> if it was not found.\n */\n public static byte[] unpackEntry(ZipFile zf, String name) {\n try {\n return doUnpackEntry(zf, name);\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n }\n\n /**\n * Unpacks a single entry from a ZIP file.\n *\n * @param zf\n * ZIP file.\n * @param name\n * entry name.\n * @return contents of the entry or <code>null</code> if it was not found.\n */\n private static byte[] doUnpackEntry(ZipFile zf, String name) throws IOException {\n ZipEntry ze = zf.getEntry(name);\n if (ze == null) {\n return null; // entry not found\n }\n\n InputStream is = zf.getInputStream(ze);\n try {\n return IOUtils.toByteArray(is);\n }\n finally {\n IOUtils.closeQuietly(is);\n }\n }\n\n /**\n * Unpacks a single entry from a ZIP stream.\n *\n * @param is\n * ZIP stream.\n * @param name\n * entry name.\n * @return contents of the entry or <code>null</code> if it was not found.\n */\n public static byte[] unpackEntry(InputStream is, String name) {\n ByteArrayUnpacker action = new ByteArrayUnpacker();\n if (!handle(is, name, action))\n return null; // entry not found\n return action.getBytes();\n }\n\n /**\n * Copies an entry into a byte array.\n *\n * @author Rein Raudjärv\n */\n private static class ByteArrayUnpacker implements ZipEntryCallback {\n\n private byte[] bytes;\n\n public void process(InputStream in, ZipEntry zipEntry) throws IOException {\n bytes = IOUtils.toByteArray(in);\n }\n\n public byte[] getBytes() {\n return bytes;\n }\n\n }\n\n /**\n * Unpacks a single file from a ZIP archive to a file.\n *\n * @param zip\n * ZIP file.\n * @param name\n * entry name.\n * @param file\n * target file to be created or overwritten.\n * @return <code>true</code> if the entry was found and unpacked,\n * <code>false</code> if the entry was not found.\n */\n public static boolean unpackEntry(File zip, String name, File file) {\n return unpackEntry(zip, name, file, null);\n }\n\n /**\n * Unpacks a single file from a ZIP archive to a file.\n *\n * @param zip\n * ZIP file.\n * @param name\n * entry name.\n * @param file\n * target file to be created or overwritten.\n * @param charset\n * charset to be used processing the zip\n *\n * @return <code>true</code> if the entry was found and unpacked,\n * <code>false</code> if the entry was not found.\n */\n public static boolean unpackEntry(File zip, String name, File file, Charset charset) {\n ZipFile zf = null;\n try {\n if (charset != null) {\n zf = new ZipFile(zip, charset);\n }\n else {\n zf = new ZipFile(zip);\n }\n return doUnpackEntry(zf, name, file);\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n finally {\n closeQuietly(zf);\n }\n }\n\n /**\n * Unpacks a single file from a ZIP archive to a file.\n *\n * @param zf\n * ZIP file.\n * @param name\n * entry name.\n * @param file\n * target file to be created or overwritten.\n * @return <code>true</code> if the entry was found and unpacked,\n * <code>false</code> if the entry was not found.\n */\n public static boolean unpackEntry(ZipFile zf, String name, File file) {\n try {\n return doUnpackEntry(zf, name, file);\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n }\n\n /**\n * Unpacks a single file from a ZIP archive to a file.\n *\n * @param zf\n * ZIP file.\n * @param name\n * entry name.\n * @param file\n * target file to be created or overwritten.\n * @return <code>true</code> if the entry was found and unpacked,\n * <code>false</code> if the entry was not found.\n */\n private static boolean doUnpackEntry(ZipFile zf, String name, File file) throws IOException {\n if (log.isTraceEnabled()) {\n log.trace(\"Extracting '\" + zf.getName() + \"' entry '\" + name + \"' into '\" + file + \"'.\");\n }\n\n ZipEntry ze = zf.getEntry(name);\n if (ze == null) {\n return false; // entry not found\n }\n\n if (ze.isDirectory() || zf.getInputStream(ze) == null) {\n if (file.isDirectory()) {\n return true;\n }\n if (file.exists()) {\n FileUtils.forceDelete(file);\n }\n return file.mkdirs();\n }\n\n InputStream in = new BufferedInputStream(zf.getInputStream(ze));\n try {\n FileUtils.copy(in, file);\n }\n finally {\n IOUtils.closeQuietly(in);\n }\n return true;\n }\n\n /**\n * Unpacks a single file from a ZIP stream to a file.\n *\n * @param is\n * ZIP stream.\n * @param name\n * entry name.\n * @param file\n * target file to be created or overwritten.\n * @return <code>true</code> if the entry was found and unpacked,\n * <code>false</code> if the entry was not found.\n * @throws java.io.IOException if file is not found or writing to it fails\n */\n public static boolean unpackEntry(InputStream is, String name, File file) throws IOException {\n return handle(is, name, new FileUnpacker(file));\n }\n\n /**\n * Copies an entry into a File.\n *\n * @author Rein Raudjärv\n */\n private static class FileUnpacker implements ZipEntryCallback {\n\n private final File file;\n\n public FileUnpacker(File file) {\n this.file = file;\n }\n\n public void process(InputStream in, ZipEntry zipEntry) throws IOException {\n FileUtils.copy(in, file);\n }\n\n }\n\n /* Traversing ZIP files */\n\n /**\n * Reads the given ZIP file and executes the given action for each entry.\n * <p>\n * For each entry the corresponding input stream is also passed to the action. If you want to stop the loop\n * then throw a ZipBreakException.\n *\n * @param zip\n * input ZIP file.\n * @param action\n * action to be called for each entry.\n *\n * @see ZipEntryCallback\n * @see #iterate(File, ZipInfoCallback)\n */\n public static void iterate(File zip, ZipEntryCallback action) {\n iterate(zip, action, null);\n }\n\n /**\n * Reads the given ZIP file and executes the given action for each entry.\n * <p>\n * For each entry the corresponding input stream is also passed to the action. If you want to stop the loop\n * then throw a ZipBreakException.\n *\n * @param zip\n * input ZIP file.\n * @param action\n * action to be called for each entry.\n *\n * @param charset\n * Charset used to processed the ZipFile with\n *\n * @see ZipEntryCallback\n * @see #iterate(File, ZipInfoCallback)\n */\n public static void iterate(File zip, ZipEntryCallback action, Charset charset) {\n ZipFile zf = null;\n try {\n if (charset == null) {\n zf = new ZipFile(zip);\n }\n else {\n zf = new ZipFile(zip, charset);\n }\n\n Enumeration<? extends ZipEntry> en = zf.entries();\n while (en.hasMoreElements()) {\n ZipEntry e = (ZipEntry) en.nextElement();\n\n InputStream is = zf.getInputStream(e);\n try {\n action.process(is, e);\n }\n catch (IOException ze) {\n throw new ZipException(\"Failed to process zip entry '\" + e.getName() + \"' with action \" + action, ze);\n }\n catch (ZipBreakException ex) {\n break;\n }\n finally {\n IOUtils.closeQuietly(is);\n }\n }\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n finally {\n closeQuietly(zf);\n }\n }\n\n /**\n * Reads the given ZIP file and executes the given action for each given entry.\n * <p>\n * For each given entry the corresponding input stream is also passed to the action. If you want to stop the loop then throw a ZipBreakException.\n *\n * @param zip\n * input ZIP file.\n * @param entryNames\n * names of entries to iterate\n * @param action\n * action to be called for each entry.\n *\n * @see ZipEntryCallback\n * @see #iterate(File, String[], ZipInfoCallback)\n */\n public static void iterate(File zip, String[] entryNames, ZipEntryCallback action) {\n iterate(zip, entryNames, action, null);\n }\n\n /**\n * Reads the given ZIP file and executes the given action for each given entry.\n * <p>\n * For each given entry the corresponding input stream is also passed to the action. If you want to stop the loop then throw a ZipBreakException.\n *\n * @param zip\n * input ZIP file.\n * @param entryNames\n * names of entries to iterate\n * @param action\n * action to be called for each entry.\n * @param charset\n * charset used to process the zip file\n *\n * @see ZipEntryCallback\n * @see #iterate(File, String[], ZipInfoCallback)\n */\n public static void iterate(File zip, String[] entryNames, ZipEntryCallback action, Charset charset) {\n ZipFile zf = null;\n try {\n if (charset == null) {\n zf = new ZipFile(zip);\n }\n else {\n zf = new ZipFile(zip, charset);\n }\n\n for (int i = 0; i < entryNames.length; i++) {\n ZipEntry e = zf.getEntry(entryNames[i]);\n if (e == null) {\n continue;\n }\n InputStream is = zf.getInputStream(e);\n try {\n action.process(is, e);\n }\n catch (IOException ze) {\n throw new ZipException(\"Failed to process zip entry '\" + e.getName() + \" with action \" + action, ze);\n }\n catch (ZipBreakException ex) {\n break;\n }\n finally {\n IOUtils.closeQuietly(is);\n }\n }\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n finally {\n closeQuietly(zf);\n }\n }\n\n /**\n * Scans the given ZIP file and executes the given action for each entry.\n * <p>\n * Only the meta-data without the actual data is read. If you want to stop the loop\n * then throw a ZipBreakException.\n *\n * @param zip\n * input ZIP file.\n * @param action\n * action to be called for each entry.\n *\n * @see ZipInfoCallback\n * @see #iterate(File, ZipEntryCallback)\n */\n public static void iterate(File zip, ZipInfoCallback action) {\n ZipFile zf = null;\n try {\n zf = new ZipFile(zip);\n\n Enumeration<? extends ZipEntry> en = zf.entries();\n while (en.hasMoreElements()) {\n ZipEntry e = (ZipEntry) en.nextElement();\n try {\n action.process(e);\n }\n catch (IOException ze) {\n throw new ZipException(\"Failed to process zip entry '\" + e.getName() + \" with action \" + action, ze);\n }\n catch (ZipBreakException ex) {\n break;\n }\n }\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n finally {\n closeQuietly(zf);\n }\n }\n\n /**\n * Scans the given ZIP file and executes the given action for each given entry.\n * <p>\n * Only the meta-data without the actual data is read. If you want to stop the loop then throw a ZipBreakException.\n *\n * @param zip\n * input ZIP file.\n * @param entryNames\n * names of entries to iterate\n * @param action\n * action to be called for each entry.\n *\n * @see ZipInfoCallback\n * @see #iterate(File, String[], ZipEntryCallback)\n */\n public static void iterate(File zip, String[] entryNames, ZipInfoCallback action) {\n ZipFile zf = null;\n try {\n zf = new ZipFile(zip);\n\n for (int i = 0; i < entryNames.length; i++) {\n ZipEntry e = zf.getEntry(entryNames[i]);\n if (e == null) {\n continue;\n }\n try {\n action.process(e);\n }\n catch (IOException ze) {\n throw new ZipException(\"Failed to process zip entry '\" + e.getName() + \" with action \" + action, ze);\n }\n catch (ZipBreakException ex) {\n break;\n }\n }\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n finally {\n closeQuietly(zf);\n }\n }\n\n /**\n * Reads the given ZIP stream and executes the given action for each entry.\n * <p>\n * For each entry the corresponding input stream is also passed to the action. If you want to stop the loop\n * then throw a ZipBreakException.\n *\n * @param is\n * input ZIP stream (it will not be closed automatically).\n * @param action\n * action to be called for each entry.\n * @param charset\n * charset to process entries in\n *\n * @see ZipEntryCallback\n * @see #iterate(File, ZipEntryCallback)\n */\n public static void iterate(InputStream is, ZipEntryCallback action, Charset charset) {\n try {\n ZipInputStream in = null;\n try {\n in = newCloseShieldZipInputStream(is, charset);\n ZipEntry entry;\n while ((entry = in.getNextEntry()) != null) {\n try {\n action.process(in, entry);\n }\n catch (IOException ze) {\n throw new ZipException(\"Failed to process zip entry '\" + entry.getName() + \" with action \" + action, ze);\n }\n catch (ZipBreakException ex) {\n break;\n }\n }\n }\n finally {\n if (in != null) {\n in.close();\n }\n }\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n }\n\n /**\n * See {@link #iterate(InputStream, ZipEntryCallback, Charset)}. This method\n * is a shorthand for a version where no Charset is specified.\n *\n * @param is\n * input ZIP stream (it will not be closed automatically).\n * @param action\n * action to be called for each entry.\n *\n * @see ZipEntryCallback\n * @see #iterate(File, ZipEntryCallback)\n */\n public static void iterate(InputStream is, ZipEntryCallback action) {\n iterate(is, action, null);\n }\n\n /**\n * Reads the given ZIP stream and executes the given action for each given entry.\n * <p>\n * For each given entry the corresponding input stream is also passed to the action. If you want to stop the loop then throw a ZipBreakException.\n *\n * @param is\n * input ZIP stream (it will not be closed automatically).\n * @param entryNames\n * names of entries to iterate\n * @param action\n * action to be called for each entry.\n * @param charset\n * charset to process entries in\n *\n * @see ZipEntryCallback\n * @see #iterate(File, String[], ZipEntryCallback)\n */\n public static void iterate(InputStream is, String[] entryNames, ZipEntryCallback action, Charset charset) {\n Set<String> namesSet = new HashSet<String>();\n for (int i = 0; i < entryNames.length; i++) {\n namesSet.add(entryNames[i]);\n }\n try {\n ZipInputStream in = null;\n try {\n in = newCloseShieldZipInputStream(is, charset);\n ZipEntry entry;\n while ((entry = in.getNextEntry()) != null) {\n if (!namesSet.contains(entry.getName())) {\n // skip the unnecessary entry\n continue;\n }\n try {\n action.process(in, entry);\n }\n catch (IOException ze) {\n throw new ZipException(\"Failed to process zip entry '\" + entry.getName() + \" with action \" + action, ze);\n }\n catch (ZipBreakException ex) {\n break;\n }\n }\n }\n finally {\n if (in != null) {\n in.close();\n }\n }\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n }\n\n /**\n * See @link{ {@link #iterate(InputStream, ZipEntryCallback, Charset)}. It is a\n * shorthand where no Charset is specified.\n *\n * @param is\n * input ZIP stream (it will not be closed automatically).\n * @param entryNames\n * names of entries to iterate\n * @param action\n * action to be called for each entry.\n *\n * @see ZipEntryCallback\n * @see #iterate(File, String[], ZipEntryCallback)\n */\n public static void iterate(InputStream is, String[] entryNames, ZipEntryCallback action) {\n iterate(is, entryNames, action, null);\n }\n\n /**\n * Creates a new {@link ZipInputStream} based on the given {@link InputStream}. It will be buffered and close-shielded.\n * Closing the result stream flushes the buffers and frees up resources of the {@link ZipInputStream}. However the source stream itself remains open.\n */\n private static ZipInputStream newCloseShieldZipInputStream(final InputStream is, Charset charset) {\n InputStream in = new BufferedInputStream(new CloseShieldInputStream(is));\n if (charset == null) {\n return new ZipInputStream(in);\n }\n return ZipFileUtil.createZipInputStream(in, charset);\n }\n\n /**\n * Reads the given ZIP file and executes the given action for a single entry.\n *\n * @param zip\n * input ZIP file.\n * @param name\n * entry name.\n * @param action\n * action to be called for this entry.\n * @return <code>true</code> if the entry was found, <code>false</code> if the\n * entry was not found.\n *\n * @see ZipEntryCallback\n */\n public static boolean handle(File zip, String name, ZipEntryCallback action) {\n ZipFile zf = null;\n try {\n zf = new ZipFile(zip);\n\n ZipEntry ze = zf.getEntry(name);\n if (ze == null) {\n return false; // entry not found\n }\n\n InputStream in = new BufferedInputStream(zf.getInputStream(ze));\n try {\n action.process(in, ze);\n }\n finally {\n IOUtils.closeQuietly(in);\n }\n return true;\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n finally {\n closeQuietly(zf);\n }\n }\n\n /**\n * Reads the given ZIP stream and executes the given action for a single\n * entry.\n *\n * @param is\n * input ZIP stream (it will not be closed automatically).\n * @param name\n * entry name.\n * @param action\n * action to be called for this entry.\n * @return <code>true</code> if the entry was found, <code>false</code> if the\n * entry was not found.\n *\n * @see ZipEntryCallback\n */\n public static boolean handle(InputStream is, String name, ZipEntryCallback action) {\n SingleZipEntryCallback helper = new SingleZipEntryCallback(name, action);\n iterate(is, helper);\n return helper.found();\n }\n\n /**\n * ZipEntryCallback which is only applied to single entry.\n *\n * @author Rein Raudjärv\n */\n private static class SingleZipEntryCallback implements ZipEntryCallback {\n\n private final String name;\n\n private final ZipEntryCallback action;\n\n private boolean found;\n\n public SingleZipEntryCallback(String name, ZipEntryCallback action) {\n this.name = name;\n this.action = action;\n }\n\n public void process(InputStream in, ZipEntry zipEntry) throws IOException {\n if (name.equals(zipEntry.getName())) {\n found = true;\n action.process(in, zipEntry);\n }\n }\n\n public boolean found() {\n return found;\n }\n\n }\n\n /* Extracting whole ZIP files. */\n\n /**\n * Unpacks a ZIP file to the given directory.\n * <p>\n * The output directory must not be a file.\n *\n * @param zip\n * input ZIP file.\n * @param outputDir\n * output directory (created automatically if not found).\n */\n public static void unpack(File zip, final File outputDir) {\n unpack(zip, outputDir, IdentityNameMapper.INSTANCE);\n }\n\n /**\n * Unpacks a ZIP file to the given directory using a specific Charset\n * for the input file.\n *\n * <p>\n * The output directory must not be a file.\n *\n * @param zip\n * input ZIP file.\n * @param outputDir\n * output directory (created automatically if not found).\n *\n * @param charset\n * charset used to unpack the zip file\n */\n public static void unpack(File zip, final File outputDir, Charset charset) {\n unpack(zip, outputDir, IdentityNameMapper.INSTANCE, charset);\n }\n\n /**\n * Unpacks a ZIP file to the given directory.\n * <p>\n * The output directory must not be a file.\n *\n * @param zip\n * input ZIP file.\n * @param outputDir\n * output directory (created automatically if not found).\n * @param mapper\n * call-back for renaming the entries.\n * @param charset\n * charset used to process the zip file\n */\n public static void unpack(File zip, File outputDir, NameMapper mapper, Charset charset) {\n log.debug(\"Extracting '{}' into '{}'.\", zip, outputDir);\n iterate(zip, new Unpacker(outputDir, mapper), charset);\n }\n\n /**\n * Unpacks a ZIP file to the given directory using a specific Charset\n * for the input file.\n *\n * <p>\n * The output directory must not be a file.\n *\n * @param zip\n * input ZIP file.\n * @param outputDir\n * output directory (created automatically if not found).\n * @param mapper\n * call-back for renaming the entries.\n */\n public static void unpack(File zip, File outputDir, NameMapper mapper) {\n log.debug(\"Extracting '{}' into '{}'.\", zip, outputDir);\n iterate(zip, new Unpacker(outputDir, mapper));\n }\n\n /**\n * Unwraps a ZIP file to the given directory shaving of root dir.\n * If there are multiple root dirs or entries in the root of zip,\n * ZipException is thrown.\n * <p>\n * The output directory must not be a file.\n *\n * @param zip\n * input ZIP file.\n * @param outputDir\n * output directory (created automatically if not found).\n */\n public static void unwrap(File zip, final File outputDir) {\n unwrap(zip, outputDir, IdentityNameMapper.INSTANCE);\n }\n\n /**\n * Unwraps a ZIP file to the given directory shaving of root dir.\n * If there are multiple root dirs or entries in the root of zip,\n * ZipException is thrown.\n * <p>\n * The output directory must not be a file.\n *\n * @param zip\n * input ZIP file.\n * @param outputDir\n * output directory (created automatically if not found).\n * @param mapper\n * call-back for renaming the entries.\n */\n public static void unwrap(File zip, File outputDir, NameMapper mapper) {\n log.debug(\"Unwrapping '{}' into '{}'.\", zip, outputDir);\n iterate(zip, new Unwraper(outputDir, mapper));\n }\n\n /**\n * Unpacks a ZIP stream to the given directory.\n * <p>\n * The output directory must not be a file.\n *\n * @param is\n * inputstream for ZIP file.\n * @param outputDir\n * output directory (created automatically if not found).\n */\n public static void unpack(InputStream is, File outputDir) {\n unpack(is, outputDir, IdentityNameMapper.INSTANCE, null);\n }\n\n /**\n * Unpacks a ZIP stream to the given directory.\n * <p>\n * The output directory must not be a file.\n *\n * @param is\n * inputstream for ZIP file.\n * @param outputDir\n * output directory (created automatically if not found).\n * @param charset\n * charset used to process the zip stream\n */\n public static void unpack(InputStream is, File outputDir, Charset charset) {\n unpack(is, outputDir, IdentityNameMapper.INSTANCE, charset);\n }\n\n /**\n * Unpacks a ZIP stream to the given directory.\n * <p>\n * The output directory must not be a file.\n *\n * @param is\n * inputstream for ZIP file.\n * @param outputDir\n * output directory (created automatically if not found).\n * @param mapper\n * call-back for renaming the entries.\n */\n public static void unpack(InputStream is, File outputDir, NameMapper mapper) {\n unpack(is, outputDir, mapper, null);\n }\n\n /**\n * Unpacks a ZIP stream to the given directory.\n * <p>\n * The output directory must not be a file.\n *\n * @param is\n * inputstream for ZIP file.\n * @param outputDir\n * output directory (created automatically if not found).\n * @param mapper\n * call-back for renaming the entries.\n * @param charset\n * charset to use when unpacking the stream\n */\n public static void unpack(InputStream is, File outputDir, NameMapper mapper, Charset charset) {\n log.debug(\"Extracting {} into '{}'.\", is, outputDir);\n iterate(is, new Unpacker(outputDir, mapper), charset);\n }\n\n /**\n * Unwraps a ZIP file to the given directory shaving of root dir.\n * If there are multiple root dirs or entries in the root of zip,\n * ZipException is thrown.\n * <p>\n * The output directory must not be a file.\n *\n * @param is\n * inputstream for ZIP file.\n * @param outputDir\n * output directory (created automatically if not found).\n */\n public static void unwrap(InputStream is, File outputDir) {\n unwrap(is, outputDir, IdentityNameMapper.INSTANCE);\n }\n\n /**\n * Unwraps a ZIP file to the given directory shaving of root dir.\n * If there are multiple root dirs or entries in the root of zip,\n * ZipException is thrown.\n * <p>\n * The output directory must not be a file.\n *\n * @param is\n * inputstream for ZIP file.\n * @param outputDir\n * output directory (created automatically if not found).\n * @param mapper\n * call-back for renaming the entries.\n */\n public static void unwrap(InputStream is, File outputDir, NameMapper mapper) {\n log.debug(\"Unwrapping {} into '{}'.\", is, outputDir);\n iterate(is, new Unwraper(outputDir, mapper));\n }\n\n /**\n * Unpacks each ZIP entry.\n *\n * @author Rein Raudjärv\n */\n private static class Unpacker implements ZipEntryCallback {\n\n private final File outputDir;\n private final NameMapper mapper;\n\n public Unpacker(File outputDir, NameMapper mapper) {\n this.outputDir = outputDir;\n this.mapper = mapper;\n }\n\n public void process(InputStream in, ZipEntry zipEntry) throws IOException {\n String name = mapper.map(zipEntry.getName());\n if (name != null) {\n File file = new File(outputDir, name);\n if (zipEntry.isDirectory()) {\n FileUtils.forceMkdir(file);\n }\n else {\n FileUtils.forceMkdir(file.getParentFile());\n\n if (log.isDebugEnabled() && file.exists()) {\n log.debug(\"Overwriting file '{}'.\", zipEntry.getName());\n }\n\n FileUtils.copy(in, file);\n }\n\n ZTFilePermissions permissions = ZipEntryUtil.getZTFilePermissions(zipEntry);\n if (permissions != null) {\n ZTFilePermissionsUtil.getDefaultStategy().setPermissions(file, permissions);\n }\n }\n }\n }\n\n /**\n * Unpacks each ZIP entries. Presumes they are packed with the backslash separator.\n * Some archives can have this problem if they are created with some software\n * that is not following the ZIP specification.\n *\n * @since zt-zip 1.9\n */\n public static class BackslashUnpacker implements ZipEntryCallback {\n\n private final File outputDir;\n private final NameMapper mapper;\n\n public BackslashUnpacker(File outputDir, NameMapper mapper) {\n this.outputDir = outputDir;\n this.mapper = mapper;\n }\n\n public BackslashUnpacker(File outputDir) {\n this(outputDir, IdentityNameMapper.INSTANCE);\n }\n\n public void process(InputStream in, ZipEntry zipEntry) throws IOException {\n String name = mapper.map(zipEntry.getName());\n if (name != null) {\n /**\n * We assume that EVERY backslash will denote a directory\n * separator. Also such broken archives don't have entries that\n * are just directories. Everything is a file. See the example\n *\n * Archive: backSlashTest.zip\n * testing: testDirectory\\testfileInTestDirectory.txt OK\n * testing: testDirectory\\testSubdirectory\\testFileInTestSubdirectory.txt OK\n * No errors detected in compressed data of backSlashTest.zip.\n */\n if (name.indexOf('\\\\') != -1) {\n File parentDirectory = outputDir;\n String[] dirs = name.split(\"\\\\\\\\\");\n\n // lets create all the directories and the last entry is the file as EVERY entry is a file\n for (int i = 0; i < dirs.length - 1; i++) {\n File file = new File(parentDirectory, dirs[i]);\n if (!file.exists()) {\n FileUtils.forceMkdir(file);\n }\n parentDirectory = file;\n }\n File destFile = new File(parentDirectory, dirs[dirs.length - 1]);\n FileUtils.copy(in, destFile);\n }\n // it could be that there are just top level files that the unpacker is used for\n else {\n File destFile = new File(outputDir, name);\n FileUtils.copy(in, destFile);\n }\n }\n }\n }\n\n /**\n * Unwraps entries excluding a single parent dir. If there are multiple roots\n * ZipException is thrown.\n *\n * @author Oleg Shelajev\n */\n private static class Unwraper implements ZipEntryCallback {\n\n private final File outputDir;\n private final NameMapper mapper;\n private String rootDir;\n\n public Unwraper(File outputDir, NameMapper mapper) {\n this.outputDir = outputDir;\n this.mapper = mapper;\n }\n\n public void process(InputStream in, ZipEntry zipEntry) throws IOException {\n String root = getRootName(zipEntry.getName());\n if (rootDir == null) {\n rootDir = root;\n }\n else if (!rootDir.equals(root)) {\n throw new ZipException(\"Unwrapping with multiple roots is not supported, roots: \" + rootDir + \", \" + root);\n }\n\n String name = mapper.map(getUnrootedName(root, zipEntry.getName()));\n if (name != null) {\n File file = new File(outputDir, name);\n if (zipEntry.isDirectory()) {\n FileUtils.forceMkdir(file);\n }\n else {\n FileUtils.forceMkdir(file.getParentFile());\n\n if (log.isDebugEnabled() && file.exists()) {\n log.debug(\"Overwriting file '{}'.\", zipEntry.getName());\n }\n\n FileUtils.copy(in, file);\n }\n }\n }\n\n private String getUnrootedName(String root, String name) {\n return name.substring(root.length());\n }\n\n private String getRootName(final String name) {\n String newName = name.substring(FilenameUtils.getPrefixLength(name));\n int idx = newName.indexOf(PATH_SEPARATOR);\n if (idx < 0) {\n throw new ZipException(\"Entry \" + newName + \" from the root of the zip is not supported\");\n }\n return newName.substring(0, newName.indexOf(PATH_SEPARATOR));\n }\n }\n\n /**\n * Unpacks a ZIP file to its own location.\n * <p>\n * The ZIP file will be first renamed (using a temporary name). After the\n * extraction it will be deleted.\n *\n * @param zip\n * input ZIP file as well as the target directory.\n *\n * @see #unpack(File, File)\n */\n public static void explode(File zip) {\n try {\n // Find a new unique name is the same directory\n File tempFile = FileUtils.getTempFileFor(zip);\n\n // Rename the archive\n FileUtils.moveFile(zip, tempFile);\n\n // Unpack it\n unpack(tempFile, zip);\n\n // Delete the archive\n if (!tempFile.delete()) {\n throw new IOException(\"Unable to delete file: \" + tempFile);\n }\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n }\n\n /* Compressing single entries to ZIP files. */\n\n /**\n * Compresses the given file into a ZIP file with single entry.\n *\n * @param file file to be compressed.\n * @return ZIP file created.\n */\n public static byte[] packEntry(File file) {\n log.trace(\"Compressing '{}' into a ZIP file with single entry.\", file);\n\n ByteArrayOutputStream result = new ByteArrayOutputStream();\n try {\n ZipOutputStream out = new ZipOutputStream(result);\n ZipEntry entry = ZipEntryUtil.fromFile(file.getName(), file);\n InputStream in = new BufferedInputStream(new FileInputStream(file));\n try {\n ZipEntryUtil.addEntry(entry, in, out);\n }\n finally {\n IOUtils.closeQuietly(in);\n }\n out.close();\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n return result.toByteArray();\n }\n\n /* Compressing ZIP files. */\n\n /**\n * Compresses the given directory and all its sub-directories into a ZIP file.\n * <p>\n * The ZIP file must not be a directory and its parent directory must exist.\n * Will not include the root directory name in the archive.\n *\n * @param rootDir\n * root directory.\n * @param zip\n * ZIP file that will be created or overwritten.\n */\n public static void pack(File rootDir, File zip) {\n pack(rootDir, zip, DEFAULT_COMPRESSION_LEVEL);\n }\n\n /**\n * Compresses the given directory and all its sub-directories into a ZIP file.\n * <p>\n * The ZIP file must not be a directory and its parent directory must exist.\n * Will not include the root directory name in the archive.\n *\n * @param rootDir\n * root directory.\n * @param zip\n * ZIP file that will be created or overwritten.\n * @param compressionLevel\n * compression level\n */\n public static void pack(File rootDir, File zip, int compressionLevel) {\n pack(rootDir, zip, IdentityNameMapper.INSTANCE, compressionLevel);\n }\n\n /**\n * Compresses the given directory and all its sub-directories into a ZIP file.\n * <p>\n * The ZIP file must not be a directory and its parent directory must exist.\n * Will not include the root directory name in the archive.\n *\n * @param sourceDir\n * root directory.\n * @param targetZipFile\n * ZIP file that will be created or overwritten.\n * @param preserveRoot\n * true if the resulted archive should have the top directory entry\n */\n public static void pack(final File sourceDir, final File targetZipFile, final boolean preserveRoot) {\n if (preserveRoot) {\n final String parentName = sourceDir.getName();\n pack(sourceDir, targetZipFile, new NameMapper() {\n public String map(String name) {\n return parentName + PATH_SEPARATOR + name;\n }\n });\n }\n else {\n pack(sourceDir, targetZipFile);\n }\n }\n\n /**\n * Compresses the given file into a ZIP file.\n * <p>\n * The ZIP file must not be a directory and its parent directory must exist.\n *\n * @param fileToPack\n * file that needs to be zipped.\n * @param destZipFile\n * ZIP file that will be created or overwritten.\n */\n public static void packEntry(File fileToPack, File destZipFile) {\n packEntry(fileToPack, destZipFile, IdentityNameMapper.INSTANCE);\n }\n\n /**\n * Compresses the given file into a ZIP file.\n * <p>\n * The ZIP file must not be a directory and its parent directory must exist.\n *\n * @param fileToPack\n * file that needs to be zipped.\n * @param destZipFile\n * ZIP file that will be created or overwritten.\n * @param fileName\n * the name for the file inside the archive\n */\n public static void packEntry(File fileToPack, File destZipFile, final String fileName) {\n packEntry(fileToPack, destZipFile, new NameMapper() {\n public String map(String name) {\n return fileName;\n }\n });\n }\n\n /**\n * Compresses the given file into a ZIP file.\n * <p>\n * The ZIP file must not be a directory and its parent directory must exist.\n *\n * @param fileToPack\n * file that needs to be zipped.\n * @param destZipFile\n * ZIP file that will be created or overwritten.\n * @param mapper\n * call-back for renaming the entries.\n */\n public static void packEntry(File fileToPack, File destZipFile, NameMapper mapper) {\n packEntries(new File[] { fileToPack }, destZipFile, mapper);\n }\n\n /**\n * Compresses the given files into a ZIP file.\n * <p>\n * The ZIP file must not be a directory and its parent directory must exist.\n *\n * @param filesToPack\n * files that needs to be zipped.\n * @param destZipFile\n * ZIP file that will be created or overwritten.\n */\n public static void packEntries(File[] filesToPack, File destZipFile) {\n packEntries(filesToPack, destZipFile, IdentityNameMapper.INSTANCE);\n }\n\n /**\n * Compresses the given files into a ZIP file.\n * <p>\n * The ZIP file must not be a directory and its parent directory must exist.\n *\n * @param filesToPack\n * files that needs to be zipped.\n * @param destZipFile\n * ZIP file that will be created or overwritten.\n * @param mapper\n * call-back for renaming the entries.\n */\n public static void packEntries(File[] filesToPack, File destZipFile, NameMapper mapper) {\n packEntries(filesToPack, destZipFile, mapper, DEFAULT_COMPRESSION_LEVEL);\n }\n\n /**\n * Compresses the given files into a ZIP file.\n * <p>\n * The ZIP file must not be a directory and its parent directory must exist.\n *\n * @param filesToPack\n * files that needs to be zipped.\n * @param destZipFile\n * ZIP file that will be created or overwritten.\n * @param compressionLevel\n * ZIP file compression level (speed versus filesize), e.g. <code>Deflater.NO_COMPRESSION</code>, <code>Deflater.BEST_SPEED</code>, or\n * <code>Deflater.BEST_COMPRESSION</code>\n */\n public static void packEntries(File[] filesToPack, File destZipFile, int compressionLevel) {\n packEntries(filesToPack, destZipFile, IdentityNameMapper.INSTANCE, compressionLevel);\n }\n\n /**\n * Compresses the given files into a ZIP file.\n * <p>\n * The ZIP file must not be a directory and its parent directory must exist.\n *\n * @param filesToPack\n * files that needs to be zipped.\n * @param destZipFile\n * ZIP file that will be created or overwritten.\n * @param mapper\n * call-back for renaming the entries.\n * @param compressionLevel\n * ZIP file compression level (speed versus filesize), e.g. <code>Deflater.NO_COMPRESSION</code>, <code>Deflater.BEST_SPEED</code>, or\n * <code>Deflater.BEST_COMPRESSION</code>\n */\n public static void packEntries(File[] filesToPack, File destZipFile, NameMapper mapper, int compressionLevel) {\n log.debug(\"Compressing '{}' into '{}'.\", filesToPack, destZipFile);\n\n ZipOutputStream out = null;\n FileOutputStream fos = null;\n try {\n fos = new FileOutputStream(destZipFile);\n out = new ZipOutputStream(new BufferedOutputStream(fos));\n out.setLevel(compressionLevel);\n\n for (int i = 0; i < filesToPack.length; i++) {\n File fileToPack = filesToPack[i];\n\n ZipEntry zipEntry = ZipEntryUtil.fromFile(mapper.map(fileToPack.getName()), fileToPack);\n out.putNextEntry(zipEntry);\n FileUtils.copy(fileToPack, out);\n out.closeEntry();\n }\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n finally {\n IOUtils.closeQuietly(out);\n IOUtils.closeQuietly(fos);\n }\n }\n\n /**\n * Compresses the given directory and all its sub-directories into a ZIP file.\n * <p>\n * The ZIP file must not be a directory and its parent directory must exist.\n *\n * @param sourceDir\n * root directory.\n * @param targetZip\n * ZIP file that will be created or overwritten.\n * @param mapper\n * call-back for renaming the entries.\n */\n public static void pack(File sourceDir, File targetZip, NameMapper mapper) {\n pack(sourceDir, targetZip, mapper, DEFAULT_COMPRESSION_LEVEL);\n }\n\n /**\n * Compresses the given directory and all its sub-directories into a ZIP file.\n * <p>\n * The ZIP file must not be a directory and its parent directory must exist.\n *\n * @param sourceDir\n * root directory.\n * @param targetZip\n * ZIP file that will be created or overwritten.\n * @param mapper\n * call-back for renaming the entries.\n * @param compressionLevel\n * compression level\n */\n public static void pack(File sourceDir, File targetZip, NameMapper mapper, int compressionLevel) {\n log.debug(\"Compressing '{}' into '{}'.\", sourceDir, targetZip);\n if (!sourceDir.exists()) {\n throw new ZipException(\"Given file '\" + sourceDir + \"' doesn't exist!\");\n }\n ZipOutputStream out = null;\n try {\n out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(targetZip)));\n out.setLevel(compressionLevel);\n pack(sourceDir, out, mapper, \"\", true);\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n finally {\n IOUtils.closeQuietly(out);\n }\n }\n\n /**\n * Compresses the given directory and all of its sub-directories into the passed in\n * stream. It is the responsibility of the caller to close the passed in\n * stream properly.\n *\n * @param sourceDir\n * root directory.\n * @param os\n * output stream (will be buffered in this method).\n *\n * @since 1.10\n */\n public static void pack(File sourceDir, OutputStream os) {\n pack(sourceDir, os, IdentityNameMapper.INSTANCE, DEFAULT_COMPRESSION_LEVEL);\n }\n\n /**\n * Compresses the given directory and all of its sub-directories into the passed in\n * stream. It is the responsibility of the caller to close the passed in\n * stream properly.\n *\n * @param sourceDir\n * root directory.\n * @param os\n * output stream (will be buffered in this method).\n * @param compressionLevel\n * compression level\n *\n * @since 1.10\n */\n public static void pack(File sourceDir, OutputStream os, int compressionLevel) {\n pack(sourceDir, os, IdentityNameMapper.INSTANCE, compressionLevel);\n }\n\n /**\n * Compresses the given directory and all of its sub-directories into the passed in\n * stream. It is the responsibility of the caller to close the passed in\n * stream properly.\n *\n * @param sourceDir\n * root directory.\n * @param os\n * output stream (will be buffered in this method).\n * @param mapper\n * call-back for renaming the entries.\n *\n * @since 1.10\n */\n public static void pack(File sourceDir, OutputStream os, NameMapper mapper) {\n pack(sourceDir, os, mapper, DEFAULT_COMPRESSION_LEVEL);\n }\n\n /**\n * Compresses the given directory and all of its sub-directories into the passed in\n * stream. It is the responsibility of the caller to close the passed in\n * stream properly.\n *\n * @param sourceDir\n * root directory.\n * @param os\n * output stream (will be buffered in this method).\n * @param mapper\n * call-back for renaming the entries.\n * @param compressionLevel\n * compression level\n *\n * @since 1.10\n */\n public static void pack(File sourceDir, OutputStream os, NameMapper mapper, int compressionLevel) {\n log.debug(\"Compressing '{}' into a stream.\", sourceDir);\n if (!sourceDir.exists()) {\n throw new ZipException(\"Given file '\" + sourceDir + \"' doesn't exist!\");\n }\n ZipOutputStream out = null;\n IOException error = null;\n try {\n out = new ZipOutputStream(new BufferedOutputStream(os));\n out.setLevel(compressionLevel);\n pack(sourceDir, out, mapper, \"\", true);\n }\n catch (IOException e) {\n error = e;\n }\n finally {\n if (out != null && error == null) {\n try {\n out.finish();\n out.flush();\n }\n catch (IOException e) {\n error = e;\n }\n }\n }\n if (error != null) {\n throw ZipExceptionUtil.rethrow(error);\n }\n }\n\n /**\n * Compresses the given directory and all its sub-directories into a ZIP file.\n *\n * @param dir\n * root directory.\n * @param out\n * ZIP output stream.\n * @param mapper\n * call-back for renaming the entries.\n * @param pathPrefix\n * prefix to be used for the entries.\n * @param mustHaveChildren\n * if true, but directory to pack doesn't have any files, throw an exception.\n */\n private static void pack(File dir, ZipOutputStream out, NameMapper mapper, String pathPrefix, boolean mustHaveChildren) throws IOException {\n String[] filenames = dir.list();\n if (filenames == null) {\n if (!dir.exists()) {\n throw new ZipException(\"Given file '\" + dir + \"' doesn't exist!\");\n }\n throw new IOException(\"Given file is not a directory '\" + dir + \"'\");\n }\n\n if (mustHaveChildren && filenames.length == 0) {\n throw new ZipException(\"Given directory '\" + dir + \"' doesn't contain any files!\");\n }\n\n for (int i = 0; i < filenames.length; i++) {\n String filename = filenames[i];\n File file = new File(dir, filename);\n boolean isDir = file.isDirectory();\n String path = pathPrefix + file.getName(); // NOSONAR\n if (isDir) {\n path += PATH_SEPARATOR; // NOSONAR\n }\n\n // Create a ZIP entry\n String name = mapper.map(path);\n if (name != null) {\n ZipEntry zipEntry = ZipEntryUtil.fromFile(name, file);\n\n out.putNextEntry(zipEntry);\n\n // Copy the file content\n if (!isDir) {\n FileUtils.copy(file, out);\n }\n\n out.closeEntry();\n }\n\n // Traverse the directory\n if (isDir) {\n pack(file, out, mapper, path, false);\n }\n }\n }\n\n /**\n * Repacks a provided ZIP file into a new ZIP with a given compression level.\n * <p>\n *\n * @param srcZip\n * source ZIP file.\n * @param dstZip\n * destination ZIP file.\n * @param compressionLevel\n * compression level.\n */\n public static void repack(File srcZip, File dstZip, int compressionLevel) {\n\n log.debug(\"Repacking '{}' into '{}'.\", srcZip, dstZip);\n\n RepackZipEntryCallback callback = new RepackZipEntryCallback(dstZip, compressionLevel);\n\n try {\n iterate(srcZip, callback);\n }\n finally {\n callback.closeStream();\n }\n }\n\n /**\n * Repacks a provided ZIP input stream into a ZIP file with a given compression level.\n * <p>\n *\n * @param is\n * ZIP input stream.\n * @param dstZip\n * destination ZIP file.\n * @param compressionLevel\n * compression level.\n */\n public static void repack(InputStream is, File dstZip, int compressionLevel) {\n\n log.debug(\"Repacking from input stream into '{}'.\", dstZip);\n\n RepackZipEntryCallback callback = new RepackZipEntryCallback(dstZip, compressionLevel);\n\n try {\n iterate(is, callback);\n }\n finally {\n callback.closeStream();\n }\n }\n\n /**\n * Repacks a provided ZIP file and replaces old file with the new one.\n * <p>\n *\n * @param zip\n * source ZIP file to be repacked and replaced.\n * @param compressionLevel\n * compression level.\n */\n public static void repack(File zip, int compressionLevel) {\n try {\n File tmpZip = FileUtils.getTempFileFor(zip);\n\n repack(zip, tmpZip, compressionLevel);\n\n // Delete original zip\n if (!zip.delete()) {\n throw new IOException(\"Unable to delete the file: \" + zip);\n }\n\n // Rename the archive\n FileUtils.moveFile(tmpZip, zip);\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n }\n\n /**\n * RepackZipEntryCallback used in repacking methods.\n *\n * @author Pavel Grigorenko\n */\n private static final class RepackZipEntryCallback implements ZipEntryCallback {\n\n private ZipOutputStream out;\n\n private RepackZipEntryCallback(File dstZip, int compressionLevel) {\n try {\n this.out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(dstZip)));\n this.out.setLevel(compressionLevel);\n }\n catch (IOException e) {\n ZipExceptionUtil.rethrow(e);\n }\n }\n\n public void process(InputStream in, ZipEntry zipEntry) throws IOException {\n ZipEntryUtil.copyEntry(zipEntry, in, out);\n }\n\n private void closeStream() {\n IOUtils.closeQuietly(out);\n }\n }\n\n /**\n * Compresses a given directory in its own location.\n * <p>\n * A ZIP file will be first created with a temporary name. After the\n * compressing the directory will be deleted and the ZIP file will be renamed\n * as the original directory.\n *\n * @param dir\n * input directory as well as the target ZIP file.\n *\n * @see #pack(File, File)\n */\n public static void unexplode(File dir) {\n unexplode(dir, DEFAULT_COMPRESSION_LEVEL);\n }\n\n /**\n * Compresses a given directory in its own location.\n * <p>\n * A ZIP file will be first created with a temporary name. After the\n * compressing the directory will be deleted and the ZIP file will be renamed\n * as the original directory.\n *\n * @param dir\n * input directory as well as the target ZIP file.\n * @param compressionLevel\n * compression level\n *\n * @see #pack(File, File)\n */\n public static void unexplode(File dir, int compressionLevel) {\n try {\n // Find a new unique name is the same directory\n File zip = FileUtils.getTempFileFor(dir);\n\n // Pack it\n pack(dir, zip, compressionLevel);\n\n // Delete the directory\n FileUtils.deleteDirectory(dir);\n\n // Rename the archive\n FileUtils.moveFile(zip, dir);\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n }\n\n /**\n * Compresses the given entries into an output stream.\n *\n * @param entries\n * ZIP entries added.\n * @param os\n * output stream for the new ZIP (does not have to be buffered)\n *\n * @since 1.9\n */\n public static void pack(ZipEntrySource[] entries, OutputStream os) {\n if (log.isDebugEnabled()) {\n log.debug(\"Creating stream from {}.\", Arrays.asList(entries));\n }\n pack(entries, os, false);\n }\n\n private static void pack(ZipEntrySource[] entries, OutputStream os, boolean closeStream) {\n try {\n ZipOutputStream out = new ZipOutputStream(os);\n for (int i = 0; i < entries.length; i++) {\n addEntry(entries[i], out);\n }\n out.flush();\n out.finish();\n if (closeStream) {\n out.close();\n }\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n }\n\n /**\n * Compresses the given entries into a new ZIP file.\n *\n * @param entries\n * ZIP entries added.\n * @param zip\n * new ZIP file created.\n */\n public static void pack(ZipEntrySource[] entries, File zip) {\n if (log.isDebugEnabled()) {\n log.debug(\"Creating '{}' from {}.\", zip, Arrays.asList(entries));\n }\n\n OutputStream out = null;\n try {\n out = new BufferedOutputStream(new FileOutputStream(zip));\n pack(entries, out, true);\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n finally {\n IOUtils.closeQuietly(out);\n }\n }\n\n /**\n * Copies an existing ZIP file and appends it with one new entry.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param path\n * new ZIP entry path.\n * @param file\n * new entry to be added.\n * @param destZip\n * new ZIP file created.\n */\n public static void addEntry(File zip, String path, File file, File destZip) {\n addEntry(zip, new FileSource(path, file), destZip);\n }\n\n /**\n * Changes a zip file, adds one new entry in-place.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param path\n * new ZIP entry path.\n * @param file\n * new entry to be added.\n */\n public static void addEntry(final File zip, final String path, final File file) {\n operateInPlace(zip, new InPlaceAction() {\n public boolean act(File tmpFile) {\n addEntry(zip, path, file, tmpFile);\n return true;\n }\n });\n }\n\n /**\n * Copies an existing ZIP file and appends it with one new entry.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param path\n * new ZIP entry path.\n * @param bytes\n * new entry bytes (or <code>null</code> if directory).\n * @param destZip\n * new ZIP file created.\n */\n public static void addEntry(File zip, String path, byte[] bytes, File destZip) {\n addEntry(zip, new ByteSource(path, bytes), destZip);\n }\n\n /**\n * Copies an existing ZIP file and appends it with one new entry.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param path\n * new ZIP entry path.\n * @param bytes\n * new entry bytes (or <code>null</code> if directory).\n * @param destZip\n * new ZIP file created.\n * @param compressionMethod\n * the new compression method (<code>ZipEntry.STORED</code> or <code>ZipEntry.DEFLATED</code>).\n */\n public static void addEntry(File zip, String path, byte[] bytes, File destZip, final int compressionMethod) {\n addEntry(zip, new ByteSource(path, bytes, compressionMethod), destZip);\n }\n\n /**\n * Changes a zip file, adds one new entry in-place.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param path\n * new ZIP entry path.\n * @param bytes\n * new entry bytes (or <code>null</code> if directory).\n */\n public static void addEntry(final File zip, final String path, final byte[] bytes) {\n operateInPlace(zip, new InPlaceAction() {\n public boolean act(File tmpFile) {\n addEntry(zip, path, bytes, tmpFile);\n return true;\n }\n });\n }\n\n /**\n * Changes a zip file, adds one new entry in-place.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param path\n * new ZIP entry path.\n * @param bytes\n * new entry bytes (or <code>null</code> if directory).\n * @param compressionMethod\n * the new compression method (<code>ZipEntry.STORED</code> or <code>ZipEntry.DEFLATED</code>).\n */\n public static void addEntry(final File zip, final String path, final byte[] bytes, final int compressionMethod) {\n operateInPlace(zip, new InPlaceAction() {\n public boolean act(File tmpFile) {\n addEntry(zip, path, bytes, tmpFile, compressionMethod);\n return true;\n }\n });\n }\n\n /**\n * Copies an existing ZIP file and appends it with one new entry.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param entry\n * new ZIP entry appended.\n * @param destZip\n * new ZIP file created.\n */\n public static void addEntry(File zip, ZipEntrySource entry, File destZip) {\n addEntries(zip, new ZipEntrySource[] { entry }, destZip);\n }\n\n /**\n * Changes a zip file, adds one new entry in-place.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param entry\n * new ZIP entry appended.\n */\n public static void addEntry(final File zip, final ZipEntrySource entry) {\n operateInPlace(zip, new InPlaceAction() {\n public boolean act(File tmpFile) {\n addEntry(zip, entry, tmpFile);\n return true;\n }\n });\n }\n\n /**\n * Copies an existing ZIP file and appends it with new entries.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param entries\n * new ZIP entries appended.\n * @param destZip\n * new ZIP file created.\n */\n public static void addEntries(File zip, ZipEntrySource[] entries, File destZip) {\n if (log.isDebugEnabled()) {\n log.debug(\"Copying '\" + zip + \"' to '\" + destZip + \"' and adding \" + Arrays.asList(entries) + \".\");\n }\n\n OutputStream destOut = null;\n try {\n destOut = new BufferedOutputStream(new FileOutputStream(destZip));\n addEntries(zip, entries, destOut);\n }\n catch (IOException e) {\n ZipExceptionUtil.rethrow(e);\n }\n finally {\n IOUtils.closeQuietly(destOut);\n }\n }\n\n /**\n * Copies an existing ZIP file and appends it with new entries.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param entries\n * new ZIP entries appended.\n * @param destOut\n * new ZIP destination output stream\n */\n public static void addEntries(File zip, ZipEntrySource[] entries, OutputStream destOut) {\n if (log.isDebugEnabled()) {\n log.debug(\"Copying '\" + zip + \"' to a stream and adding \" + Arrays.asList(entries) + \".\");\n }\n\n ZipOutputStream out = null;\n try {\n out = new ZipOutputStream(destOut);\n copyEntries(zip, out);\n for (int i = 0; i < entries.length; i++) {\n addEntry(entries[i], out);\n }\n out.finish();\n }\n catch (IOException e) {\n ZipExceptionUtil.rethrow(e);\n }\n }\n\n /**\n * Copies an existing ZIP file and appends it with new entries.\n *\n * @param is\n * an existing ZIP input stream.\n * @param entries\n * new ZIP entries appended.\n * @param destOut\n * new ZIP destination output stream\n *\n * @since 1.9\n */\n public static void addEntries(InputStream is, ZipEntrySource[] entries, OutputStream destOut) {\n if (log.isDebugEnabled()) {\n log.debug(\"Copying input stream to an output stream and adding \" + Arrays.asList(entries) + \".\");\n }\n\n ZipOutputStream out = null;\n try {\n out = new ZipOutputStream(destOut);\n copyEntries(is, out);\n for (int i = 0; i < entries.length; i++) {\n addEntry(entries[i], out);\n }\n out.finish();\n }\n catch (IOException e) {\n ZipExceptionUtil.rethrow(e);\n }\n }\n\n /**\n * Changes a zip file it with with new entries. in-place.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param entries\n * new ZIP entries appended.\n */\n public static void addEntries(final File zip, final ZipEntrySource[] entries) {\n operateInPlace(zip, new InPlaceAction() {\n public boolean act(File tmpFile) {\n addEntries(zip, entries, tmpFile);\n return true;\n }\n });\n }\n\n /**\n * Copies an existing ZIP file and removes entry with a given path.\n *\n * @param zip\n * an existing ZIP file (only read)\n * @param path\n * path of the entry to remove\n * @param destZip\n * new ZIP file created.\n * @since 1.7\n */\n public static void removeEntry(File zip, String path, File destZip) {\n removeEntries(zip, new String[] { path }, destZip);\n }\n\n /**\n * Changes an existing ZIP file: removes entry with a given path.\n *\n * @param zip\n * an existing ZIP file\n * @param path\n * path of the entry to remove\n * @since 1.7\n */\n public static void removeEntry(final File zip, final String path) {\n operateInPlace(zip, new InPlaceAction() {\n public boolean act(File tmpFile) {\n removeEntry(zip, path, tmpFile);\n return true;\n }\n });\n }\n\n /**\n * Copies an existing ZIP file and removes entries with given paths.\n *\n * @param zip\n * an existing ZIP file (only read)\n * @param paths\n * paths of the entries to remove\n * @param destZip\n * new ZIP file created.\n * @since 1.7\n */\n public static void removeEntries(File zip, String[] paths, File destZip) {\n if (log.isDebugEnabled()) {\n log.debug(\"Copying '\" + zip + \"' to '\" + destZip + \"' and removing paths \" + Arrays.asList(paths) + \".\");\n }\n\n ZipOutputStream out = null;\n try {\n out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(destZip)));\n copyEntries(zip, out, new HashSet<String>(Arrays.asList(paths)));\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n finally {\n IOUtils.closeQuietly(out);\n }\n }\n\n /**\n * Changes an existing ZIP file: removes entries with given paths.\n *\n * @param zip\n * an existing ZIP file\n * @param paths\n * paths of the entries to remove\n * @since 1.7\n */\n public static void removeEntries(final File zip, final String[] paths) {\n operateInPlace(zip, new InPlaceAction() {\n public boolean act(File tmpFile) {\n removeEntries(zip, paths, tmpFile);\n return true;\n }\n });\n }\n\n /**\n * Copies all entries from one ZIP file to another.\n *\n * @param zip\n * source ZIP file.\n * @param out\n * target ZIP stream.\n */\n private static void copyEntries(File zip, final ZipOutputStream out) {\n // this one doesn't call copyEntries with ignoredEntries, because that has poorer performance\n final Set<String> names = new HashSet<String>();\n iterate(zip, new ZipEntryCallback() {\n public void process(InputStream in, ZipEntry zipEntry) throws IOException {\n String entryName = zipEntry.getName();\n if (names.add(entryName)) {\n ZipEntryUtil.copyEntry(zipEntry, in, out);\n }\n else if (log.isDebugEnabled()) {\n log.debug(\"Duplicate entry: {}\", entryName);\n }\n }\n });\n }\n\n /**\n * Copies all entries from one ZIP stream to another.\n *\n * @param is\n * source stream (contains ZIP file).\n * @param out\n * target ZIP stream.\n */\n private static void copyEntries(InputStream is, final ZipOutputStream out) {\n // this one doesn't call copyEntries with ignoredEntries, because that has poorer performance\n final Set<String> names = new HashSet<String>();\n iterate(is, new ZipEntryCallback() {\n public void process(InputStream in, ZipEntry zipEntry) throws IOException {\n String entryName = zipEntry.getName();\n if (names.add(entryName)) {\n ZipEntryUtil.copyEntry(zipEntry, in, out);\n }\n else if (log.isDebugEnabled()) {\n log.debug(\"Duplicate entry: {}\", entryName);\n }\n }\n });\n }\n\n /**\n * Copies all entries from one ZIP file to another, ignoring entries with path in ignoredEntries\n *\n * @param zip\n * source ZIP file.\n * @param out\n * target ZIP stream.\n * @param ignoredEntries\n * paths of entries not to copy\n */\n private static void copyEntries(File zip, final ZipOutputStream out, final Set<String> ignoredEntries) {\n final Set<String> names = new HashSet<String>();\n final Set<String> dirNames = filterDirEntries(zip, ignoredEntries);\n iterate(zip, new ZipEntryCallback() {\n public void process(InputStream in, ZipEntry zipEntry) throws IOException {\n String entryName = zipEntry.getName();\n if (ignoredEntries.contains(entryName)) {\n return;\n }\n\n for (String dirName : dirNames) {\n if (entryName.startsWith(dirName)) {\n return;\n }\n }\n\n if (names.add(entryName)) {\n ZipEntryUtil.copyEntry(zipEntry, in, out);\n }\n else if (log.isDebugEnabled()) {\n log.debug(\"Duplicate entry: {}\", entryName);\n }\n }\n });\n }\n\n /**\n *\n * @param zip\n * zip file to traverse\n * @param names\n * names of entries to filter dirs from\n * @return Set<String> names of entries that are dirs.\n *\n */\n static Set<String> filterDirEntries(File zip, Collection<String> names) {\n Set<String> dirs = new HashSet<String>();\n if (zip == null) {\n return dirs;\n }\n ZipFile zf = null;\n try {\n zf = new ZipFile(zip);\n for (String entryName : names) {\n ZipEntry entry = zf.getEntry(entryName);\n if (entry != null) {\n if (entry.isDirectory()) {\n dirs.add(entry.getName());\n }\n else if (zf.getInputStream(entry) == null) {\n // no input stream means that this is a dir.\n dirs.add(entry.getName() + PATH_SEPARATOR);\n }\n }\n }\n\n }\n catch (IOException e) {\n ZipExceptionUtil.rethrow(e);\n }\n finally {\n closeQuietly(zf);\n }\n return dirs;\n }\n\n /**\n * Copies an existing ZIP file and replaces a given entry in it.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param path\n * new ZIP entry path.\n * @param file\n * new entry.\n * @param destZip\n * new ZIP file created.\n * @return <code>true</code> if the entry was replaced.\n */\n public static boolean replaceEntry(File zip, String path, File file, File destZip) {\n return replaceEntry(zip, new FileSource(path, file), destZip);\n }\n\n /**\n * Changes an existing ZIP file: replaces a given entry in it.\n *\n * @param zip\n * an existing ZIP file.\n * @param path\n * new ZIP entry path.\n * @param file\n * new entry.\n * @return <code>true</code> if the entry was replaced.\n */\n public static boolean replaceEntry(final File zip, final String path, final File file) {\n return operateInPlace(zip, new InPlaceAction() {\n public boolean act(File tmpFile) {\n return replaceEntry(zip, new FileSource(path, file), tmpFile);\n }\n });\n }\n\n /**\n * Copies an existing ZIP file and replaces a given entry in it.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param path\n * new ZIP entry path.\n * @param bytes\n * new entry bytes (or <code>null</code> if directory).\n * @param destZip\n * new ZIP file created.\n * @return <code>true</code> if the entry was replaced.\n */\n public static boolean replaceEntry(File zip, String path, byte[] bytes, File destZip) {\n return replaceEntry(zip, new ByteSource(path, bytes), destZip);\n }\n\n /**\n * Changes an existing ZIP file: replaces a given entry in it.\n *\n * @param zip\n * an existing ZIP file.\n * @param path\n * new ZIP entry path.\n * @param bytes\n * new entry bytes (or <code>null</code> if directory).\n * @return <code>true</code> if the entry was replaced.\n */\n public static boolean replaceEntry(final File zip, final String path, final byte[] bytes) {\n return operateInPlace(zip, new InPlaceAction() {\n public boolean act(File tmpFile) {\n return replaceEntry(zip, new ByteSource(path, bytes), tmpFile);\n }\n });\n }\n\n /**\n * Changes an existing ZIP file: replaces a given entry in it.\n *\n * @param zip\n * an existing ZIP file.\n * @param path\n * new ZIP entry path.\n * @param bytes\n * new entry bytes (or <code>null</code> if directory).\n * @param compressionMethod\n * the new compression method (<code>ZipEntry.STORED</code> or <code>ZipEntry.DEFLATED</code>).\n * @return <code>true</code> if the entry was replaced.\n */\n public static boolean replaceEntry(final File zip, final String path, final byte[] bytes,\n final int compressionMethod) {\n return operateInPlace(zip, new InPlaceAction() {\n public boolean act(File tmpFile) {\n return replaceEntry(zip, new ByteSource(path, bytes, compressionMethod), tmpFile);\n }\n });\n }\n\n /**\n * Copies an existing ZIP file and replaces a given entry in it.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param entry\n * new ZIP entry.\n * @param destZip\n * new ZIP file created.\n * @return <code>true</code> if the entry was replaced.\n */\n public static boolean replaceEntry(File zip, ZipEntrySource entry, File destZip) {\n return replaceEntries(zip, new ZipEntrySource[] { entry }, destZip);\n }\n\n /**\n * Changes an existing ZIP file: replaces a given entry in it.\n *\n * @param zip\n * an existing ZIP file.\n * @param entry\n * new ZIP entry.\n * @return <code>true</code> if the entry was replaced.\n */\n public static boolean replaceEntry(final File zip, final ZipEntrySource entry) {\n return operateInPlace(zip, new InPlaceAction() {\n public boolean act(File tmpFile) {\n return replaceEntry(zip, entry, tmpFile);\n }\n });\n }\n\n /**\n * Copies an existing ZIP file and replaces the given entries in it.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param entries\n * new ZIP entries to be replaced with.\n * @param destZip\n * new ZIP file created.\n * @return <code>true</code> if at least one entry was replaced.\n */\n public static boolean replaceEntries(File zip, ZipEntrySource[] entries, File destZip) {\n if (log.isDebugEnabled()) {\n log.debug(\"Copying '\" + zip + \"' to '\" + destZip + \"' and replacing entries \" + Arrays.asList(entries) + \".\");\n }\n\n final Map<String, ZipEntrySource> entryByPath = entriesByPath(entries);\n final int entryCount = entryByPath.size();\n try {\n final ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(destZip)));\n try {\n final Set<String> names = new HashSet<String>();\n iterate(zip, new ZipEntryCallback() {\n public void process(InputStream in, ZipEntry zipEntry) throws IOException {\n if (names.add(zipEntry.getName())) {\n ZipEntrySource entry = (ZipEntrySource) entryByPath.remove(zipEntry.getName());\n if (entry != null) {\n addEntry(entry, out);\n }\n else {\n ZipEntryUtil.copyEntry(zipEntry, in, out);\n }\n }\n else if (log.isDebugEnabled()) {\n log.debug(\"Duplicate entry: {}\", zipEntry.getName());\n }\n }\n });\n }\n finally {\n IOUtils.closeQuietly(out);\n }\n }\n catch (IOException e) {\n ZipExceptionUtil.rethrow(e);\n }\n return entryByPath.size() < entryCount;\n }\n\n /**\n * Changes an existing ZIP file: replaces a given entry in it.\n *\n * @param zip\n * an existing ZIP file.\n * @param entries\n * new ZIP entries to be replaced with.\n * @return <code>true</code> if at least one entry was replaced.\n */\n public static boolean replaceEntries(final File zip, final ZipEntrySource[] entries) {\n return operateInPlace(zip, new InPlaceAction() {\n public boolean act(File tmpFile) {\n return replaceEntries(zip, entries, tmpFile);\n }\n });\n }\n\n /**\n * Copies an existing ZIP file and adds/replaces the given entries in it.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param entries\n * ZIP entries to be replaced or added.\n * @param destZip\n * new ZIP file created.\n */\n public static void addOrReplaceEntries(File zip, ZipEntrySource[] entries, File destZip) {\n if (log.isDebugEnabled()) {\n log.debug(\"Copying '\" + zip + \"' to '\" + destZip + \"' and adding/replacing entries \" + Arrays.asList(entries)\n + \".\");\n }\n\n final Map<String, ZipEntrySource> entryByPath = entriesByPath(entries);\n try {\n final ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(destZip)));\n try {\n // Copy and replace entries\n final Set<String> names = new HashSet<String>();\n iterate(zip, new ZipEntryCallback() {\n public void process(InputStream in, ZipEntry zipEntry) throws IOException {\n if (names.add(zipEntry.getName())) {\n ZipEntrySource entry = (ZipEntrySource) entryByPath.remove(zipEntry.getName());\n if (entry != null) {\n addEntry(entry, out);\n }\n else {\n ZipEntryUtil.copyEntry(zipEntry, in, out);\n }\n }\n else if (log.isDebugEnabled()) {\n log.debug(\"Duplicate entry: {}\", zipEntry.getName());\n }\n }\n });\n\n // Add new entries\n for (ZipEntrySource zipEntrySource : entryByPath.values()) {\n addEntry(zipEntrySource, out);\n }\n }\n finally {\n IOUtils.closeQuietly(out);\n }\n }\n catch (IOException e) {\n ZipExceptionUtil.rethrow(e);\n }\n }\n\n /**\n * Changes a ZIP file: adds/replaces the given entries in it.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param entries\n * ZIP entries to be replaced or added.\n */\n public static void addOrReplaceEntries(final File zip, final ZipEntrySource[] entries) {\n operateInPlace(zip, new InPlaceAction() {\n public boolean act(File tmpFile) {\n addOrReplaceEntries(zip, entries, tmpFile);\n return true;\n }\n });\n }\n\n /**\n * @return given entries indexed by path.\n */\n static Map<String, ZipEntrySource> entriesByPath(ZipEntrySource... entries) {\n Map<String, ZipEntrySource> result = new HashMap<String, ZipEntrySource>();\n for (int i = 0; i < entries.length; i++) {\n ZipEntrySource source = entries[i];\n result.put(source.getPath(), source);\n }\n return result;\n }\n\n /**\n * Copies an existing ZIP file and transforms a given entry in it.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param path\n * new ZIP entry path.\n * @param transformer\n * transformer for the given ZIP entry.\n * @param destZip\n * new ZIP file created.\n * @throws IllegalArgumentException if the destination is the same as the location\n * @return <code>true</code> if the entry was replaced.\n */\n public static boolean transformEntry(File zip, String path, ZipEntryTransformer transformer, File destZip) {\n if(zip.equals(destZip)){throw new IllegalArgumentException(\"Input (\" +zip.getAbsolutePath()+ \") is the same as the destination!\" +\n \"Please use the transformEntry method without destination for in-place transformation.\" );}\n return transformEntry(zip, new ZipEntryTransformerEntry(path, transformer), destZip);\n }\n\n /**\n * Changes an existing ZIP file: transforms a given entry in it.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param path\n * new ZIP entry path.\n * @param transformer\n * transformer for the given ZIP entry.\n * @return <code>true</code> if the entry was replaced.\n */\n public static boolean transformEntry(final File zip, final String path, final ZipEntryTransformer transformer) {\n return operateInPlace(zip, new InPlaceAction() {\n public boolean act(File tmpFile) {\n return transformEntry(zip, path, transformer, tmpFile);\n }\n });\n }\n\n /**\n * Copies an existing ZIP file and transforms a given entry in it.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param entry\n * transformer for a ZIP entry.\n * @param destZip\n * new ZIP file created.\n * @return <code>true</code> if the entry was replaced.\n */\n public static boolean transformEntry(File zip, ZipEntryTransformerEntry entry, File destZip) {\n return transformEntries(zip, new ZipEntryTransformerEntry[] { entry }, destZip);\n }\n\n /**\n * Changes an existing ZIP file: transforms a given entry in it.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param entry\n * transformer for a ZIP entry.\n * @return <code>true</code> if the entry was replaced.\n */\n public static boolean transformEntry(final File zip, final ZipEntryTransformerEntry entry) {\n return operateInPlace(zip, new InPlaceAction() {\n public boolean act(File tmpFile) {\n return transformEntry(zip, entry, tmpFile);\n }\n });\n }\n\n /**\n * Copies an existing ZIP file and transforms the given entries in it.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param entries\n * ZIP entry transformers.\n * @param destZip\n * new ZIP file created.\n * @return <code>true</code> if at least one entry was replaced.\n */\n public static boolean transformEntries(File zip, ZipEntryTransformerEntry[] entries, File destZip) {\n if (log.isDebugEnabled())\n log.debug(\"Copying '\" + zip + \"' to '\" + destZip + \"' and transforming entries \" + Arrays.asList(entries) + \".\");\n\n try {\n ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(destZip)));\n try {\n TransformerZipEntryCallback action = new TransformerZipEntryCallback(Arrays.asList(entries), out);\n iterate(zip, action);\n return action.found();\n }\n finally {\n IOUtils.closeQuietly(out);\n }\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n }\n\n /**\n * Changes an existing ZIP file: transforms a given entries in it.\n *\n * @param zip\n * an existing ZIP file (only read).\n * @param entries\n * ZIP entry transformers.\n * @return <code>true</code> if the entry was replaced.\n */\n public static boolean transformEntries(final File zip, final ZipEntryTransformerEntry[] entries) {\n return operateInPlace(zip, new InPlaceAction() {\n public boolean act(File tmpFile) {\n return transformEntries(zip, entries, tmpFile);\n }\n });\n }\n\n /**\n * Copies an existing ZIP file and transforms a given entry in it.\n *\n * @param is\n * a ZIP input stream.\n * @param path\n * new ZIP entry path.\n * @param transformer\n * transformer for the given ZIP entry.\n * @param os\n * a ZIP output stream.\n * @return <code>true</code> if the entry was replaced.\n */\n public static boolean transformEntry(InputStream is, String path, ZipEntryTransformer transformer, OutputStream os) {\n return transformEntry(is, new ZipEntryTransformerEntry(path, transformer), os);\n }\n\n /**\n * Copies an existing ZIP file and transforms a given entry in it.\n *\n * @param is\n * a ZIP input stream.\n * @param entry\n * transformer for a ZIP entry.\n * @param os\n * a ZIP output stream.\n * @return <code>true</code> if the entry was replaced.\n */\n public static boolean transformEntry(InputStream is, ZipEntryTransformerEntry entry, OutputStream os) {\n return transformEntries(is, new ZipEntryTransformerEntry[] { entry }, os);\n }\n\n /**\n * Copies an existing ZIP file and transforms the given entries in it.\n *\n * @param is\n * a ZIP input stream.\n * @param entries\n * ZIP entry transformers.\n * @param os\n * a ZIP output stream.\n * @return <code>true</code> if at least one entry was replaced.\n */\n public static boolean transformEntries(InputStream is, ZipEntryTransformerEntry[] entries, OutputStream os) {\n if (log.isDebugEnabled())\n log.debug(\"Copying '\" + is + \"' to '\" + os + \"' and transforming entries \" + Arrays.asList(entries) + \".\");\n\n try {\n ZipOutputStream out = new ZipOutputStream(os);\n TransformerZipEntryCallback action = new TransformerZipEntryCallback(Arrays.asList(entries), out);\n iterate(is, action);\n // Finishes writing the contents of the ZIP output stream without closing\n // the underlying stream.\n out.finish();\n return action.found();\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n }\n\n private static class TransformerZipEntryCallback implements ZipEntryCallback {\n\n private final Map<String, ZipEntryTransformer> entryByPath;\n private final int entryCount;\n private final ZipOutputStream out;\n private final Set<String> names = new HashSet<String>();\n\n public TransformerZipEntryCallback(List<ZipEntryTransformerEntry> entries, ZipOutputStream out) {\n entryByPath = transformersByPath(entries);\n entryCount = entryByPath.size();\n this.out = out;\n }\n\n public void process(InputStream in, ZipEntry zipEntry) throws IOException {\n if (names.add(zipEntry.getName())) {\n ZipEntryTransformer entry = (ZipEntryTransformer) entryByPath.remove(zipEntry.getName());\n if (entry != null) {\n entry.transform(in, zipEntry, out);\n }\n else {\n ZipEntryUtil.copyEntry(zipEntry, in, out);\n }\n }\n else if (log.isDebugEnabled()) {\n log.debug(\"Duplicate entry: {}\", zipEntry.getName());\n }\n }\n\n /**\n * @return <code>true</code> if at least one entry was replaced.\n */\n public boolean found() {\n return entryByPath.size() < entryCount;\n }\n\n }\n\n /**\n * @return transformers by path.\n */\n static Map<String, ZipEntryTransformer> transformersByPath(List<ZipEntryTransformerEntry> entries) {\n Map<String, ZipEntryTransformer> result = new HashMap<String, ZipEntryTransformer>();\n for (ZipEntryTransformerEntry entry : entries) {\n result.put(entry.getPath(), entry.getTransformer());\n }\n return result;\n }\n\n /**\n * Adds a given ZIP entry to a ZIP file.\n *\n * @param entry\n * new ZIP entry.\n * @param out\n * target ZIP stream.\n */\n private static void addEntry(ZipEntrySource entry, ZipOutputStream out) throws IOException {\n out.putNextEntry(entry.getEntry());\n InputStream in = entry.getInputStream();\n if (in != null) {\n try {\n IOUtils.copy(in, out);\n }\n finally {\n IOUtils.closeQuietly(in);\n }\n }\n out.closeEntry();\n }\n\n /* Comparing two ZIP files. */\n\n /**\n * Compares two ZIP files and returns <code>true</code> if they contain same\n * entries.\n * <p>\n * First the two files are compared byte-by-byte. If a difference is found the\n * corresponding entries of both ZIP files are compared. Thus if same contents\n * is packed differently the two archives may still be the same.\n * </p>\n * <p>\n * Two archives are considered the same if\n * <ol>\n * <li>they contain same number of entries,</li>\n * <li>for each entry in the first archive there exists an entry with the same\n * in the second archive</li>\n * <li>for each entry in the first archive and the entry with the same name in\n * the second archive\n * <ol>\n * <li>both are either directories or files,</li>\n * <li>both have the same size,</li>\n * <li>both have the same CRC,</li>\n * <li>both have the same contents (compared byte-by-byte).</li>\n * </ol>\n * </li>\n * </ol>\n *\n * @param f1\n * first ZIP file.\n * @param f2\n * second ZIP file.\n * @return <code>true</code> if the two ZIP files contain same entries,\n * <code>false</code> if a difference was found or an error occurred\n * during the comparison.\n */\n public static boolean archiveEquals(File f1, File f2) {\n try {\n // Check the files byte-by-byte\n if (FileUtils.contentEquals(f1, f2)) {\n return true;\n }\n\n log.debug(\"Comparing archives '{}' and '{}'...\", f1, f2);\n\n long start = System.currentTimeMillis();\n boolean result = archiveEqualsInternal(f1, f2);\n long time = System.currentTimeMillis() - start;\n if (time > 0) {\n log.debug(\"Archives compared in \" + time + \" ms.\");\n }\n return result;\n }\n catch (Exception e) {\n log.debug(\"Could not compare '\" + f1 + \"' and '\" + f2 + \"':\", e);\n return false;\n }\n }\n\n private static boolean archiveEqualsInternal(File f1, File f2) throws IOException {\n ZipFile zf1 = null;\n ZipFile zf2 = null;\n try {\n zf1 = new ZipFile(f1);\n zf2 = new ZipFile(f2);\n\n // Check the number of entries\n if (zf1.size() != zf2.size()) {\n log.debug(\"Number of entries changed (\" + zf1.size() + \" vs \" + zf2.size() + \").\");\n return false;\n }\n /*\n * As there are same number of entries in both archives we can traverse\n * all entries of one of the archives and get the corresponding entries\n * from the other archive.\n *\n * If a corresponding entry is missing from the second archive the\n * archives are different and we finish the comparison.\n *\n * We guarantee that no entry of the second archive is skipped as there\n * are same number of unique entries in both archives.\n */\n Enumeration<? extends ZipEntry> en = zf1.entries();\n while (en.hasMoreElements()) {\n ZipEntry e1 = (ZipEntry) en.nextElement();\n String path = e1.getName();\n ZipEntry e2 = zf2.getEntry(path);\n\n // Check meta data\n if (!metaDataEquals(path, e1, e2)) {\n return false;\n }\n\n // Check the content\n InputStream is1 = null;\n InputStream is2 = null;\n try {\n is1 = zf1.getInputStream(e1);\n is2 = zf2.getInputStream(e2);\n\n if (!IOUtils.contentEquals(is1, is2)) {\n log.debug(\"Entry '{}' content changed.\", path);\n return false;\n }\n }\n finally {\n IOUtils.closeQuietly(is1);\n IOUtils.closeQuietly(is2);\n }\n }\n }\n finally {\n closeQuietly(zf1);\n closeQuietly(zf2);\n }\n\n log.debug(\"Archives are the same.\");\n\n return true;\n }\n\n /**\n * Compares meta-data of two ZIP entries.\n * <p>\n * Two entries are considered the same if\n * <ol>\n * <li>both entries exist,</li>\n * <li>both entries are either directories or files,</li>\n * <li>both entries have the same size,</li>\n * <li>both entries have the same CRC.</li>\n * </ol>\n *\n * @param path\n * name of the entries.\n * @param e1\n * first entry (required).\n * @param e2\n * second entry (may be <code>null</code>).\n * @return <code>true</code> if no difference was found.\n */\n private static boolean metaDataEquals(String path, ZipEntry e1, ZipEntry e2) throws IOException {\n // Check if the same entry exists in the second archive\n if (e2 == null) {\n log.debug(\"Entry '{}' removed.\", path);\n return false;\n }\n\n // Check the directory flag\n if (e1.isDirectory()) {\n if (e2.isDirectory()) {\n return true; // Let's skip the directory as there is nothing to compare\n }\n else {\n log.debug(\"Entry '{}' not a directory any more.\", path);\n return false;\n }\n }\n else if (e2.isDirectory()) {\n log.debug(\"Entry '{}' now a directory.\", path);\n return false;\n }\n\n // Check the size\n long size1 = e1.getSize();\n long size2 = e2.getSize();\n if (size1 != -1 && size2 != -1 && size1 != size2) {\n log.debug(\"Entry '\" + path + \"' size changed (\" + size1 + \" vs \" + size2 + \").\");\n return false;\n }\n\n // Check the CRC\n long crc1 = e1.getCrc();\n long crc2 = e2.getCrc();\n if (crc1 != -1 && crc2 != -1 && crc1 != crc2) {\n log.debug(\"Entry '\" + path + \"' CRC changed (\" + crc1 + \" vs \" + crc2 + \").\");\n return false;\n }\n\n // Check the time (ignored, logging only)\n if (log.isTraceEnabled()) {\n long time1 = e1.getTime();\n long time2 = e2.getTime();\n if (time1 != -1 && time2 != -1 && time1 != time2) {\n log.trace(\"Entry '\" + path + \"' time changed (\" + new Date(time1) + \" vs \" + new Date(time2) + \").\");\n }\n }\n\n return true;\n }\n\n /**\n * Compares same entry in two ZIP files (byte-by-byte).\n *\n * @param f1\n * first ZIP file.\n * @param f2\n * second ZIP file.\n * @param path\n * name of the entry.\n * @return <code>true</code> if the contents of the entry was same in both ZIP\n * files.\n */\n public static boolean entryEquals(File f1, File f2, String path) {\n return entryEquals(f1, f2, path, path);\n }\n\n /**\n * Compares two ZIP entries (byte-by-byte). .\n *\n * @param f1\n * first ZIP file.\n * @param f2\n * second ZIP file.\n * @param path1\n * name of the first entry.\n * @param path2\n * name of the second entry.\n * @return <code>true</code> if the contents of the entries were same.\n */\n public static boolean entryEquals(File f1, File f2, String path1, String path2) {\n ZipFile zf1 = null;\n ZipFile zf2 = null;\n\n try {\n zf1 = new ZipFile(f1);\n zf2 = new ZipFile(f2);\n\n return doEntryEquals(zf1, zf2, path1, path2);\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n finally {\n closeQuietly(zf1);\n closeQuietly(zf2);\n }\n }\n\n /**\n * Compares two ZIP entries (byte-by-byte). .\n *\n * @param zf1\n * first ZIP file.\n * @param zf2\n * second ZIP file.\n * @param path1\n * name of the first entry.\n * @param path2\n * name of the second entry.\n * @return <code>true</code> if the contents of the entries were same.\n */\n public static boolean entryEquals(ZipFile zf1, ZipFile zf2, String path1, String path2) {\n try {\n return doEntryEquals(zf1, zf2, path1, path2);\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n }\n\n /**\n * Compares two ZIP entries (byte-by-byte). .\n *\n * @param zf1\n * first ZIP file.\n * @param zf2\n * second ZIP file.\n * @param path1\n * name of the first entry.\n * @param path2\n * name of the second entry.\n * @return <code>true</code> if the contents of the entries were same.\n */\n private static boolean doEntryEquals(ZipFile zf1, ZipFile zf2, String path1, String path2) throws IOException {\n InputStream is1 = null;\n InputStream is2 = null;\n try {\n ZipEntry e1 = zf1.getEntry(path1);\n ZipEntry e2 = zf2.getEntry(path2);\n\n if (e1 == null && e2 == null) {\n return true;\n }\n\n if (e1 == null || e2 == null) {\n return false;\n }\n\n is1 = zf1.getInputStream(e1);\n is2 = zf2.getInputStream(e2);\n if (is1 == null && is2 == null) {\n return true;\n }\n if (is1 == null || is2 == null) {\n return false;\n }\n\n return IOUtils.contentEquals(is1, is2);\n }\n finally {\n IOUtils.closeQuietly(is1);\n IOUtils.closeQuietly(is2);\n }\n }\n\n /**\n * Closes the ZIP file while ignoring any errors.\n *\n * @param zf\n * ZIP file to be closed.\n */\n public static void closeQuietly(ZipFile zf) {\n try {\n if (zf != null) {\n zf.close();\n }\n }\n catch (IOException e) {\n }\n }\n\n /**\n * Simple helper to make inplace operation easier\n *\n * @author shelajev\n */\n private abstract static class InPlaceAction {\n\n /**\n * @return true if something has been changed during the action.\n */\n abstract boolean act(File tmpFile);\n }\n\n /**\n *\n * This method provides a general infrastructure for in-place operations.\n * It creates temp file as a destination, then invokes the action on source and destination.\n * Then it copies the result back into src file.\n *\n * @param src - source zip file we want to modify\n * @param action - action which actually modifies the archives\n *\n * @return result of the action\n */\n private static boolean operateInPlace(File src, InPlaceAction action) {\n File tmp = null;\n try {\n tmp = File.createTempFile(\"zt-zip-tmp\", \".zip\");\n boolean result = action.act(tmp);\n if (result) { // else nothing changes\n FileUtils.forceDelete(src);\n FileUtils.moveFile(tmp, src);\n }\n return result;\n }\n catch (IOException e) {\n throw ZipExceptionUtil.rethrow(e);\n }\n finally {\n FileUtils.deleteQuietly(tmp);\n }\n }\n\n}\n\n", "input": "", "output": "【漏洞类型】路径遍历漏洞\n【严重性】High\n【漏洞摘要】此代码存在路径遍历漏洞,允许攻击者通过在ZIP文件中包含特殊构造的文件名(如包含“..”的文件路径),在文件解压过程中将文件写入目标目录之外的位置。具体表现为在`Unpacker`和`BackslashUnpacker`类的`process`方法中,未验证`name`和`destFile`的路径,导致潜在的安全风险。\n【关键代码模式】`Unpacker`和`BackslashUnpacker`类的`process`方法中,未验证`name`和`destFile`的路径。\n【修复方法说明】修复方法通过在将文件名与目标输出目录拼接后,检查生成的文件路径是否仍位于目标输出目录下,以防止路径遍历攻击。最佳实践是始终验证和清理用户输入的文件路径,确保其位于预期的目录中。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java\n### 代码内容:\npackage net.java.sip.communicator.impl.protocol.jabber;\n\nimport java.util.*;\n\nimport net.java.sip.communicator.impl.protocol.jabber.extensions.carbon.*;\nimport net.java.sip.communicator.impl.protocol.jabber.extensions.mailnotification.*;\nimport net.java.sip.communicator.impl.protocol.jabber.extensions.messagecorrection.*;\nimport net.java.sip.communicator.service.protocol.*;\nimport net.java.sip.communicator.service.protocol.Message;\nimport net.java.sip.communicator.service.protocol.event.*;\nimport net.java.sip.communicator.service.protocol.jabberconstants.*;\nimport net.java.sip.communicator.util.*;\n\nimport org.jivesoftware.smack.*;\nimport org.jivesoftware.smack.filter.*;\nimport org.jivesoftware.smack.packet.*;\nimport org.jivesoftware.smack.provider.*;\nimport org.jivesoftware.smack.util.*;\nimport org.jivesoftware.smackx.*;\nimport org.jivesoftware.smackx.packet.*;\n\n/**\n * A straightforward implementation of the basic instant messaging operation\n * set.\n *\n * @author Damian Minkov\n * @author Matthieu Helleringer\n * @author Alain Knaebel\n * @author Emil Ivov\n * @author Hristo Terezov\n */\npublic class OperationSetBasicInstantMessagingJabberImpl\n extends AbstractOperationSetBasicInstantMessaging\n implements OperationSetMessageCorrection\n{\n /**\n * Our class logger\n */\n private static final Logger logger =\n Logger.getLogger(OperationSetBasicInstantMessagingJabberImpl.class);\n\n /**\n * The maximum number of unread threads that we'd be notifying the user of.\n */\n private static final String PNAME_MAX_GMAIL_THREADS_PER_NOTIFICATION\n = \"net.java.sip.communicator.impl.protocol.jabber.\"\n +\"MAX_GMAIL_THREADS_PER_NOTIFICATION\";\n\n /**\n * A table mapping contact addresses to full jids that can be used to\n * target a specific resource (rather than sending a message to all logged\n * instances of a user).\n */\n private Map<String, StoredThreadID> jids\n = new Hashtable<String, StoredThreadID>();\n\n /**\n * The most recent full JID used for the contact address.\n */\n private Map<String, String> recentJIDForAddress\n = new Hashtable<String, String>();\n /**\n * The smackMessageListener instance listens for incoming messages.\n * Keep a reference of it so if anything goes wrong we don't add\n * two different instances.\n */\n private SmackMessageListener smackMessageListener = null;\n\n /**\n * Contains the complete jid of a specific user and the time that it was\n * last used so that we could remove it after a certain point.\n */\n public static class StoredThreadID\n {\n /** The time that we last sent or received a message from this jid */\n long lastUpdatedTime;\n\n /** The last chat used, this way we will reuse the thread-id */\n String threadID;\n }\n\n /**\n * A prefix helps to make sure that thread ID's are unique across mutliple\n * instances.\n */\n private static String prefix = StringUtils.randomString(5);\n\n /**\n * Keeps track of the current increment, which is appended to the prefix to\n * forum a unique thread ID.\n */\n private static long id = 0;\n\n /**\n * The number of milliseconds that we preserve threads with no traffic\n * before considering them dead.\n */\n private static final long JID_INACTIVITY_TIMEOUT = 10*60*1000;//10 min.\n\n /**\n * Indicates the time of the last Mailbox report that we received from\n * Google (if this is a Google server we are talking to). Should be included\n * in all following mailbox queries\n */\n private long lastReceivedMailboxResultTime = -1;\n\n /**\n * The provider that created us.\n */\n private final ProtocolProviderServiceJabberImpl jabberProvider;\n\n /**\n * A reference to the persistent presence operation set that we use\n * to match incoming messages to <tt>Contact</tt>s and vice versa.\n */\n private OperationSetPersistentPresenceJabberImpl opSetPersPresence = null;\n\n /**\n * The opening BODY HTML TAG: <body>\n */\n private static final String OPEN_BODY_TAG = \"<body>\";\n\n /**\n * The closing BODY HTML TAG: <body>\n */\n private static final String CLOSE_BODY_TAG = \"</body>\";\n\n /**\n * The html namespace used as feature\n * XHTMLManager.namespace\n */\n private final static String HTML_NAMESPACE =\n \"http://jabber.org/protocol/xhtml-im\";\n\n /**\n * List of filters to be used to filter which messages to handle\n * current Operation Set.\n */\n private List<PacketFilter> packetFilters = new ArrayList<PacketFilter>();\n\n /**\n * Whether carbon is enabled or not.\n */\n private boolean isCarbonEnabled = false;\n\n /**\n * Creates an instance of this operation set.\n * @param provider a reference to the <tt>ProtocolProviderServiceImpl</tt>\n * that created us and that we'll use for retrieving the underlying aim\n * connection.\n */\n OperationSetBasicInstantMessagingJabberImpl(\n ProtocolProviderServiceJabberImpl provider)\n {\n this.jabberProvider = provider;\n\n packetFilters.add(new GroupMessagePacketFilter());\n packetFilters.add(\n new PacketTypeFilter(org.jivesoftware.smack.packet.Message.class));\n\n provider.addRegistrationStateChangeListener(\n new RegistrationStateListener());\n\n ProviderManager man = ProviderManager.getInstance();\n MessageCorrectionExtensionProvider extProvider =\n new MessageCorrectionExtensionProvider();\n man.addExtensionProvider(MessageCorrectionExtension.ELEMENT_NAME,\n MessageCorrectionExtension.NAMESPACE,\n extProvider);\n }\n\n /**\n * Create a Message instance with the specified UID, content type\n * and a default encoding.\n * This method can be useful when message correction is required. One can\n * construct the corrected message to have the same UID as the message\n * before correction.\n *\n * @param messageText the string content of the message.\n * @param contentType the MIME-type for <tt>content</tt>\n * @param messageUID the unique identifier of this message.\n * @return Message the newly created message\n */\n public Message createMessageWithUID(\n String messageText, String contentType, String messageUID)\n {\n return new MessageJabberImpl(messageText, contentType,\n DEFAULT_MIME_ENCODING, null, messageUID);\n }\n\n /**\n * Create a Message instance for sending arbitrary MIME-encoding content.\n *\n * @param content content value\n * @param contentType the MIME-type for <tt>content</tt>\n * @return the newly created message.\n */\n public Message createMessage(String content, String contentType)\n {\n return createMessage(content, contentType, DEFAULT_MIME_ENCODING, null);\n }\n\n /**\n * Create a Message instance for sending arbitrary MIME-encoding content.\n *\n * @param content content value\n * @param contentType the MIME-type for <tt>content</tt>\n * @param subject the Subject of the message that we'd like to create.\n * @param encoding the enconding of the message that we will be sending.\n *\n * @return the newly created message.\n */\n @Override\n public Message createMessage(String content, String contentType,\n String encoding, String subject)\n {\n return new MessageJabberImpl(content, contentType, encoding, subject);\n }\n\n Message createMessage(String content, String contentType,\n String messageUID)\n {\n return new MessageJabberImpl(content, contentType,\n DEFAULT_MIME_ENCODING, null, messageUID);\n }\n\n /**\n * Determines wheter the protocol provider (or the protocol itself) support\n * sending and receiving offline messages. Most often this method would\n * return true for protocols that support offline messages and false for\n * those that don't. It is however possible for a protocol to support these\n * messages and yet have a particular account that does not (i.e. feature\n * not enabled on the protocol server). In cases like this it is possible\n * for this method to return true even when offline messaging is not\n * supported, and then have the sendMessage method throw an\n * OperationFailedException with code - OFFLINE_MESSAGES_NOT_SUPPORTED.\n *\n * @return <tt>true</tt> if the protocol supports offline messages and\n * <tt>false</tt> otherwise.\n */\n public boolean isOfflineMessagingSupported()\n {\n return true;\n }\n\n /**\n * Determines wheter the protocol supports the supplied content type\n *\n * @param contentType the type we want to check\n * @return <tt>true</tt> if the protocol supports it and\n * <tt>false</tt> otherwise.\n */\n public boolean isContentTypeSupported(String contentType)\n {\n return\n (contentType.equals(DEFAULT_MIME_TYPE)\n || contentType.equals(HTML_MIME_TYPE));\n }\n\n /**\n * Determines whether the protocol supports the supplied content type\n * for the given contact.\n *\n * @param contentType the type we want to check\n * @param contact contact which is checked for supported contentType\n * @return <tt>true</tt> if the contact supports it and\n * <tt>false</tt> otherwise.\n */\n @Override\n public boolean isContentTypeSupported(String contentType, Contact contact)\n {\n // by default we support default mime type, for other mimetypes\n // method must be overriden\n if(contentType.equals(DEFAULT_MIME_TYPE))\n return true;\n else if(contentType.equals(HTML_MIME_TYPE))\n {\n String toJID = recentJIDForAddress.get(contact.getAddress());\n\n if (toJID == null)\n toJID = contact.getAddress();\n\n return jabberProvider.isFeatureListSupported(\n toJID,\n HTML_NAMESPACE);\n }\n\n return false;\n }\n\n /**\n * Remove from our <tt>jids</tt> map all entries that have not seen any\n * activity (i.e. neither outgoing nor incoming messags) for more than\n * JID_INACTIVITY_TIMEOUT. Note that this method is not synchronous and that\n * it is only meant for use by the {@link #getThreadIDForAddress(String)} and\n * {@link #putJidForAddress(String, String)}\n */\n private void purgeOldJids()\n {\n long currentTime = System.currentTimeMillis();\n\n Iterator<Map.Entry<String, StoredThreadID>> entries\n = jids.entrySet().iterator();\n\n while( entries.hasNext() )\n {\n Map.Entry<String, StoredThreadID> entry = entries.next();\n StoredThreadID target = entry.getValue();\n\n if (currentTime - target.lastUpdatedTime\n > JID_INACTIVITY_TIMEOUT)\n entries.remove();\n }\n }\n\n /**\n * Returns the last jid that the party with the specified <tt>address</tt>\n * contacted us from or <tt>null</tt>(or bare jid) if we don't have a jid\n * for the specified <tt>address</tt> yet. The method would also purge all\n * entries that haven't seen any activity (i.e. no one has tried to get or\n * remap it) for a delay longer than <tt>JID_INACTIVITY_TIMEOUT</tt>.\n *\n * @param jid the <tt>jid</tt> that we'd like to obtain a threadID for.\n *\n * @return the last jid that the party with the specified <tt>address</tt>\n * contacted us from or <tt>null</tt> if we don't have a jid for the\n * specified <tt>address</tt> yet.\n */\n String getThreadIDForAddress(String jid)\n {\n synchronized(jids)\n {\n purgeOldJids();\n StoredThreadID ta = jids.get(jid);\n\n if (ta == null)\n return null;\n\n ta.lastUpdatedTime = System.currentTimeMillis();\n\n return ta.threadID;\n }\n }\n\n /**\n * Maps the specified <tt>address</tt> to <tt>jid</tt>. The point of this\n * method is to allow us to send all messages destined to the contact with\n * the specified <tt>address</tt> to the <tt>jid</tt> that they last\n * contacted us from.\n *\n * @param threadID the threadID of conversation.\n * @param jid the jid (i.e. address/resource) that the contact with the\n * specified <tt>address</tt> last contacted us from.\n */\n private void putJidForAddress(String jid, String threadID)\n {\n synchronized(jids)\n {\n purgeOldJids();\n\n StoredThreadID ta = jids.get(jid);\n\n if (ta == null)\n {\n ta = new StoredThreadID();\n jids.put(jid, ta);\n }\n\n recentJIDForAddress.put(StringUtils.parseBareAddress(jid), jid);\n\n ta.lastUpdatedTime = System.currentTimeMillis();\n ta.threadID = threadID;\n }\n }\n\n /**\n * Helper function used to send a message to a contact, with the given\n * extensions attached.\n *\n * @param to The contact to send the message to.\n * @param toResource The resource to send the message to or null if no\n * resource has been specified\n * @param message The message to send.\n * @param extensions The XMPP extensions that should be attached to the\n * message before sending.\n * @return The MessageDeliveryEvent that resulted after attempting to\n * send this message, so the calling function can modify it if needed.\n */\n private MessageDeliveredEvent sendMessage( Contact to,\n ContactResource toResource,\n Message message,\n PacketExtension[] extensions)\n {\n if( !(to instanceof ContactJabberImpl) )\n throw new IllegalArgumentException(\n \"The specified contact is not a Jabber contact.\"\n + to);\n\n assertConnected();\n\n org.jivesoftware.smack.packet.Message msg =\n new org.jivesoftware.smack.packet.Message();\n\n String toJID = null;\n\n if (toResource != null)\n {\n if(toResource.equals(ContactResource.BASE_RESOURCE))\n {\n toJID = to.getAddress();\n }\n else\n toJID =\n ((ContactResourceJabberImpl) toResource).getFullJid();\n }\n\n if (toJID == null)\n {\n toJID = to.getAddress();\n }\n\n msg.setPacketID(message.getMessageUID());\n msg.setTo(toJID);\n\n for (PacketExtension ext : extensions)\n {\n msg.addExtension(ext);\n }\n\n if (logger.isTraceEnabled())\n logger.trace(\"Will send a message to:\" + toJID\n + \" chat.jid=\" + toJID);\n\n MessageDeliveredEvent msgDeliveryPendingEvt\n = new MessageDeliveredEvent(message, to, toResource);\n\n MessageDeliveredEvent[] transformedEvents = messageDeliveryPendingTransform(msgDeliveryPendingEvt);\n\n if (transformedEvents == null || transformedEvents.length == 0)\n return null;\n\n for (MessageDeliveredEvent event : transformedEvents)\n {\n String content = event.getSourceMessage().getContent();\n\n if (message.getContentType().equals(HTML_MIME_TYPE))\n {\n msg.setBody(Html2Text.extractText(content));\n\n // Check if the other user supports XHTML messages\n // make sure we use our discovery manager as it caches calls\n if (jabberProvider\n .isFeatureListSupported(toJID, HTML_NAMESPACE))\n {\n // Add the XHTML text to the message\n XHTMLManager.addBody(msg, OPEN_BODY_TAG + content\n + CLOSE_BODY_TAG);\n }\n }\n else\n {\n // this is plain text so keep it as it is.\n msg.setBody(content);\n }\n\n // msg.addExtension(new Version());\n\n if (event.isMessageEncrypted() && isCarbonEnabled)\n {\n msg.addExtension(new CarbonPacketExtension.PrivateExtension());\n }\n\n MessageEventManager.addNotificationsRequests(msg, true, false,\n false, true);\n\n String threadID = getThreadIDForAddress(toJID);\n if (threadID == null)\n threadID = nextThreadID();\n\n msg.setThread(threadID);\n msg.setType(org.jivesoftware.smack.packet.Message.Type.chat);\n msg.setFrom(jabberProvider.getConnection().getUser());\n\n jabberProvider.getConnection().sendPacket(msg);\n\n putJidForAddress(toJID, threadID);\n }\n\n return new MessageDeliveredEvent(message, to, toResource);\n }\n\n /**\n * Sends the <tt>message</tt> to the destination indicated by the\n * <tt>to</tt> contact.\n *\n * @param to the <tt>Contact</tt> to send <tt>message</tt> to\n * @param message the <tt>Message</tt> to send.\n * @throws java.lang.IllegalStateException if the underlying stack is\n * not registered and initialized.\n * @throws java.lang.IllegalArgumentException if <tt>to</tt> is not an\n * instance of ContactImpl.\n */\n public void sendInstantMessage(Contact to, Message message)\n throws IllegalStateException, IllegalArgumentException\n {\n sendInstantMessage(to, null, message);\n }\n\n /**\n * Sends the <tt>message</tt> to the destination indicated by the\n * <tt>to</tt>. Provides a default implementation of this method.\n *\n * @param to the <tt>Contact</tt> to send <tt>message</tt> to\n * @param toResource the resource to which the message should be send\n * @param message the <tt>Message</tt> to send.\n * @throws java.lang.IllegalStateException if the underlying ICQ stack is\n * not registered and initialized.\n * @throws java.lang.IllegalArgumentException if <tt>to</tt> is not an\n * instance belonging to the underlying implementation.\n */\n @Override\n public void sendInstantMessage( Contact to,\n ContactResource toResource,\n Message message)\n throws IllegalStateException,\n IllegalArgumentException\n {\n MessageDeliveredEvent msgDelivered =\n sendMessage(to, toResource, message, new PacketExtension[0]);\n\n fireMessageEvent(msgDelivered);\n }\n\n /**\n * Replaces the message with ID <tt>correctedMessageUID</tt> sent to\n * the contact <tt>to</tt> with the message <tt>message</tt>\n *\n * @param to The contact to send the message to.\n * @param message The new message.\n * @param correctedMessageUID The ID of the message being replaced.\n */\n public void correctMessage(\n Contact to, ContactResource resource,\n Message message, String correctedMessageUID)\n {\n PacketExtension[] exts = new PacketExtension[1];\n exts[0] = new MessageCorrectionExtension(correctedMessageUID);\n MessageDeliveredEvent msgDelivered\n = sendMessage(to, resource, message, exts);\n msgDelivered.setCorrectedMessageUID(correctedMessageUID);\n fireMessageEvent(msgDelivered);\n }\n\n /**\n * Utility method throwing an exception if the stack is not properly\n * initialized.\n *\n * @throws java.lang.IllegalStateException if the underlying stack is\n * not registered and initialized.\n */\n private void assertConnected()\n throws IllegalStateException\n {\n if (opSetPersPresence == null)\n {\n throw\n new IllegalStateException(\n \"The provider must be signed on the service before\"\n + \" being able to communicate.\");\n }\n else\n opSetPersPresence.assertConnected();\n }\n\n /**\n * Our listener that will tell us when we're registered to\n */\n private class RegistrationStateListener\n implements RegistrationStateChangeListener\n {\n /**\n * The method is called by a ProtocolProvider implementation whenever\n * a change in the registration state of the corresponding provider had\n * occurred.\n * @param evt ProviderStatusChangeEvent the event describing the status\n * change.\n */\n public void registrationStateChanged(RegistrationStateChangeEvent evt)\n {\n if (logger.isDebugEnabled())\n logger.debug(\"The provider changed state from: \"\n + evt.getOldState()\n + \" to: \" + evt.getNewState());\n\n if (evt.getNewState() == RegistrationState.REGISTERING)\n {\n opSetPersPresence\n = (OperationSetPersistentPresenceJabberImpl)\n jabberProvider.getOperationSet(\n OperationSetPersistentPresence.class);\n\n if(smackMessageListener == null)\n {\n smackMessageListener = new SmackMessageListener();\n }\n else\n {\n // make sure this listener is not already installed in this\n // connection\n jabberProvider.getConnection()\n .removePacketListener(smackMessageListener);\n }\n\n jabberProvider.getConnection().addPacketListener(\n smackMessageListener,\n new AndFilter(\n packetFilters.toArray(\n new PacketFilter[packetFilters.size()])));\n }\n else if (evt.getNewState() == RegistrationState.REGISTERED)\n {\n new Thread(new Runnable()\n {\n @Override\n public void run()\n {\n initAdditionalServices();\n }\n }).start();\n }\n else if(evt.getNewState() == RegistrationState.UNREGISTERED\n || evt.getNewState() == RegistrationState.CONNECTION_FAILED\n || evt.getNewState() == RegistrationState.AUTHENTICATION_FAILED)\n {\n if(jabberProvider.getConnection() != null)\n {\n if(smackMessageListener != null)\n jabberProvider.getConnection().removePacketListener(\n smackMessageListener);\n }\n\n smackMessageListener = null;\n }\n }\n }\n\n /**\n * Initialize additional services, like gmail notifications and message\n * carbons.\n */\n private void initAdditionalServices()\n {\n //subscribe for Google (Gmail or Google Apps) notifications\n //for new mail messages.\n boolean enableGmailNotifications\n = jabberProvider\n .getAccountID()\n .getAccountPropertyBoolean(\n \"GMAIL_NOTIFICATIONS_ENABLED\",\n false);\n\n if (enableGmailNotifications)\n subscribeForGmailNotifications();\n\n boolean enableCarbon\n = isCarbonSupported() && !jabberProvider.getAccountID()\n .getAccountPropertyBoolean(\n ProtocolProviderFactory.IS_CARBON_DISABLED,\n false);\n if(enableCarbon)\n {\n enableDisableCarbon(true);\n }\n else\n {\n isCarbonEnabled = false;\n }\n }\n\n /**\n * Sends enable or disable carbon packet to the server.\n * @param enable if <tt>true</tt> sends enable packet otherwise sends\n * disable packet.\n */\n private void enableDisableCarbon(final boolean enable)\n {\n IQ iq = new IQ(){\n\n @Override\n public String getChildElementXML()\n {\n return \"<\" + (enable? \"enable\" : \"disable\") + \" xmlns='urn:xmpp:carbons:2' />\";\n }\n\n };\n\n Packet response = null;\n try\n {\n PacketCollector packetCollector\n = jabberProvider.getConnection().createPacketCollector(\n new PacketIDFilter(iq.getPacketID()));\n iq.setFrom(jabberProvider.getOurJID());\n iq.setType(IQ.Type.SET);\n jabberProvider.getConnection().sendPacket(iq);\n response\n = packetCollector.nextResult(\n SmackConfiguration.getPacketReplyTimeout());\n\n packetCollector.cancel();\n }\n catch(Exception e)\n {\n logger.error(\"Failed to enable carbon.\", e);\n }\n\n isCarbonEnabled = false;\n\n if (response == null)\n {\n logger.error(\n \"Failed to enable carbon. No response is received.\");\n }\n else if (response.getError() != null)\n {\n logger.error(\n \"Failed to enable carbon: \"\n + response.getError());\n }\n else if (!(response instanceof IQ)\n || !((IQ) response).getType().equals(IQ.Type.RESULT))\n {\n logger.error(\n \"Failed to enable carbon. The response is not correct.\");\n }\n else\n {\n isCarbonEnabled = true;\n }\n }\n\n /**\n * Checks whether the carbon is supported by the server or not.\n * @return <tt>true</tt> if carbon is supported by the server and\n * <tt>false</tt> if not.\n */\n private boolean isCarbonSupported()\n {\n try\n {\n return jabberProvider.getDiscoveryManager().discoverInfo(\n jabberProvider.getAccountID().getService())\n .containsFeature(CarbonPacketExtension.NAMESPACE);\n }\n catch (XMPPException e)\n {\n logger.warn(\"Failed to retrieve carbon support.\" + e.getMessage());\n }\n return false;\n }\n\n /**\n * The listener that we use in order to handle incoming messages.\n */\n @SuppressWarnings(\"unchecked\")\n private class SmackMessageListener\n implements PacketListener\n {\n /**\n * Handles incoming messages and dispatches whatever events are\n * necessary.\n * @param packet the packet that we need to handle (if it is a message).\n */\n public void processPacket(Packet packet)\n {\n if(!(packet instanceof org.jivesoftware.smack.packet.Message))\n return;\n\n org.jivesoftware.smack.packet.Message msg =\n (org.jivesoftware.smack.packet.Message)packet;\n\n boolean isForwardedSentMessage = false;\n if(msg.getBody() == null)\n {\n\n CarbonPacketExtension carbonExt\n = (CarbonPacketExtension) msg.getExtension(\n CarbonPacketExtension.NAMESPACE);\n if(carbonExt == null)\n return;\n\n isForwardedSentMessage\n = (carbonExt.getElementName()\n == CarbonPacketExtension.SENT_ELEMENT_NAME);\n List<ForwardedPacketExtension> extensions\n = carbonExt.getChildExtensionsOfType(\n ForwardedPacketExtension.class);\n if(extensions.isEmpty())\n return;\n ForwardedPacketExtension forwardedExt = extensions.get(0);\n msg = forwardedExt.getMessage();\n if(msg == null || msg.getBody() == null)\n return;\n\n }\n\n Object multiChatExtension =\n msg.getExtension(\"x\", \"http://jabber.org/protocol/muc#user\");\n\n // its not for us\n if(multiChatExtension != null)\n return;\n\n String userFullId\n = isForwardedSentMessage? msg.getTo() : msg.getFrom();\n\n String userBareID = StringUtils.parseBareAddress(userFullId);\n\n boolean isPrivateMessaging = false;\n ChatRoom privateContactRoom = null;\n OperationSetMultiUserChatJabberImpl mucOpSet =\n (OperationSetMultiUserChatJabberImpl)jabberProvider\n .getOperationSet(OperationSetMultiUserChat.class);\n if(mucOpSet != null)\n privateContactRoom = mucOpSet.getChatRoom(userBareID);\n\n if(privateContactRoom != null)\n {\n isPrivateMessaging = true;\n }\n\n if(logger.isDebugEnabled())\n {\n if (logger.isDebugEnabled())\n logger.debug(\"Received from \"\n + userBareID\n + \" the message \"\n + msg.toXML());\n }\n\n Message newMessage = createMessage(msg.getBody(),\n DEFAULT_MIME_TYPE, msg.getPacketID());\n\n //check if the message is available in xhtml\n PacketExtension ext = msg.getExtension(\n \"http://jabber.org/protocol/xhtml-im\");\n\n if(ext != null)\n {\n XHTMLExtension xhtmlExt\n = (XHTMLExtension)ext;\n\n //parse all bodies\n Iterator<String> bodies = xhtmlExt.getBodies();\n StringBuffer messageBuff = new StringBuffer();\n while (bodies.hasNext())\n {\n String body = bodies.next();\n messageBuff.append(body);\n }\n\n if (messageBuff.length() > 0)\n {\n // we remove body tags around message cause their\n // end body tag is breaking\n // the visualization as html in the UI\n String receivedMessage =\n messageBuff.toString()\n // removes body start tag\n .replaceAll(\"\\\\<[bB][oO][dD][yY].*?>\",\"\")\n // removes body end tag\n .replaceAll(\"\\\\</[bB][oO][dD][yY].*?>\",\"\");\n\n // for some reason ' is not rendered correctly\n // from our ui, lets use its equivalent. Other\n // similar chars(< > & \") seem ok.\n receivedMessage =\n receivedMessage.replaceAll(\"'\", \"'\");\n\n newMessage = createMessage(receivedMessage,\n HTML_MIME_TYPE, msg.getPacketID());\n }\n }\n\n PacketExtension correctionExtension =\n msg.getExtension(MessageCorrectionExtension.NAMESPACE);\n String correctedMessageUID = null;\n if (correctionExtension != null)\n {\n correctedMessageUID = ((MessageCorrectionExtension)\n correctionExtension).getCorrectedMessageUID();\n }\n\n Contact sourceContact\n = opSetPersPresence.findContactByID(\n (isPrivateMessaging? userFullId : userBareID));\n if(msg.getType()\n == org.jivesoftware.smack.packet.Message.Type.error)\n {\n // error which is multichat and we don't know about the contact\n // is a muc message error which is missing muc extension\n // and is coming from the room, when we try to send message to\n // room which was deleted or offline on the server\n if(isPrivateMessaging && sourceContact == null)\n {\n if(privateContactRoom != null)\n {\n XMPPError error = packet.getError();\n int errorResultCode\n = ChatRoomMessageDeliveryFailedEvent.UNKNOWN_ERROR;\n\n if(error != null && error.getCode() == 403)\n {\n errorResultCode\n = ChatRoomMessageDeliveryFailedEvent.FORBIDDEN;\n }\n\n String errorReason = error.getMessage();\n\n ChatRoomMessageDeliveryFailedEvent evt =\n new ChatRoomMessageDeliveryFailedEvent(\n privateContactRoom,\n null,\n errorResultCode,\n errorReason,\n new Date(),\n newMessage);\n ((ChatRoomJabberImpl)privateContactRoom)\n .fireMessageEvent(evt);\n }\n\n return;\n }\n\n if (logger.isInfoEnabled())\n logger.info(\"Message error received from \" + userBareID);\n\n int errorResultCode = MessageDeliveryFailedEvent.UNKNOWN_ERROR;\n if (packet.getError() != null)\n {\n int errorCode = packet.getError().getCode();\n \n if(errorCode == 503)\n {\n org.jivesoftware.smackx.packet.MessageEvent msgEvent =\n (org.jivesoftware.smackx.packet.MessageEvent)\n packet.getExtension(\"x\", \"jabber:x:event\");\n if(msgEvent != null && msgEvent.isOffline())\n {\n errorResultCode =\n MessageDeliveryFailedEvent\n .OFFLINE_MESSAGES_NOT_SUPPORTED;\n }\n }\n }\n\n if (sourceContact == null)\n {\n sourceContact = opSetPersPresence.createVolatileContact(\n userFullId, isPrivateMessaging);\n }\n\n MessageDeliveryFailedEvent ev\n = new MessageDeliveryFailedEvent(newMessage,\n sourceContact,\n correctedMessageUID,\n errorResultCode);\n\n // ev = messageDeliveryFailedTransform(ev);\n\n if (ev != null)\n fireMessageEvent(ev);\n return;\n }\n putJidForAddress(userFullId, msg.getThread());\n\n // In the second condition we filter all group chat messages,\n // because they are managed by the multi user chat operation set.\n if(sourceContact == null)\n {\n if (logger.isDebugEnabled())\n logger.debug(\"received a message from an unknown contact: \"\n + userBareID);\n //create the volatile contact\n sourceContact = opSetPersPresence\n .createVolatileContact(\n userFullId,\n isPrivateMessaging);\n }\n\n Date timestamp = new Date();\n //Check for XEP-0091 timestamp (deprecated)\n PacketExtension delay = msg.getExtension(\"x\", \"jabber:x:delay\");\n if(delay != null && delay instanceof DelayInformation)\n {\n timestamp = ((DelayInformation)delay).getStamp();\n }\n //check for XEP-0203 timestamp\n delay = msg.getExtension(\"delay\", \"urn:xmpp:delay\");\n if(delay != null && delay instanceof DelayInfo)\n {\n timestamp = ((DelayInfo)delay).getStamp();\n }\n\n ContactResource resource = ((ContactJabberImpl) sourceContact)\n .getResourceFromJid(userFullId);\n\n EventObject msgEvt = null;\n if(!isForwardedSentMessage)\n msgEvt\n = new MessageReceivedEvent( newMessage,\n sourceContact,\n resource,\n timestamp,\n correctedMessageUID,\n isPrivateMessaging,\n privateContactRoom);\n else\n msgEvt = new MessageDeliveredEvent(newMessage, sourceContact, timestamp);\n // msgReceivedEvt = messageReceivedTransform(msgReceivedEvt);\n if (msgEvt != null)\n fireMessageEvent(msgEvt);\n }\n }\n\n /**\n * A filter that prevents this operation set from handling multi user chat\n * messages.\n */\n private static class GroupMessagePacketFilter implements PacketFilter\n {\n /**\n * Returns <tt>true</tt> if <tt>packet</tt> is a <tt>Message</tt> and\n * false otherwise.\n *\n * @param packet the packet that we need to check.\n *\n * @return <tt>true</tt> if <tt>packet</tt> is a <tt>Message</tt> and\n * false otherwise.\n */\n public boolean accept(Packet packet)\n {\n if(!(packet instanceof org.jivesoftware.smack.packet.Message))\n return false;\n\n org.jivesoftware.smack.packet.Message msg\n = (org.jivesoftware.smack.packet.Message) packet;\n\n return\n !msg.getType().equals(\n org.jivesoftware.smack.packet.Message.Type.groupchat);\n }\n }\n\n /**\n * Subscribes this provider as interested in receiving notifications for\n * new mail messages from Google mail services such as Gmail or Google Apps.\n */\n private void subscribeForGmailNotifications()\n {\n // first check support for the notification service\n String accountIDService = jabberProvider.getAccountID().getService();\n boolean notificationsAreSupported\n = jabberProvider.isFeatureSupported(\n accountIDService,\n NewMailNotificationIQ.NAMESPACE);\n\n if (!notificationsAreSupported)\n {\n if (logger.isDebugEnabled())\n logger.debug(accountIDService\n +\" does not seem to provide a Gmail notification \"\n +\" service so we won't be trying to subscribe for it\");\n return;\n }\n\n if (logger.isDebugEnabled())\n logger.debug(accountIDService\n +\" seems to provide a Gmail notification \"\n +\" service so we will try to subscribe for it\");\n\n ProviderManager providerManager = ProviderManager.getInstance();\n\n providerManager.addIQProvider(\n MailboxIQ.ELEMENT_NAME,\n MailboxIQ.NAMESPACE,\n new MailboxIQProvider());\n providerManager.addIQProvider(\n NewMailNotificationIQ.ELEMENT_NAME,\n NewMailNotificationIQ.NAMESPACE,\n new NewMailNotificationProvider());\n\n Connection connection = jabberProvider.getConnection();\n\n connection.addPacketListener(\n new MailboxIQListener(), new PacketTypeFilter(MailboxIQ.class));\n connection.addPacketListener(\n new NewMailNotificationListener(),\n new PacketTypeFilter(NewMailNotificationIQ.class));\n\n if(opSetPersPresence.getCurrentStatusMessage().equals(\n JabberStatusEnum.OFFLINE))\n return;\n\n //create a query with -1 values for newer-than-tid and\n //newer-than-time attributes\n MailboxQueryIQ mailboxQuery = new MailboxQueryIQ();\n\n if (logger.isTraceEnabled())\n logger.trace(\"sending mailNotification for acc: \"\n + jabberProvider.getAccountID().getAccountUniqueID());\n jabberProvider.getConnection().sendPacket(mailboxQuery);\n }\n\n /**\n * Creates an html description of the specified mailbox.\n *\n * @param mailboxIQ the mailboxIQ that we are to describe.\n *\n * @return an html description of <tt>mailboxIQ</tt>\n */\n private String createMailboxDescription(MailboxIQ mailboxIQ)\n {\n int threadCount = mailboxIQ.getThreadCount();\n\n String resourceHeaderKey = threadCount > 1\n ? \"service.gui.NEW_GMAIL_MANY_HEADER\"\n : \"service.gui.NEW_GMAIL_HEADER\";\n\n String resourceFooterKey = threadCount > 1\n ? \"service.gui.NEW_GMAIL_MANY_FOOTER\"\n : \"service.gui.NEW_GMAIL_FOOTER\";\n\n // FIXME Escape HTML!\n String newMailHeader = JabberActivator.getResources().getI18NString(\n resourceHeaderKey,\n new String[]\n {\n jabberProvider.getAccountID()\n .getService(), //{0} - service name\n mailboxIQ.getUrl(), //{1} - inbox URI\n Integer.toString( threadCount )//{2} - thread count\n });\n\n StringBuilder message = new StringBuilder(newMailHeader);\n\n //we now start an html table for the threads.\n message.append(\"<table width=100% cellpadding=2 cellspacing=0 \");\n message.append(\"border=0 bgcolor=#e8eef7>\");\n\n Iterator<MailThreadInfo> threads = mailboxIQ.threads();\n\n String maxThreadsStr = (String)JabberActivator.getConfigurationService()\n .getProperty(PNAME_MAX_GMAIL_THREADS_PER_NOTIFICATION);\n\n int maxThreads = 5;\n\n try\n {\n if(maxThreadsStr != null)\n maxThreads = Integer.parseInt(maxThreadsStr);\n }\n catch (NumberFormatException e)\n {\n if (logger.isDebugEnabled())\n logger.debug(\"Failed to parse max threads count: \"+maxThreads\n +\". Going for default.\");\n }\n\n //print a maximum of MAX_THREADS\n for (int i = 0; i < maxThreads && threads.hasNext(); i++)\n {\n message.append(threads.next().createHtmlDescription());\n }\n message.append(\"</table><br/>\");\n\n if(threadCount > maxThreads)\n {\n String messageFooter = JabberActivator.getResources().getI18NString(\n resourceFooterKey,\n new String[]\n {\n mailboxIQ.getUrl(), //{0} - inbox URI\n Integer.toString(\n threadCount - maxThreads )//{1} - thread count\n });\n message.append(messageFooter);\n }\n\n return message.toString();\n }\n\n public String getRecentJIDForAddress(String address)\n {\n return recentJIDForAddress.get(address);\n }\n\n /**\n * Receives incoming MailNotification Packets\n */\n private class MailboxIQListener\n implements PacketListener\n {\n /**\n * Handles incoming <tt>MailboxIQ</tt> packets.\n *\n * @param packet the IQ that we need to handle in case it is a\n * <tt>MailboxIQ</tt>.\n */\n public void processPacket(Packet packet)\n {\n if(packet != null && !(packet instanceof MailboxIQ))\n return;\n\n MailboxIQ mailboxIQ = (MailboxIQ) packet;\n\n if(mailboxIQ.getTotalMatched() < 1)\n return;\n\n //Get a reference to a dummy volatile contact\n Contact sourceContact = opSetPersPresence\n .findContactByID(jabberProvider.getAccountID().getService());\n\n if(sourceContact == null)\n sourceContact = opSetPersPresence.createVolatileContact(\n jabberProvider.getAccountID().getService());\n\n lastReceivedMailboxResultTime = mailboxIQ.getResultTime();\n\n String newMail = createMailboxDescription(mailboxIQ);\n\n Message newMailMessage = new MessageJabberImpl(\n newMail, HTML_MIME_TYPE, DEFAULT_MIME_ENCODING, null);\n\n MessageReceivedEvent msgReceivedEvt = new MessageReceivedEvent(\n newMailMessage, sourceContact, new Date(),\n MessageReceivedEvent.SYSTEM_MESSAGE_RECEIVED);\n\n fireMessageEvent(msgReceivedEvt);\n }\n }\n\n /**\n * Receives incoming NewMailNotification Packets.\n */\n private class NewMailNotificationListener\n implements PacketListener\n {\n /**\n * Handles incoming <tt>NewMailNotificationIQ</tt> packets.\n *\n * @param packet the IQ that we need to handle in case it is a\n * <tt>NewMailNotificationIQ</tt>.\n */\n public void processPacket(Packet packet)\n {\n if(packet != null && !(packet instanceof NewMailNotificationIQ))\n return;\n\n //check whether we are still enabled.\n boolean enableGmailNotifications\n = jabberProvider\n .getAccountID()\n .getAccountPropertyBoolean(\n \"GMAIL_NOTIFICATIONS_ENABLED\",\n false);\n\n if (!enableGmailNotifications)\n return;\n\n if(opSetPersPresence.getCurrentStatusMessage()\n .equals(JabberStatusEnum.OFFLINE))\n return;\n\n MailboxQueryIQ mailboxQueryIQ = new MailboxQueryIQ();\n\n if(lastReceivedMailboxResultTime != -1)\n mailboxQueryIQ.setNewerThanTime(\n lastReceivedMailboxResultTime);\n\n if (logger.isTraceEnabled())\n logger.trace(\n \"send mailNotification for acc: \"\n + jabberProvider.getAccountID().getAccountUniqueID());\n\n jabberProvider.getConnection().sendPacket(mailboxQueryIQ);\n }\n }\n\n /**\n * Returns the inactivity timeout in milliseconds.\n *\n * @return The inactivity timeout in milliseconds. Or -1 if undefined\n */\n public long getInactivityTimeout()\n {\n return JID_INACTIVITY_TIMEOUT;\n }\n\n /**\n * Adds additional filters for incoming messages. To be able to skip some\n * messages.\n * @param filter to add\n */\n public void addMessageFilters(PacketFilter filter)\n {\n this.packetFilters.add(filter);\n }\n\n /**\n * Returns the next unique thread id. Each thread id made up of a short\n * alphanumeric prefix along with a unique numeric value.\n *\n * @return the next thread id.\n */\n public static synchronized String nextThreadID() {\n return prefix + Long.toString(id++);\n }\n\n}\n\n", "input": "", "output": "【漏洞类型】认证绕过漏洞\n【严重性】High\n【漏洞摘要】原始代码中存在认证绕过漏洞,允许攻击者通过伪造Carbon副本消息的来源(from字段)来绕过消息发送的认证检查。具体来说,在`SmackMessageListener.processPacket`方法中,Carbon副本消息的来源没有被验证,攻击者可以发送一个伪造的Carbon副本消息,其来源与实际的bare jid不同,从而绕过安全检查。\n【关键代码模式】`processPacket`方法中的Carbon副本消息处理、`StringUtils.parseBareAddress`函数、`jabberProvider.getOurJID`函数。\n【修复方法说明】修复方法通过验证Carbon副本消息的来源是否与实际的bare jid匹配来解决问题。最佳实践是始终验证外部来源的合法性,特别是在处理可能被篡改的数据时。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nengine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/SafeHtmlValidator.java\n### 代码内容:\n/*\n * Hibernate Validator, declare and validate application constraints\n *\n * License: Apache License, Version 2.0\n * See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.\n */\npackage org.hibernate.validator.internal.constraintvalidators.hv;\n\nimport java.util.Iterator;\n\nimport javax.validation.ConstraintValidator;\nimport javax.validation.ConstraintValidatorContext;\n\nimport org.hibernate.validator.constraints.SafeHtml;\nimport org.jsoup.Jsoup;\nimport org.jsoup.nodes.Document;\nimport org.jsoup.nodes.Element;\nimport org.jsoup.parser.Parser;\nimport org.jsoup.safety.Cleaner;\nimport org.jsoup.safety.Whitelist;\n\n/**\n * Validate that the string does not contain malicious code.\n *\n * It uses <a href=\"http://www.jsoup.org\">JSoup</a> as the underlying parser/sanitizer library.\n *\n * @author George Gastaldi\n * @author Hardy Ferentschik\n * @author Marko Bekhta\n */\npublic class SafeHtmlValidator implements ConstraintValidator<SafeHtml, CharSequence> {\n\tprivate Whitelist whitelist;\n\n\tprivate String baseURI;\n\n\t@Override\n\tpublic void initialize(SafeHtml safeHtmlAnnotation) {\n\t\tswitch ( safeHtmlAnnotation.whitelistType() ) {\n\t\t\tcase BASIC:\n\t\t\t\twhitelist = Whitelist.basic();\n\t\t\t\tbreak;\n\t\t\tcase BASIC_WITH_IMAGES:\n\t\t\t\twhitelist = Whitelist.basicWithImages();\n\t\t\t\tbreak;\n\t\t\tcase NONE:\n\t\t\t\twhitelist = Whitelist.none();\n\t\t\t\tbreak;\n\t\t\tcase RELAXED:\n\t\t\t\twhitelist = Whitelist.relaxed();\n\t\t\t\tbreak;\n\t\t\tcase SIMPLE_TEXT:\n\t\t\t\twhitelist = Whitelist.simpleText();\n\t\t\t\tbreak;\n\t\t}\n\t\tbaseURI = safeHtmlAnnotation.baseURI();\n\t\twhitelist.addTags( safeHtmlAnnotation.additionalTags() );\n\n\t\tfor ( SafeHtml.Tag tag : safeHtmlAnnotation.additionalTagsWithAttributes() ) {\n\t\t\twhitelist.addTags( tag.name() );\n\t\t\tif ( tag.attributes().length > 0 ) {\n\t\t\t\twhitelist.addAttributes( tag.name(), tag.attributes() );\n\t\t\t}\n\n\t\t\tfor ( SafeHtml.Attribute attribute : tag.attributesWithProtocols() ) {\n\t\t\t\twhitelist.addAttributes( tag.name(), attribute.name() );\n\n\t\t\t\tif ( attribute.protocols().length > 0 ) {\n\t\t\t\t\twhitelist.addProtocols( tag.name(), attribute.name(), attribute.protocols() );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isValid(CharSequence value, ConstraintValidatorContext context) {\n\t\tif ( value == null ) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn new Cleaner( whitelist ).isValid( getFragmentAsDocument( value ) );\n\t}\n\n\t/**\n\t * Returns a document whose {@code <body>} element contains the given HTML fragment.\n\t */\n\tprivate Document getFragmentAsDocument(CharSequence value) {\n\t\t// using the XML parser ensures that all elements in the input are retained, also if they actually are not allowed at the given\n\t\t// location; E.g. a <td> element isn't allowed directly within the <body> element, so it would be used by the default HTML parser.\n\t\t// we need to retain it though to apply the given white list properly; See HV-873\n\t\tDocument fragment = Jsoup.parse( value.toString(), baseURI, Parser.xmlParser() );\n\t\tDocument document = Document.createShell( baseURI );\n\n\t\t// add the fragment's nodes to the body of resulting document\n\t\tIterator<Element> nodes = fragment.children().iterator();\n\t\twhile ( nodes.hasNext() ) {\n\t\t\tdocument.body().appendChild( nodes.next() );\n\t\t}\n\n\t\treturn document;\n\t}\n}\n\n", "input": "", "output": "【漏洞类型】HTML内容注入漏洞\n【严重性】High\n【漏洞摘要】在`SafeHtmlValidator`类中,`getFragmentAsDocument`函数使用了`Jsoup.parse`方法来解析用户输入的HTML片段。由于使用了`Parser.xmlParser()`,它会保留所有输入元素,包括那些在特定位置不允许的元素。这可能导致恶意代码注入,因为攻击者可以利用这一点在允许的元素中嵌入非法的HTML或JavaScript。具体问题在于,当`additionalTagsWithAttributes`中的标签和属性被添加到白名单中时,没有对这些标签和属性进行严格的验证,攻击者可能利用这一点注入恶意内容。\n【关键代码模式】JSoup.parse、Parser.xmlParser()、additionalTagsWithAttributes、appendChild\n【修复方法说明】修复方法通过将`fragment.children()`替换为`fragment.childNodes()`,并使用`node.clone()`来克隆节点,从而避免了直接使用XML解析器保留非法元素的问题。这将确保只有白名单中允许的元素和属性被添加到结果文档中,从而防止恶意代码注入。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nindex.js\n### 代码内容:\n'use strict'\n\nconst Buffer = require('safe-buffer').Buffer\n\nconst crypto = require('crypto')\nconst Transform = require('stream').Transform\n\nconst SPEC_ALGORITHMS = ['sha256', 'sha384', 'sha512']\n\nconst BASE64_REGEX = /^[a-z0-9+/]+(?:=?=?)$/i\nconst SRI_REGEX = /^([^-]+)-([^?]+)([?\\S*]*)$/\nconst STRICT_SRI_REGEX = /^([^-]+)-([A-Za-z0-9+/]+(?:=?=?))([?\\x21-\\x7E]*)$/\nconst VCHAR_REGEX = /^[\\x21-\\x7E]+$/\n\nclass Hash {\n get isHash () { return true }\n constructor (hash, opts) {\n const strict = !!(opts && opts.strict)\n this.source = hash.trim()\n // 3.1. Integrity metadata (called \"Hash\" by ssri)\n // https://w3c.github.io/webappsec-subresource-integrity/#integrity-metadata-description\n const match = this.source.match(\n strict\n ? STRICT_SRI_REGEX\n : SRI_REGEX\n )\n if (!match) { return }\n if (strict && !SPEC_ALGORITHMS.some(a => a === match[1])) { return }\n this.algorithm = match[1]\n this.digest = match[2]\n\n const rawOpts = match[3]\n this.options = rawOpts ? rawOpts.slice(1).split('?') : []\n }\n hexDigest () {\n return this.digest && Buffer.from(this.digest, 'base64').toString('hex')\n }\n toJSON () {\n return this.toString()\n }\n toString (opts) {\n if (opts && opts.strict) {\n // Strict mode enforces the standard as close to the foot of the\n // letter as it can.\n if (!(\n // The spec has very restricted productions for algorithms.\n // https://www.w3.org/TR/CSP2/#source-list-syntax\n SPEC_ALGORITHMS.some(x => x === this.algorithm) &&\n // Usually, if someone insists on using a \"different\" base64, we\n // leave it as-is, since there's multiple standards, and the\n // specified is not a URL-safe variant.\n // https://www.w3.org/TR/CSP2/#base64_value\n this.digest.match(BASE64_REGEX) &&\n // Option syntax is strictly visual chars.\n // https://w3c.github.io/webappsec-subresource-integrity/#grammardef-option-expression\n // https://tools.ietf.org/html/rfc5234#appendix-B.1\n (this.options || []).every(opt => opt.match(VCHAR_REGEX))\n )) {\n return ''\n }\n }\n const options = this.options && this.options.length\n ? `?${this.options.join('?')}`\n : ''\n return `${this.algorithm}-${this.digest}${options}`\n }\n}\n\nclass Integrity {\n get isIntegrity () { return true }\n toJSON () {\n return this.toString()\n }\n toString (opts) {\n opts = opts || {}\n let sep = opts.sep || ' '\n if (opts.strict) {\n // Entries must be separated by whitespace, according to spec.\n sep = sep.replace(/\\S+/g, ' ')\n }\n return Object.keys(this).map(k => {\n return this[k].map(hash => {\n return Hash.prototype.toString.call(hash, opts)\n }).filter(x => x.length).join(sep)\n }).filter(x => x.length).join(sep)\n }\n concat (integrity, opts) {\n const other = typeof integrity === 'string'\n ? integrity\n : stringify(integrity, opts)\n return parse(`${this.toString(opts)} ${other}`, opts)\n }\n hexDigest () {\n return parse(this, {single: true}).hexDigest()\n }\n match (integrity, opts) {\n const other = parse(integrity, opts)\n const algo = other.pickAlgorithm(opts)\n return (\n this[algo] &&\n other[algo] &&\n this[algo].find(hash =>\n other[algo].find(otherhash =>\n hash.digest === otherhash.digest\n )\n )\n ) || false\n }\n pickAlgorithm (opts) {\n const pickAlgorithm = (opts && opts.pickAlgorithm) || getPrioritizedHash\n const keys = Object.keys(this)\n if (!keys.length) {\n throw new Error(`No algorithms available for ${\n JSON.stringify(this.toString())\n }`)\n }\n return keys.reduce((acc, algo) => {\n return pickAlgorithm(acc, algo) || acc\n })\n }\n}\n\nmodule.exports.parse = parse\nfunction parse (sri, opts) {\n opts = opts || {}\n if (typeof sri === 'string') {\n return _parse(sri, opts)\n } else if (sri.algorithm && sri.digest) {\n const fullSri = new Integrity()\n fullSri[sri.algorithm] = [sri]\n return _parse(stringify(fullSri, opts), opts)\n } else {\n return _parse(stringify(sri, opts), opts)\n }\n}\n\nfunction _parse (integrity, opts) {\n // 3.4.3. Parse metadata\n // https://w3c.github.io/webappsec-subresource-integrity/#parse-metadata\n if (opts.single) {\n return new Hash(integrity, opts)\n }\n return integrity.trim().split(/\\s+/).reduce((acc, string) => {\n const hash = new Hash(string, opts)\n if (hash.algorithm && hash.digest) {\n const algo = hash.algorithm\n if (!acc[algo]) { acc[algo] = [] }\n acc[algo].push(hash)\n }\n return acc\n }, new Integrity())\n}\n\nmodule.exports.stringify = stringify\nfunction stringify (obj, opts) {\n if (obj.algorithm && obj.digest) {\n return Hash.prototype.toString.call(obj, opts)\n } else if (typeof obj === 'string') {\n return stringify(parse(obj, opts), opts)\n } else {\n return Integrity.prototype.toString.call(obj, opts)\n }\n}\n\nmodule.exports.fromHex = fromHex\nfunction fromHex (hexDigest, algorithm, opts) {\n const optString = (opts && opts.options && opts.options.length)\n ? `?${opts.options.join('?')}`\n : ''\n return parse(\n `${algorithm}-${\n Buffer.from(hexDigest, 'hex').toString('base64')\n }${optString}`, opts\n )\n}\n\nmodule.exports.fromData = fromData\nfunction fromData (data, opts) {\n opts = opts || {}\n const algorithms = opts.algorithms || ['sha512']\n const optString = opts.options && opts.options.length\n ? `?${opts.options.join('?')}`\n : ''\n return algorithms.reduce((acc, algo) => {\n const digest = crypto.createHash(algo).update(data).digest('base64')\n const hash = new Hash(\n `${algo}-${digest}${optString}`,\n opts\n )\n if (hash.algorithm && hash.digest) {\n const algo = hash.algorithm\n if (!acc[algo]) { acc[algo] = [] }\n acc[algo].push(hash)\n }\n return acc\n }, new Integrity())\n}\n\nmodule.exports.fromStream = fromStream\nfunction fromStream (stream, opts) {\n opts = opts || {}\n const P = opts.Promise || Promise\n const istream = integrityStream(opts)\n return new P((resolve, reject) => {\n stream.pipe(istream)\n stream.on('error', reject)\n istream.on('error', reject)\n let sri\n istream.on('integrity', s => { sri = s })\n istream.on('end', () => resolve(sri))\n istream.on('data', () => {})\n })\n}\n\nmodule.exports.checkData = checkData\nfunction checkData (data, sri, opts) {\n opts = opts || {}\n sri = parse(sri, opts)\n if (!Object.keys(sri).length) { return false }\n const algorithm = sri.pickAlgorithm(opts)\n const digest = crypto.createHash(algorithm).update(data).digest('base64')\n return parse({algorithm, digest}).match(sri, opts)\n}\n\nmodule.exports.checkStream = checkStream\nfunction checkStream (stream, sri, opts) {\n opts = opts || {}\n const P = opts.Promise || Promise\n const checker = integrityStream(Object.assign({}, opts, {\n integrity: sri\n }))\n return new P((resolve, reject) => {\n stream.pipe(checker)\n stream.on('error', reject)\n checker.on('error', reject)\n let sri\n checker.on('verified', s => { sri = s })\n checker.on('end', () => resolve(sri))\n checker.on('data', () => {})\n })\n}\n\nmodule.exports.integrityStream = integrityStream\nfunction integrityStream (opts) {\n opts = opts || {}\n // For verification\n const sri = opts.integrity && parse(opts.integrity, opts)\n const goodSri = sri && Object.keys(sri).length\n const algorithm = goodSri && sri.pickAlgorithm(opts)\n const digests = goodSri && sri[algorithm]\n // Calculating stream\n const algorithms = (opts.algorithms || ['sha512'])\n .concat(algorithm ? [algorithm] : [])\n const hashes = algorithms.map(crypto.createHash)\n let streamSize = 0\n const stream = new Transform({\n transform (chunk, enc, cb) {\n streamSize += chunk.length\n hashes.forEach(h => h.update(chunk, enc))\n cb(null, chunk, enc)\n }\n }).on('end', () => {\n const optString = (opts.options && opts.options.length)\n ? `?${opts.options.join('?')}`\n : ''\n const newSri = parse(hashes.map((h, i) => {\n return `${algorithms[i]}-${h.digest('base64')}${optString}`\n }).join(' '), opts)\n // Integrity verification mode\n const match = goodSri && newSri.match(sri, opts)\n if (typeof opts.size === 'number' && streamSize !== opts.size) {\n const err = new Error(`stream size mismatch when checking ${sri}.\\n Wanted: ${opts.size}\\n Found: ${streamSize}`)\n err.code = 'EBADSIZE'\n err.found = streamSize\n err.expected = opts.size\n err.sri = sri\n stream.emit('error', err)\n } else if (opts.integrity && !match) {\n const err = new Error(`${sri} integrity checksum failed when using ${algorithm}: wanted ${digests} but got ${newSri}. (${streamSize} bytes)`)\n err.code = 'EINTEGRITY'\n err.found = newSri\n err.expected = digests\n err.algorithm = algorithm\n err.sri = sri\n stream.emit('error', err)\n } else {\n stream.emit('size', streamSize)\n stream.emit('integrity', newSri)\n match && stream.emit('verified', match)\n }\n })\n return stream\n}\n\nmodule.exports.create = createIntegrity\nfunction createIntegrity (opts) {\n opts = opts || {}\n const algorithms = opts.algorithms || ['sha512']\n const optString = opts.options && opts.options.length\n ? `?${opts.options.join('?')}`\n : ''\n\n const hashes = algorithms.map(crypto.createHash)\n\n return {\n update: function (chunk, enc) {\n hashes.forEach(h => h.update(chunk, enc))\n return this\n },\n digest: function (enc) {\n const integrity = algorithms.reduce((acc, algo) => {\n const digest = hashes.shift().digest('base64')\n const hash = new Hash(\n `${algo}-${digest}${optString}`,\n opts\n )\n if (hash.algorithm && hash.digest) {\n const algo = hash.algorithm\n if (!acc[algo]) { acc[algo] = [] }\n acc[algo].push(hash)\n }\n return acc\n }, new Integrity())\n\n return integrity\n }\n }\n}\n\n// This is a Best Effort™ at a reasonable priority for hash algos\nconst DEFAULT_PRIORITY = [\n 'md5', 'whirlpool', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512',\n // TODO - it's unclear _which_ of these Node will actually use as its name\n // for the algorithm, so we guesswork it based on the OpenSSL names.\n 'sha3',\n 'sha3-256', 'sha3-384', 'sha3-512',\n 'sha3_256', 'sha3_384', 'sha3_512'\n]\nfunction getPrioritizedHash (algo1, algo2) {\n return DEFAULT_PRIORITY.indexOf(algo1.toLowerCase()) >= DEFAULT_PRIORITY.indexOf(algo2.toLowerCase())\n ? algo1\n : algo2\n}\n\n", "input": "", "output": "【漏洞类型】正则表达式拒绝服务 (ReDoS) 漏洞 \n【严重性】High \n【漏洞摘要】原始代码中的 `STRICT_SRI_REGEX` 正则表达式存在 ReDoS 漏洞。具体表现为,如果用户输入一个异常长的 base64 哈希字符串,将会导致正则表达式匹配时间过长,进而引发拒绝服务攻击。根本原因是正则表达式中的 `([A-Za-z0-9+/]{44,88})` 模式可能会进行大量的回溯,导致性能问题。 \n【关键代码模式】 \n- 正则表达式匹配:`STRICT_SRI_REGEX` \n- 用户输入处理:`Hash` 类的构造函数 \n- 选项处理:`opts` 参数中的 `strict` 选项 \n【修复方法说明】通过调整 `STRICT_SRI_REGEX` 正则表达式,使其限制 base64 哈希字符串的长度,从而防止 ReDoS 攻击。最佳实践是避免使用可能导致回溯的正则表达式模式,并对用户输入进行严格的长度和格式验证。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\ninlong-manager/manager-dao/src/main/resources/mappers/InlongClusterTagEntityMapper.xml\n### 代码内容:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements. See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership. The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License. You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing,\n software distributed under the License is distributed on an\n \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n KIND, either express or implied. See the License for the\n specific language governing permissions and limitations\n under the License.\n-->\n\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"org.apache.inlong.manager.dao.mapper.InlongClusterTagEntityMapper\">\n <resultMap id=\"BaseResultMap\" type=\"org.apache.inlong.manager.dao.entity.InlongClusterTagEntity\">\n <id column=\"id\" jdbcType=\"INTEGER\" property=\"id\"/>\n <result column=\"cluster_tag\" jdbcType=\"VARCHAR\" property=\"clusterTag\"/>\n <result column=\"ext_params\" jdbcType=\"LONGVARCHAR\" property=\"extParams\"/>\n <result column=\"description\" jdbcType=\"VARCHAR\" property=\"description\"/>\n <result column=\"in_charges\" jdbcType=\"VARCHAR\" property=\"inCharges\"/>\n <result column=\"status\" jdbcType=\"INTEGER\" property=\"status\"/>\n <result column=\"is_deleted\" jdbcType=\"INTEGER\" property=\"isDeleted\"/>\n <result column=\"creator\" jdbcType=\"VARCHAR\" property=\"creator\"/>\n <result column=\"modifier\" jdbcType=\"VARCHAR\" property=\"modifier\"/>\n <result column=\"create_time\" jdbcType=\"TIMESTAMP\" property=\"createTime\"/>\n <result column=\"modify_time\" jdbcType=\"TIMESTAMP\" property=\"modifyTime\"/>\n <result column=\"version\" jdbcType=\"INTEGER\" property=\"version\"/>\n </resultMap>\n <sql id=\"Base_Column_List\">\n id, cluster_tag, ext_params, description, in_charges, status, is_deleted,\n creator, modifier, create_time, modify_time, version\n </sql>\n\n <insert id=\"insert\" useGeneratedKeys=\"true\" keyProperty=\"id\"\n parameterType=\"org.apache.inlong.manager.dao.entity.InlongClusterTagEntity\">\n insert into inlong_cluster_tag (id, cluster_tag, ext_params,\n description, in_charges,\n status, creator, modifier)\n values (#{id,jdbcType=INTEGER}, #{clusterTag,jdbcType=VARCHAR}, #{extParams,jdbcType=LONGVARCHAR},\n #{description, jdbcType=VARCHAR}, #{inCharges,jdbcType=VARCHAR},\n #{status,jdbcType=INTEGER}, #{creator,jdbcType=VARCHAR}, #{modifier,jdbcType=VARCHAR})\n </insert>\n\n <select id=\"selectById\" parameterType=\"java.lang.Integer\" resultMap=\"BaseResultMap\">\n select\n <include refid=\"Base_Column_List\"/>\n from inlong_cluster_tag\n where id = #{id,jdbcType=INTEGER}\n </select>\n <select id=\"selectByTag\" resultType=\"org.apache.inlong.manager.dao.entity.InlongClusterTagEntity\">\n select\n <include refid=\"Base_Column_List\"/>\n from inlong_cluster_tag\n where cluster_tag = #{clusterTag, jdbcType=VARCHAR}\n and is_deleted = 0\n </select>\n <select id=\"selectByCondition\"\n parameterType=\"org.apache.inlong.manager.pojo.cluster.ClusterTagPageRequest\"\n resultType=\"org.apache.inlong.manager.dao.entity.InlongClusterTagEntity\">\n select\n <include refid=\"Base_Column_List\"/>\n from inlong_cluster_tag\n <where>\n is_deleted = 0\n <if test=\"keyword != null and keyword != ''\">\n and cluster_tag like CONCAT('%', #{keyword}, '%')\n </if>\n </where>\n order by modify_time desc\n </select>\n\n <update id=\"updateByIdSelective\" parameterType=\"org.apache.inlong.manager.dao.entity.InlongClusterTagEntity\">\n update inlong_cluster_tag\n <set>\n <if test=\"clusterTag != null\">\n cluster_tag = #{clusterTag,jdbcType=VARCHAR},\n </if>\n <if test=\"extParams != null\">\n ext_params = #{extParams,jdbcType=LONGVARCHAR},\n </if>\n <if test=\"description != null\">\n description = #{description,jdbcType=VARCHAR},\n </if>\n <if test=\"inCharges != null\">\n in_charges = #{inCharges,jdbcType=VARCHAR},\n </if>\n <if test=\"status != null\">\n tatus = #{status,jdbcType=INTEGER},\n </if>\n <if test=\"isDeleted != null\">\n is_deleted = #{isDeleted,jdbcType=INTEGER},\n </if>\n <if test=\"modifier != null\">\n modifier = #{modifier,jdbcType=VARCHAR},\n </if>\n version = #{version,jdbcType=INTEGER} + 1\n </set>\n where id = #{id,jdbcType=INTEGER}\n and version = #{version,jdbcType=INTEGER}\n </update>\n\n <delete id=\"deleteByPrimaryKey\" parameterType=\"java.lang.Integer\">\n delete\n from inlong_cluster_tag\n where id = #{id,jdbcType=INTEGER}\n </delete>\n</mapper>\n## 内容2\n### 文件路径:\ninlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/ClusterTagPageRequest.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.pojo.cluster;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.apache.inlong.manager.pojo.common.PageRequest;\n\n/**\n * Cluster tag paging query conditions\n */\n@Data\n@EqualsAndHashCode(callSuper = false)\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\n@ApiModel(\"Cluster tag paging query request\")\npublic class ClusterTagPageRequest extends PageRequest {\n\n @ApiModelProperty(value = \"Keywords, used for fuzzy query\")\n private String keyword;\n\n @ApiModelProperty(value = \"Status\")\n private Integer status;\n\n @ApiModelProperty(value = \"Current user\", hidden = true)\n private String currentUser;\n\n}\n\n## 内容3\n### 文件路径:\ninlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/user/UserRequest.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.pojo.user;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.apache.inlong.manager.pojo.common.PageRequest;\nimport org.apache.inlong.manager.common.enums.UserTypeEnum;\nimport org.apache.inlong.manager.common.validation.InEnumInt;\nimport org.hibernate.validator.constraints.Length;\nimport org.hibernate.validator.constraints.Range;\n\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\n\n/**\n * User info request\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\n@EqualsAndHashCode(callSuper = false)\n@ApiModel(\"User info request\")\npublic class UserRequest extends PageRequest {\n\n @ApiModelProperty(value = \"Primary key\")\n private Integer id;\n\n @NotBlank(message = \"User name cannot be blank\")\n @ApiModelProperty(value = \"User name\", required = true)\n @Length(min = 1, max = 256, message = \"length must be between 1 and 256\")\n private String name;\n\n @ApiModelProperty(value = \"Keyword, can be user name\")\n private String keyword;\n\n @ApiModelProperty(value = \"User password\")\n @Length(min = 6, max = 64, message = \"length must be between 6 and 64\")\n private String password;\n\n @ApiModelProperty(value = \"New password, is required if needs updated\")\n @Length(min = 6, max = 64, message = \"length must be between 6 and 64\")\n private String newPassword;\n\n @ApiModelProperty(\"Secret key\")\n @Length(min = 1, max = 256, message = \"length must be between 1 and 256\")\n private String secretKey;\n\n @ApiModelProperty(\"Public key\")\n @Length(min = 1, max = 163840, message = \"length must be between 1 and 163840\")\n private String publicKey;\n\n @ApiModelProperty(\"Private key\")\n @Length(min = 1, max = 163840, message = \"length must be between 1 and 163840\")\n private String privateKey;\n\n @ApiModelProperty(\"Encryption key version\")\n private Integer encryptVersion;\n\n @NotNull(message = \"accountType cannot be null\")\n @InEnumInt(UserTypeEnum.class)\n @ApiModelProperty(value = \"Account type: 0 - manager, 1 - operator\", required = true)\n @Range(min = 0, max = 1, message = \"only supports [0: manager, 1: operator]\")\n private Integer accountType;\n\n @Min(1)\n @NotNull(message = \"validDays cannot be null\")\n @ApiModelProperty(value = \"Valid days\", required = true)\n private Integer validDays;\n\n @ApiModelProperty(value = \"Version number\")\n private Integer version;\n\n @ApiModelProperty(value = \"Extension json info\")\n @Length(min = 1, max = 163840, message = \"length must be between 1 and 163840\")\n private String extParams;\n\n}\n\n## 内容4\n### 文件路径:\ninlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/user/UserServiceImpl.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.service.user;\n\nimport com.github.pagehelper.Page;\nimport com.github.pagehelper.PageHelper;\nimport org.apache.commons.lang3.RandomStringUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.inlong.manager.common.consts.InlongConstants;\nimport org.apache.inlong.manager.common.enums.ErrorCodeEnum;\nimport org.apache.inlong.manager.common.enums.UserTypeEnum;\nimport org.apache.inlong.manager.common.exceptions.BusinessException;\nimport org.apache.inlong.manager.common.util.AESUtils;\nimport org.apache.inlong.manager.common.util.CommonBeanUtils;\nimport org.apache.inlong.manager.common.util.DateUtils;\nimport org.apache.inlong.manager.common.util.Preconditions;\nimport org.apache.inlong.manager.common.util.RSAUtils;\nimport org.apache.inlong.manager.common.util.SHAUtils;\nimport org.apache.inlong.manager.dao.entity.UserEntity;\nimport org.apache.inlong.manager.dao.mapper.UserEntityMapper;\nimport org.apache.inlong.manager.pojo.common.PageResult;\nimport org.apache.inlong.manager.pojo.user.UserInfo;\nimport org.apache.inlong.manager.pojo.user.UserLoginLockStatus;\nimport org.apache.inlong.manager.pojo.user.UserLoginRequest;\nimport org.apache.inlong.manager.pojo.user.UserRequest;\nimport org.apache.shiro.SecurityUtils;\nimport org.apache.shiro.authc.AuthenticationException;\nimport org.apache.shiro.authc.UsernamePasswordToken;\nimport org.apache.shiro.subject.Subject;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.nio.charset.StandardCharsets;\nimport java.time.Duration;\nimport java.time.LocalDateTime;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * User service layer implementation\n */\n@Service\npublic class UserServiceImpl implements UserService {\n\n private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);\n\n private static final Integer SECRET_KEY_SIZE = 16;\n\n /**\n * locked time, the unit is minute\n */\n private static final Integer LOCKED_TIME = 3;\n private static final Integer LOCKED_THRESHOLD = 10;\n\n private final Map<String, UserLoginLockStatus> loginLockStatusMap = new ConcurrentHashMap<>();\n\n @Autowired\n private UserEntityMapper userMapper;\n\n @Override\n public Integer save(UserRequest request, String currentUser) {\n String username = request.getName();\n UserEntity userExists = userMapper.selectByName(username);\n String password = request.getPassword();\n Preconditions.expectNull(userExists, \"username [\" + username + \"] already exists\");\n Preconditions.expectTrue(StringUtils.isNotBlank(password), \"password cannot be blank\");\n\n UserEntity entity = new UserEntity();\n entity.setName(username);\n entity.setPassword(SHAUtils.encrypt(password));\n entity.setAccountType(request.getAccountType());\n entity.setDueDate(DateUtils.getExpirationDate(request.getValidDays()));\n entity.setCreator(currentUser);\n entity.setModifier(currentUser);\n entity.setExtParams(request.getExtParams());\n try {\n Map<String, String> keyPairs = RSAUtils.generateRSAKeyPairs();\n String publicKey = keyPairs.get(RSAUtils.PUBLIC_KEY);\n String privateKey = keyPairs.get(RSAUtils.PRIVATE_KEY);\n String secretKey = RandomStringUtils.randomAlphanumeric(SECRET_KEY_SIZE);\n Integer encryptVersion = AESUtils.getCurrentVersion(null);\n entity.setEncryptVersion(encryptVersion);\n entity.setPublicKey(AESUtils.encryptToString(publicKey.getBytes(StandardCharsets.UTF_8), encryptVersion));\n entity.setPrivateKey(AESUtils.encryptToString(privateKey.getBytes(StandardCharsets.UTF_8), encryptVersion));\n entity.setSecretKey(AESUtils.encryptToString(secretKey.getBytes(StandardCharsets.UTF_8), encryptVersion));\n } catch (Exception e) {\n String errMsg = String.format(\"generate rsa key error: %s\", e.getMessage());\n LOGGER.error(errMsg, e);\n throw new BusinessException(errMsg);\n }\n\n Preconditions.expectTrue(userMapper.insert(entity) > 0, \"Create user failed\");\n LOGGER.debug(\"success to create user info={}\", request);\n return entity.getId();\n }\n\n @Override\n public UserInfo getById(Integer userId, String currentUser) {\n Preconditions.expectNotNull(userId, \"User id cannot be null\");\n UserEntity entity = userMapper.selectById(userId);\n Preconditions.expectNotNull(entity, \"User not exists with id \" + userId);\n UserEntity curUser = userMapper.selectByName(currentUser);\n Preconditions.expectTrue(Objects.equals(UserTypeEnum.ADMIN.getCode(), curUser.getAccountType())\n || Objects.equals(entity.getName(), currentUser),\n \"Current user does not have permission to get other users' info\");\n\n UserInfo result = new UserInfo();\n result.setId(entity.getId());\n result.setName(entity.getName());\n result.setValidDays(DateUtils.getValidDays(entity.getCreateTime(), entity.getDueDate()));\n result.setAccountType(entity.getAccountType());\n result.setVersion(entity.getVersion());\n\n if (StringUtils.isNotBlank(entity.getSecretKey()) && StringUtils.isNotBlank(entity.getPublicKey())) {\n try {\n // decipher according to stored key version\n // note that if the version is null then the string is treated as unencrypted plain text\n Integer version = entity.getEncryptVersion();\n byte[] secretKeyBytes = AESUtils.decryptAsString(entity.getSecretKey(), version);\n byte[] publicKeyBytes = AESUtils.decryptAsString(entity.getPublicKey(), version);\n result.setSecretKey(new String(secretKeyBytes, StandardCharsets.UTF_8));\n result.setPublicKey(new String(publicKeyBytes, StandardCharsets.UTF_8));\n } catch (Exception e) {\n String errMsg = String.format(\"decryption error: %s\", e.getMessage());\n LOGGER.error(errMsg, e);\n throw new BusinessException(errMsg);\n }\n }\n\n LOGGER.debug(\"success to get user info by id={}\", userId);\n return result;\n }\n\n @Override\n public UserInfo getByName(String name) {\n Preconditions.expectNotBlank(name, ErrorCodeEnum.INVALID_PARAMETER, \"User name cannot be null\");\n UserEntity entity = userMapper.selectByName(name);\n if (entity == null) {\n return null;\n }\n UserInfo userInfo = CommonBeanUtils.copyProperties(entity, UserInfo::new);\n userInfo.setValidDays(DateUtils.getValidDays(entity.getCreateTime(), entity.getDueDate()));\n return userInfo;\n }\n\n @Override\n public PageResult<UserInfo> list(UserRequest request) {\n PageHelper.startPage(request.getPageNum(), request.getPageSize());\n Page<UserEntity> entityPage = (Page<UserEntity>) userMapper.selectByCondition(request);\n List<UserInfo> userList = CommonBeanUtils.copyListProperties(entityPage, UserInfo::new);\n\n // Check whether the user account has expired\n userList.forEach(entity -> entity.setStatus(entity.getDueDate().after(new Date()) ? \"valid\" : \"invalid\"));\n\n PageResult<UserInfo> pageResult = new PageResult<>(userList, entityPage.getTotal(),\n entityPage.getPageNum(), entityPage.getPageSize());\n\n LOGGER.debug(\"success to list users for request={}, result size={}\", request, pageResult.getTotal());\n return pageResult;\n }\n\n @Override\n public Integer update(UserRequest request, String currentUser) {\n LOGGER.debug(\"begin to update user info={} by {}\", request, currentUser);\n Preconditions.expectNotNull(request, \"Userinfo cannot be null\");\n Preconditions.expectNotNull(request.getId(), \"User id cannot be null\");\n\n // Whether the current user is a manager\n UserEntity currentUserEntity = userMapper.selectByName(currentUser);\n String updateName = request.getName();\n boolean isAdmin = Objects.equals(UserTypeEnum.ADMIN.getCode(), currentUserEntity.getAccountType());\n Preconditions.expectTrue(isAdmin || Objects.equals(updateName, currentUser),\n \"You are not a manager and do not have permission to update other users\");\n\n // manager cannot set himself as an ordinary\n boolean managerToOrdinary = isAdmin\n && Objects.equals(UserTypeEnum.OPERATOR.getCode(), request.getAccountType())\n && Objects.equals(currentUser, updateName);\n Preconditions.expectFalse(managerToOrdinary, \"You are a manager and you cannot change to an ordinary user\");\n\n // target username must not exist\n UserEntity updateUserEntity = userMapper.selectById(request.getId());\n Preconditions.expectNotNull(updateUserEntity, \"User not exists with id=\" + request.getId());\n String errMsg = String.format(\"user has already updated with username=%s, reqVersion=%s, storedVersion=%s\",\n updateName, request.getVersion(), updateUserEntity.getVersion());\n if (!Objects.equals(updateUserEntity.getVersion(), request.getVersion())) {\n LOGGER.error(errMsg);\n throw new BusinessException(ErrorCodeEnum.CONFIG_EXPIRED);\n }\n\n UserEntity targetUserEntity = userMapper.selectByName(updateName);\n Preconditions.expectTrue(\n Objects.isNull(targetUserEntity)\n || Objects.equals(targetUserEntity.getName(), updateUserEntity.getName()),\n \"Username [\" + updateName + \"] already exists\");\n\n // if the current user is not a manager, needs to check the password before updating user info\n if (!isAdmin) {\n String oldPassword = request.getPassword();\n String oldPasswordHash = SHAUtils.encrypt(oldPassword);\n Preconditions.expectTrue(oldPasswordHash.equals(updateUserEntity.getPassword()), \"Old password is wrong\");\n Integer validDays = DateUtils.getValidDays(updateUserEntity.getCreateTime(), updateUserEntity.getDueDate());\n Preconditions.expectTrue((request.getValidDays() <= validDays),\n \"Ordinary users are not allowed to add valid days\");\n Preconditions.expectTrue(Objects.equals(updateUserEntity.getAccountType(), request.getAccountType()),\n \"Ordinary users are not allowed to update account type\");\n }\n\n // update password\n if (!StringUtils.isBlank(request.getNewPassword())) {\n String newPasswordHash = SHAUtils.encrypt(request.getNewPassword());\n updateUserEntity.setPassword(newPasswordHash);\n }\n updateUserEntity.setDueDate(DateUtils.getExpirationDate(request.getValidDays()));\n updateUserEntity.setAccountType(request.getAccountType());\n updateUserEntity.setName(updateName);\n updateUserEntity.setExtParams(request.getExtParams());\n\n int rowCount = userMapper.updateByPrimaryKeySelective(updateUserEntity);\n if (rowCount != InlongConstants.AFFECTED_ONE_ROW) {\n LOGGER.error(errMsg);\n throw new BusinessException(ErrorCodeEnum.CONFIG_EXPIRED);\n }\n LOGGER.debug(\"success to update user info={} by {}\", request, currentUser);\n return updateUserEntity.getId();\n }\n\n @Override\n public Boolean delete(Integer userId, String currentUser) {\n Preconditions.expectNotNull(userId, \"User id should not be empty\");\n\n // Whether the current user is an administrator\n UserEntity curUser = userMapper.selectByName(currentUser);\n UserEntity entity = userMapper.selectById(userId);\n Preconditions.expectTrue(curUser.getAccountType().equals(UserTypeEnum.ADMIN.getCode()),\n \"Current user is not a manager and does not have permission to delete users\");\n Preconditions.expectTrue(!Objects.equals(entity.getName(), currentUser),\n \"Current user does not have permission to delete himself\");\n userMapper.deleteById(userId);\n\n LOGGER.debug(\"success to delete user by id={}, current user={}\", userId, currentUser);\n return true;\n }\n\n /**\n * This implementation is just to intercept some error requests and reduce the pressure on the database.\n * <p/>\n * This is a memory-based implementation. There is a problem with concurrency security when there are\n * multiple service nodes because the data in memory cannot be shared.\n *\n * @param req username login request\n */\n @Override\n public void login(UserLoginRequest req) {\n String username = req.getUsername();\n UserLoginLockStatus userLoginLockStatus = loginLockStatusMap.getOrDefault(username, new UserLoginLockStatus());\n LocalDateTime lockoutTime = userLoginLockStatus.getLockoutTime();\n if (lockoutTime != null && lockoutTime.isAfter(LocalDateTime.now())) {\n // part of a minute counts as one minute\n long waitMinutes = Duration.between(LocalDateTime.now(), lockoutTime).toMinutes() + 1;\n throw new BusinessException(\"account has been locked, please try again in \" + waitMinutes + \" minutes\");\n }\n\n Subject subject = SecurityUtils.getSubject();\n UsernamePasswordToken token = new UsernamePasswordToken(username, req.getPassword());\n try {\n subject.login(token);\n } catch (AuthenticationException e) {\n LOGGER.error(\"login error for request {}\", req, e);\n int loginErrorCount = userLoginLockStatus.getLoginErrorCount() + 1;\n\n if (loginErrorCount % LOCKED_THRESHOLD == 0) {\n LocalDateTime lockedTime = LocalDateTime.now().plusMinutes(LOCKED_TIME);\n userLoginLockStatus.setLockoutTime(lockedTime);\n LOGGER.error(\"account {} is locked, lockout time: {}\", username, lockedTime);\n }\n userLoginLockStatus.setLoginErrorCount(loginErrorCount);\n\n loginLockStatusMap.put(username, userLoginLockStatus);\n throw e;\n }\n\n LoginUserUtils.setUserLoginInfo((UserInfo) subject.getPrincipal());\n\n // login successfully, clear error count\n userLoginLockStatus.setLoginErrorCount(0);\n loginLockStatusMap.put(username, userLoginLockStatus);\n }\n\n public void checkUser(String inCharges, String user, String errMsg) {\n UserEntity userEntity = userMapper.selectByName(user);\n boolean isInCharge = Preconditions.inSeparatedString(user, inCharges, InlongConstants.COMMA);\n Preconditions.expectTrue(isInCharge || UserTypeEnum.ADMIN.getCode().equals(userEntity.getAccountType()),\n errMsg);\n }\n\n}\n\n## 内容5\n### 文件路径:\ninlong-manager/manager-web/src/main/java/org/apache/inlong/manager/web/controller/AnnoController.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.web.controller;\n\nimport io.swagger.annotations.Api;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.inlong.manager.pojo.common.Response;\nimport org.apache.inlong.manager.pojo.user.UserLoginRequest;\nimport org.apache.inlong.manager.pojo.user.UserRequest;\nimport org.apache.inlong.manager.service.user.LoginUserUtils;\nimport org.apache.inlong.manager.service.user.UserService;\nimport org.apache.shiro.SecurityUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Anno controller, such as login, register, etc.\n */\n@Slf4j\n@RestController\n@RequestMapping(\"/api\")\n@Api(tags = \"User-Anno-API\")\npublic class AnnoController {\n\n @Autowired\n private UserService userService;\n\n @PostMapping(\"/anno/login\")\n public Response<Boolean> login(@Validated @RequestBody UserLoginRequest loginRequest) {\n userService.login(loginRequest);\n return Response.success(true);\n }\n\n @PostMapping(\"/anno/register\")\n public Response<Integer> register(@Validated @RequestBody UserRequest request) {\n String currentUser = LoginUserUtils.getLoginUser().getName();\n return Response.success(userService.save(request, currentUser));\n }\n\n @GetMapping(\"/anno/logout\")\n public Response<Boolean> logout() {\n SecurityUtils.getSubject().logout();\n return Response.success(true);\n }\n\n}\n\n## 内容6\n### 文件路径:\ninlong-manager/manager-web/src/main/java/org/apache/inlong/manager/web/controller/InlongClusterController.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.web.controller;\n\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiImplicitParam;\nimport io.swagger.annotations.ApiImplicitParams;\nimport io.swagger.annotations.ApiOperation;\nimport org.apache.inlong.manager.common.enums.OperationType;\nimport org.apache.inlong.manager.common.enums.UserTypeEnum;\nimport org.apache.inlong.manager.common.validation.SaveValidation;\nimport org.apache.inlong.manager.common.validation.UpdateByIdValidation;\nimport org.apache.inlong.manager.common.validation.UpdateByKeyValidation;\nimport org.apache.inlong.manager.common.validation.UpdateValidation;\nimport org.apache.inlong.manager.pojo.cluster.BindTagRequest;\nimport org.apache.inlong.manager.pojo.cluster.ClusterInfo;\nimport org.apache.inlong.manager.pojo.cluster.ClusterNodeRequest;\nimport org.apache.inlong.manager.pojo.cluster.ClusterNodeResponse;\nimport org.apache.inlong.manager.pojo.cluster.ClusterPageRequest;\nimport org.apache.inlong.manager.pojo.cluster.ClusterRequest;\nimport org.apache.inlong.manager.pojo.cluster.ClusterTagPageRequest;\nimport org.apache.inlong.manager.pojo.cluster.ClusterTagRequest;\nimport org.apache.inlong.manager.pojo.cluster.ClusterTagResponse;\nimport org.apache.inlong.manager.pojo.common.PageResult;\nimport org.apache.inlong.manager.pojo.common.Response;\nimport org.apache.inlong.manager.pojo.common.UpdateResult;\nimport org.apache.inlong.manager.pojo.user.UserRoleCode;\nimport org.apache.inlong.manager.service.cluster.InlongClusterService;\nimport org.apache.inlong.manager.service.operationlog.OperationLog;\nimport org.apache.inlong.manager.service.user.LoginUserUtils;\nimport org.apache.shiro.authz.annotation.RequiresRoles;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.List;\n\n/**\n * Inlong cluster controller\n */\n@RestController\n@RequestMapping(\"/api\")\n@Api(tags = \"Inlong-Cluster-API\")\npublic class InlongClusterController {\n\n @Autowired\n private InlongClusterService clusterService;\n\n @PostMapping(value = \"/cluster/tag/save\")\n @ApiOperation(value = \"Save cluster tag\")\n @OperationLog(operation = OperationType.CREATE)\n @RequiresRoles(value = UserRoleCode.ADMIN)\n public Response<Integer> saveTag(@Validated(SaveValidation.class) @RequestBody ClusterTagRequest request) {\n String currentUser = LoginUserUtils.getLoginUser().getName();\n return Response.success(clusterService.saveTag(request, currentUser));\n }\n\n @GetMapping(value = \"/cluster/tag/get/{id}\")\n @ApiOperation(value = \"Get cluster tag by id\")\n @ApiImplicitParam(name = \"id\", value = \"Cluster ID\", dataTypeClass = Integer.class, required = true)\n public Response<ClusterTagResponse> getTag(@PathVariable Integer id) {\n String currentUser = LoginUserUtils.getLoginUser().getName();\n return Response.success(clusterService.getTag(id, currentUser));\n }\n\n @PostMapping(value = \"/cluster/tag/list\")\n @ApiOperation(value = \"List cluster tags\")\n public Response<PageResult<ClusterTagResponse>> listTag(@RequestBody ClusterTagPageRequest request) {\n request.setCurrentUser(LoginUserUtils.getLoginUser().getName());\n return Response.success(clusterService.listTag(request));\n }\n\n @PostMapping(value = \"/cluster/tag/update\")\n @OperationLog(operation = OperationType.UPDATE)\n @ApiOperation(value = \"Update cluster tag\")\n public Response<Boolean> updateTag(@Validated(UpdateValidation.class) @RequestBody ClusterTagRequest request) {\n String username = LoginUserUtils.getLoginUser().getName();\n return Response.success(clusterService.updateTag(request, username));\n }\n\n @DeleteMapping(value = \"/cluster/tag/delete/{id}\")\n @ApiOperation(value = \"Delete cluster tag by id\")\n @OperationLog(operation = OperationType.DELETE)\n @ApiImplicitParam(name = \"id\", value = \"Cluster tag ID\", dataTypeClass = Integer.class, required = true)\n @RequiresRoles(value = UserRoleCode.ADMIN)\n public Response<Boolean> deleteTag(@PathVariable Integer id) {\n return Response.success(clusterService.deleteTag(id, LoginUserUtils.getLoginUser().getName()));\n }\n\n @PostMapping(value = \"/cluster/save\")\n @ApiOperation(value = \"Save cluster\")\n @OperationLog(operation = OperationType.CREATE)\n @RequiresRoles(value = UserRoleCode.ADMIN)\n public Response<Integer> save(@Validated(SaveValidation.class) @RequestBody ClusterRequest request) {\n String currentUser = LoginUserUtils.getLoginUser().getName();\n return Response.success(clusterService.save(request, currentUser));\n }\n\n @GetMapping(value = \"/cluster/get/{id}\")\n @ApiOperation(value = \"Get cluster by id\")\n @ApiImplicitParam(name = \"id\", value = \"Cluster ID\", dataTypeClass = Integer.class, required = true)\n public Response<ClusterInfo> get(@PathVariable Integer id) {\n String currentUser = LoginUserUtils.getLoginUser().getName();\n return Response.success(clusterService.get(id, currentUser));\n }\n\n @PostMapping(value = \"/cluster/list\")\n @ApiOperation(value = \"List clusters\")\n public Response<PageResult<ClusterInfo>> list(@RequestBody ClusterPageRequest request) {\n request.setCurrentUser(LoginUserUtils.getLoginUser().getName());\n request.setIsAdminRole(LoginUserUtils.getLoginUser().getRoles().contains(UserTypeEnum.ADMIN.name()));\n return Response.success(clusterService.list(request));\n }\n\n @PostMapping(value = \"/cluster/update\")\n @OperationLog(operation = OperationType.UPDATE)\n @ApiOperation(value = \"Update cluster\")\n public Response<Boolean> update(@Validated(UpdateByIdValidation.class) @RequestBody ClusterRequest request) {\n String username = LoginUserUtils.getLoginUser().getName();\n return Response.success(clusterService.update(request, username));\n }\n\n @PostMapping(value = \"/cluster/updateByKey\")\n @OperationLog(operation = OperationType.UPDATE)\n @ApiOperation(value = \"Update cluster by key\")\n public Response<UpdateResult> updateByKey(\n @Validated(UpdateByKeyValidation.class) @RequestBody ClusterRequest request) {\n String username = LoginUserUtils.getLoginUser().getName();\n return Response.success(clusterService.updateByKey(request, username));\n }\n\n @PostMapping(value = \"/cluster/bindTag\")\n @OperationLog(operation = OperationType.UPDATE)\n @ApiOperation(value = \"Bind or unbind cluster tag\")\n public Response<Boolean> bindTag(@Validated @RequestBody BindTagRequest request) {\n String username = LoginUserUtils.getLoginUser().getName();\n return Response.success(clusterService.bindTag(request, username));\n }\n\n @DeleteMapping(value = \"/cluster/delete/{id}\")\n @ApiOperation(value = \"Delete cluster by id\")\n @OperationLog(operation = OperationType.DELETE)\n @ApiImplicitParam(name = \"id\", value = \"Cluster ID\", dataTypeClass = Integer.class, required = true)\n @RequiresRoles(value = UserRoleCode.ADMIN)\n public Response<Boolean> delete(@PathVariable Integer id) {\n return Response.success(clusterService.delete(id, LoginUserUtils.getLoginUser().getName()));\n }\n\n @DeleteMapping(value = \"/cluster/deleteByKey\")\n @ApiOperation(value = \"Delete cluster by cluster name and type\")\n @OperationLog(operation = OperationType.DELETE)\n @ApiImplicitParams({\n @ApiImplicitParam(name = \"name\", value = \"Cluster name\", dataTypeClass = String.class, required = true),\n @ApiImplicitParam(name = \"type\", value = \"Cluster type\", dataTypeClass = String.class, required = true),\n })\n @RequiresRoles(value = UserRoleCode.ADMIN)\n public Response<Boolean> deleteByKey(@RequestParam String name, @RequestParam String type) {\n return Response.success(clusterService.deleteByKey(name, type,\n LoginUserUtils.getLoginUser().getName()));\n }\n\n @PostMapping(value = \"/cluster/node/save\")\n @ApiOperation(value = \"Save cluster node\")\n @OperationLog(operation = OperationType.CREATE)\n public Response<Integer> saveNode(@Validated @RequestBody ClusterNodeRequest request) {\n String currentUser = LoginUserUtils.getLoginUser().getName();\n return Response.success(clusterService.saveNode(request, currentUser));\n }\n\n @GetMapping(value = \"/cluster/node/get/{id}\")\n @ApiOperation(value = \"Get cluster node by id\")\n @ApiImplicitParam(name = \"id\", value = \"Cluster node ID\", dataTypeClass = Integer.class, required = true)\n public Response<ClusterNodeResponse> getNode(@PathVariable Integer id) {\n String currentUser = LoginUserUtils.getLoginUser().getName();\n return Response.success(clusterService.getNode(id, currentUser));\n }\n\n @PostMapping(value = \"/cluster/node/list\")\n @ApiOperation(value = \"List cluster nodes by pagination\")\n public Response<PageResult<ClusterNodeResponse>> listNode(@RequestBody ClusterPageRequest request) {\n String currentUser = LoginUserUtils.getLoginUser().getName();\n return Response.success(clusterService.listNode(request, currentUser));\n }\n\n @GetMapping(value = \"/cluster/node/listByGroupId\")\n @ApiOperation(value = \"List cluster nodes by groupId, clusterType and protocolType\")\n @ApiImplicitParams({\n @ApiImplicitParam(name = \"inlongGroupId\", dataTypeClass = String.class, required = true),\n @ApiImplicitParam(name = \"clusterType\", dataTypeClass = String.class, required = true),\n @ApiImplicitParam(name = \"protocolType\", dataTypeClass = String.class, required = false)\n })\n @OperationLog(operation = OperationType.GET)\n public Response<List<ClusterNodeResponse>> listByGroupId(@RequestParam String inlongGroupId,\n @RequestParam String clusterType, @RequestParam(required = false) String protocolType) {\n return Response.success(clusterService.listNodeByGroupId(inlongGroupId, clusterType, protocolType));\n }\n\n @RequestMapping(value = \"/cluster/node/update\", method = RequestMethod.POST)\n @OperationLog(operation = OperationType.UPDATE)\n @ApiOperation(value = \"Update cluster node\")\n public Response<Boolean> updateNode(@Validated(UpdateValidation.class) @RequestBody ClusterNodeRequest request) {\n String username = LoginUserUtils.getLoginUser().getName();\n return Response.success(clusterService.updateNode(request, username));\n }\n\n @RequestMapping(value = \"/cluster/node/delete/{id}\", method = RequestMethod.DELETE)\n @ApiOperation(value = \"Delete cluster node\")\n @OperationLog(operation = OperationType.DELETE)\n @ApiImplicitParam(name = \"id\", value = \"Cluster node ID\", dataTypeClass = Integer.class, required = true)\n public Response<Boolean> deleteNode(@PathVariable Integer id) {\n return Response.success(clusterService.deleteNode(id, LoginUserUtils.getLoginUser().getName()));\n }\n\n @PostMapping(\"/cluster/testConnection\")\n @ApiOperation(value = \"Test connection for inlong cluster\")\n public Response<Boolean> testConnection(@RequestBody ClusterRequest request) {\n return Response.success(clusterService.testConnection(request));\n }\n}\n\n", "input": "", "output": "【漏洞类型】权限控制漏洞\n【严重性】High\n【漏洞摘要】在删除用户时,原始代码没有更新或删除该用户在其他实体(如集群、集群标签、数据节点等)中的负责人信息。这可能导致删除用户后,这些实体仍然显示该用户作为负责人,从而绕过权限控制。根本原因是删除用户操作未考虑到用户在其他实体中的角色和责任。\n【关键代码模式】delete方法、权限控制逻辑、MyBatis的Mapper文件。\n【修复方法说明】修复方法通过在删除用户时更新或删除该用户在其他实体中的负责人信息,确保删除操作不会导致权限控制失效。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nxwiki-platform-core/xwiki-platform-localization/xwiki-platform-localization-sources/xwiki-platform-localization-source-wiki/src/main/java/org/xwiki/localization/wiki/internal/DocumentTranslationBundleFactory.java\n### 代码内容:\n/*\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n */\npackage org.xwiki.localization.wiki.internal;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport javax.inject.Inject;\nimport javax.inject.Named;\nimport javax.inject.Provider;\nimport javax.inject.Singleton;\n\nimport org.apache.commons.lang3.EnumUtils;\nimport org.slf4j.Logger;\nimport org.xwiki.bridge.event.DocumentCreatedEvent;\nimport org.xwiki.bridge.event.DocumentDeletedEvent;\nimport org.xwiki.bridge.event.DocumentUpdatedEvent;\nimport org.xwiki.bridge.event.WikiReadyEvent;\nimport org.xwiki.cache.Cache;\nimport org.xwiki.cache.CacheException;\nimport org.xwiki.cache.CacheManager;\nimport org.xwiki.cache.config.CacheConfiguration;\nimport org.xwiki.component.annotation.Component;\nimport org.xwiki.component.descriptor.ComponentDescriptor;\nimport org.xwiki.component.descriptor.ComponentInstantiationStrategy;\nimport org.xwiki.component.descriptor.DefaultComponentDescriptor;\nimport org.xwiki.component.internal.multi.ComponentManagerManager;\nimport org.xwiki.component.manager.ComponentLifecycleException;\nimport org.xwiki.component.manager.ComponentLookupException;\nimport org.xwiki.component.manager.ComponentManager;\nimport org.xwiki.component.manager.ComponentRepositoryException;\nimport org.xwiki.component.phase.Disposable;\nimport org.xwiki.component.phase.Initializable;\nimport org.xwiki.component.phase.InitializationException;\nimport org.xwiki.localization.TranslationBundle;\nimport org.xwiki.localization.TranslationBundleDoesNotExistsException;\nimport org.xwiki.localization.TranslationBundleFactory;\nimport org.xwiki.localization.message.TranslationMessageParser;\nimport org.xwiki.localization.wiki.internal.TranslationDocumentModel.Scope;\nimport org.xwiki.model.reference.DocumentReference;\nimport org.xwiki.model.reference.DocumentReferenceResolver;\nimport org.xwiki.model.reference.EntityReferenceSerializer;\nimport org.xwiki.model.reference.WikiReference;\nimport org.xwiki.observation.EventListener;\nimport org.xwiki.observation.ObservationManager;\nimport org.xwiki.observation.event.Event;\nimport org.xwiki.query.Query;\nimport org.xwiki.query.QueryManager;\nimport org.xwiki.security.authorization.AccessDeniedException;\nimport org.xwiki.security.authorization.AuthorizationManager;\nimport org.xwiki.security.authorization.Right;\nimport org.xwiki.wiki.descriptor.WikiDescriptorManager;\n\nimport com.xpn.xwiki.XWikiContext;\nimport com.xpn.xwiki.XWikiException;\nimport com.xpn.xwiki.doc.XWikiDocument;\nimport com.xpn.xwiki.objects.BaseObject;\nimport com.xpn.xwiki.objects.StringProperty;\n\n/**\n * Generate and manage wiki document based translations bundles.\n * \n * @version $Id$\n * @since 4.3M2\n */\n@Component\n@Named(DocumentTranslationBundleFactory.ID)\n@Singleton\npublic class DocumentTranslationBundleFactory implements TranslationBundleFactory, Initializable, Disposable\n{\n /**\n * The identifier of this {@link TranslationBundleFactory}.\n */\n public final static String ID = \"document\";\n\n /**\n * The prefix to use in all wiki document based translations.\n */\n public static final String ID_PREFIX = ID + ':';\n\n private static final List<Event> EVENTS = Arrays.<Event>asList(new DocumentUpdatedEvent(),\n new DocumentDeletedEvent(), new DocumentCreatedEvent());\n\n private static final List<Event> WIKIEVENTS = Arrays.<Event>asList(new WikiReadyEvent());\n\n @Inject\n @Named(\"context\")\n private Provider<ComponentManager> componentManagerProvider;\n\n @Inject\n @Named(\"uid\")\n private EntityReferenceSerializer<String> uidSerializer;\n\n @Inject\n private EntityReferenceSerializer<String> serializer;\n\n @Inject\n @Named(\"current\")\n private DocumentReferenceResolver<String> currentResolver;\n\n @Inject\n private CacheManager cacheManager;\n\n @Inject\n private ObservationManager observation;\n\n @Inject\n private Provider<XWikiContext> xcontextProvider;\n\n @Inject\n @Named(\"messagetool/1.0\")\n private TranslationMessageParser translationParser;\n\n @Inject\n private ComponentManagerManager cmManager;\n\n @Inject\n private WikiDescriptorManager wikiManager;\n\n @Inject\n private Logger logger;\n\n @Inject\n private QueryManager queryManager;\n\n @Inject\n private AuthorizationManager authorizationManager;\n\n /**\n * Used to cache on demand document bundles (those that are not registered as components).\n */\n private Cache<TranslationBundle> onDemandBundleCache;\n\n private final EventListener listener = new EventListener()\n {\n @Override\n public void onEvent(Event event, Object arg1, Object arg2)\n {\n translationDocumentUpdated((XWikiDocument) arg1);\n }\n\n @Override\n public String getName()\n {\n return \"localization.bundle.document\";\n }\n\n @Override\n public List<Event> getEvents()\n {\n return EVENTS;\n }\n };\n\n private final EventListener wikilistener = new EventListener()\n {\n @Override\n public void onEvent(Event event, Object arg1, Object arg2)\n {\n loadTranslations(((WikiReadyEvent) event).getWikiId());\n }\n\n @Override\n public String getName()\n {\n return \"localization.wikiready\";\n }\n\n @Override\n public List<Event> getEvents()\n {\n return WIKIEVENTS;\n }\n };\n\n @Override\n public void initialize() throws InitializationException\n {\n // Cache\n CacheConfiguration cacheConfiguration = new CacheConfiguration(\"localization.bundle.document\");\n\n try {\n this.onDemandBundleCache = this.cacheManager.createNewCache(cacheConfiguration);\n } catch (CacheException e) {\n this.logger.error(\"Failed to create cache [{}]\", cacheConfiguration.getConfigurationId(), e);\n }\n\n // Load existing translations from main wiki, wait for WikiReaderEvent for other wikis\n\n loadTranslations(this.wikiManager.getMainWikiId());\n\n // Listeners\n this.observation.addListener(this.listener);\n this.observation.addListener(this.wikilistener);\n }\n\n private void loadTranslations(String wiki)\n {\n XWikiContext xcontext = this.xcontextProvider.get();\n WikiReference wikiReference = new WikiReference(wiki);\n\n List<String> documents;\n try {\n Query query =\n this.queryManager.createQuery(String.format(\n \"select distinct doc.fullName from Document doc, doc.object(%s) as translation\",\n TranslationDocumentModel.TRANSLATIONCLASS_REFERENCE_STRING), Query.XWQL);\n\n query.setWiki(wiki);\n\n documents = query.execute();\n } catch (Exception e) {\n this.logger.error(\"Failed to find translation documents\", e);\n\n return ;\n }\n\n for (String documentName : documents) {\n DocumentReference reference = this.currentResolver.resolve(documentName, wikiReference);\n\n try {\n XWikiDocument document = xcontext.getWiki().getDocument(reference, xcontext);\n\n registerTranslationBundle(document);\n } catch (Exception e) {\n this.logger.error(\"Failed to load and register the translation bundle from document [{}]\", reference,\n e);\n }\n }\n }\n\n @Override\n public TranslationBundle getBundle(String bundleId) throws TranslationBundleDoesNotExistsException\n {\n String roleHint = ID_PREFIX + bundleId;\n\n if (this.componentManagerProvider.get().hasComponent(TranslationBundle.class, roleHint)) {\n try {\n return this.componentManagerProvider.get().getInstance(TranslationBundle.class, roleHint);\n } catch (ComponentLookupException e) {\n this.logger.debug(\"Failed to lookup component [{}] with hint [{}].\", TranslationBundle.class, bundleId,\n e);\n }\n }\n\n return getOnDemandDocumentBundle(this.currentResolver.resolve(bundleId));\n }\n\n /**\n * Get non-component bundle.\n */\n private TranslationBundle getOnDemandDocumentBundle(DocumentReference documentReference)\n throws TranslationBundleDoesNotExistsException\n {\n String uid = this.uidSerializer.serialize(documentReference);\n\n TranslationBundle bundle = this.onDemandBundleCache.get(uid);\n if (bundle == null) {\n synchronized (this.onDemandBundleCache) {\n bundle = this.onDemandBundleCache.get(uid);\n if (bundle == null) {\n bundle = createOnDemandDocumentBundle(documentReference, uid);\n this.onDemandBundleCache.set(uid, bundle);\n }\n }\n }\n\n return bundle;\n }\n\n private OnDemandDocumentTranslationBundle createOnDemandDocumentBundle(DocumentReference documentReference,\n String uid) throws TranslationBundleDoesNotExistsException\n {\n XWikiContext context = this.xcontextProvider.get();\n\n XWikiDocument document;\n try {\n document = context.getWiki().getDocument(documentReference, context);\n } catch (XWikiException e) {\n throw new TranslationBundleDoesNotExistsException(\"Failed to get translation document\", e);\n }\n\n if (document.isNew()) {\n throw new TranslationBundleDoesNotExistsException(String.format(\"Document [%s] does not exists\",\n documentReference));\n }\n\n OnDemandDocumentTranslationBundle documentBundle;\n try {\n documentBundle =\n new OnDemandDocumentTranslationBundle(ID_PREFIX, document.getDocumentReference(),\n this.componentManagerProvider.get(), this.translationParser, this, uid);\n } catch (ComponentLookupException e) {\n throw new TranslationBundleDoesNotExistsException(\"Failed to create document bundle\", e);\n }\n\n return documentBundle;\n }\n\n private ComponentDocumentTranslationBundle createComponentDocumentBundle(XWikiDocument document,\n ComponentDescriptor<TranslationBundle> descriptor) throws TranslationBundleDoesNotExistsException\n {\n ComponentDocumentTranslationBundle documentBundle;\n try {\n documentBundle =\n new ComponentDocumentTranslationBundle(ID_PREFIX, document.getDocumentReference(),\n this.componentManagerProvider.get(), this.translationParser, descriptor);\n } catch (ComponentLookupException e) {\n throw new TranslationBundleDoesNotExistsException(\"Failed to create document bundle\", e);\n }\n\n return documentBundle;\n }\n\n /**\n * @param uid remove the bundle from the cache\n */\n void clear(String uid)\n {\n this.onDemandBundleCache.remove(uid);\n }\n\n /**\n * @param document the translation document\n */\n private void translationDocumentUpdated(XWikiDocument document)\n {\n if (!document.getOriginalDocument().isNew()) {\n unregisterTranslationBundle(document.getOriginalDocument());\n }\n\n if (!document.isNew()) {\n try {\n registerTranslationBundle(document);\n } catch (Exception e) {\n this.logger.error(\"Failed to register translation bundle from document [{}]\",\n document.getDocumentReference(), e);\n }\n }\n }\n\n private Scope getScope(XWikiDocument document)\n {\n BaseObject obj = document.getXObject(TranslationDocumentModel.TRANSLATIONCLASS_REFERENCE);\n\n if (obj != null) {\n return getScope(obj);\n }\n\n return null;\n }\n\n /**\n * @param obj the translation object\n * @return the {@link Scope} stored in the object, null not assigned or unknown\n */\n private Scope getScope(BaseObject obj)\n {\n if (obj != null) {\n StringProperty scopeProperty =\n (StringProperty) obj.getField(TranslationDocumentModel.TRANSLATIONCLASS_PROP_SCOPE);\n\n if (scopeProperty != null) {\n String scopeString = scopeProperty.getValue();\n\n return EnumUtils.getEnum(Scope.class, scopeString.toUpperCase());\n }\n }\n\n return null;\n }\n\n /**\n * @param document the translation document\n */\n private void unregisterTranslationBundle(XWikiDocument document)\n {\n Scope scope = getScope(document);\n\n // Unregister component\n if (scope != null && scope != Scope.ON_DEMAND) {\n ComponentDescriptor<TranslationBundle> descriptor =\n createComponentDescriptor(document.getDocumentReference());\n\n getComponentManager(document, scope, true).unregisterComponent(descriptor);\n }\n\n // Remove from cache\n this.onDemandBundleCache.remove(this.uidSerializer.serialize(document.getDocumentReference()));\n }\n\n /**\n * @param document the translation document\n * @throws TranslationBundleDoesNotExistsException when no translation bundle could be created from the provided\n * document\n * @throws ComponentRepositoryException when the actual registration of the document bundle failed\n * @throws AccessDeniedException when the document author does not have enough right to register the translation\n * bundle\n */\n private void registerTranslationBundle(XWikiDocument document) throws TranslationBundleDoesNotExistsException,\n ComponentRepositoryException, AccessDeniedException\n {\n Scope scope = getScope(document);\n\n if (scope != null && scope != Scope.ON_DEMAND) {\n checkRegistrationAuthorization(document, scope);\n\n ComponentDescriptor<TranslationBundle> descriptor =\n createComponentDescriptor(document.getDocumentReference());\n\n ComponentDocumentTranslationBundle bundle = createComponentDocumentBundle(document, descriptor);\n\n getComponentManager(document, scope, true).registerComponent(descriptor, bundle);\n }\n }\n\n /**\n * @param document the translation document\n * @param scope the scope\n * @throws AccessDeniedException thrown when the document author does not have enough right for the provided\n * {@link Scope}\n */\n private void checkRegistrationAuthorization(XWikiDocument document, Scope scope) throws AccessDeniedException\n {\n switch (scope) {\n case GLOBAL:\n this.authorizationManager.checkAccess(Right.PROGRAM, document.getAuthorReference(), null);\n break;\n case WIKI:\n this.authorizationManager.checkAccess(Right.ADMIN, document.getAuthorReference(), document\n .getDocumentReference().getWikiReference());\n break;\n default:\n break;\n }\n }\n\n /**\n * @param documentReference the translation document reference\n * @return the component descriptor to use to register/unregister the translation bundle\n */\n private ComponentDescriptor<TranslationBundle> createComponentDescriptor(DocumentReference documentReference)\n {\n DefaultComponentDescriptor<TranslationBundle> descriptor = new DefaultComponentDescriptor<TranslationBundle>();\n\n descriptor.setImplementation(ComponentDocumentTranslationBundle.class);\n descriptor.setInstantiationStrategy(ComponentInstantiationStrategy.SINGLETON);\n descriptor.setRoleHint(ID_PREFIX + this.serializer.serialize(documentReference));\n descriptor.setRoleType(TranslationBundle.class);\n\n return descriptor;\n }\n\n /**\n * Get the right component manager based on the scope.\n * \n * @param document the translation document\n * @param scope the translation scope\n * @param create true if the component manager should be created if it does not exists\n * @return the component manager corresponding to the provided {@link Scope}\n */\n private ComponentManager getComponentManager(XWikiDocument document, Scope scope, boolean create)\n {\n String hint;\n\n switch (scope) {\n case WIKI:\n hint = \"wiki:\" + document.getDocumentReference().getWikiReference().getName();\n break;\n case USER:\n hint = \"user:\" + this.serializer.serialize(document.getAuthorReference());\n break;\n default:\n hint = null;\n break;\n }\n\n return this.cmManager.getComponentManager(hint, create);\n }\n\n @Override\n public void dispose() throws ComponentLifecycleException\n {\n this.observation.removeListener(this.listener.getName());\n this.observation.removeListener(this.wikilistener.getName());\n }\n}\n\n## 内容2\n### 文件路径:\nxwiki-platform-core/xwiki-platform-localization/xwiki-platform-localization-sources/xwiki-platform-localization-source-wiki/src/main/resources/META-INF/components.txt\n### 代码内容:\norg.xwiki.localization.wiki.internal.DocumentTranslationBundleFactory\norg.xwiki.localization.wiki.internal.DocumentTranslationBundleInitializer\norg.xwiki.localization.wiki.internal.TranslationDocumentClassInitializer\n\n## 内容3\n### 文件路径:\nxwiki-platform-tools/xwiki-platform-tool-configuration-resources/src/main/resources/xwiki.properties.vm\n### 代码内容:\n# ---------------------------------------------------------------------------\n# See the NOTICE file distributed with this work for additional\n# information regarding copyright ownership.\n#\n# This is free software; you can redistribute it and/or modify it\n# under the terms of the GNU Lesser General Public License as\n# published by the Free Software Foundation; either version 2.1 of\n# the License, or (at your option) any later version.\n#\n# This software is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n# Lesser General Public License for more details.\n#\n# You should have received a copy of the GNU Lesser General Public\n# License along with this software; if not, write to the Free\n# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n# 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n# ---------------------------------------------------------------------------\n\n# This is the new XWiki configuration file. In the future it'll replace the old\n# xwiki.cfg file. However right now it's only used by some XWiki components.\n# As time progresses more and more component will get their configurations from\n# this file.\n# \n# This file come from one of those locations (in this order):\n# * [since 9.3] /etc/xwiki/xwiki.properties\n# * /WEB-INF/xwiki.properties in the web application resources\n\n#-------------------------------------------------------------------------------------\n# Core\n#-------------------------------------------------------------------------------------\n\n#-# [Since 1.8RC2]\n#-# Specifies the default syntax to use when creating new documents.\n#-# Default value is xwiki/2.1.\n# core.defaultDocumentSyntax = xwiki/2.1\n\n#-# [Since 2.4M1]\n#-# Indicate if the rendering cache is enabled.\n#-# Default value is false.\n# core.renderingcache.enabled = true\n\n#-# [Since 2.4M1]\n#-# A list of Java regex patterns matching full documents reference.\n# core.renderingcache.documents = wiki:Space\\.Page\n# core.renderingcache.documents = wiki:Space\\..*\n# core.renderingcache.documents = Space\\.PageOnWhateverWiki\n\n#-# [Since 2.4M1]\n#-# The time (in seconds) after which data should be removed from the cache when not used.\n#-# Default value is 300 (5 min).\n# core.renderingcache.duration = 300\n\n#-# [Since 2.4M1]\n#-# The size of the rendering cache. Not that it's not the number of cached documents but the number of cached results.\n#-# (For a single document several cache entries are created, because each action, language and request query string\n#-# produces a unique rendering result)\n#-# Default value is 100.\n# core.renderingcache.size = 100\n\n#-# [Since 7.2M2]\n#-# Define which hierarchy is used between pages (for example inside the breadcrumb).\n#-# Possible values are \"reference\" (default) or \"parentchild\".\n#-# If \"parentchild\" is used, the hierachy is based on the parent/child relationship, which means that any document\n#-# could be defined as the parent of an other document.\n#-# If \"reference\" is used, then the children of a document must be placed inside this document. It's less flexible but\n#-# more clear.\n# core.hierarchyMode = reference\n\n#-------------------------------------------------------------------------------------\n# Environment\n#-------------------------------------------------------------------------------------\n\n#-# [Since 3.5M1, replaces the container.persistentDirectory property]\n#-# The directory used to store persistent data (data that should persist across server restarts). This is an\n#-# important directory containing important data and thus it should never be deleted (it should be backed-up along\n#-# with the database).\n#-# For example this is where the Extension Manager stores downloaded extensions if the extension.localRepository\n#-# property isn't configured.\n#-#\n#-# You can set:\n#-# * an absolute path (recommended)\n#-# * a relative path (not recommended at all)but in this case the directory will be relative to where the XWiki server\n#-# is started and thus the user under which XWiki is started will need write permissions for the current directory\n#-#\n#-# Note if the system property xwiki.data.dir is set then this property is not used.\n#-# If neither the system property nor this configuration value here are set then the Servlet container's temporary\n#-# directory is used; This is absolutely not recommended since that directory could be wiped out at any time and you\n#-# should specify a value.\n#if ($xwikiPropertiesEnvironmentPermanentDirectory)\nenvironment.permanentDirectory = $xwikiPropertiesEnvironmentPermanentDirectory\n#else\n# environment.permanentDirectory = /var/lib/xwiki/data/\n#end\n\n#-------------------------------------------------------------------------------------\n# Rendering\n#-------------------------------------------------------------------------------------\n\n#-# [Since 1.8RC2]\n#-# Specifies how links labels are displayed when the user doesn't specify the label explicitly.\n#-# Valid values:\n#-# %w: wiki name\n#-# %s: full space name (e.g. space1.space2)\n#-# Note: Prior to 7.4.2/8.0M2 this was only displaying the last space name\n#-# %ls: last space name. New in 7.4.2/8.0M2\n#-# %p: page name\n#-# %np: nested page name (i.e. will display the space name for Nested Pages). New in 7.4.2/8.0M2\n#-# %P: page name with spaces between camel case words, i.e. \"My Page\" if the page name is \"MyPage\"\n#-# %NP: nested page name with spaces between camel case words, i.e. \"My Page\" if the page name is \"MyPage\".\n#-# New in 7.4.2/8.0M2\n#-# %t: page title\n#-#\n#-# Note that if the page title is empty or not defined then it defaults to %np. This is also the case\n#-# if the title cannot be retrieved for the document.\n#-#\n#-# The default is \"%np\". Some examples: \"%s.%p\", \"%w:%s.%p\".\n# rendering.linkLabelFormat = %np\n\n#-# [Since 2.0M3]\n#-# Overrides default macro categories (Each macro has default categories already defined, for example\n#-# \"Navigation\" for the Table of Contents Macro).\n#-# Note: the categories are case sensitive.\n#-#\n#-# Ex: To redefine the macro categories for the TOC macro so that it'd be in the \"My Category\" and \"Deprecated\" \n#-# categories + redefine the category for the Script Macro to be \"My Other Category\", you'd use:\n# rendering.transformation.macro.categories = toc = My Category\\,Deprecated\n# rendering.transformation.macro.categories = script = My Other Category\n\n#-# [Since 14.8RC1]\n#-# Override the default hidden macro categories.\n#-# Note: the categories are case sensitive.\n#-#\n#-# For instance, to make the \"Development\" category hidden by default, in addition to the default \"Internal\" and \n#-# \"Deprecated\" categories, you'd use:\n# rendering.transformation.macro.hiddenCategories = Development,Internal,Deprecated\n\n#-# [Since 2.5M2]\n#-# Specify whether the image dimensions should be extracted from the image parameters and included in the image URL\n#-# or not. When image dimensions are included in the image URL the image can be resized on the server side before being\n#-# downloaded, improving thus the page loading speed.\n#-#\n#-# Default value is true.\n# rendering.imageDimensionsIncludedInImageURL = true\n\n#-# [Since 2.5M2]\n#-# One way to improve page load speed is to resize images on the server side just before rendering the page. The\n#-# rendering module can use the image width provided by the user to scale the image (See\n#-# rendering.includeImageDimensionsInImageURL configuration parameter). When the user doesn't specify the image width\n#-# the rendering module can limit the width of the image based on this configuration parameter.\n#-#\n#-# The default value is -1 which means image width is not limited by default. Use a value greater than 0 to limit the\n#-# image width (pixels). Note that the aspect ratio is kept even when both the width and the height of the image are\n#-# limited.\n# rendering.imageWidthLimit = 1024\n# rendering.imageWidthLimit = -1\n\n#-# [Since 2.5M2]\n#-# See rendering.imageWidthLimit\n# rendering.imageHeightLimit = 768\n# rendering.imageHeightLimit = -1\n\n#-# [Since 2.5M2]\n#-# InterWiki definitions in the format alias = URL\n#-# See http://en.wikipedia.org/wiki/Interwiki_links for a definition of an InterWiki link\n# Some examples:\n# rendering.interWikiDefinitions = wikipedia = http://en.wikipedia.org/wiki/\n# rendering.interWikiDefinitions = definition = http://www.yourdictionary.com/\n\n#-# [Since 2.4M1]\n#-# Change the Pygments style used in the code macro (see http://pygments.org/docs/styles/)\n#-# If not set \"default\" style is used.\n# rendering.macro.code.pygments.style = vs\n\n#-------------------------------------------------------------------------------------\n# Rendering Transformations\n#-------------------------------------------------------------------------------------\n\n#-# [Since 2.6RC1]\n#-# Controls what transformations will be executed when rendering content.\n#-# A transformation modifies the parsed content. For example the Icon transformation replaces some characters with\n#-# icons, a WikiWord transformation will automatically create links when it finds wiki words, etc.\n#-# Note that the Macro transformation is a special transformation that replaces macro markers by the result of the\n#-# macro execution. If you don't list it, macros won't get executed.\n#-# The default value is: macro, icon\n#if ($xwikiRenderingTransformations)\nrendering.transformations = $xwikiRenderingTransformations\n#else\n# rendering.transformations = macro, icon\n#end\n\n#-# [Since 2.6RC1]\n#-# Icon Transformation Configuration\n#-# Defines mappings between suite of characters and the icon to display when those characters are found.\n#-# The format is: rendering.transformation.icon.mappings = <suite of characters> = <icon name>\n#-# The following mappings are already predefined and you don't need to redefine them unless you wish to override them\n#-# [Since 9.3RC1/8.4.5] To disable a default mapping, define it with an empty value. For example:\n#-# rendering.transformation.icon.mappings = (n) =\n#-#\n#-# rendering.transformation.icon.mappings = :) = emoticon_smile\n#-# rendering.transformation.icon.mappings = :( = emoticon_unhappy\n#-# rendering.transformation.icon.mappings = :P = emoticon_tongue\n#-# rendering.transformation.icon.mappings = :D = emoticon_grin\n#-# rendering.transformation.icon.mappings = ;) = emoticon_wink\n#-# rendering.transformation.icon.mappings = (y) = thumb_up\n#-# rendering.transformation.icon.mappings = (n) = thumb_down\n#-# rendering.transformation.icon.mappings = (i) = information\n#-# rendering.transformation.icon.mappings = (/) = accept\n#-# rendering.transformation.icon.mappings = (x) = cancel\n#-# rendering.transformation.icon.mappings = (!) = error\n#-# rendering.transformation.icon.mappings = (+) = add\n#-# rendering.transformation.icon.mappings = (-) = delete\n#-# rendering.transformation.icon.mappings = (?) = help\n#-# rendering.transformation.icon.mappings = (on) = lightbulb\n#-# rendering.transformation.icon.mappings = (off) = lightbulb_off\n#-# rendering.transformation.icon.mappings = (*) = star\n\n#-------------------------------------------------------------------------------------\n# LinkChecker Transformation\n#-------------------------------------------------------------------------------------\n\n#-# [Since 3.3M2]\n#-# Defines the time (in ms) after which an external link should be checked again for validity.\n#-# the default configuration is:\n# rendering.transformation.linkchecker.timeout = 3600000\n\n#-# [Since 5.3RC1]\n#-# List of document references that are excluded from link checking, specified using regexes.\n#-# the default configuration is:\n# rendering.transformation.linkchecker.excludedReferencePatterns = .*:XWiki\\.ExternalLinksJSON\n\n#-------------------------------------------------------------------------------------\n# Rendering Macros\n#-------------------------------------------------------------------------------------\n\n#-# Velocity Macro\n\n#-# [Since 2.0M1]\n#-# Defines which Velocity Filter to use by default. This offers the possibility to filter the Velocity macro content\n#-# before and after the Velocity Engine execution.\n#-# The following filters are available:\n#-# - indent (the default): Remove all first whites spaces of lines to support source code indentation without\n#-# generating whitespaces in the resulting XDOM.\n#-# - none: Doesn't change the content\n#-# - html: Removes all leading and trailing white spaces and new lines. If you need a space you'll need to use\n#-# \\$sp and if you need a new line you'll need to use \\$nl\n#-# rendering.macro.velocity.filter = indent\n\n#-------------------------------------------------------------------------------------\n# HTML Sanitization\n#-------------------------------------------------------------------------------------\n\n#-# [Since 14.6RC1]\n#-# The HTML sanitization strategy to use for user-generated content to avoid JavaScript injection. The following\n#-# strategies are available by default:\n#-# - secure (default): Only allows known elements and attributes that are considered safe. The following options\n#-# allow customizing its behavior.\n#-# - insecure: Allows everything including JavaScript. Use this only if you absolutely trust everybody who can\n#-# write wiki syntax (in particular, all users, but also anonymous users commenting when enabled).\n# xml.htmlElementSanitizer = secure\n\n#-# [Since 14.6RC1]\n#-# Comma-separated list of additional tags that should be allowed by the HTML sanitizer. These tags will be allowed\n#-# in addition to the already extensive built-in list of tags that are considered safe. Use with care to avoid\n#-# introducing security issues. By default, the following tags are allowed:\n#-# HTML tags: https://github.com/xwiki/xwiki-commons/blob/b11eae9d82cb53f32962056b5faa73f3720c6182/xwiki-commons-core/xwiki-commons-xml/src/main/java/org/xwiki/xml/internal/html/HTMLDefinitions.java#L63-L73\n#-# SVG tags: https://github.com/xwiki/xwiki-commons/blob/b11eae9d82cb53f32962056b5faa73f3720c6182/xwiki-commons-core/xwiki-commons-xml/src/main/java/org/xwiki/xml/internal/html/SVGDefinitions.java#L91-L102\n#-# MathML tags: https://github.com/xwiki/xwiki-commons/blob/b11eae9d82cb53f32962056b5faa73f3720c6182/xwiki-commons-core/xwiki-commons-xml/src/main/java/org/xwiki/xml/internal/html/MathMLDefinitions.java#L62-L64\n# xml.htmlElementSanitizer.extraAllowedTags =\n\n#-# [Since 14.6RC1]\n#-# Comma-separated list of additional attributes that should be allowed by the HTML sanitizer. These attributes will\n#-# be allowed in addition to the already extensive built-in list of attributes that are considered safe. This option\n#-# is useful if your content uses attributes that are invalid in HTML. Use with care to avoid introducing security\n#-# issues. By default, the following attributes are allowed:\n#-# HTML attributes: https://github.com/xwiki/xwiki-commons/blob/b11eae9d82cb53f32962056b5faa73f3720c6182/xwiki-commons-core/xwiki-commons-xml/src/main/java/org/xwiki/xml/internal/html/HTMLDefinitions.java#L78-L90\n#-# SVG attributes: https://github.com/xwiki/xwiki-commons/blob/b11eae9d82cb53f32962056b5faa73f3720c6182/xwiki-commons-core/xwiki-commons-xml/src/main/java/org/xwiki/xml/internal/html/SVGDefinitions.java#L66-L89\n#-# MathML attributes: https://github.com/xwiki/xwiki-commons/blob/b11eae9d82cb53f32962056b5faa73f3720c6182/xwiki-commons-core/xwiki-commons-xml/src/main/java/org/xwiki/xml/internal/html/MathMLDefinitions.java#L73-L79\n#-# XML attributes: https://github.com/xwiki/xwiki-commons/blob/b11eae9d82cb53f32962056b5faa73f3720c6182/xwiki-commons-core/xwiki-commons-xml/src/main/java/org/xwiki/xml/internal/html/SecureHTMLElementSanitizer.java#L135\n# xml.htmlElementSanitizer.extraAllowedAttributes =\n\n#-# [Since 14.6RC1]\n#-# Comma-separated list of tags that should be forbidden. This takes precedence over any tags allowed by default or\n#-# configured above. This can be used, for example, to forbid video or audio elements.\n# xml.htmlElementSanitizer.forbidTags =\n\n#-# [Since 14.6RC1]\n#-# Comma-separated list of attributes that should be forbidden. This takes precedence over any attributes allowed by\n#-# default or configured above. This can be used, for example, to forbid inline styles by forbidding the \"style\"\n#-# attribute.\n# xml.htmlElementSanitizer.forbidAttributes =\n\n#-# [Since 14.6RC1]\n#-# If unknown protocols shall be allowed. This means all protocols like \"xwiki:\" will be allowed in links, however,\n#-# script and data-URIs will still be forbidden (for data-URIs see also below). By default, unknown protocols are\n#-# allowed.\n# xml.htmlElementSanitizer.allowUnknownProtocols = true\n\n#-# [Since 14.6RC1]\n#-# If unknown protocols are disallowed (see above), the (Java) regular expression URIs are matched against.\n#-# The default values is ^(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):\n# xml.htmlElementSanitizer.allowedUriRegexp = ^(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):\n\n#-# [Since 14.6RC1]\n#-# Comma-separated list of additional tags on which data-URIs should be allowed in \"src\", \"xlink:href\" or \"href\".\n#-# Adding \"a\" here, for example, would allow linking to data-URIs which is disabled by default due to the potential of\n#-# security issues. Modern browsers should mitigate them, though, see for example\n#-# https://blog.mozilla.org/security/2017/11/27/blocking-top-level-navigations-data-urls-firefox-59/ so you could\n#-# use this to allow defining images, PDF files or files to be downloaded inline as data-URIs in links.\n# xml.htmlElementSanitizer.extraDataUriTags =\n\n#-# [Since 14.6RC1]\n#-# Comma-separated list of additional attributes that are considered safe for arbitrary content including\n#-# script-URIs, on these attributes the above-mentioned URI-checks aren't used. Use with care to avoid introducing\n#-# security issues.\n# xml.htmlElementSanitizer.extraURISafeAttributes =\n\n#-------------------------------------------------------------------------------------\n# Cache\n#-------------------------------------------------------------------------------------\n\n#-# [Since 1.7M1]\n#-# The standard cache component implementation to use (can be local or distributed depending on the implementation).\n#-# The default standard cache implementation is Infinispan.\n# cache.defaultCache = infinispan\n\n#-# [Since 1.7M1]\n#-# The local cache implementation to use.\n#-# The default local cache implementation is Infinispan.\n# cache.defaultLocalCache = infinispan/local\n\n#-------------------------------------------------------------------------------------\n# Settings for the OpenOffice server instance consumed by the OfficeImporter component\n#-------------------------------------------------------------------------------------\n\n#-# [Since 1.9M2]\n#-# Type of the openoffice server instance used by officeimporter component.\n#-# 0 - Internally managed server instance. (Default)\n#-# 1 - Externally managed (local) server instance.\n# openoffice.serverType = 0\n\n#-# [Since 12.1RC1]\n#-# Port numbers used for connecting to the openoffice server instance.\n#-# For an internally managed server instance, it will create the process for all ports.\n#-# For an externally managed server instance, only the first server port is taken into account.\n# openoffice.serverPorts = 8100,8101\n\n#-# [Since 1.9M2]\n#-# If the openoffice server should be started / connected upon XE start.\n#-# Default value is false\n# openoffice.autoStart = false\n\n#-# [Since 1.8RC3]\n#-# Path to openoffice installation (serverType:0 only).\n#-# If no path is provided, a default value will be calculated based on the operating environment.\n# openoffice.homePath = /opt/openoffice.org3/\n\n#-# [Since 1.8RC3]\n#-# Path to openoffice execution profile (serverType:0 only).\n#-# If no path is provided, a default value will be calculated based on the operating environment.\n# openoffice.profilePath = /home/user/.openoffice.org/3\n\n#-# [Since 1.8RC3]\n#-# Maximum number of simultaneous conversion tasks to be handled by a single openoffice process (serverType:0 only).\n#-# Default value is 50\n# openoffice.maxTasksPerProcess = 50\n\n#-# [Since 1.8RC3]\n#-# Timeout for conversion tasks (in milliseconds) (serverType:0 only).\n#-# Default value is 60 seconds\n# openoffice.taskExecutionTimeout = 60000\n\n#-------------------------------------------------------------------------------------\n# Velocity\n#-------------------------------------------------------------------------------------\n\n#-# [Since 2.0M1]\n#-# Velocity Tools that will be available from your scripts. The format is\n#-# velocity.tools = <name under which it'll be available in the context> = <class name>\n#-# Default values (no need to add them)\n#-# velocity.tools = numbertool = org.xwiki.velocity.internal.XWikiNumberTool\n#-# velocity.tools = datetool = org.xwiki.velocity.internal.XWikiDateTool\n#-# velocity.tools = mathtool = org.xwiki.velocity.internal.XWikiMathTool\n#-# velocity.tools = escapetool = org.apache.velocity.tools.generic.EscapeTool\n#-# velocity.tools = regextool = org.xwiki.velocity.tools.RegexTool\n#-# velocity.tools = collectiontool = org.xwiki.velocity.tools.CollectionTool\n#-# velocity.tools = stringtool = org.xwiki.text.StringUtils\n#-# velocity.tools = jsontool = org.xwiki.velocity.tools.JSONTool\n#-# velocity.tools = urltool = org.xwiki.velocity.tools.URLTool\n#-# velocity.tools = exceptiontool = org.apache.commons.lang3.exception.ExceptionUtils\n#-# velocity.tools = niotool = org.xwiki.velocity.tools.nio.NIOTool\n#-# velocity.tools = logtool = org.apache.velocity.tools.generic.LogTool\n#-# velocity.tools = objecttool = org.xwiki.velocity.tools.ObjectTool\n#-# velocity.tools = listtool = org.apache.velocity.tools.generic.ListTool (deprecated)\n#-# velocity.tools = sorttool = org.apache.velocity.tools.generic.SortTool (deprecated)\n#-# velocity.tools = collectionstool = org.xwiki.velocity.tools.CollectionsTool (deprecated)\n\n#-# [Since 2.0M1]\n#-# Velocity configuration properties. The format is\n#-# velocity.properties = <Velocity property name> = <value>\n#-# Default values (no need to add them)\n#-# velocity.properties = resource.loader = xwiki\n#-# velocity.properties = xwiki.resource.loader.class = org.xwiki.velocity.XWikiWebappResourceLoader\n#-# velocity.properties = velocimacro.library = \"/templates/macros.vm\"\n#-# velocity.properties = velocimacro.max_depth = 100\n#-# velocity.properties = resource.manager.log_when_found = false\n#-# velocity.properties = velocimacro.inline.local_scope = true\n#-# velocity.properties = velocimacro.inline.replace_global = true\n#-# velocity.properties = directive.if.empty_check = false\n#-# velocity.properties = parser.space_gobbling = bc\n#-# velocity.properties = parser.allow_hyphen_in_identifiers = true\n#-# velocity.properties = velocimacro.enable_bc_mode = true\n#-# velocity.properties = context.scope_control.template = true\n#-# velocity.properties = context.scope_control.macro = true\n#-# velocity.properties = event_handler.include.class = org.xwiki.velocity.internal.util.RestrictParseLocationEventHandler\n#-# velocity.properties = runtime.introspection.uberspect = org.xwiki.velocity.introspection.SecureUberspector\\,org.apache.velocity.util.introspection.DeprecatedCheckUberspector\\,org.xwiki.velocity.introspection.MethodArgumentsUberspector\n\n#-------------------------------------------------------------------------------------\n# Groovy\n#-------------------------------------------------------------------------------------\n\n#-# [Since 4.1M1]\n#-# Allows to specify Compiler customization for the Groovy execution engine.\n#-# There's no customizers defined by default. Available customizer ids:\n#-# - timedinterrupt: interrupt script execution if it takes longer than a given time (default to 1 minute)\n#-# - secure: runs Groovy in a security sandbox\n#-# groovy.compilationCustomizers = <list of customizer ids here>\n\n#-# Timed Interrupt Customizer\n\n#-# [Since 4.1M1]\n#-# Default execution time for a script before a timeout occurs, in seconds.\n#-# groovy.customizer.timedInterrupt.timeout = 60\n\n#-------------------------------------------------------------------------------------\n# Events distribution\n#-------------------------------------------------------------------------------------\n\n#-# [Since 2.0M3]\n#-# Indicate if the network distribution module is enabled or not.\n#-# By default remote events are disabled.\n# observation.remote.enabled = false\n\n#-# [Since 2.0M3]\n#-# The list of events communication channels to start when the application starts.\n#-# By default no channel is configured.\n#-#\n#-# The default remote event distribution implementation is using JGroups and you'll need to either use embedded\n#-# JGroups configuration files or drop your custom configuration in the WEB-INF/observation/remote/jgroups/ directory.\n#-# There's a README file in that directory with more information.\n#-# Example: observation.remote.channels = public, cluster\n\n#-# [Since 2.0M4]\n#-# The implementation of network adapter to use.\n#-# The default is jgroups.\n#-#\n#-# By default only jgroups is provided. To add one implements NetworkAdaptor component interface. The identifier\n#-# provided in the configuration is matched with the component role hint.\n#-# Example: observation.remote.networkadapter = jgroups\n\n#-------------------------------------------------------------------------------------\n# CSRF token component\n#-------------------------------------------------------------------------------------\n\n#-# [Since 2.5M2]\n#-# Controls whether secret token validation mechanism should be used (to prevent CSRF attacks).\n#-#\n#-# If enabled, all actions requiring \"comment\", \"edit\", \"delete\", \"admin\" or \"programming\" rights\n#-# will check that the parameter \"form_token\" with the value of a random secret token is present\n#-# in the request.\n#-#\n#-# Valid values:\n#-# true : Enabled\n#-# false: Disabled\n#-#\n#-# Default value is true\n# csrf.enabled = true\n\n#-------------------------------------------------------------------------------------\n# Jobs\n#-------------------------------------------------------------------------------------\n\n#-# [Since 4.0M1]\n#-# The folder containing job executing status.\n#-# The default is {environment.permanentDirectory}/jobs/\n# job.statusFolder = /var/lib/xwiki/data/jobs/\n\n#-# [Since 7.2M2]\n#-# The maximum number of entries to put in the job status cache.\n#-# The default is 50.\n# job.statusCacheSize = 50\n\n#-# [Since 12.5RC1]\n#-# The maximum number of entries to put in cache for the GroupedJobInitializer components.\n#-# The default is 100.\n# job.groupedJobInitializerCacheSize = 100\n\n#-# [Since 12.5RC1]\n#-# The thread keep-alive time in milliseconds for the single job executor.\n#-# This value defines how long a thread can be idle before being terminated by the executor.\n#-# The default value is 60000 for 60 000ms.\n# job.singleJobThreadKeepAliveTime = 60000\n\n#-# [Since 12.5RC1]\n#-# The thread keep-alive time in milliseconds for the grouped job executors.\n#-# This value defines how long a thread can be idle before being terminated by an executor.\n#-# The default value is 60000 for 60 000ms.\n# job.groupedJobThreadKeepAliveTime = 60000\n\n#-------------------------------------------------------------------------------------\n# Extension Manager\n#-------------------------------------------------------------------------------------\n\n#-# [Since 2.5]\n#-# Repositories to use when searching and downloading extensions.\n#-# Repositories will be checked in the same order they have in this configuration file.\n#-#\n#-# The format is <id>:<type>:<url> where\n#-# * id can be anything as long as there is only one\n#-# * type is the type of the repository (maven, xwiki, etc.)\n#-# * url is the URL or the root of the repository\n#-#\n#-# [Since 4.3] It's also possible to associate various properties to each repository.\n#-# Here are the standard properties:\n#-# * user: the user to use to authenticate to the repository\n#-# * password: the password to use to authenticate to the repository\n#-# \n#-# And here those for \"maven\" repositories:\n#-# * [Since 10.7RC1] checksumPolicy: what to do when checksum validation fail. Possible values are \"fail\", \"warn\"\n#-# (the default) and \"ignore\"\n#-# * [Since 13.0RC1] http.headers: Custom HTTP headers to be used when connecting to the maven repository.\n#-#\n#-# Here is an example:\n# extension.repositories = privatemavenid:maven:http://host.com/private/maven/\n# extension.repositories.privatemavenid.auth.user = someuser\n# extension.repositories.privatemavenid.auth.password = thepassword\n# extension.repositories.privatemavenid.http.headers.headername = headervalue\n#-#\n#-# Here's an example to add your local Maven Repository\n## Note: Since this file is parsed by Velocity in the Maven Remote Resource plugin we need to escape the\n## Apache Commons Configuration syntax for specifying the user.home system property!\n#set ($userHome = '${sys:user.home}')\n# extension.repositories = maven-local:maven:file://${userHome}/.m2/repository\n#-#\n#-# And an example to add the XWiki Maven Snapshot Repository\n# extension.repositories = maven-xwiki-snapshot:maven:https://nexus.xwiki.org/nexus/content/groups/public-snapshots\n#-#\n#-# When not set the following is taken (in this order):\n# extension.repositories = maven-xwiki:maven:https://nexus.xwiki.org/nexus/content/groups/public\n# extension.repositories = store.xwiki.com:xwiki:https://store.xwiki.com/xwiki/rest/\n# extension.repositories = extensions.xwiki.org:xwiki:https://extensions.xwiki.org/xwiki/rest/\n#-#\n#-# To not have any repository enabled (including disabling default repositories) you can explicitly make this list\n#-# empty:\n# extension.repositories=\n#if ($xwikiExtensionRepositories)\n#foreach ($extensionRepository in $xwikiExtensionRepositories.split(','))\nextension.repositories = $extensionRepository\n#end\n#end\n\n#-# [Since 2.5]\n#-# The directory where extensions are stored after being downloaded.\n#-#\n#-# The default is extension/repository in whatever is the general persistent directory.\n#-# See container.persistentDirectory.\n# extension.localRepository=extension/repository\n\n#-# [Since 3.4]\n#-# The user agent to use when communication with external services (generally repositories).\n#-#\n#-# The default is:\n# extension.userAgent=XWikiExtensionManager\n\n#-# [Since 7.1RC1]\n#-# Some extensions considered now as flavor but released before the category exists\n#-#\n#if ($xwikiPropertiesExtensionOldFlavors)\n#foreach($oldFlavor in $xwikiPropertiesExtensionOldFlavors.split(','))\nextension.oldflavors=$oldFlavor.trim()\n#end\n#else\n# extension.oldflavors=\n#end\n\n#-# [Since 8.3]\n#-# Indicate if XWiki should try to find more informations about the core extension in the repositories.\n#-#\n#-# The default is:\n# extension.core.resolve=true\n\n#-# [Since 9.6]\n#-# Indicate a list of pattern extension ids and the corresponding recommended version.\n#-# This version will be used for dependencies matching the pattern and fallback on the version declared by the\n#-# extension in case of failure.\n#-#\n#-# Here is an example:\n# extension.recommendedVersions=org.xwiki.commons:.*/[9.6]\n# extension.recommendedVersions=org.xwiki.rendering:.*/[9.6]\n# extension.recommendedVersions=org.xwiki.platform:.*/[9.6]\n# extension.recommendedVersions=com.mygroupid:.*/[1.9]\n\n#-# [Since 9.9]\n#-# Indicate whether the server should automatically check for new available environment versions.\n#-#\n#if ($xwikiPropertiesEnvironmentVersionCheck)\nextension.versioncheck.environment.enabled=$xwikiPropertiesEnvironmentVersionCheck\n#else\n#-# The default is:\n# extension.versioncheck.environment.enabled=false\n#end\n#-#\n#-# If the version check is enabled (through extension.versioncheck.environment.enabled), the following properties\n#-# can be used to customize how the version check is performed.\n#-#\n#-# Indicate the number of seconds between each check for a new version of the server.\n#-# The default is:\n# extension.versioncheck.environment.interval=3600\n#-#\n#-# Indicate a pattern that will be used to filter which version should be considered as a new version.\n#-# By default, no pattern is given an the pattern is not applied.\n#-# Example:\n# extension.versioncheck.environment.allowedVersions=9.*\n\n#-# [Since 10.5RC1]\n#-# Indicate how extension documents are protected.\n#-#\n#-# The possible choices are:\n#-# * none: no protection at all\n#-# * warning (the default): everyone get a warning when trying to edit a protected document\n#-# * deny = EDIT/DELETE right is denied for everyone except for admins who just get a warning\n#-# * forcedDeny = EDIT/DELETE right is denied for everyone, admins can't force edit/delete\n#-# * denySimple = EDIT/DELETE right is denied for simple users except for simple admins who just get a warning\n#-# * forcedDenySimple = EDIT/DELETE right is denied for all simple users, simple admins can't force edit/delete\n# extension.xar.protection=warning\n\n#-# [Since 12.2RC and 11.10.4]\n#-# Indicate a list of extension dependencies to ignore\n#-# \n#-# The default list is:\n# extension.ignoredDependencies=stax:stax\n# extension.ignoredDependencies=javax.xml.stream:stax-api\n# extension.ignoredDependencies=stax:stax-api\n# extension.ignoredDependencies=xalan:xalan\n# extension.ignoredDependencies=xalan:serializer\n# extension.ignoredDependencies=xml-apis:xml-apis\n# extension.ignoredDependencies=xerces:xmlParserAPIs\n\n#-------------------------------------------------------------------------------------\n# Distribution Wizard\n#-------------------------------------------------------------------------------------\n\n#-# [Since 7.1RC1] Enable or disable the automatic start of Distribution Wizard on empty/outdated wiki.\n#-#\n#-# The default is:\n\n# distribution.automaticStartOnMainWiki=true\n# distribution.automaticStartOnWiki=true\n#if ($xwikiPropertiesAutomaticStartOnMainWiki)\ndistribution.automaticStartOnMainWiki=$xwikiPropertiesAutomaticStartOnMainWiki\n#end\n#if ($xwikiPropertiesAutomaticStartOnWiki)\ndistribution.automaticStartOnWiki=$xwikiPropertiesAutomaticStartOnWiki\n#end\n\n#-# [Since 10.2RC1] Control if the Distribution Wizard should be automatic or interactive (the default)\n#-# \n#-# On the main wiki\n# distribution.job.interactive=true\n#-# On the subwikis\n# distribution.job.interactive.wiki=true\n\n#-# [Since 10.2RC1] Override the default UI indicated in the \"environment extension\" (usually means the WAR).\n#-# It usually make more sense to set that at the WAR level (since it's usually synchronized with it).\n#-# If this is set it also means the Distribution Wizard will switch to default UI mode in which you cannot choose the\n#-# flavor.\n#-#\n#-# The id[/version] of the default UI for the main wiki. If the version is not indicated it will be the version of the\n#-# environment extension.\n# distribution.defaultUI=org.my.groupid:artifactid-mainwiki/1.5\n#-# The id[/version] default UI for the subwikis. If the version is not indicated it will be the version of the\n#-# environment extension.\n# distribution.defaultWikiUI=org.my.groupid:artifactid-wiki/1.5\n\n#-------------------------------------------------------------------------------------\n# Solr Search\n#-------------------------------------------------------------------------------------\n\n#-# [Since 4.5M1]\n#-# The Solr server type. Currently accepted values are \"embedded\" (default) and \"remote\".\n# solr.type=embedded\n\n#-# [Since 4.5M1]\n#-# The location where the embedded Solr instance home folder is located.\n#-# The default is the subfolder \"store/solr\" inside folder defined by the property \"environment.permanentDirectory\".\n# solr.embedded.home=/var/local/xwiki/store/solr\n\n#-# [Since 12.2]\n#-# The URL of the Solr server (the root server and not the URL of a core).\n#-# The default value assumes that the remote Solr server is started in a different process on the same machine,\n#-# using the default port.\n# solr.remote.baseURL=http://localhost:8983/solr\n\n#-# [Since 12.6]\n#-# The prefix to add in front on each remote core name to avoid collisions with non-XWiki cores.\n#-# The default is \"xwiki\" which will produce names likes \"xwiki_events\" for example.\n# solr.remote.corePrefix=xwiki\n\n#-# [Since 5.1M1]\n#-# Elements to index are not sent to the Solr server one by one but in batch to improve performances.\n#-# It's possible to configure this behavior with the following properties:\n#-#\n#-# The maximum number of elements sent at the same time to the Solr server\n#-# The default is 50.\n# solr.indexer.batch.size=50\n#-# The maximum number of characters in the batch of elements to send to the Solr server.\n#-# The default is 10000.\n# solr.indexer.batch.maxLength=10000\n\n#-# [Since 5.1M1]\n#-# The maximum number of elements in the background queue of elements to index/delete\n#-# The default is 10000.\n# solr.indexer.queue.capacity=100000\n\n#-# [Since 6.1M2]\n#-# Indicates if a synchronization between SOLR index and XWiki database should be performed at startup.\n#-# Synchronization can be started from the search administration UI.\n#-# The default is true.\n# solr.synchronizeAtStartup=false\n\n#-# [Since 12.5RC1]\n#-# Indicates which wiki synchronization to perform when the \"solr.synchronizeAtStartup\" property is set to true.\n#-# Two modes are available:\n#-# - WIKI: indicate that the synchronization is performed when each wiki is accessed for the first time.\n#-# - FARM: indicate that the synchronization is performed once for the full farm when XWiki is started.\n#-# For large farms and in order to spread the machine's indexing load, the WIKI value is recommended, especially if\n#-# some wikis are not used.\n#-# The default is:\n# solr.synchronizeAtStartupMode=FARM\n\n#-------------------------------------------------------------------------------------\n# Security\n#-------------------------------------------------------------------------------------\n\n#-# [Since 5.0M2]\n#-# Define the authorization policies by choosing another implementation of the AuthorizationSettler. This component\n#-# is solely responsible for settling access decisions based on user, target entity and available security rules.\n#-# The identifier provided here is matched with the component role hint.\n#-#\n#-# The default is:\n# security.authorization.settler = default\n\n#-# [Since 13.0]\n#-# Control if document save API should also check the right of the script author when saving a document.\n#-# When false only the current user right is checked.\n#-#\n#-# The default is:\n# security.script.save.checkAuthor = true\n\n#-# [Since 13.10]\n#-# Prevent against DOS attacks by limiting the number of entries returned by DB queries, for guest users.\n#-#\n#-# The default is:\n# security.queryItemsLimit = 100\n\n#-# [Since 13.10.1]\n#-# [Since 14.0RC1]\n#-# Define the lifetime of the token used for resetting passwords in minutes. Note that this value is only used after\n#-# first access.\n#-# Default value is 0 meaning that the token is immediately revoked when first accessed.\n#-# Use a different value if the reset password email link might be accessed several times (e.g. in case of using an\n#-# email link verification system): in such case the user will have the defined lifetime to use again the email link.\n#-#\n#-# The default is:\n# security.authentication.resetPasswordTokenLifetime = 0\n\n#-# [Since 14.6RC1]\n#-# [Since 14.4.3]\n#-# [Since 13.10.8]\n#-# This option is only used when performing a migration from a wiki before the versions mentioned above.\n#-#\n#-# This parameter defines if as part of the migration R140600000XWIKI19869 the passwords of impacted user should be\n#-# reset or not. It's advised to keep this value as true, now for some usecases advertised administrators might want\n#-# their users to keep their passwords nevertheless, then enable the configuration and set it to false before the\n#-# migration is executed.\n# security.migration.R140600000XWIKI19869.resetPassword = true\n\n#-# [Since 14.6RC1]\n#-# [Since 14.4.3]\n#-# [Since 13.10.8]\n#-# This option is only used when performing a migration from a wiki before the versions mentioned above.\n#-#\n#-# This parameter defines if reset password emails should be sent as part of the migration R140600000XWIKI19869.\n#-# By default this value is set to true, so emails will be automatically produced. Now it's possible for admin to set\n#-# this option to false: note that in such case a file containing the list of users for whom a reset password email\n#-# should be sent will still be created in the permanent directory (named 140600000XWIKI19869DataMigration-users.txt).\n#-# If this file exists and this property is set back to true after the migration, the file will still be consumed to\n#-# send the emails, so it's possible to perform the migration and send the emails only later if needed.\n# security.migration.R140600000XWIKI19869.sendResetPasswordEmail = true\n\n#-# [Since 14.6RC1]\n#-# [Since 14.4.3]\n#-# [Since 13.10.8]\n#-# This option is only used when performing a migration from a wiki before the versions mentioned above.\n#-#\n#-# This parameter defines if a security email information should be sent as part of the migration R140600000XWIKI19869.\n#-# By default this value is set to true, so emails will be automatically produced. Now it's possible for admin to set\n#-# this option to false: note that in such case a file containing the list of users for whom a reset password email\n#-# should be sent will still be created in the permanent directory (named 140600000XWIKI19869DataMigration-users.txt).\n#-# If this file exists and this property is set back to true after the migration, the file will still be consumed to\n#-# send the emails, so it's possible to perform the migration and send the emails only later if needed.\n# security.migration.R140600000XWIKI19869.sendSecurityEmail = true\n\n#-------------------------------------------------------------------------------------\n# URL\n#-------------------------------------------------------------------------------------\n\n#-# IMPORTANT: The URL module is a feature still in development and as such should be considered experimental at the\n#-# moment. The configuration parameters below are used only in some part of the code at the moment. The idea is to\n#-# progressively refactor more and more till only the new properties are used. For the moment you should continue to\n#-# use the following old properties located in xwiki.cfg:\n#-# xwiki.virtual.usepath\n#-# xwiki.virtual.usepath.servletpath\n\n#-# [Since 5.1M1]\n#-# The id of the URL format to use. This allows to plug in different implementations and thus allows to completely\n#-# control the format of XWiki URLs.\n#-#\n#-# The default is:\n# url.format=standard\n\n#-# [Since 5.1M1]\n#-# Defines where the wiki part is defined in a URL pointing to a subwiki\n#-# If true then the wiki part is located in the URL path (a.k.a path-based), for example:\n#-# http://server/xwiki/wiki/mywiki/view/Space/Page\n#-# If false then the wiki part is located in the URL host domain (a.k.a domain-based), for example:\n#-# http://mywiki.domain/xwiki/bin/view/Space/Page\n#-#\n#-# The default is:\n# url.standard.multiwiki.isPathBased=true\n\n#-# [Since 5.1M1]\n#-# For path-based setups, this property defines the path segment before the one identifying the subwiki in the URL.\n#-# For example if set to \"thewiki\", then the following URL will point to a subwiki named \"mywiki\":\n#-# http://server/xwiki/thewiki/mywiki/view/Space/Page\n#-# Note that the mapping in web.xml has to be modified accordingly if you don't use the default value:\n#-# <servlet-mapping>\n#-# <servlet-name>action</servlet-name>\n#-# <url-pattern>/wiki/*</url-pattern>\n#-# </servlet-mapping>\n#-#\n#-# The default is:\n# url.standard.multiwiki.wikiPathPrefix=wiki\n\n#-# [Since 5.2M1]\n#-# Defines the URL path prefix used for Entity URLs, i.e. URLs pointing to a Document, Space, Object, etc.\n#-# For example this is the \"bin\" part in the following URL:\n#-# http://server/xwiki/bin/view/space/page\n#-# Note that this replaces the old xwiki.defaultservletpath property in the xwiki.cfg file.\n#-#\n#-# The default is:\n# url.standard.entityPathPrefix=bin\n\n#-# [Since 5.3M1]\n#-# The action to take when a subwiki is not found (ie there's no wiki descriptor for it). Valid values are:\n#-# - redirect_to_main_wiki: default to displaying the main wiki\n#-# - display_error: redirect to a vm to display an error\n#-#\n#-# The default is:\n# url.standard.multiwiki.notFoundBehavior=redirect_to_main_wiki\n\n#-# [Since 7.2M1]\n#-# Whether the \"view\" action is omitted in URLs (in order to have shorter URLs).\n#-# Note that this replaces the old xwiki.showviewaction property in the xwiki.cfg file.\n#-#\n#-# The default is:\n# url.standard.hideViewAction=false\n\n#-# [Since 11.1RC1]\n#-# Whether a the last modified date of the file to be loaded should be checked and put in the URL query parameter.\n#-# Disabling this might improve a bit the performance on some old hard drives, or custom filesystem, however\n#-# it might imply the need to force-reload some resources in the browser, when migrating.\n#-#\n#-# The default is:\n# url.useResourceLastModificationDate=true\n\n#-# [Since 13.3RC1]\n#-# [Since 12.10.7]\n#-# Define a list of trusted domains that can be used in the wiki for performing requests or redirections even if\n#-# the wiki does not use it. Domains are listed without http and separated with a comma in the list. Subdomains can be\n#-# specified.\n#-# Example of accepted value: foo.acme.org,enterprise.org\n#-#\n#-# By default the list of trusted domains is empty:\n# url.trustedDomains=\n\n#-# [Since 13.3RC1]\n#-# [Since 12.10.7]\n#-# Allow to enable or disable checks performed on domains by taking into account the list of trusted domains.\n#-# Disable this property only if you experienced some issues on your wiki: some security check won't be performed when\n#-# this property is set to false.\n#-#\n#-# By default this property is set to true:\n# url.trustedDomainsEnabled=true\n\n#-------------------------------------------------------------------------------------\n# Attachment\n#-------------------------------------------------------------------------------------\n\n#-# [Since 5.2M2]\n#-# Define the kind of attachment that can be displayed inline. You can either choose to do it through a whitelist\n#-# (only the mimetypes defined in this list would be displayed inline) or a blacklist (every mimetype that is not in\n#-# this list would be displayed inline if possible).\n#-# Note that only one configuration is used between the whitelist and the blacklist, and the whitelist always have\n#-# the priority over the blacklist. Also note that these configurations exist for security reason so they are only\n#-# impacting attachments added by users who do not have programming rights.\n#-# If you want to force downloading some attachments types please check the configuration below.\n#-#\n#-# By default we use the following whitelist (coma separated list of values).\n# attachment.download.whitelist=audio/basic,audio/L24,audio/mp4,audio/mpeg,audio/ogg,audio/vorbis,audio/vnd.rn-realaudio,audio/vnd.wave,audio/webm,image/gif,image/jpeg,image/pjpeg,image/png,image/tiff,text/csv,text/plain,text/xml,text/rtf,video/mpeg,video/ogg,video/quicktime,video/webm,video/x-matroska,video/x-ms-wmv,video/x-flv\n#-#\n#-# If you prefer to use a blacklist instead, you can define the forbidden types here, as a coma separated list of\n#-# values. We advise you to forbid at least the following mimetypes : text/html, text/javascript\n# attachment.download.blacklist=text/html,text/javascript\n\n#-# [Since 12.10]\n#-# Define the kind of attachment that you always want to be downloaded and never displayed inline.\n#-# By default this list is empty, but you can specify a list of mime-types (coma separated list of values) which\n#-# should be always downloaded no matter who attached them or what is the whitelist/blacklist configuration.\n#-#\n#-# The distinction with the blacklist configuration above is that the blacklist won't affect file attached by a user\n#-# with programming rights, while this configuration affect any attachment.\n# attachment.download.forceDownload=\n\n#-# [Since 14.10]\n#-# Define the list of allowed attachment mimetypes. By default, this list is empty, but you can specify a \n#-# comma-separated list of allowed mimetypes. Note that a joker (*) can be used to accept any media type. Once this\n#-# list is not empty, any attachment with an unlisted mimetype will be rejected.\n#-# For instance, if you want to only accept plain text and any kind of images, you can set the list to:\n#-# text/plain,image/*\n# attachment.upload.allowList=\n\n#-# [Since 14.10]\n#-# Define the list of blocked attachment mimetypes. By default, this list is empty, but you can specify a \n#-# comma-separated list of blocked mimetypes. Note that a joker (*) can be used to accept any media type. Once this \n#-# list is not empty, any attachment matching one of the listed mimetype will be rejected.\n#-# For instance, if you want to reject plain text and any kind of images, you can set the list to:\n#-# text/plain,image/*\n#-#\n# attachment.upload.blockList=\n\n#-------------------------------------------------------------------------------------\n# Active Installs\n#-------------------------------------------------------------------------------------\n\n#-# [Since 5.2M2]\n#-# The URL of where the Active Installs module should connect to in order to send a ping of activity. This feature\n#-# regularly sends the following information to xwiki.org about the current instance:\n#-# - its unique id\n#-# - the id and versions of all installed extensions)\n#-# The goal is to count the number of active installs of XWiki out there and provide statistics on xwiki.org\n#-#\n#-# The default is:\n# activeinstalls.pingURL=http://extensions.xwiki.org/activeinstalls\n\n#-------------------------------------------------------------------------------------\n# Wikis\n#-------------------------------------------------------------------------------------\n\n#-# [Since 5.4.4]\n#-# Add a default suffix to the alias of a new wiki in the wiki creation wizard, only when the path mode is not used\n#-# (i.e. domain-based, see url.standard.multiwiki.isPathBased). If this value is empty, XWiki will try to compute it\n#-# automatically from the request URL.\n#-#\n#-# eg: if wiki.alias.suffix is \"xwiki.org\" and the wiki name is \"playground\"\n#-# then the computed alias will be: \"playground.xwiki.org\".\n#-#\n#-# The default is:\n# wiki.alias.suffix=\n\n#-# [Since 14.9RC1]\n#-# Allows delegating the database/schema/user creation (depends on the database) for a new wiki to an infra admin.\n#-# Said differently, if the following property is false then XWiki will not create any database/schema/user when\n#-# creating a wiki. It's assumed that it will exist prior to using the Wiki creation wizard.\n#-#\n#-# The default is:\n# wiki.createDatabase = true\n\n#-------------------------------------------------------------------------------------\n# Store\n#-------------------------------------------------------------------------------------\n\n#-# [Since 6.1M2]\n#-# If active, the Filesystem Attachment Store will automatically clear empty directories on startup,\n#-# in some cases this may create undue load on the server and may need to be disabled. To do that,\n#-# set the following to false.\n#-# Note that if you disable this feature, empty directories will accumulate and you are responsible\n#-# for cleaning them up.\n# store.fsattach.cleanOnStartup=true\n\n#-# [Since 11.4RC1]\n#-# The root directory used by the various \"file\" stores implementation (attachment, deleted document, etc.).\n#-# \n#-# The default is ${environment.permanentDirectory}/store/file.\n#if ($xwikiPropertiesStoreFileDirectory)\nstore.file.directory=$xwikiPropertiesStoreFileDirectory\n#else\n# store.file.directory=/var/lib/xwiki/data/store/file/\n#end\n\n#-------------------------------------------------------------------------------------\n# Mail\n#-------------------------------------------------------------------------------------\n\n#-# [Since 6.1M2]\n#-# SMTP host when sending emails, defaults to \"localhost\".\n#-# This configuration property can be overridden in XWikiPreferences objects, by using the \"smtp_server\" property name.\n# mail.sender.host = localhost\n\n#-# [Since 6.1M2]\n#-# SMTP port when sending emails, defaults to 25.\n#-# This configuration property can be overridden in XWikiPreferences objects, by using the \"smtp_port\" property name.\n# mail.sender.port = 25\n\n#-# [Since 6.1M2]\n#-# From email address to use. Not defined by default and needs to be set up when calling the mail API.\n#-# This configuration property can be overridden in XWikiPreferences objects, by using the \"admin_email\" property name.\n# mail.sender.from = john@doe.com\n\n#-# [Since 6.1M2]\n#-# Username to authenticate on the SMTP server, if needed. By default no authentication is performed.\n#-# This configuration property can be overridden in XWikiPreferences objects, by using the \"smtp_server_username\"\n#-# property name.\n# mail.sender.username = someuser\n\n#-# [Since 6.1M2]\n#-# Password to authenticate on the SMTP server, if needed. By default no authentication is performed.\n#-# This configuration property can be overridden in XWikiPreferences objects, by using the \"smtp_server_password\"\n#-# property name.\n# mail.sender.password = somepassword\n\n#-# [Since 6.1M2]\n#-# Extra Java Mail options (see https://javamail.java.net/nonav/docs/api/).\n#-# This configuration property can be overridden in XWikiPreferences objects, by using the \"javamail_extra_props\"\n#-# property name.\n#-# By default the following properties are set automatically:\n#-# mail.transport.protocol = smtp\n#-# mail.smtp.host = <value of the mail.sender.host property>\n#-# mail.smtp.port = <value of the mail.sender.port property>\n#-# mail.smtp.user = <value of the mail.sender.username property>\n#-# mail.smtp.from = <value of the mail.sender.from property>\n#-# Example:\n# mail.sender.properties = mail.smtp.starttls.enable = true\n# mail.sender.properties = mail.smtp.socks.host = someserver\n\n#-# [Since 6.4M2]\n#-# Defines which authorization checks are done when sending mails using the Mail Sender Script Service.\n#-# Example of valid values:\n#-# - \"programmingrights\": the current document must have Programming Rights\n#-# - \"alwaysallow\": no check is performed. This is useful when running XWiki in a secure environment where we\n#-# want to allow all users to be able to send emails through the Script Service.\n#-# The default is:\n# mail.sender.scriptServiceCheckerHint = programmingrights\n\n#-# [Since 6.4M2]\n#-# optional default email addresses to add to the BCC mail header when sending email.\n# mail.sender.bcc = john@doe.com,mary@doe.com\n\n#-# [Since 6.4RC1]\n#-# The delay to wait between each mail being sent, in milliseconds. This is done to support mail throttling and not\n#-# be considered a spammer by mail servers.\n#-# The default is 8 seconds:\n# mail.sender.sendWaitTime = 8000\n\n#-# [Since 6.4.1, 7.0M1]\n#-# When using the Database Mail Listener, whether mail statuses for mails that have been sent successfully must be\n#-# discarded or not. They could be kept for tracability purpose for example.\n#-# The default is:\n# mail.sender.database.discardSuccessStatuses = true\n\n#-# [Since 11.6RC1]\n#-# Max queue size for the prepare mail thread. When the max size is reached, asynchronously sending a mail will block\n#-# till the first mail item in the prepare queue has been processed.\n# mail.sender.prepareQueueCapacity = 1000\n\n#-# [Since 11.6RC1]\n#-# Max queue size for the send mail thread. When the max size is reached, the prepare queue will block till the first\n# mail item in the send queue has been sent.\n# mail.sender.sendQueueCapacity = 1000\n\n#-# [Since 12.9RC1]\n#-# Decides if XWiki should resend automatically mails that were prepared but not sent, at restart.\n#-# Note that emails that were in the prepare_error state are not resent since they failed to be saved and thus cannot\n#-# be resent.\n# mail.sender.database.resendAutomaticallyAtStartup = true\n\n#-------------------------------------------------------------------------------------\n# Debug\n#-------------------------------------------------------------------------------------\n\n#-# [Since 7.0RC1]\n#-# Indicate if web resources should be loaded minified by default.\n#-# It's enabled by default which can make js/css hard to read.\n# debug.minify=false\n\n#-------------------------------------------------------------------------------------\n# LESS CSS\n#-------------------------------------------------------------------------------------\n\n#-# [Since 7.4.2, 8.0M2]\n#-# The number of LESS compilations that can be performed simultaneously. Put a little number if your resources are\n#-# limited.\n#-#\n#-# The default is:\n# lesscss.maximumSimultaneousCompilations = 4\n\n#-# [Since 8.0RC1]\n#-# Generate sourcemaps inline in the CSS files.\n#-#\n#-# The default is:\n# lesscss.generateInlineSourceMaps = false\n\n#-------------------------------------------------------------------------------------\n# Edit\n#-------------------------------------------------------------------------------------\n\n#-# [Since 8.2RC1]\n#-# Indicate the default editor to use for a specific data type.\n#-# The editors are components so they are specified using their role hints.\n#-# Some data types can be edited in multiple ways, by different types of editors.\n#-# Thus you can also indicate the default editor to use from a specific category (editor type).\n#-#\n#-# The format is this:\n#-# edit.defaultEditor.<dataType>=<roleHintOrCategory>\n#-# edit.defaultEditor.<dataType>#<category>=<roleHintOrSubCategory>\n#-#\n#-# The default bindings are:\n# edit.defaultEditor.org.xwiki.rendering.syntax.SyntaxContent=text\n# edit.defaultEditor.org.xwiki.rendering.syntax.SyntaxContent#text=text\n# edit.defaultEditor.org.xwiki.rendering.block.XDOM=text\n# edit.defaultEditor.org.xwiki.rendering.block.XDOM#text=text\n#if ($xwikiPropertiesDefaultWysiwygEditor)\nedit.defaultEditor.org.xwiki.rendering.syntax.SyntaxContent#wysiwyg=$xwikiPropertiesDefaultWysiwygEditor\nedit.defaultEditor.org.xwiki.rendering.block.XDOM#wysiwyg=$xwikiPropertiesDefaultWysiwygEditor\n#end\n\n#-# [Since 11.3.2, 11.6RC1]\n#-# Indicate if the mechanism to detect and handle edition conflicts should be enabled or not.\n#-# If disabled, it means that in case of edition conflicts, the latest save will be always take into account, and\n#-# erase previous data (which can always be recovered in the history of the document).\n#-# This option is provided because the feature is still experimental and it could be useful is some specific usecases\n#-# to switch it off. However it is not recommended to do so.\n#-#\n#-# The default is:\n# edit.conflictChecking.enabled = true\n\n#-# [Since 12.5]\n#-# Indicate if the XWiki documents should be edited in-place, without leaving the view mode, whenever possible (e.g. if\n#-# the default edit mode for that document and the preferred editor both support in-place editing). When enabled,\n#-# clicking on the document Edit button makes the document title and content editable in-place, without leaving the\n#-# view mode. When disabled, clicking on the document Edit button loads the default (stand-alone) edit mode for that\n#-# document.\n#-#\n#-# The default is:\n# edit.document.inPlaceEditing.enabled = true\n\n#-------------------------------------------------------------------------------------\n# Notifications\n#-------------------------------------------------------------------------------------\n\n#-# [Since 9.4RC1]\n#-# Indicates if the notifications module should be enabled on the platform.\n#-#\n#-# The default is :\n# notifications.enabled = true\n\n#-# [Since 9.5C1]\n#-# Indicates if the notifications module can send emails.\n#-#\n#-# The default is :\n# notifications.emails.enabled = true\n\n#-# [Since 9.6RC1]\n#-# Indicate the grace time used when sending live email notifications.\n#-# When an event is triggered in the wiki (for example, a document update), the platform will wait X minutes\n#-# before sending live notifications emails. During this time, if events of the same kind are recieved, they will\n#-# be grouped in the same email.\n#-#\n#-# The grace time define the period (in minutes) for which the platform should wait before sending a notification\n#-# mail after an event.\n#-#\n#-# The default is :\n# notifications.emails.live.graceTime = 10\n\n#-# [Since 9.8RC1]\n#-# Indicate if the \"watched entities\" feature is enabled on the platform.\n#-# This feature mimics what the \"Watchlist Application\" does so it may not be a good idea to have both on the platform.\n#-#\n#-# The default is :\n# notifications.watchedEntities.enabled = true\n\n#-# [Since 9.11.8]\n#-# [Since 10.6RC1]\n#-# The automatic watch mode used by default. The value can be changed by an administrator in the wiki's administration\n#-# or by any user in their own settings.\n#-#\n#-# Possible values:\n#-#\n#-# - none: no page is added to the list of watched pages automatically.\n#-# - all: everytime a user makes a change on a page, it is automatically added to her list of watched pages.\n#-# - major: everytime a user makes a major change on a page, it is automatically added to her list of watched pages.\n#-# - new: only pages created by a user are added to her list of watched pages.\n#-#\n#-# The default is :\n# notifications.watchedEntities.autoWatch = major\n\n#-# [Since 10.11.4]\n#-# [Since 11.2]\n#-# The search for notifications in the REST API is done trough a thread pool to limit the impact on the rest of the\n#-# XWiki instance.\n#-# This properties controls the size of this pool. Any number lower than 1 disable the thread pool system.\n#-# \n#-# The default is :\n# notifications.rest.poolSize = 2\n\n#-# [Since 10.11.8]\n#-# [Since 11.3]\n#-# Enable or disable caching of notification search result in the REST or async API.\n#-# \n#-# The default is :\n# notifications.rest.cache = true\n\n#-# [Since 12.6]\n#-# When this option is enabled the relation between users and events is evaluated and stored when the events are\n#-# generated instead of each time they are displayed.\n#-# /!\\ The capability to disable the prefiltering is now deprecated and will be removed probably in 15.x XWiki cycle.\n#-#\n#-# The default is :\n# notifications.eventPrefilteringEnabled = true\n\n#-# [Since 12.5RC1]\n#-# The async notifications renderer is using a dedicated thread pool to limit the impact on the rest of the XWiki\n#-# instance.\n#-# This properties controls the size of this pool.\n#-#\n#-# The default is :\n# notifications.async.poolSize = 2\n\n#-------------------------------------------------------------------------------------\n# Mentions\n#-------------------------------------------------------------------------------------\n\n#-# [Since 12.6]\n#-# The detection and notification for the user mentions in created or edited content is done asynchronously \n#-# by a pool of consumers.\n#-# This properties controls the size of this pool.\n#-#\n#-# The default is :\n# mentions.poolSize = 1\n\n#-------------------------------------------------------------------------------------\n# Event Stream\n#-------------------------------------------------------------------------------------\n\n#-# [Since 11.1RC1]\n#-# The Event Stream, on which the notifications are based, stores data in a dedicated table in the database.\n#-# Each wiki has its own database. The feature can be configured to store its data into the database corresponding to\n#-# the wiki, into the main database (default: xwiki) or both. These options should not be both set to false (in this\n#-# case the local store will be forced).\n#-# Important note: disabling storage in the main store will prevent the notifications from retrieving events from\n#-# subwikis. Moreover, disabling the main store makes impossible for global users to watch pages from subwikis. It is\n#-# not recommended.\n#-#\n#-# This feature was originally implemented by the Activity Stream plugin so these settings might sound familiar to you.\n#-#\n#-# The default is :\n# eventstream.uselocalstore = true\n#-#\n#-# The default is :\n# eventstream.usemainstore = true\n#-#\n#-# Number of days the events should be kept (0 or any negative value: infinite duration)\n#-# Note: if this value is greater than 0 a scheduler job will be created, this job will then be fired every week to\n#-# delete events older than the configured value.\n#-# The default is :\n# eventstream.daystokeepevents = 0\n\n#-# [Since 12.4]\n#-# Indicate if the new no-SQL oriented event store is enabled. Once enabled the Solr implementation is\n#-# used by default but other implementations (like a MongoDB based implementation for example) might come later.\n#-# It's enabled by default since 12.6.\n# eventstream.store.enabled = true\n#-# Indicate which event store implementation to use. The default implementation is based on Solr.\n# eventstream.store = solr\n\n#-------------------------------------------------------------------------------------\n# Logging\n#-------------------------------------------------------------------------------------\n\n#-# [Since 12.0RC1]\n#-# Indicate of logging about the use of deprecated APIs/feature should be enabled.\n#-# \n#-# The default is:\n# logging.deprecated.enabled = true\n\n#-------------------------------------------------------------------------------------\n# User\n#-------------------------------------------------------------------------------------\n\n#-# [Since 12.2]\n#-# Indicate where users are stored.\n#-#\n#-# The default (and currently the only supported option) is:\n# user.store.hint = document\n\n#-# [Since 12.2]\n#-# Define preferences for the SuperAdmin user.\n#-#\n#-# The format is:\n#-# user.preferences.superadmin.<preference name> = <value>\n#-#\n#-# Values set by default for the SuperAdmin user:\n#-# user.preferences.superadmin.displayHiddenDocuments = 1\n#-# user.preferences.superadmin.active = 1\n#-# user.preferences.superadmin.first_name = SuperAdmin\n#-# user.preferences.superadmin.email_checked = 1\n#-# user.preferences.superadmin.usertype = Advanced\n#-#\n#-# Examples:\n#-# user.preferences.superadmin.displayHiddenDocuments = 0\n#-# user.preferences.superadmin.editor = Text\n\n#-# [Since 12.2]\n#-# Define preferences for the Guest user.\n#-#\n#-# The format is:\n#-# user.preferences.guest.<preference name> = <value>\n#-#\n#-# Values set by default for the Guest user:\n#-# user.preferences.guest.displayHiddenDocuments = 0\n#-# user.preferences.guest.active = 0\n#-# user.preferences.guest.first_name = Guest\n#-# user.preferences.guest.email_checked = 0\n#-#\n#-# Examples:\n#-# user.preferences.guest.displayHiddenDocuments = 1\n#-# user.preferences.guest.editor = Text\n\n#-------------------------------------------------------------------------------------\n# Refactoring\n#-------------------------------------------------------------------------------------\n\n#-# [Since 12.5RC1]\n#-# Indicate to use the old implementation based on copy and delete, or the new implementation using an atomic operation\n#-# on database (which is a lot more performant).\n#-# This property aims at being removed in the future once the atomic rename has been validated to work 100% fine.\n#-# Only use it if you are facing an issue with the move/rename features.\n#-#\n#-# The default value is:\n# refactoring.rename.useAtomicRename = true\n\n#-# [Since 12.9RC1]\n#-# Indicates whether skipping the recycle bin when deleting pages is allowed for Advanced users.\n#-# It is disabled by default.\n#-# This setting is only used if the wiki has a recycle bin activated (xwiki.recyclebin=1 in xwiki.cfg).\n#-# This setting can be overloaded:\n#-# * By the main wiki in the Refactoring.Code.RefactoringConfigurationClass class of the\n#-# Refactoring.Code.RefactoringConfiguration document of the main wiki.\n#-# * By sub-wikis in the Refactoring.Code.RefactoringConfigurationClass class of the\n#-# Refactoring.Code.RefactoringConfiguration document of the sub-wikis (itself overloading the main wiki's\n#-# configuration).\n#-#\n#-# The default value is:\n# refactoring.isRecycleBinSkippingActivated = false\n\n#-------------------------------------------------------------------------------------\n# Skin Extensions\n#-------------------------------------------------------------------------------------\n\n#-# [Since 12.7.1, 12.8RC1]\n#-# Indicate whether the JavaScript skin extensions should be parsed and minified using strict mode. When strict mode is\n#-# enabled:\n#-# * the JavaScript minification may fail if the code is poorly written. See\n#-# https://github.com/google/closure-compiler/wiki/Warnings for a list of errors that may occur. When this happens\n#-# XWiki uses the original (unminified) JavaScript source as a fall-back and logs some error messages that indicate\n#-# how the bad code can be fixed.\n#-# * the minified JavaScript includes the \"use strict;\" statement which means the code may fail at runtime if it doesn't\n#-# follow the ECMAScript strict rules. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode\n#-#\n#-# The default value is:\n# skinx.jsStrictModeEnabled = false\n\n$!xwikiPropertiesAdditionalProperties\n\n", "input": "", "output": "【漏洞类型】权限控制漏洞 \n【严重性】高\n\n【漏洞摘要】 \n原始代码中存在权限控制漏洞,具体表现为`registerTranslationBundle`方法在处理`Scope.USER`类型的翻译文档时,没有对用户进行足够的权限检查。这个方法允许任何用户创建或更新具有用户作用域的翻译文档,而无需验证用户是否具有`SCRIPT`权限。这可能导致未授权的用户能够修改其他用户的翻译配置,从而影响系统的安全性和稳定性。\n\n【关键代码模式】 \n- `registerTranslationBundle`方法中对`Scope.USER`类型的处理:\n ```java\n case USER:\n this.authorizationManager.checkAccess(Right.ADMIN, document.getAuthorReference(), document.getDocumentReference());\n break;\n ```\n- 相关的配置属性:\n ```properties\n # localization.wiki.restrictUserTranslations = true\n ```\n\n【修复方法说明】 \n修复方法通过引入新的配置选项`localization.wiki.restrictUserTranslations`,并在`checkRegistrationAuthorization`方法中添加对`SCRIPT`权限的检查来解决问题。这样,只有在配置允许的情况下,具有`SCRIPT`权限的用户才能创建或更新具有用户作用域的翻译文档,从而增强系统的权限控制。\n\n【最佳实践或安全编码技术】 \n- 使用配置文件来动态控制安全策略,使得安全设置更加灵活和可管理。\n- 在处理用户输入或用户操作时,始终进行适当的权限验证,确保用户只能访问和修改他们被授权的资源。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nxwiki-platform-core/xwiki-platform-skin/xwiki-platform-skin-ui/pom.xml\n### 代码内容:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n-->\n\n<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/maven-v4_0_0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n <parent>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-skin</artifactId>\n <version>15.0-SNAPSHOT</version>\n </parent>\n <artifactId>xwiki-platform-skin-ui</artifactId>\n <name>XWiki Platform - Skin - UI</name>\n <description>Create, edit and manage Skins</description>\n <packaging>xar</packaging>\n <properties>\n <!-- Name to display by the Extension Manager -->\n <xwiki.extension.name>Skin Application</xwiki.extension.name>\n </properties>\n <dependencies>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-localization-script</artifactId>\n <version>${project.version}</version>\n <scope>runtime</scope>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-rendering-macro-velocity</artifactId>\n <version>${project.version}</version>\n <scope>runtime</scope>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-csrf</artifactId>\n <version>${project.version}</version>\n <scope>runtime</scope>\n </dependency>\n <dependency>\n <groupId>org.xwiki.rendering</groupId>\n <artifactId>xwiki-rendering-macro-html</artifactId>\n <version>${rendering.version}</version>\n <scope>runtime</scope>\n </dependency>\n </dependencies>\n\n <build>\n <plugins>\n <plugin>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-tool-xar-plugin</artifactId>\n <configuration>\n <entries>\n <entry>\n <document>XWiki.DefaultSkin</document>\n <type>demo</type>\n </entry>\n </entries>\n </configuration>\n </plugin>\n </plugins>\n </build>\n</project>\n\n## 内容2\n### 文件路径:\nxwiki-platform-core/xwiki-platform-skin/xwiki-platform-skin-ui/src/main/resources/SkinsCode/XWikiSkinsSheet.xml\n### 代码内容:\n<?xml version=\"1.1\" encoding=\"UTF-8\"?>\n\n<!--\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n-->\n\n<xwikidoc version=\"1.1\">\n <web>SkinsCode</web>\n <name>XWikiSkinsSheet</name>\n <language/>\n <defaultLanguage/>\n <translation>0</translation>\n <creator>xwiki:XWiki.Admin</creator>\n <parent>XWiki.XWikiSkins</parent>\n <author>xwiki:XWiki.Admin</author>\n <contentAuthor>xwiki:XWiki.Admin</contentAuthor>\n <version>1.1</version>\n <title>#set ($docTitle = $doc.getObject('XWiki.XWikiSkins').getValue('name').trim())#if (\"$!docTitle\" == '')$services.localization.render('xe.admin.skin')#else${docTitle}#end</title>\n <comment/>\n <minorEdit>false</minorEdit>\n <syntaxId>xwiki/2.1</syntaxId>\n <hidden>true</hidden>\n <content>{{velocity}}\n#####################################\n## XWikiSkins Sheet\n#####################################\n#set($discard = $xwiki.ssx.use('SkinsCode.XWikiSkinsSheet'))\n#set($discard = $xwiki.jsx.use('SkinsCode.XWikiSkinsSheet'))\n#set($discard = $services.localization.use('document', 'SkinsCode.Translations'))\n#####################\n## CONTROLLER\n#####################\n## Keep testing the inline action for backward compatibility with existing skin documents.\n#if ($xcontext.action == 'edit' || $xcontext.action == 'inline')\n #edit()\n#elseif ($xcontext.action == 'get')\n #if($request.action == 'newObj')\n #newObj()\n #elseif($request.action == 'displayObj')\n #set($obj = $doc.getObject('XWiki.XWikiSkinFileOverrideClass', $numbertool.toNumber($request.objNumber).intValue()))\n #displayOverrideObject($obj)\n #elseif($request.action == 'deleteObj')\n #deleteObj()\n #end\n#else\n #view()\n#end\n#####################\n## VIEW\n#####################\n#macro(view)\n ## Call getDefaultEditMode() for backward compatibility with existing skins.\n * [[{{translation key=\"xe.admin.skin.editskin\"/}}>>path:${doc.getURL($doc.getDefaultEditMode())}]]\n * [[{{translation key=\"xe.admin.skin.testskin\"/}}>>${services.model.resolveDocument('', 'default', $doc.documentReference.extractReference('WIKI'))}||queryString=\"skin=${doc.fullName}\"]]\n#end\n#####################\n## EDIT\n#####################\n#macro(edit)\n == {{translation key=\"skin.sheet.properties\" /}} ==\n ## Display the class properties\n #set ($deprecatedProperties = [])\n #set ($className = 'XWiki.XWikiSkins')\n #set ($class = $doc.getObject($className).xWikiClass)\n #foreach ($prop in $class.properties)\n #if ($prop.classType != 'TextArea')\n ; $prop.prettyName\n : #if($prop.name == 'logo'){{attachmentSelector classname=\"${className}\" property=\"${prop.name}\" filter=\"png,jpg,gif,svg\" displayImage=\"true\"/}}#else $doc.display($prop.name)#end\n \n #else\n #set($discard = $deprecatedProperties.add($prop))\n #end\n #end\n (% id=\"overrideSection\" %)\n (((\n == {{translation key=\"skin.sheet.overriddenTemplates\" /}} ==\n \n {{warning}}\n {{translation key=\"skin.sheet.overrideTemplateWarning\" /}}\n {{/warning}}\n \n {{html}}\n <noscript>\n <div class=\"box errormessage\">\n $services.localization.render('skin.sheet.nojs', [\"<a href='$doc.getURL('edit', 'editor=object')'>\", '</a>', 'XWiki.XWikiSkinFileOverrideClass'])\n </div>\n </noscript>\n {{/html}}\n \n {{html}}\n <div id=\"addOverride\">\n <label for=\"newPath\" class=\"newPathLabel\">$services.icon.renderHTML('add') $services.localization.render(\"skin.sheet.addTemplate\")</label>\n <input placeholder=\"file.vm\" id=\"newPath\" name=\"newPath\" value=\"file.vm\" size=\"20\" class=\"withTip\" type=\"text\" />\n <button class=\"btn btn-primary\" id=\"newPathAdd\">$services.localization.render(\"skin.sheet.add\")</button>\n </div>\n {{/html}}\n ## Display the XWiki.XWikiSkinFileOverrideClass objects\n #foreach ($obj in $doc.getObjects('XWiki.XWikiSkinFileOverrideClass'))\n #displayOverrideObject($obj)\n #end\n )))\n \n #if ($deprecatedProperties.size() > 0)\n (% class=\"deprecatedProperties\" %)\n (((\n == {{translation key=\"skin.sheet.deprecatedProperties\" /}} ==\n \n {{warning}}\n {{translation key=\"skin.sheet.deprecatedPropertiesWarning\" /}}\n {{/warning}}\n \n #foreach ($prop in $deprecatedProperties)\n #if ($prop.classType == 'TextArea')\n ; $services.icon.render('file-white') $prop.prettyName\n : $doc.display($prop.name)\n \n #end\n #end\n )))\n #end\n#end\n###########################\n## DISPLAY OVERRIDE OBJ\n###########################\n#macro(displayOverrideObject $obj)\n (% class=\"overrideObject\" %)\n (((\n (% class=\"overrideTitle\" %)\n === $services.icon.render('file-white') $obj.display('path', 'view') ===\n (% class=\"overrideObjectDelete\" %)\n (((\n {{html}}<button class=\"btn btn-default btn-xs\">$services.icon.renderHTML('cross')</button>{{/html}}\n )))\n (% class=\"overrideProperties hidden\" %)\n (((\n #set ($className = 'XWiki.XWikiSkinFileOverrideClass')\n #set ($class = $doc.getObject($className).xWikiClass)\n #foreach ($prop in $class.properties)\n ; $prop.prettyName\n : $obj.display($prop.name, 'edit')\n \n #end\n )))\n ----\n )))\n#end\n###########################\n## CHECK RIGHTS\n###########################\n#macro(checkRights $response)\n #if(!$services.csrf.isTokenValid($request.form_token))\n #set($discard = $response.put('status', 'fail'))\n #set($discard = $response.put('cause', 'form_token'))\n #elseif(!$xwiki.hasAccessLevel('edit', $xcontext.user, $doc.fullname))\n #set($discard = $response.put('status', 'fail'))\n #set($discard = $response.put('cause', 'rights'))\n #end\n#end\n###########################\n## NEW OBJ\n###########################\n#macro(newObj)\n #set($response = {})\n #checkRights($response)\n #if(\"$!response.get('status')\" != 'fail')\n #set($obj = $doc.newObject('XWiki.XWikiSkinFileOverrideClass'))\n #set($discard = $obj.set('path', $request.path))\n #set($discard = $doc.save('Add a new XWiki.XWikiSkinFileOverrideClass object'))\n #set($discard = $response.put('objNumber', $obj.number))\n #set($discard = $response.put('status', 'success'))\n #end\n $jsontool.serialize($response)\n#end\n###########################\n## DELETE OBJ\n###########################\n#macro(deleteObj)\n #set($response = {})\n #checkRights($response)\n #if(\"$!response.get('status')\" != 'fail')\n #set($objNumber = $numbertool.toNumber($request.objNumber).intValue())\n #set($obj = $doc.getObject('XWiki.XWikiSkinFileOverrideClass', $objNumber))\n #set($discard = $doc.removeObject($obj))\n #set($saveMessage = \"Remove the object XWiki.XWikiSkinFileOverrideClass[\" + $obj.getValue('path') + \"]\")\n #set($discard = $doc.save($saveMessage))\n #set($discard = $response.put('status', 'success'))\n #end\n $jsontool.serialize($response)\n#end\n{{/velocity}}\n</content>\n <object>\n <name>SkinsCode.XWikiSkinsSheet</name>\n <number>0</number>\n <className>XWiki.JavaScriptExtension</className>\n <guid>e262ce20-f28a-4fa0-89e2-c8413e10dce4</guid>\n <class>\n <name>XWiki.JavaScriptExtension</name>\n <customClass/>\n <customMapping/>\n <defaultViewSheet/>\n <defaultEditSheet/>\n <defaultWeb/>\n <nameField/>\n <validationScript/>\n <cache>\n <cache>0</cache>\n <disabled>0</disabled>\n <displayType>select</displayType>\n <multiSelect>0</multiSelect>\n <name>cache</name>\n <number>5</number>\n <prettyName>Caching policy</prettyName>\n <relationalStorage>0</relationalStorage>\n <separator> </separator>\n <separators>|, </separators>\n <size>1</size>\n <unmodifiable>0</unmodifiable>\n <values>long|short|default|forbid</values>\n <classType>com.xpn.xwiki.objects.classes.StaticListClass</classType>\n </cache>\n <code>\n <disabled>0</disabled>\n <name>code</name>\n <number>2</number>\n <prettyName>Code</prettyName>\n <rows>20</rows>\n <size>50</size>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.TextAreaClass</classType>\n </code>\n <name>\n <disabled>0</disabled>\n <name>name</name>\n <number>1</number>\n <prettyName>Name</prettyName>\n <size>30</size>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.StringClass</classType>\n </name>\n <parse>\n <disabled>0</disabled>\n <displayFormType>select</displayFormType>\n <displayType>yesno</displayType>\n <name>parse</name>\n <number>4</number>\n <prettyName>Parse content</prettyName>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.BooleanClass</classType>\n </parse>\n <use>\n <cache>0</cache>\n <disabled>0</disabled>\n <displayType>select</displayType>\n <multiSelect>0</multiSelect>\n <name>use</name>\n <number>3</number>\n <prettyName>Use this extension</prettyName>\n <relationalStorage>0</relationalStorage>\n <separator> </separator>\n <separators>|, </separators>\n <size>1</size>\n <unmodifiable>0</unmodifiable>\n <values>currentPage|onDemand|always</values>\n <classType>com.xpn.xwiki.objects.classes.StaticListClass</classType>\n </use>\n </class>\n <property>\n <cache>long</cache>\n </property>\n <property>\n <code>#set($discard = $services.localization.use('document', 'SkinsCode.Translations'))\nrequire(['jquery', 'xwiki-meta'], function($, xm) {\n\n // The global variable that will hold the notification widget (wo we can use it in different functions without adding\n // it to the parameters of these functions)\n var notif = 0;\n\n /**\n * Function called when the user clicks on the 'delete' button of an object\n */\n var onDeleteObj = function() {\n var obj = $(this).parents('.overrideObject');\n // Getting the object number via the name of an input (eg: XWiki.XWikiSkinFileOverrideClass_3_path -> 3).\n var objNumber = obj.find('input').prop('name').split('_')[1];\n // Called when the user confirm the deletion\n var deleteObjOnConfirm = function() {\n // Perform asynchronously the deletion\n notif = new XWiki.widgets.Notification('$escapetool.javascript($services.localization.render('skin.sheet.js.deleting'))', 'inprogress');\n $.ajax({\n 'url' : XWiki.currentDocument.getURL('get'),\n 'type' : 'POST',\n 'dataType' : 'json',\n 'data' : {\n 'action' : 'deleteObj',\n 'objNumber' : objNumber ,\n 'form_token' : xm.form_token,\n 'outputSyntax': 'plain'\n }\n }).then(data => {\n if (data.status === 'success') {\n obj.fadeOut();\n xm.refreshVersion();\n notif.replace(new XWiki.widgets.Notification(\n '$escapetool.javascript($services.localization.render('skin.sheet.js.deletingDone'))', 'done'));\n } else if (data.status === 'fail') {\n if (data.cause === 'form_token') {\n notif.replace(new XWiki.widgets.Notification(\n '$escapetool.javascript($services.localization.render('skin.sheet.js.error.csrf'))', 'error'));\n } else if (data.cause === 'rights') {\n notif.replace(new XWiki.widgets.Notification(\n '$escapetool.javascript($services.localization.render('skin.sheet.js.error.rights'))', 'error'));\n }\n }\n });\n }\n\n var confirmationBoxParams = {\n confirmationText: '$escapetool.javascript($services.localization.render('skin.sheet.js.deleteConfirmationText'))',\n yesButtonText: '$escapetool.javascript($services.localization.render('skin.sheet.js.deleteConfirmationYes'))',\n noButtonText: '$escapetool.javascript($services.localization.render('skin.sheet.js.deleteConfirmationNo'))'\n };\n\n // Display a confirmation box\n new XWiki.widgets.ConfirmationBox({onYes: deleteObjOnConfirm}, confirmationBoxParams);\n\n // Do not perform the default action bound to the <button> element\n return false;\n }\n\n /**\n * Function called when an override object is clicked.\n * It shows/hides the properties of the object.\n */\n var objTitleClicked = function() {\n var properties = $(this).parent().find('.overrideProperties');\n if (properties.hasClass('hidden')) {\n // It is the first time we open the object\n // So we remove our own 'hidden' property\n properties.removeClass('hidden');\n // Then we hide the object via jQuery\n properties.hide();\n // So now we can make it slide (not possible if it were not hidden by jQuery first)\n properties.slideDown();\n } else {\n // The other time, we just use the toggle function\n properties.slideToggle();\n }\n }\n\n /**\n * Function that performs an ajax request to get the code that displays an object and inject it in the page.\n */\n var displayObj = function(objNumber) {\n $.ajax({\n 'url' : XWiki.currentDocument.getURL('get'),\n 'type' : 'GET',\n 'data' : {\n 'action' : 'displayObj',\n 'objNumber' : objNumber\n }\n }).then(data => {\n notif.replace(new XWiki.widgets.Notification(\n '$escapetool.javascript($services.localization.render('skin.sheet.js.addingDone'))', 'done'));\n // Add the html code into the document\n $('#overrideSection').append(data);\n // Get the new object\n var newObj = $('.overrideObject').last();\n // Bind the events of the new objects\n newObj.find('.overrideTitle').on('click', objTitleClicked);\n newObj.find('button').on('click', onDeleteObj);\n // Show the new object\n // First we remove our own 'hidden' class\n newObj.find('.overrideProperties').removeClass('hidden');\n // Then we hide the object via the jQuery API\n newObj.hide();\n // So now we can make it fade (not possible if it were not hidden by jQuery first)\n newObj.fadeIn();\n // Scroll to the element\n $('html, body').animate({scrollTop: newObj.offset().top}, 500);\n });\n }\n\n /**\n * Function called when the user add a new override object\n */\n $('#newPathAdd').on('click', function() {\n $.ajax({\n 'url' : XWiki.currentDocument.getURL('get'),\n 'type' : 'POST',\n 'dataType' : 'json',\n 'data' : {\n 'action' : 'newObj',\n 'path' : $('#newPath').val(),\n 'form_token' : xm.form_token,\n 'outputSyntax': 'plain'\n }\n }).then(data => {\n notif = new XWiki.widgets.Notification(\n '$escapetool.javascript($services.localization.render('skin.sheet.js.adding'))', 'inprogress');\n if (data.status === 'success') {\n xm.refreshVersion();\n displayObj(data.objNumber);\n } else if (data.status === 'fail') {\n if (data.cause === 'form_token') {\n notif.replace(new XWiki.widgets.Notification(\n '$escapetool.javascript($services.localization.render('skin.sheet.js.error.csrf'))', 'error'));\n } else if (data.cause === 'rights') {\n notif.replace(new XWiki.widgets.Notification(\n '$escapetool.javascript($services.localization.render('skin.sheet.js.error.rights'))', 'error'));\n }\n }\n });\n\n // Do not perform the default action bound to the <button> element\n return false;\n });\n\n var onDeprecatedPropertyClicked = function() {\n $('.deprecatedProperties > dl').slideToggle();\n }\n\n /**\n * Function that initialize the javascript events\n */\n var init = function() {\n // Bind objTitleClicked to the existing objects\n $('.overrideTitle').on('click', objTitleClicked);\n // Bind onDeleteObj to the existing objects\n $('.overrideObject button').on('click', onDeleteObj);\n $('.deprecatedProperties > dl').hide();\n $('.deprecatedProperties > h2').on('click', onDeprecatedPropertyClicked);\n }\n \n init();\n\n});\n</code>\n </property>\n <property>\n <name>JS</name>\n </property>\n <property>\n <parse>1</parse>\n </property>\n <property>\n <use>currentPage</use>\n </property>\n </object>\n <object>\n <name>SkinsCode.XWikiSkinsSheet</name>\n <number>0</number>\n <className>XWiki.StyleSheetExtension</className>\n <guid>1e4faabf-d1ba-4fff-806a-2b88c6688e92</guid>\n <class>\n <name>XWiki.StyleSheetExtension</name>\n <customClass/>\n <customMapping/>\n <defaultViewSheet/>\n <defaultEditSheet/>\n <defaultWeb/>\n <nameField/>\n <validationScript/>\n <cache>\n <cache>0</cache>\n <disabled>0</disabled>\n <displayType>select</displayType>\n <multiSelect>0</multiSelect>\n <name>cache</name>\n <number>5</number>\n <prettyName>Caching policy</prettyName>\n <relationalStorage>0</relationalStorage>\n <separator> </separator>\n <separators>|, </separators>\n <size>1</size>\n <unmodifiable>0</unmodifiable>\n <values>long|short|default|forbid</values>\n <classType>com.xpn.xwiki.objects.classes.StaticListClass</classType>\n </cache>\n <code>\n <disabled>0</disabled>\n <name>code</name>\n <number>2</number>\n <prettyName>Code</prettyName>\n <rows>20</rows>\n <size>50</size>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.TextAreaClass</classType>\n </code>\n <contentType>\n <cache>0</cache>\n <disabled>0</disabled>\n <displayType>select</displayType>\n <multiSelect>0</multiSelect>\n <name>contentType</name>\n <number>6</number>\n <prettyName>Content Type</prettyName>\n <relationalStorage>0</relationalStorage>\n <separator> </separator>\n <separators>|, </separators>\n <size>1</size>\n <unmodifiable>0</unmodifiable>\n <values>CSS|LESS</values>\n <classType>com.xpn.xwiki.objects.classes.StaticListClass</classType>\n </contentType>\n <name>\n <disabled>0</disabled>\n <name>name</name>\n <number>1</number>\n <prettyName>Name</prettyName>\n <size>30</size>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.StringClass</classType>\n </name>\n <parse>\n <disabled>0</disabled>\n <displayFormType>select</displayFormType>\n <displayType>yesno</displayType>\n <name>parse</name>\n <number>4</number>\n <prettyName>Parse content</prettyName>\n <unmodifiable>0</unmodifiable>\n <classType>com.xpn.xwiki.objects.classes.BooleanClass</classType>\n </parse>\n <use>\n <cache>0</cache>\n <disabled>0</disabled>\n <displayType>select</displayType>\n <multiSelect>0</multiSelect>\n <name>use</name>\n <number>3</number>\n <prettyName>Use this extension</prettyName>\n <relationalStorage>0</relationalStorage>\n <separator> </separator>\n <separators>|, </separators>\n <size>1</size>\n <unmodifiable>0</unmodifiable>\n <values>currentPage|onDemand|always</values>\n <classType>com.xpn.xwiki.objects.classes.StaticListClass</classType>\n </use>\n </class>\n <property>\n <cache>long</cache>\n </property>\n <property>\n <code>#template('colorThemeInit.vm')\n\n.overrideTitle:hover, .deprecatedProperties h2:hover{\n background-color: $theme.highlightColor;\n cursor: pointer;\n}\n\n.overrideProperties {\n margin-left: 25px;\n}\n\n.overrideObjectDelete {\n position: relative;\n text-align: right;\n margin-top: -40px;\n float: right;\n font-size: 130%;\n}\n\n.skin-colibri .overrideObjectDelete {\n margin-top: -55px;\n}\n\n.newPathLabel {\n color: $theme.linkColor;\n display: inline;\n font-size: 100%;\n font-style: italic;\n font-weight: normal;\n margin: 0px;\n text-transform: none;\n}\n</code>\n </property>\n <property>\n <contentType>CSS</contentType>\n </property>\n <property>\n <name>CSS</name>\n </property>\n <property>\n <parse>1</parse>\n </property>\n <property>\n <use>onDemand</use>\n </property>\n </object>\n</xwikidoc>\n\n", "input": "", "output": "【漏洞类型】路径遍历漏洞\n【严重性】High\n【漏洞摘要】此代码中存在路径遍历漏洞,主要由于未对用户输入的路径进行适当的验证和转义。具体表现为在`newObj`和`deleteObj`宏中,用户提交的`path`参数未经过滤直接用于创建和删除对象。这可能导致攻击者利用此漏洞访问或修改未经授权的文件,以执行恶意操作。\n【关键代码模式】`newObj`、`deleteObj`宏中对`path`参数的直接使用。\n【修复方法说明】修复方法通过使用`$services.rendering.escape`函数对用户输入的路径进行转义,以防止路径遍历攻击。此修复方法应用了适当的输入验证和转义最佳实践,以确保用户输入不会被用于执行未经授权的操作。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nlib/util/extract.js\n### 代码内容:\nvar path = require('path');\nvar fs = require('./fs');\nvar zlib = require('zlib');\nvar DecompressZip = require('decompress-zip');\nvar tar = require('tar-fs');\nvar Q = require('q');\nvar mout = require('mout');\nvar junk = require('junk');\nvar createError = require('./createError');\nvar createWriteStream = require('fs-write-stream-atomic');\nvar destroy = require('destroy');\nvar tmp = require('tmp');\n\n// This forces the default chunk size to something small in an attempt\n// to avoid issue #314\nzlib.Z_DEFAULT_CHUNK = 1024 * 8;\n\nvar extractors;\nvar extractorTypes;\n\nextractors = {\n '.zip': extractZip,\n '.tar': extractTar,\n '.tar.gz': extractTarGz,\n '.tgz': extractTarGz,\n '.gz': extractGz,\n 'application/zip': extractZip,\n 'application/x-zip': extractZip,\n 'application/x-zip-compressed': extractZip,\n 'application/x-tar': extractTar,\n 'application/x-tgz': extractTarGz,\n 'application/x-gzip': extractGz\n};\n\nextractorTypes = Object.keys(extractors);\n\nfunction extractZip(archive, dst) {\n var deferred = Q.defer();\n\n new DecompressZip(archive)\n .on('error', deferred.reject)\n .on('extract', deferred.resolve.bind(deferred, dst))\n .extract({\n path: dst,\n follow: false, // Do not follow symlinks (#699)\n filter: filterSymlinks // Filter symlink files\n });\n\n return deferred.promise;\n}\n\nfunction extractTar(archive, dst) {\n var deferred = Q.defer();\n\n var stream = fs.createReadStream(archive);\n\n var reject = function(error) {\n destroy(stream);\n deferred.reject(error);\n };\n\n stream\n .on('error', reject)\n .pipe(\n tar.extract(dst, {\n ignore: isSymlink, // Filter symlink files\n dmode: 0555, // Ensure dirs are readable\n fmode: 0444 // Ensure files are readable\n })\n )\n .on('error', reject)\n .on('finish', function(result) {\n destroy(stream);\n deferred.resolve(dst);\n });\n\n return deferred.promise;\n}\n\nfunction extractTarGz(archive, dst) {\n var deferred = Q.defer();\n\n var stream = fs.createReadStream(archive);\n\n var reject = function(error) {\n destroy(stream);\n deferred.reject(error);\n };\n\n stream\n .on('error', reject)\n .pipe(zlib.createGunzip())\n .on('error', reject)\n .pipe(\n tar.extract(dst, {\n ignore: isSymlink, // Filter symlink files\n dmode: 0555, // Ensure dirs are readable\n fmode: 0444 // Ensure files are readable\n })\n )\n .on('error', reject)\n .on('finish', function(result) {\n destroy(stream);\n deferred.resolve(dst);\n });\n\n return deferred.promise;\n}\n\nfunction extractGz(archive, dst) {\n var deferred = Q.defer();\n\n var stream = fs.createReadStream(archive);\n\n var reject = function(error) {\n destroy(stream);\n deferred.reject(error);\n };\n stream\n .on('error', reject)\n .pipe(zlib.createGunzip())\n .on('error', reject)\n .pipe(createWriteStream(dst))\n .on('error', reject)\n .on('finish', function(result) {\n destroy(stream);\n deferred.resolve(dst);\n });\n\n return deferred.promise;\n}\n\nfunction isSymlink(entry) {\n return entry.type === 'SymbolicLink';\n}\n\nfunction filterSymlinks(entry) {\n return entry.type !== 'SymbolicLink';\n}\n\nfunction getExtractor(archive) {\n // Make the archive lower case to match against the types\n // This ensures that upper-cased extensions work\n archive = archive.toLowerCase();\n\n var type = mout.array.find(extractorTypes, function(type) {\n return mout.string.endsWith(archive, type);\n });\n\n return type ? extractors[type] : null;\n}\n\nfunction isSingleDir(dir) {\n return Q.nfcall(fs.readdir, dir).then(function(files) {\n var singleDir;\n\n // Remove any OS specific files from the files array\n // before checking its length\n files = files.filter(junk.isnt);\n\n if (files.length !== 1) {\n return false;\n }\n\n singleDir = path.join(dir, files[0]);\n\n return Q.nfcall(fs.stat, singleDir).then(function(stat) {\n return stat.isDirectory() ? singleDir : false;\n });\n });\n}\n\nfunction moveDirectory(srcDir, destDir) {\n return Q.nfcall(fs.readdir, srcDir)\n .then(function(files) {\n var promises = files.map(function(file) {\n var src = path.join(srcDir, file);\n var dst = path.join(destDir, file);\n\n return Q.nfcall(fs.rename, src, dst);\n });\n\n return Q.all(promises);\n })\n .then(function() {\n return Q.nfcall(fs.rmdir, srcDir);\n });\n}\n\n// -----------------------------\n\nfunction canExtract(src, mimeType) {\n if (mimeType && mimeType !== 'application/octet-stream') {\n return !!getExtractor(mimeType);\n }\n\n return !!getExtractor(src);\n}\n\n// Available options:\n// - keepArchive: true to keep the archive afterwards (defaults to false)\n// - keepStructure: true to keep the extracted structure unchanged (defaults to false)\nfunction extract(src, dst, opts) {\n var extractor;\n var promise;\n\n opts = opts || {};\n extractor = getExtractor(src);\n\n // Try to get extractor from mime type\n if (!extractor && opts.mimeType) {\n extractor = getExtractor(opts.mimeType);\n }\n\n // If extractor is null, then the archive type is unknown\n if (!extractor) {\n return Q.reject(\n createError(\n 'File ' + src + ' is not a known archive',\n 'ENOTARCHIVE'\n )\n );\n }\n\n // Extract to a temporary directory in case of file name clashes\n return Q.nfcall(tmp.dir, {\n template: dst + '-XXXXXX',\n mode: 0777 & ~process.umask()\n })\n .then(function(tempDir) {\n // nfcall may return multiple callback arguments as an array\n return Array.isArray(tempDir) ? tempDir[0] : tempDir;\n })\n .then(function(tempDir) {\n // Check archive file size\n promise = Q.nfcall(fs.stat, src).then(function(stat) {\n if (stat.size <= 8) {\n throw createError(\n 'File ' + src + ' is an invalid archive',\n 'ENOTARCHIVE'\n );\n }\n\n // Extract archive\n return extractor(src, tempDir);\n });\n\n // Remove archive\n if (!opts.keepArchive) {\n promise = promise.then(function() {\n return Q.nfcall(fs.unlink, src);\n });\n }\n\n // Move contents from the temporary directory\n // If the contents are a single directory (and we're not preserving structure),\n // move its contents directly instead.\n promise = promise\n .then(function() {\n return isSingleDir(tempDir);\n })\n .then(function(singleDir) {\n if (singleDir && !opts.keepStructure) {\n return moveDirectory(singleDir, dst);\n } else {\n return moveDirectory(tempDir, dst);\n }\n });\n\n // Resolve promise to the dst dir\n return promise.then(function() {\n return dst;\n });\n });\n}\n\nmodule.exports = extract;\nmodule.exports.canExtract = canExtract;\n\n## 内容2\n### 文件路径:\npackage.json\n### 代码内容:\n{\n \"name\": \"bower\",\n \"version\": \"1.8.7\",\n \"description\": \"The browser package manager\",\n \"author\": \"Twitter\",\n \"license\": \"MIT\",\n \"repository\": \"bower/bower\",\n \"main\": \"lib\",\n \"bin\": \"bin/bower\",\n \"homepage\": \"http://bower.io\",\n \"engines\": {\n \"node\": \">=0.10.0\"\n },\n \"keywords\": [\n \"bower\"\n ],\n \"dependencies\": {\n \"abbrev\": \"^1.0.5\",\n \"archy\": \"1.0.0\",\n \"bower-config\": \"^1.4.1\",\n \"bower-endpoint-parser\": \"^0.2.2\",\n \"bower-json\": \"^0.8.1\",\n \"bower-logger\": \"^0.2.2\",\n \"bower-registry-client\": \"^1.0.0\",\n \"cardinal\": \"0.4.4\",\n \"chalk\": \"^1.0.0\",\n \"chmodr\": \"^1.0.2\",\n \"configstore\": \"^2.0.0\",\n \"decompress-zip\": \"^0.2.2\",\n \"destroy\": \"^1.0.3\",\n \"findup-sync\": \"^0.3.0\",\n \"fs-write-stream-atomic\": \"1.0.8\",\n \"fstream\": \"^1.0.3\",\n \"fstream-ignore\": \"^1.0.2\",\n \"github\": \"^0.2.3\",\n \"glob\": \"^4.3.2\",\n \"graceful-fs\": \"^4.1.3\",\n \"handlebars\": \"^4.0.5\",\n \"inquirer\": \"0.10.0\",\n \"is-root\": \"^1.0.0\",\n \"junk\": \"^1.0.0\",\n \"lockfile\": \"^1.0.0\",\n \"lru-cache\": \"^2.5.0\",\n \"md5-hex\": \"^1.0.2\",\n \"mkdirp\": \"0.5.0\",\n \"mout\": \"^0.11.0\",\n \"nopt\": \"^3.0.1\",\n \"opn\": \"^4.0.0\",\n \"p-throttler\": \"0.1.1\",\n \"promptly\": \"0.2.0\",\n \"q\": \"^1.1.2\",\n \"request\": \"2.67.0\",\n \"request-progress\": \"0.3.1\",\n \"requireg\": \"^0.1.5\",\n \"resolve\": \"^1.1.7\",\n \"retry\": \"0.6.1\",\n \"rimraf\": \"^2.2.8\",\n \"semver\": \"^2.3.0\",\n \"semver-utils\": \"^1.1.1\",\n \"shell-quote\": \"^1.4.2\",\n \"stringify-object\": \"^1.0.0\",\n \"tar-fs\": \"^1.4.1\",\n \"tmp\": \"0.0.28\",\n \"update-notifier\": \"^0.6.0\",\n \"user-home\": \"^1.1.0\",\n \"which\": \"^1.0.8\"\n },\n \"devDependencies\": {\n \"arr-diff\": \"^2.0.0\",\n \"chai\": \"^3.5.0\",\n \"coveralls\": \"^2.11.9\",\n \"expect.js\": \"^0.3.1\",\n \"grunt\": \"^1.0.1\",\n \"grunt-cli\": \"^1.1.0\",\n \"grunt-contrib-watch\": \"^1.0.0\",\n \"grunt-eslint\": \"^18.1.0\",\n \"grunt-exec\": \"^0.4.7\",\n \"grunt-simple-mocha\": \"^0.4.1\",\n \"husky\": \"^0.14.3\",\n \"in-publish\": \"^2.0.0\",\n \"istanbul\": \"^0.4.3\",\n \"lint-staged\": \"^7.0.0\",\n \"load-grunt-tasks\": \"^3.5.0\",\n \"mocha\": \"^2.5.3\",\n \"multiline\": \"^1.0.2\",\n \"nock\": \"^9.2.3\",\n \"node-uuid\": \"^1.4.7\",\n \"prettier\": \"^1.11.1\",\n \"proxyquire\": \"^1.7.9\",\n \"spawn-sync\": \"1.0.15\",\n \"wrench\": \"^1.5.8\"\n },\n \"scripts\": {\n \"test\": \"grunt test\",\n \"ci\": \"grunt travis\",\n \"coveralls\": \"coveralls\",\n \"prepublish\": \"in-publish && echo 'You need to use \\\"grunt publish\\\" to publish bower' && false || not-in-publish\",\n \"format\": \"prettier --write --single-quote --tab-width 4 '**/*.js'\",\n \"precommit\": \"lint-staged\"\n },\n \"lint-staged\": {\n \"*.js\": [\n \"prettier --single-quote --tab-width 4\",\n \"git add\"\n ]\n },\n \"files\": [\n \"bin\",\n \"lib\"\n ]\n}\n\n", "input": "", "output": "【漏洞类型】路径遍历漏洞\n【严重性】High\n【漏洞摘要】此代码存在路径遍历漏洞,问题在于`extractTarGz`函数中未对解压路径进行验证。攻击者可以通过构造特殊的压缩文件,使得解压过程中的文件路径超出预期的安全边界,从而可能覆盖或读取目标系统中任意位置的文件。\n【关键代码模式】`extractTarGz`函数中的`tar.extract(dst, { ... })`调用,未对`dst`参数进行验证。\n【修复方法说明】修复方法通过修改`isSymlink`函数,使其对符号链接类型进行更严格的匹配,从而减少路径遍历的可能性。同时,更新了`package.json`中的脚本钩子名称,以遵循最新的npm发布规范。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nextensions/database/src/com/google/refine/extension/database/DatabaseConfiguration.java\n### 代码内容:\npackage com.google.refine.extension.database;\n\npublic class DatabaseConfiguration {\n\n private String connectionName;\n private String databaseType;\n private String databaseHost;\n private int databasePort;\n private String databaseUser;\n private String databasePassword;\n private String databaseName;\n private String databaseSchema;\n\n // optional parameters\n private boolean useSSL;\n\n public String getConnectionName() {\n return connectionName;\n }\n\n public void setConnectionName(String connectionName) {\n this.connectionName = connectionName;\n }\n\n public String getDatabaseType() {\n return databaseType;\n }\n\n public void setDatabaseType(String databaseType) {\n this.databaseType = databaseType;\n }\n\n public String getDatabaseHost() {\n return databaseHost;\n }\n\n public void setDatabaseHost(String databaseServer) {\n this.databaseHost = databaseServer;\n }\n\n public int getDatabasePort() {\n return databasePort;\n }\n\n public void setDatabasePort(int databasePort) {\n this.databasePort = databasePort;\n }\n\n public String getDatabaseUser() {\n return databaseUser;\n }\n\n public void setDatabaseUser(String databaseUser) {\n this.databaseUser = databaseUser;\n }\n\n public String getDatabasePassword() {\n return databasePassword;\n }\n\n public void setDatabasePassword(String databasePassword) {\n this.databasePassword = databasePassword;\n }\n\n public String getDatabaseName() {\n return databaseName;\n }\n\n public void setDatabaseName(String initialDatabase) {\n this.databaseName = initialDatabase;\n }\n\n public String getDatabaseSchema() {\n return databaseSchema;\n }\n\n public void setDatabaseSchema(String initialSchema) {\n this.databaseSchema = initialSchema;\n }\n\n public boolean isUseSSL() {\n return useSSL;\n }\n\n public void setUseSSL(boolean useSSL) {\n this.useSSL = useSSL;\n }\n\n @Override\n public String toString() {\n return \"DatabaseConfiguration [connectionName=\" + connectionName + \", databaseType=\" + databaseType\n + \", databaseHost=\" + databaseHost + \", databasePort=\" + databasePort + \", databaseUser=\" + databaseUser\n + \", databaseName=\" + databaseName + \", databaseSchema=\"\n + databaseSchema + \", useSSL=\" + useSSL + \"]\";\n }\n\n}\n\n## 内容2\n### 文件路径:\nextensions/database/src/com/google/refine/extension/database/mariadb/MariaDBConnectionManager.java\n### 代码内容:\npackage com.google.refine.extension.database.mariadb;\n\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.refine.extension.database.DatabaseConfiguration;\nimport com.google.refine.extension.database.DatabaseServiceException;\nimport com.google.refine.extension.database.SQLType;\n\npublic class MariaDBConnectionManager {\n\n private static final Logger logger = LoggerFactory.getLogger(\"MariaDBConnectionManager\");\n private Connection connection;\n private SQLType type;\n\n private static MariaDBConnectionManager instance;\n\n /**\n * \n * @param type\n * @param databaseConfiguration\n * @throws SQLException\n */\n private MariaDBConnectionManager() {\n type = SQLType.forName(MariaDBDatabaseService.DB_NAME);\n\n }\n\n /**\n * Create a new instance of this connection manager.\n *\n * @return an instance of the manager\n *\n * @throws DatabaseServiceException\n */\n public static MariaDBConnectionManager getInstance() throws DatabaseServiceException {\n if (instance == null) {\n // logger.info(\"::Creating new MariaDB Connection Manager ::\");\n instance = new MariaDBConnectionManager();\n\n }\n return instance;\n }\n\n /**\n * Get the SQL Database type.\n *\n * @return the type\n */\n public SQLType getType() {\n return this.type;\n }\n\n /**\n * testConnection\n * \n * @param databaseConfiguration\n * @return\n */\n public boolean testConnection(DatabaseConfiguration databaseConfiguration) throws DatabaseServiceException {\n\n try {\n boolean connResult = false;\n\n Connection conn = getConnection(databaseConfiguration, true);\n if (conn != null) {\n connResult = true;\n conn.close();\n }\n\n return connResult;\n\n } catch (SQLException e) {\n logger.error(\"Test connection Failed!\", e);\n throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage());\n }\n\n }\n\n /**\n * Get a connection form the connection pool.\n *\n * @return connection from the pool\n */\n public Connection getConnection(DatabaseConfiguration databaseConfiguration, boolean forceNewConnection)\n throws DatabaseServiceException {\n try {\n\n // logger.info(\"connection::{}, forceNewConnection: {}\", connection, forceNewConnection);\n\n if (connection != null && !forceNewConnection) {\n // logger.debug(\"connection closed::{}\", connection.isClosed());\n if (!connection.isClosed()) {\n if (logger.isDebugEnabled()) {\n logger.debug(\"Returning existing connection::{}\", connection);\n }\n\n return connection;\n }\n }\n\n Class.forName(type.getClassPath());\n DriverManager.setLoginTimeout(10);\n String dbURL = getDatabaseUrl(databaseConfiguration);\n connection = DriverManager.getConnection(dbURL, databaseConfiguration.getDatabaseUser(),\n databaseConfiguration.getDatabasePassword());\n\n if (logger.isDebugEnabled()) {\n logger.debug(\"*** Acquired New connection for ::{} **** \", dbURL);\n }\n\n return connection;\n\n } catch (ClassNotFoundException e) {\n logger.error(\"Jdbc Driver not found\", e);\n throw new DatabaseServiceException(e.getMessage());\n } catch (SQLException e) {\n logger.error(\"SQLException::Couldn't get a Connection!\", e);\n throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage());\n }\n }\n\n public void shutdown() {\n\n if (connection != null) {\n try {\n connection.close();\n } catch (SQLException e) {\n logger.warn(\"Non-Managed connection could not be closed. Whoops!\", e);\n }\n }\n\n }\n\n private static String getDatabaseUrl(DatabaseConfiguration dbConfig) {\n\n int port = dbConfig.getDatabasePort();\n return \"jdbc:\" + dbConfig.getDatabaseType().toLowerCase() + \"://\" + dbConfig.getDatabaseHost()\n + ((port == 0) ? \"\" : (\":\" + port)) + \"/\" + dbConfig.getDatabaseName();\n\n }\n}\n\n## 内容3\n### 文件路径:\nextensions/database/src/com/google/refine/extension/database/mysql/MySQLConnectionManager.java\n### 代码内容:\npackage com.google.refine.extension.database.mysql;\n\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.refine.extension.database.DatabaseConfiguration;\nimport com.google.refine.extension.database.DatabaseServiceException;\nimport com.google.refine.extension.database.SQLType;\n\npublic class MySQLConnectionManager {\n\n private static final Logger logger = LoggerFactory.getLogger(\"MySQLConnectionManager\");\n private Connection connection;\n private SQLType type;\n\n private static MySQLConnectionManager instance;\n\n /**\n * \n * @param type\n * @param databaseConfiguration\n * @throws SQLException\n */\n private MySQLConnectionManager() {\n type = SQLType.forName(MySQLDatabaseService.DB_NAME);\n\n }\n\n /**\n * Create a new instance of this connection manager.\n *\n * @return an instance of the manager\n *\n * @throws DatabaseServiceException\n */\n public static MySQLConnectionManager getInstance() throws DatabaseServiceException {\n if (instance == null) {\n logger.debug(\"::Creating new MySQLConnectionManager ::\");\n instance = new MySQLConnectionManager();\n\n }\n return instance;\n }\n\n /**\n * Get the SQL Database type.\n *\n * @return the type\n */\n public SQLType getType() {\n return this.type;\n }\n\n /**\n * testConnection\n * \n * @param databaseConfiguration\n * @return\n */\n public boolean testConnection(DatabaseConfiguration databaseConfiguration) throws DatabaseServiceException {\n\n try {\n boolean connResult = false;\n\n Connection conn = getConnection(databaseConfiguration, true);\n if (conn != null) {\n connResult = true;\n conn.close();\n }\n\n return connResult;\n\n } catch (SQLException e) {\n logger.error(\"Test connection Failed!\", e);\n throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage());\n }\n\n }\n\n /**\n * Get a connection form the connection pool.\n *\n * @return connection from the pool\n */\n public Connection getConnection(DatabaseConfiguration databaseConfiguration, boolean forceNewConnection)\n throws DatabaseServiceException {\n try {\n\n if (connection != null && !forceNewConnection) {\n // logger.info(\"connection closed::{}\", connection.isClosed());\n if (!connection.isClosed()) {\n if (logger.isDebugEnabled()) {\n logger.debug(\"Returning existing connection::{}\", connection);\n }\n\n return connection;\n }\n }\n String dbURL = getDatabaseUrl(databaseConfiguration);\n Class.forName(type.getClassPath());\n\n // logger.info(\"*** type.getClassPath() ::{}, {}**** \", type.getClassPath());\n\n DriverManager.setLoginTimeout(10);\n\n connection = DriverManager.getConnection(dbURL, databaseConfiguration.getDatabaseUser(),\n databaseConfiguration.getDatabasePassword());\n\n if (logger.isDebugEnabled()) {\n logger.debug(\"*** Acquired New connection for ::{} **** \", dbURL);\n }\n\n return connection;\n\n } catch (ClassNotFoundException e) {\n logger.error(\"Jdbc Driver not found\", e);\n throw new DatabaseServiceException(e.getMessage());\n } catch (SQLException e) {\n logger.error(\"SQLException::Couldn't get a Connection!\", e);\n throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage());\n }\n }\n\n public void shutdown() {\n\n if (connection != null) {\n try {\n connection.close();\n } catch (SQLException e) {\n logger.warn(\"Non-Managed connection could not be closed. Whoops!\", e);\n }\n }\n\n }\n\n private String getDatabaseUrl(DatabaseConfiguration dbConfig) {\n\n int port = dbConfig.getDatabasePort();\n return \"jdbc:\" + dbConfig.getDatabaseType() + \"://\" + dbConfig.getDatabaseHost()\n + ((port == 0) ? \"\" : (\":\" + port)) + \"/\" + dbConfig.getDatabaseName() + \"?useSSL=\" + dbConfig.isUseSSL();\n\n }\n}\n\n## 内容4\n### 文件路径:\nextensions/database/src/com/google/refine/extension/database/pgsql/PgSQLConnectionManager.java\n### 代码内容:\npackage com.google.refine.extension.database.pgsql;\n\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.refine.extension.database.DatabaseConfiguration;\nimport com.google.refine.extension.database.DatabaseServiceException;\nimport com.google.refine.extension.database.SQLType;\n\npublic class PgSQLConnectionManager {\n\n private static final Logger logger = LoggerFactory.getLogger(\"PgSQLConnectionManager\");\n private Connection connection;\n private SQLType type;\n\n private static PgSQLConnectionManager instance;\n\n /**\n * \n * @param type\n * @param databaseConfiguration\n * @throws SQLException\n */\n private PgSQLConnectionManager() {\n type = SQLType.forName(PgSQLDatabaseService.DB_NAME);\n\n }\n\n /**\n * Create a new instance of this connection manager.\n *\n * @return an instance of the manager\n *\n * @throws DatabaseServiceException\n */\n public static PgSQLConnectionManager getInstance() throws DatabaseServiceException {\n if (instance == null) {\n if (logger.isDebugEnabled()) {\n logger.debug(\"::Creating new PgSQL ConnectionManager ::\");\n }\n\n instance = new PgSQLConnectionManager();\n\n }\n return instance;\n }\n\n /**\n * Get the SQL Database type.\n *\n * @return the type\n */\n public SQLType getType() {\n return this.type;\n }\n\n /**\n * testConnection\n * \n * @param databaseConfiguration\n * @return\n */\n public boolean testConnection(DatabaseConfiguration databaseConfiguration) throws DatabaseServiceException {\n\n try {\n boolean connResult = false;\n\n Connection conn = getConnection(databaseConfiguration, true);\n if (conn != null) {\n connResult = true;\n conn.close();\n }\n\n return connResult;\n\n } catch (SQLException e) {\n logger.error(\"Test connection Failed!\", e);\n throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage());\n }\n\n }\n\n /**\n * Get a connection form the connection pool.\n *\n * @return connection from the pool\n */\n public Connection getConnection(DatabaseConfiguration databaseConfiguration, boolean forceNewConnection)\n throws DatabaseServiceException {\n try {\n\n // logger.info(\"connection::{}, forceNewConnection: {}\", connection, forceNewConnection);\n\n if (connection != null && !forceNewConnection) {\n // logger.info(\"connection closed::{}\", connection.isClosed());\n if (!connection.isClosed()) {\n if (logger.isDebugEnabled()) {\n logger.debug(\"Returning existing connection::{}\", connection);\n }\n return connection;\n }\n }\n\n Class.forName(type.getClassPath());\n DriverManager.setLoginTimeout(10);\n String dbURL = getDatabaseUrl(databaseConfiguration);\n connection = DriverManager.getConnection(dbURL, databaseConfiguration.getDatabaseUser(),\n databaseConfiguration.getDatabasePassword());\n\n logger.debug(\"*** Acquired New connection for ::{} **** \", dbURL);\n\n return connection;\n\n } catch (ClassNotFoundException e) {\n logger.error(\"Jdbc Driver not found\", e);\n throw new DatabaseServiceException(e.getMessage());\n } catch (SQLException e) {\n logger.error(\"SQLException::Couldn't get a Connection!\", e);\n throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage());\n }\n }\n\n public void shutdown() {\n\n if (connection != null) {\n try {\n connection.close();\n } catch (SQLException e) {\n logger.warn(\"Non-Managed connection could not be closed. Whoops!\", e);\n }\n }\n\n }\n\n private static String getDatabaseUrl(DatabaseConfiguration dbConfig) {\n\n int port = dbConfig.getDatabasePort();\n return \"jdbc:\" + dbConfig.getDatabaseType().toLowerCase() + \"://\" + dbConfig.getDatabaseHost()\n + ((port == 0) ? \"\" : (\":\" + port)) + \"/\" + dbConfig.getDatabaseName();\n\n }\n}\n\n## 内容5\n### 文件路径:\nextensions/database/src/com/google/refine/extension/database/sqlite/SQLiteConnectionManager.java\n### 代码内容:\npackage com.google.refine.extension.database.sqlite;\n\nimport com.google.refine.extension.database.DatabaseConfiguration;\nimport com.google.refine.extension.database.DatabaseServiceException;\nimport com.google.refine.extension.database.SQLType;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\n\npublic class SQLiteConnectionManager {\n\n private static final Logger logger = LoggerFactory.getLogger(\"SQLiteConnectionManager\");\n private static SQLiteConnectionManager instance;\n private final SQLType type;\n private Connection connection;\n\n private SQLiteConnectionManager() {\n type = SQLType.forName(SQLiteDatabaseService.DB_NAME);\n }\n\n /**\n * Create a new instance of this connection manager.\n *\n * @return an instance of the manager\n */\n public static SQLiteConnectionManager getInstance() {\n if (instance == null) {\n if (logger.isDebugEnabled()) {\n logger.debug(\"::Creating new SQLite ConnectionManager ::\");\n }\n instance = new SQLiteConnectionManager();\n }\n return instance;\n }\n\n public static String getDatabaseUrl(DatabaseConfiguration dbConfig) {\n return \"jdbc:\" + dbConfig.getDatabaseType().toLowerCase() + \":\" + dbConfig.getDatabaseName();\n }\n\n /**\n * Get the SQL Database type.\n *\n * @return the type\n */\n public SQLType getType() {\n return this.type;\n }\n\n /**\n * testConnection\n *\n * @param dbConfig\n * @return boolean\n */\n public boolean testConnection(DatabaseConfiguration dbConfig) throws DatabaseServiceException {\n try {\n boolean connResult = false;\n\n Connection conn = getConnection(dbConfig);\n if (conn != null) {\n connResult = true;\n conn.close();\n }\n\n return connResult;\n } catch (SQLException e) {\n logger.error(\"Test connection Failed!\", e);\n throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage());\n }\n }\n\n /**\n * Get a connection form the connection pool.\n *\n * @return connection from the pool\n */\n public Connection getConnection(DatabaseConfiguration databaseConfiguration) throws DatabaseServiceException {\n try {\n if (connection != null) {\n connection.close();\n }\n\n Class.forName(type.getClassPath());\n String dbURL = getDatabaseUrl(databaseConfiguration);\n connection = DriverManager.getConnection(dbURL);\n\n logger.debug(\"*** Acquired New connection for ::{} **** \", dbURL);\n\n return connection;\n } catch (ClassNotFoundException e) {\n logger.error(\"Jdbc Driver not found\", e);\n throw new DatabaseServiceException(e.getMessage());\n } catch (SQLException e) {\n logger.error(\"SQLException::Couldn't get a Connection!\", e);\n throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage());\n }\n }\n\n public void shutdown() {\n if (connection != null) {\n try {\n connection.close();\n } catch (SQLException e) {\n logger.warn(\"Non-Managed connection could not be closed. Whoops!\", e);\n }\n }\n }\n}\n\n", "input": "", "output": "【漏洞类型】路径遍历漏洞\n【严重性】High\n【漏洞摘要】此代码在构建JDBC URL时,直接使用了用户输入的数据库类型、主机名、端口、数据库名称等信息,而没有进行适当的转义或验证。这可能导致路径遍历攻击,攻击者可以通过构造特定的输入来访问或操作不应访问的文件或资源。\n【关键代码模式】与漏洞相关的关键代码模式包括DatabaseConfiguration类中的`toString`方法以及各个数据库连接管理器类中的`getDatabaseUrl`方法,其中直接拼接了用户输入的数据库配置信息,而没有进行转义。\n【修复方法说明】修复方法通过在DatabaseConfiguration类中添加`toURI`方法,将数据库配置信息转换为URI对象,自动对URL组件进行转义,从而防止路径遍历攻击。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nAPDE/src/main/java/com/calsignlabs/apde/build/dag/CopyBuildTask.java\n### 代码内容:\npackage com.calsignlabs.apde.build.dag;\n\nimport com.calsignlabs.apde.R;\n\nimport java.io.BufferedOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipInputStream;\nimport java.util.zip.ZipOutputStream;\n\n/**\n * Supports the following combination of in types, out types, and zip methods:\n * - FILE FILE COPY\n * - FILE STREAM COPY\n * - STREAM FILE COPY\n * - STREAM STREAM COPY\n * - STREAM FOLDER EXTRACT\n * - FILE FOLDER EXTRACT\n * - FOLDER STREAM COMPRESS\n * - FOLDER FILE COMPRESS\n *\n * Note that ASSET for input is equivalent to STREAM.\n */\npublic class CopyBuildTask extends BuildTask {\n\tprivate static final int BUFFER_SIZE = 8192;\n\t\n\tprivate static final int NONE = 0x00000000;\n\tprivate static final int FILE = 0x00000001;\n\tprivate static final int FOLDER = 0x00000002;\n\tprivate static final int STREAM = 0x00000004;\n\t\n\tprivate static final int COPY = 0x00000100;\n\tprivate static final int EXTRACT = 0x00000200;\n\tprivate static final int COMPRESS = 0x00000400;\n\t\n\tprivate int inputType, outputType;\n\tprivate Getter<File> inputFile, outputFile;\n\tprivate Getter<InputStream> inputStreamGetter;\n\tprivate Getter<OutputStream> outputStreamGetter;\n\tprivate int zipMethod;\n\t\n\tprivate boolean vacuousSuccess = false;\n\t\n\tpublic CopyBuildTask(BuildTask... deps) {\n\t\tsuper(deps);\n\t\tinputType = NONE;\n\t\toutputType = NONE;\n\t\tzipMethod = COPY;\n\t}\n\t\n\t// TODO add support for functional folder copies\n\tpublic CopyBuildTask inFile(Getter<File> in) {\n\t\tinputType = FILE;\n\t\tinputFile = in;\n\t\torGetterChangeNoticer(in);\n\t\treturn this;\n\t}\n\t\n\tpublic CopyBuildTask inFolder(Getter<File> in) {\n\t\tinputType = FOLDER;\n\t\tinputFile = in;\n\t\torGetterChangeNoticer(in);\n\t\treturn this;\n\t}\n\t\n\tpublic CopyBuildTask inStream(Getter<InputStream> in) {\n\t\tinputType = STREAM;\n\t\tinputStreamGetter = in;\n\t\torGetterChangeNoticer(in);\n\t\treturn this;\n\t}\n\t\n\tpublic CopyBuildTask inAsset(String assetsPath) {\n\t\tsetName(\"copy asset: \" + assetsPath);\n\t\t\n\t\treturn inStream(context -> {\n\t\t\ttry {\n\t\t\t\treturn context.getResources().getAssets().open(assetsPath);\n\t\t\t} catch (IOException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t\treturn null;\n\t\t\t}\n\t\t});\n\t}\n\t\n\tpublic CopyBuildTask outFile(Getter<File> out, boolean detectChange) {\n\t\toutputType = FILE;\n\t\toutputFile = out;\n\t\tif (detectChange && (inputType == FILE || inputType == STREAM)) {\n\t\t\tpopulateStreamsForFiles();\n\t\t\torChangeNoticer((new ChecksumChangeNoticer()).sourceDestStream(inputStreamGetter, fis(out)));\n\t\t}\n\t\torGetterChangeNoticer(out);\n\t\treturn this;\n\t}\n\t\n\tpublic CopyBuildTask outFile(Getter<File> out) {\n\t\treturn outFile(out, true);\n\t}\n\t\n\tpublic CopyBuildTask outFolder(Getter<File> out) {\n\t\toutputType = FOLDER;\n\t\toutputFile = out;\n\t\t// It's better to just do diffs on the source\n//\t\tif (inputType == FOLDER) {\n//\t\t\torChangeNoticer((new ChecksumChangeNoticer()).sourceDestFile(inputFile, out));\n//\t\t}\n\t\torGetterChangeNoticer(out);\n\t\treturn this;\n\t}\n\t\n\tpublic CopyBuildTask outStream(Getter<OutputStream> out) {\n\t\toutputType = STREAM;\n\t\toutputStreamGetter = out;\n\t\torGetterChangeNoticer(out);\n\t\treturn this;\n\t}\n\t\n\tpublic CopyBuildTask extract() {\n\t\tzipMethod = EXTRACT;\n\t\treturn this;\n\t}\n\t\n\tpublic CopyBuildTask compress() {\n\t\tzipMethod = COMPRESS;\n\t\treturn this;\n\t}\n\t\n\t/**\n\t * Default success if the file to copy doesn't exist\n\t *\n\t * @param vacuousSuccess\n\t * @return\n\t */\n\tpublic CopyBuildTask setVacuousSuccess(boolean vacuousSuccess) {\n\t\tthis.vacuousSuccess = vacuousSuccess;\n\t\treturn this;\n\t}\n\t\n\t@Override\n\tpublic CharSequence getTitle() {\n\t\treturn getBuildContext().getResources().getString(R.string.build_copying);\n\t}\n\t\n\t@Override\n\tpublic void run() throws InterruptedException {\n\t\tswitch (inputType | outputType << 4 | zipMethod) {\n\t\t\tcase FILE | FILE << 4 | COPY:\n\t\t\tcase FILE | STREAM << 4 | COPY:\n\t\t\tcase STREAM | FILE << 4 | COPY:\n\t\t\tcase STREAM | STREAM << 4 | COPY:\n\t\t\t\tpopulateStreamsForFiles();\n\t\t\t\tstreamCopy();\n\t\t\t\tbreak;\n\t\t\tcase FOLDER | FOLDER << 4 | COPY:\n\t\t\t\tfolderCopy();\n\t\t\t\tbreak;\n\t\t\tcase STREAM | FOLDER << 4 | EXTRACT:\n\t\t\tcase FILE | FOLDER << 4 | EXTRACT:\n\t\t\t\tpopulateStreamsForFiles();\n\t\t\t\tzipExtract();\n\t\t\t\tbreak;\n\t\t\tcase FOLDER | STREAM << 4 | COMPRESS:\n\t\t\tcase FOLDER | FILE << 4 | COMPRESS:\n\t\t\t\tpopulateStreamsForFiles();\n\t\t\t\tzipCompress();\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new RuntimeException(\"Invalid copy task parameters: \" + inputType + \", \" + outputType + \", \" + zipMethod);\n\t\t}\n\t}\n\t\n\tprivate void populateStreamsForFiles() {\n\t\t// We convert files into streams so that we can use the same method for interchangeably\n\t\t// copying streams and files.\n\t\t\n\t\tif (inputStreamGetter == null) {\n\t\t\tinputStreamGetter = fis(inputFile);\n\t\t}\n\t\t\n\t\tif (outputStreamGetter == null) {\n\t\t\toutputStreamGetter = fos(outputFile);\n\t\t}\n\t}\n\t\n\tprivate void streamCopy() {\n\t\tfinish(handleStreamCopy(inputStreamGetter.get(getBuildContext()), outputStreamGetter.get(getBuildContext())));\n\t}\n\t\n\tprivate void folderCopy() {\n\t\tfinish(copyFolder(inputFile.get(getBuildContext()), outputFile.get(getBuildContext()), vacuousSuccess));\n\t}\n\t\n\tprivate void zipExtract() {\n\t\tfinish(handleExtract(inputStreamGetter.get(getBuildContext()), outputFile.get(getBuildContext())));\n\t}\n\t\n\tprivate void zipCompress() {\n\t\tfinish(handleCompress(inputFile.get(getBuildContext()), outputStreamGetter.get(getBuildContext())));\n\t}\n\t\n\tprivate static boolean handleStreamCopy(InputStream in, OutputStream out) {\n\t\treturn handleStreamCopy(in, out, true, true);\n\t}\n\t\n\tprivate static boolean handleStreamCopy(InputStream inputStream, OutputStream outputStream, boolean closeIn, boolean closeOut) {\n\t\ttry {\n\t\t\tcopyStream(inputStream, outputStream);\n\t\t\treturn true;\n\t\t} catch (IOException e) {\n\t\t\te.printStackTrace();\n\t\t\treturn false;\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tif (closeIn && inputStream != null) inputStream.close();\n\t\t\t\tif (closeOut && outputStream != null) outputStream.close();\n\t\t\t} catch (IOException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic static void copyStream(InputStream inputStream, OutputStream outputStream) throws IOException {\n\t\tbyte[] buf = new byte[BUFFER_SIZE];\n\t\tint len;\n\t\twhile (-1 != (len = inputStream.read(buf))) {\n\t\t\toutputStream.write(buf, 0, len);\n\t\t}\n\t}\n\t\n\tprivate static boolean copyFolder(File source, File target, boolean vacuousSuccess) {\n\t\tif (!source.exists()) return vacuousSuccess;\n\t\tif (source.equals(target)) return false;\n\t\tif (!target.exists() && !target.mkdirs()) return false;\n\t\t\n\t\ttry {\n\t\t\tfor (String file : source.list()) {\n\t\t\t\tFile sourceFile = new File(source, file);\n\t\t\t\tFile targetFile = new File(target, file);\n\t\t\t\tif (sourceFile.isDirectory()) {\n\t\t\t\t\tif (!copyFolder(sourceFile, targetFile, vacuousSuccess)) return false;\n\t\t\t\t} else {\n\t\t\t\t\tif (!handleStreamCopy(new FileInputStream(sourceFile), new FileOutputStream(targetFile)))\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t} catch (FileNotFoundException e) {\n\t\t\te.printStackTrace();\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\tprivate static boolean handleExtract(InputStream inputStream, File folder) {\n\t\tif (folder.exists() && !folder.isDirectory()) return false;\n\t\tif (!folder.exists() && !folder.mkdirs()) return false;\n\t\t\n\t\tZipInputStream zipIn = null;\n\t\t\n\t\ttry {\n\t\t\tzipIn = new ZipInputStream(inputStream);\n\t\t\t\n\t\t\tZipEntry zipEntry;\n\t\t\twhile ((zipEntry = zipIn.getNextEntry()) != null) {\n\t\t\t\tFile file = new File(folder, zipEntry.getName());\n\t\t\t\t\n\t\t\t\tif (zipEntry.isDirectory()) {\n\t\t\t\t\tif (!file.exists() && !file.mkdirs()) return false;\n\t\t\t\t} else {\n\t\t\t\t\tif (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) return false;\n\t\t\t\t\tif (!handleStreamCopy(zipIn, new FileOutputStream(file), false, true)) return false;\n\t\t\t\t\tzipIn.closeEntry();\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\treturn true;\n\t\t} catch (IOException e) {\n\t\t\te.printStackTrace();\n\t\t\treturn false;\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tif (zipIn != null) {\n\t\t\t\t\tzipIn.close();\n\t\t\t\t}\n\t\t\t} catch (IOException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t}\n\t\n\tprivate static boolean handleCompress(File folder, OutputStream outputStream) {\n\t\tZipOutputStream out = null;\n\t\t\n\t\ttry {\n\t\t\tList<File> files = new ArrayList<>();\n\t\t\tbuildFileList(files, folder);\n\t\t\t\n\t\t\tString absPath = folder.getAbsolutePath();\n\t\t\tint folderLength = absPath.endsWith(\"/\") ? absPath.length() : absPath.length() + 1;\n\t\t\t\n\t\t\tout = new ZipOutputStream(new BufferedOutputStream(outputStream));\n\t\t\t\n\t\t\tfor (File file : files) {\n\t\t\t\tZipEntry entry = new ZipEntry(file.getAbsolutePath().substring(folderLength));\n\t\t\t\tout.putNextEntry(entry);\n\t\t\t\tif (!handleStreamCopy(new FileInputStream(file), out, true, false)) return false;\n\t\t\t\tout.closeEntry();\n\t\t\t}\n\t\t\t\n\t\t\treturn true;\n\t\t} catch (IOException e) {\n\t\t\te.printStackTrace();\n\t\t\treturn false;\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tif (out != null) {\n\t\t\t\t\tout.close();\n\t\t\t\t}\n\t\t\t} catch (IOException e2) {\n\t\t\t\te2.printStackTrace();\n\t\t\t}\n\t\t}\n\t}\n\t\n\tprivate static void buildFileList(List<File> ret, File dir) {\n\t\tif (dir != null && dir.exists()) {\n\t\t\tif (dir.isDirectory()) {\n\t\t\t\tfor (File file : dir.listFiles()) {\n\t\t\t\t\tbuildFileList(ret, file);\n\t\t\t\t}\n\t\t\t} else if (dir.isFile()) {\n\t\t\t\tret.add(dir);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tprivate static Getter<InputStream> fis(Getter<File> file) {\n\t\treturn context -> {\n\t\t\ttry {\n\t\t\t\treturn new FileInputStream(file.get(context));\n\t\t\t} catch (FileNotFoundException e) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t};\n\t}\n\t\n\tprivate static Getter<OutputStream> fos(Getter<File> file) {\n\t\treturn context -> {\n\t\t\ttry {\n\t\t\t\treturn new FileOutputStream(file.get(context));\n\t\t\t} catch (FileNotFoundException e) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t};\n\t}\n}\n\n## 内容2\n### 文件路径:\nbuild.gradle\n### 代码内容:\n// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n repositories {\n jcenter()\n google()\n }\n dependencies {\n classpath 'com.android.tools.build:gradle:3.6.2'\n }\n}\n\nallprojects {\n repositories {\n google()\n jcenter()\n }\n}\n\n", "input": "", "output": "【漏洞类型】路径遍历漏洞\n【严重性】High\n【漏洞摘要】这是一个路径遍历漏洞。代码在处理ZIP文件时,未验证从ZIP文件中提取的文件名是否在目标文件夹的子目录中。攻击者可能利用ZIP文件中的相对路径(如`../`)来在目标文件夹之外的目录中创建文件,从而实现路径遍历攻击。\n【关键代码模式】`handleExtract`函数中的`file = new File(folder, zipEntry.getName())`行,未验证`zipEntry.getName()`是否在目标文件夹的子目录中。\n【修复方法说明】修复方法通过在提取每个文件之前,检查文件的规范路径是否以目标文件夹的规范路径开头,来防止路径遍历攻击。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nxwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/documentTags.vm\n### 代码内容:\n## ---------------------------------------------------------------------------\n## See the NOTICE file distributed with this work for additional\n## information regarding copyright ownership.\n##\n## This is free software; you can redistribute it and/or modify it\n## under the terms of the GNU Lesser General Public License as\n## published by the Free Software Foundation; either version 2.1 of\n## the License, or (at your option) any later version.\n##\n## This software is distributed in the hope that it will be useful,\n## but WITHOUT ANY WARRANTY; without even the implied warranty of\n## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n## Lesser General Public License for more details.\n##\n## You should have received a copy of the GNU Lesser General Public\n## License along with this software; if not, write to the Free\n## Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n## 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n## ---------------------------------------------------------------------------\n#template(\"xwikivars.vm\")\n#set($tagsId = 'xdocTags')\n#set($tagErrorMessage = '')\n#set($xredirect = $escapetool.url($doc.getURL('view', \"#${tagsId}\")))\n##\n##\n##\n#macro(displayTag $tag)\n<span class=\"tag-wrapper\"><span class=\"tag\"><a href=\"$xwiki.getURL('Main.Tags', 'view', \"do=viewTag&tag=$!{escapetool.url($tag)}\")\">$!{escapetool.xml($tag)}</a></span>#if($hasedit)<span class=\"separator\">[</span><a href=\"$doc.getURL('view', \"xpage=documentTags&xaction=delete&tag=$!{escapetool.url($tag)}&xredirect=${xredirect}\")\" class=\"tag-tool tag-delete\" title=\"$services.localization.render('core.tags.remove.tooltip')\">X</a><span class=\"separator\">]</span>#end</span>\n#end\n##\n#macro(removeTag $tag)\n #if($xwiki.tag)\n #set($result = $xwiki.tag.removeTagFromDocument($tag, $doc.fullName))\n #if($result == 'OK' && \"$!{request.ajax}\" != '')\n $response.setStatus(200)\n #set($responseMessage = 'OK')\n #elseif($result == 'NO_EFFECT')\n $response.setStatus(409)\n #set($responseMessage = $services.localization.render('core.tags.remove.error.notFound', [$tag]))\n #elseif($result == 'NOT_ALLOWED')\n $response.setStatus(403)\n #set($responseMessage = $services.localization.render('core.tags.remove.error.notAllowed', [$tag]))\n #elseif($result == 'FAILED')\n $response.setStatus(500)\n #set($responseMessage = $services.localization.render('core.tags.remove.error.failed', [$tag]))\n #end\n #if(\"$!{request.ajax}\" != '')\n $!responseMessage\n #elseif(\"$!{request.xredirect}\" != '')\n $response.sendRedirect($request.xredirect)\n #end\n #else\n ## TODO\n #end\n#end\n##\n#macro(addTag $tag)\n #if($xwiki.tag)\n #set($oldTags = $xwiki.tag.getTagsFromDocument($doc.fullName))\n #set($result = $xwiki.tag.addTagsToDocument($tag, $doc.fullName))\n #if($result == 'OK' && \"$!{request.ajax}\" != '')\n #set($newTags = $xwiki.tag.getTagsFromDocument($doc.fullName))\n #set($discard = $newTags.removeAll($oldTags))\n #foreach($t in $newTags)\n #if($t != '' && !$oldTags.contains($t))\n #displayTag($t)\n #end\n #end\n #elseif($result == 'NO_EFFECT')\n $response.setStatus(409)\n #set($tagErrorMessage = $services.localization.render('core.tags.add.error.alreadySet', [$tag]))\n #elseif($result == 'NOT_ALLOWED')\n $response.setStatus(403)\n #set($tagErrorMessage = $services.localization.render('core.tags.add.error.notAllowed', [$tag]))\n #elseif($result == 'FAILED')\n $response.setStatus(500)\n #set($tagErrorMessage = $services.localization.render('core.tags.add.error.failed', [$tag]))\n #end\n #if(\"$!{request.ajax}\" != '')\n $tagErrorMessage\n #elseif(\"$!{request.xredirect}\" != '')\n $response.sendRedirect($request.xredirect)\n #end\n #else\n ## TODO\n #end\n#end\n##\n#macro(displayAddForm)\n<form action=\"$doc.getURL('view', \"xpage=documentTags&xaction=add&xredirect=${xredirect}\")\" method=\"post\" class=\"tag-add-form\">\n <div>\n ## CSRF prevention\n <div class=\"hidden\"><input type=\"hidden\" name=\"form_token\" value=\"$!{services.csrf.getToken()}\" /></div>\n <label for=\"tag\">$services.localization.render('core.tags.add.label')<br/>\n <input class=\"input-tag\" type=\"text\" id=\"tag\" name=\"tag\" autocomplete=\"off\"/></label><br/>\n <span class=\"buttonwrapper\"><input class=\"button button-add-tag\" type=\"submit\" value=\"$services.localization.render('core.tags.add.submit')\"/></span>\n <span class=\"buttonwrapper\"><a class=\"button button-add-tag-cancel\" href=\"$doc.getURL('view', \"#${tagsId}\")\">$services.localization.render('core.tags.add.cancel')</a></span>\n </div>\n</form>\n#end\n##\n#set($xaction = \"$!{request.xaction}\")\n#if(\"$!{request.showTagAddForm}\" == 'true' && \"$!{request.ajax}\" == '1')\n #displayAddForm()\n#elseif($xaction == 'delete')\n #removeTag($request.tag)\n#elseif($xaction == 'add')\n #addTag($request.tag)\n#else ## display\n $xwiki.ssfx.use('uicomponents/viewers/tags.css', {'forceSkinAction': true, 'colorTheme': \"$!{themeDocFullName}\"})##\n $xwiki.jsfx.use('uicomponents/viewers/tags.js', true)##\n<div class=\"doc-tags\" id=\"${tagsId}\">\n #if($xwiki.tag)\n #set($hasTagsPlugin = true)\n #set($tags = $xwiki.tag.getTagsFromDocument($doc.fullName))\n #else\n #set($hasTagsPlugin = false)\n #set($tags = $doc.getTagList())\n #end\n ## #if($!tags.size() == 0)\n ## No tags\n ## #else\n $services.localization.render('core.tags.list.label')\n #foreach($tag in $tags)\n #displayTag($tag)\n #end\n ## #end\n #if($hasedit)\n #if($xwiki.tag)\n <div class=\"tag-tool tag-add\">#if(\"$!{request.showTagAddForm}\" == '')<a href=\"$doc.getURL('view', \"showTagAddForm=true&$!{escapetool.url(${request.queryString})}#${tagsId}\")\" title=\"$services.localization.render('core.tags.add.tooltip')\" rel=\"nofollow\">[+]</a>#else #displayAddForm()#end</div>\n #else\n ## TODO\n #end\n #end\n</div>\n #if($tagErrorMessage != '')\n #error($tagErrorMessage)\n #end\n#end\n", "input": "", "output": "【漏洞类型】跨站请求伪造(CSRF)\n【严重性】Medium\n【漏洞摘要】此代码中未对删除和添加标签的操作进行CSRF保护,用户可能在不知情的情况下执行这些操作。具体表现为在`removeTag`和`addTag`宏中,未验证传入的`form_token`参数。\n【关键代码模式】`removeTag`和`addTag`宏中,未验证`form_token`参数。\n【修复方法说明】通过在`removeTag`和`addTag`宏中添加`form_token`验证,修复了CSRF漏洞。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/main/java/com/alibaba/fastjson/parser/ParserConfig.java\n### 代码内容:\npackage com.alibaba.fastjson.parser;\n\nimport java.io.*;\nimport java.lang.ref.SoftReference;\nimport java.lang.ref.WeakReference;\nimport java.lang.reflect.*;\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.net.Inet4Address;\nimport java.net.Inet6Address;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.net.URI;\nimport java.net.URL;\nimport java.nio.charset.Charset;\nimport java.security.AccessControlException;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicIntegerArray;\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.concurrent.atomic.AtomicLongArray;\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.regex.Pattern;\n\nimport com.alibaba.fastjson.*;\nimport com.alibaba.fastjson.annotation.JSONCreator;\nimport com.alibaba.fastjson.annotation.JSONField;\nimport com.alibaba.fastjson.annotation.JSONType;\nimport com.alibaba.fastjson.asm.ClassReader;\nimport com.alibaba.fastjson.asm.TypeCollector;\nimport com.alibaba.fastjson.parser.deserializer.*;\nimport com.alibaba.fastjson.serializer.*;\nimport com.alibaba.fastjson.spi.Module;\nimport com.alibaba.fastjson.support.moneta.MonetaCodec;\nimport com.alibaba.fastjson.util.*;\nimport com.alibaba.fastjson.util.IdentityHashMap;\nimport com.alibaba.fastjson.util.ServiceLoader;\n\nimport javax.xml.datatype.XMLGregorianCalendar;\n\nimport static com.alibaba.fastjson.util.TypeUtils.fnv1a_64_magic_hashcode;\nimport static com.alibaba.fastjson.util.TypeUtils.fnv1a_64_magic_prime;\n\n/**\n * @author wenshao[szujobs@hotmail.com]\n */\npublic class ParserConfig {\n\n public static final String DENY_PROPERTY_INTERNAL = \"fastjson.parser.deny.internal\";\n public static final String DENY_PROPERTY = \"fastjson.parser.deny\";\n public static final String AUTOTYPE_ACCEPT = \"fastjson.parser.autoTypeAccept\";\n public static final String AUTOTYPE_SUPPORT_PROPERTY = \"fastjson.parser.autoTypeSupport\";\n public static final String SAFE_MODE_PROPERTY = \"fastjson.parser.safeMode\";\n\n public static final String[] DENYS_INTERNAL;\n public static final String[] DENYS;\n private static final String[] AUTO_TYPE_ACCEPT_LIST;\n public static final boolean AUTO_SUPPORT;\n public static final boolean SAFE_MODE;\n private static final long[] INTERNAL_WHITELIST_HASHCODES;\n\n static {\n {\n String property = IOUtils.getStringProperty(DENY_PROPERTY_INTERNAL);\n DENYS_INTERNAL = splitItemsFormProperty(property);\n }\n {\n String property = IOUtils.getStringProperty(DENY_PROPERTY);\n DENYS = splitItemsFormProperty(property);\n }\n {\n String property = IOUtils.getStringProperty(AUTOTYPE_SUPPORT_PROPERTY);\n AUTO_SUPPORT = \"true\".equals(property);\n }\n {\n String property = IOUtils.getStringProperty(SAFE_MODE_PROPERTY);\n SAFE_MODE = \"true\".equals(property);\n }\n {\n String property = IOUtils.getStringProperty(AUTOTYPE_ACCEPT);\n String[] items = splitItemsFormProperty(property);\n if (items == null) {\n items = new String[0];\n }\n AUTO_TYPE_ACCEPT_LIST = items;\n }\n\n INTERNAL_WHITELIST_HASHCODES = new long[] {\n 0x82E8E13016B73F9EL,\n 0x863D2DD1E82B9ED9L,\n 0x8B2081CB3A50BD44L,\n 0x90003416F28ACD89L,\n 0x92F252C398C02946L,\n 0x9E404E583F254FD4L,\n 0x9F2E20FB6049A371L,\n 0xA8AAA929446FFCE4L,\n 0xAB9B8D073948CA9DL,\n 0xAFCB539973CEA3F7L,\n 0xB5114C70135C4538L,\n 0xC0FE32B8DC897DE9L,\n 0xC59AA84D9A94C640L,\n 0xC92D8F9129AF339BL,\n 0xCC720543DC5E7090L,\n 0xD0E71A6E155603C1L,\n 0xD11D2A941337A7BCL,\n 0xDB7BFFC197369352L,\n 0xDC9583F0087CC2C7L,\n 0xDDAAA11FECA77B5EL,\n 0xE08EE874A26F5EAFL,\n 0xE794F5F7DCD3AC85L,\n 0xEB7D4786C473368DL,\n 0xF4AA683928027CDAL,\n 0xF8C7EF9B13231FB6L,\n 0xD45D6F8C9017FAL,\n 0x6B949CE6C2FE009L,\n 0x76566C052E83815L,\n 0x9DF9341F0C76702L,\n 0xB81BA299273D4E6L,\n 0xD4788669A13AE74L,\n 0x111D12921C5466DAL,\n 0x178B0E2DC3AE9FE5L,\n 0x19DCAF4ADC37D6D4L,\n 0x1F10A70EE4065963L,\n 0x21082DFBF63FBCC1L,\n 0x24AE2D07FB5D7497L,\n 0x26C5D923AF21E2E1L,\n 0x34CC8E52316FA0CBL,\n 0x3F64BC3933A6A2DFL,\n 0x42646E60EC7E5189L,\n 0x44D57A1B1EF53451L,\n 0x4A39C6C7ACB6AA18L,\n 0x4BB3C59964A2FC50L,\n 0x4F0C3688E8A18F9FL,\n 0x5449EC9B0280B9EFL,\n 0x54DC66A59269BAE1L,\n 0x552D9FB02FFC9DEFL,\n 0x557F642131553498L,\n 0x604D6657082C1EE9L,\n 0x61D10AF54471E5DEL,\n 0x64DC636F343516DCL,\n 0x73A0BE903F2BCBF4L,\n 0x73FBA1E41C4C3553L,\n 0x7B606F16A261E1E6L,\n 0x7F36112F218143B6L,\n 0x7FE2B8E675DA0CEFL\n };\n }\n\n public static ParserConfig getGlobalInstance() {\n return global;\n }\n public static ParserConfig global = new ParserConfig();\n\n private final IdentityHashMap<Type, ObjectDeserializer> deserializers = new IdentityHashMap<Type, ObjectDeserializer>();\n private final IdentityHashMap<Type, IdentityHashMap<Type, ObjectDeserializer>> mixInDeserializers = new IdentityHashMap<Type, IdentityHashMap<Type, ObjectDeserializer>>(16);\n private final ConcurrentMap<String,Class<?>> typeMapping = new ConcurrentHashMap<String,Class<?>>(16, 0.75f, 1);\n\n private boolean asmEnable = !ASMUtils.IS_ANDROID;\n\n public final SymbolTable symbolTable = new SymbolTable(4096);\n\n public PropertyNamingStrategy propertyNamingStrategy;\n\n protected ClassLoader defaultClassLoader;\n\n protected ASMDeserializerFactory asmFactory;\n\n private static boolean awtError = false;\n private static boolean jdk8Error = false;\n private static boolean jodaError = false;\n private static boolean guavaError = false;\n\n private boolean autoTypeSupport = AUTO_SUPPORT;\n private long[] internalDenyHashCodes;\n private long[] denyHashCodes;\n private long[] acceptHashCodes;\n\n public final boolean fieldBased;\n private boolean jacksonCompatible = false;\n\n public boolean compatibleWithJavaBean = TypeUtils.compatibleWithJavaBean;\n private List<Module> modules = new ArrayList<Module>();\n private volatile List<AutoTypeCheckHandler> autoTypeCheckHandlers;\n private boolean safeMode = SAFE_MODE;\n\n {\n denyHashCodes = new long[]{\n 0x80D0C70BCC2FEA02L,\n 0x868385095A22725FL,\n 0x86FC2BF9BEAF7AEFL,\n 0x87F52A1B07EA33A6L,\n 0x8872F29FD0B0B7A7L,\n 0x8BAAEE8F9BF77FA7L,\n 0x8EADD40CB2A94443L,\n 0x8F75F9FA0DF03F80L,\n 0x9172A53F157930AFL,\n 0x92122D710E364FB8L,\n 0x941866E73BEFF4C9L,\n 0x94305C26580F73C5L,\n 0x9437792831DF7D3FL,\n 0xA123A62F93178B20L,\n 0xA85882CE1044C450L,\n 0xAA3DAFFDB10C4937L,\n 0xAAA9E6B7C1E1C6A7L,\n 0xAAAA0826487A3737L,\n 0xAB82562F53E6E48FL,\n 0xAC6262F52C98AA39L,\n 0xAD937A449831E8A0L,\n 0xAE50DA1FAD60A096L,\n 0xAFF6FF23388E225AL,\n 0xAFFF4C95B99A334DL,\n 0xB40F341C746EC94FL,\n 0xB7E8ED757F5D13A2L,\n 0xB98B6B5396932FE9L,\n 0xBCDD9DC12766F0CEL,\n 0xBCE0DEE34E726499L,\n 0xBE4F13E96A6796D0L,\n 0xBEBA72FB1CCBA426L,\n 0xC00BE1DEBAF2808BL,\n 0xC1086AFAE32E6258L,\n 0xC2664D0958ECFE4CL,\n 0xC41FF7C9C87C7C05L,\n 0xC664B363BACA050AL,\n 0xC7599EBFE3E72406L,\n 0xC8D49E5601E661A9L,\n 0xC8F04B3A28909935L,\n 0xC963695082FD728EL,\n 0xCBF29CE484222325L,\n 0xD1EFCDF4B3316D34L,\n 0xD54B91CC77B239EDL,\n 0xD59EE91F0B09EA01L,\n 0xD66F68AB92E7FEF5L,\n 0xD8CA3D595E982BACL,\n 0xDCD8D615A6449E3EL,\n 0xDE23A0809A8B9BD6L,\n 0xDEFC208F237D4104L,\n 0xDF2DDFF310CDB375L,\n 0xE09AE4604842582FL,\n 0xE1919804D5BF468FL,\n 0xE2EB3AC7E56C467EL,\n 0xE603D6A51FAD692BL,\n 0xE704FD19052B2A34L,\n 0xE9184BE55B1D962AL,\n 0xE9F20BAD25F60807L,\n 0xED13653CB45C4BEDL,\n 0xF2983D099D29B477L,\n 0xF3702A4A5490B8E8L,\n 0xF474E44518F26736L,\n 0xF4D93F4FB3E3D991L,\n 0xF5D77DCF8E4D71E6L,\n 0xF6C0340E73A36A69L,\n 0xF7E96E74DFA58DBCL,\n 0xFC773AE20C827691L,\n 0xFCF3E78644B98BD8L,\n 0xFD5BFC610056D720L,\n 0xFFA15BF021F1E37CL,\n 0xFFDD1A80F1ED3405L,\n 0x10E067CD55C5E5L,\n 0x761619136CC13EL,\n 0x22BAA234C5BFB8AL,\n 0x3085068CB7201B8L,\n 0x45B11BC78A3ABA3L,\n 0x55CFCA0F2281C07L,\n 0xA555C74FE3A5155L,\n 0xB6E292FA5955ADEL,\n 0xBEF8514D0B79293L,\n 0xEE6511B66FD5EF0L,\n 0x100150A253996624L,\n 0x10B2BDCA849D9B3EL,\n 0x10DBC48446E0DAE5L,\n 0x119B5B1F10210AFCL,\n 0x144277B467723158L,\n 0x14DB2E6FEAD04AF0L,\n 0x154B6CB22D294CFAL,\n 0x17924CCA5227622AL,\n 0x193B2697EAAED41AL,\n 0x1CD6F11C6A358BB7L,\n 0x1E0A8C3358FF3DAEL,\n 0x24652CE717E713BBL,\n 0x24D2F6048FEF4E49L,\n 0x24EC99D5E7DC5571L,\n 0x25E962F1C28F71A2L,\n 0x275D0732B877AF29L,\n 0x28AC82E44E933606L,\n 0x2A71CE2CC40A710CL,\n 0x2AD1CE3A112F015DL,\n 0x2ADFEFBBFE29D931L,\n 0x2B3A37467A344CDFL,\n 0x2B6DD8B3229D6837L,\n 0x2D308DBBC851B0D8L,\n 0x2FE950D3EA52AE0DL,\n 0x313BB4ABD8D4554CL,\n 0x327C8ED7C8706905L,\n 0x332F0B5369A18310L,\n 0x339A3E0B6BEEBEE9L,\n 0x33C64B921F523F2FL,\n 0x33E7F3E02571B153L,\n 0x34A81EE78429FDF1L,\n 0x37317698DCFCE894L,\n 0x378307CB0111E878L,\n 0x3826F4B2380C8B9BL,\n 0x398F942E01920CF0L,\n 0x3A31412DBB05C7FFL,\n 0x3A7EE0635EB2BC33L,\n 0x3ADBA40367F73264L,\n 0x3B0B51ECBF6DB221L,\n 0x3BF14094A524F0E2L,\n 0x42D11A560FC9FBA9L,\n 0x43320DC9D2AE0892L,\n 0x440E89208F445FB9L,\n 0x46C808A4B5841F57L,\n 0x470FD3A18BB39414L,\n 0x49312BDAFB0077D9L,\n 0x4A3797B30328202CL,\n 0x4BA3E254E758D70DL,\n 0x4BF881E49D37F530L,\n 0x4CF54EEC05E3E818L,\n 0x4DA972745FEB30C1L,\n 0x4EF08C90FF16C675L,\n 0x4FD10DDC6D13821FL,\n 0x521B4F573376DF4AL,\n 0x527DB6B46CE3BCBCL,\n 0x535E552D6F9700C1L,\n 0x54855E265FE1DAD5L,\n 0x5728504A6D454FFCL,\n 0x599B5C1213A099ACL,\n 0x5A5BD85C072E5EFEL,\n 0x5AB0CB3071AB40D1L,\n 0x5B6149820275EA42L,\n 0x5D74D3E5B9370476L,\n 0x5D92E6DDDE40ED84L,\n 0x5E61093EF8CDDDBBL,\n 0x5F215622FB630753L,\n 0x61C5BDD721385107L,\n 0x62DB241274397C34L,\n 0x636ECCA2A131B235L,\n 0x63A220E60A17C7B9L,\n 0x647AB0224E149EBEL,\n 0x65F81B84C1D920CDL,\n 0x665C53C311193973L,\n 0x6749835432E0F0D2L,\n 0x69B6E0175084B377L,\n 0x6A47501EBB2AFDB2L,\n 0x6FCABF6FA54CAFFFL,\n 0x6FE92D83FC0A4628L,\n 0x746BD4A53EC195FBL,\n 0x74B50BB9260E31FFL,\n 0x75CC60F5871D0FD3L,\n 0x767A586A5107FEEFL,\n 0x78E5935826671397L,\n 0x793ADDDED7A967F5L,\n 0x7AA7EE3627A19CF3L,\n 0x7AFA070241B8CC4BL,\n 0x7ED9311D28BF1A65L,\n 0x7ED9481D28BF417AL,\n 0x7EE6C477DA20BBE3L\n };\n\n long[] hashCodes = new long[AUTO_TYPE_ACCEPT_LIST.length];\n for (int i = 0; i < AUTO_TYPE_ACCEPT_LIST.length; i++) {\n hashCodes[i] = TypeUtils.fnv1a_64(AUTO_TYPE_ACCEPT_LIST[i]);\n }\n\n Arrays.sort(hashCodes);\n acceptHashCodes = hashCodes;\n }\n\n public ParserConfig(){\n this(false);\n }\n\n public ParserConfig(boolean fieldBase){\n this(null, null, fieldBase);\n }\n\n public ParserConfig(ClassLoader parentClassLoader){\n this(null, parentClassLoader, false);\n }\n\n public ParserConfig(ASMDeserializerFactory asmFactory){\n this(asmFactory, null, false);\n }\n\n private ParserConfig(ASMDeserializerFactory asmFactory, ClassLoader parentClassLoader, boolean fieldBased){\n this.fieldBased = fieldBased;\n if (asmFactory == null && !ASMUtils.IS_ANDROID) {\n try {\n if (parentClassLoader == null) {\n asmFactory = new ASMDeserializerFactory(new ASMClassLoader());\n } else {\n asmFactory = new ASMDeserializerFactory(parentClassLoader);\n }\n } catch (ExceptionInInitializerError error) {\n // skip\n } catch (AccessControlException error) {\n // skip\n } catch (NoClassDefFoundError error) {\n // skip\n }\n }\n\n this.asmFactory = asmFactory;\n\n if (asmFactory == null) {\n asmEnable = false;\n }\n\n initDeserializers();\n\n addItemsToDeny(DENYS);\n addItemsToDeny0(DENYS_INTERNAL);\n addItemsToAccept(AUTO_TYPE_ACCEPT_LIST);\n\n }\n\n private final Callable<Void> initDeserializersWithJavaSql = new Callable<Void>() {\n public Void call() {\n deserializers.put(java.sql.Timestamp.class, SqlDateDeserializer.instance_timestamp);\n deserializers.put(java.sql.Date.class, SqlDateDeserializer.instance);\n deserializers.put(java.sql.Time.class, TimeDeserializer.instance);\n deserializers.put(java.util.Date.class, DateCodec.instance);\n return null;\n }\n };\n\n private void initDeserializers() {\n deserializers.put(SimpleDateFormat.class, MiscCodec.instance);\n deserializers.put(Calendar.class, CalendarCodec.instance);\n deserializers.put(XMLGregorianCalendar.class, CalendarCodec.instance);\n\n deserializers.put(JSONObject.class, MapDeserializer.instance);\n deserializers.put(JSONArray.class, CollectionCodec.instance);\n\n deserializers.put(Map.class, MapDeserializer.instance);\n deserializers.put(HashMap.class, MapDeserializer.instance);\n deserializers.put(LinkedHashMap.class, MapDeserializer.instance);\n deserializers.put(TreeMap.class, MapDeserializer.instance);\n deserializers.put(ConcurrentMap.class, MapDeserializer.instance);\n deserializers.put(ConcurrentHashMap.class, MapDeserializer.instance);\n\n deserializers.put(Collection.class, CollectionCodec.instance);\n deserializers.put(List.class, CollectionCodec.instance);\n deserializers.put(ArrayList.class, CollectionCodec.instance);\n\n deserializers.put(Object.class, JavaObjectDeserializer.instance);\n deserializers.put(String.class, StringCodec.instance);\n deserializers.put(StringBuffer.class, StringCodec.instance);\n deserializers.put(StringBuilder.class, StringCodec.instance);\n deserializers.put(char.class, CharacterCodec.instance);\n deserializers.put(Character.class, CharacterCodec.instance);\n deserializers.put(byte.class, NumberDeserializer.instance);\n deserializers.put(Byte.class, NumberDeserializer.instance);\n deserializers.put(short.class, NumberDeserializer.instance);\n deserializers.put(Short.class, NumberDeserializer.instance);\n deserializers.put(int.class, IntegerCodec.instance);\n deserializers.put(Integer.class, IntegerCodec.instance);\n deserializers.put(long.class, LongCodec.instance);\n deserializers.put(Long.class, LongCodec.instance);\n deserializers.put(BigInteger.class, BigIntegerCodec.instance);\n deserializers.put(BigDecimal.class, BigDecimalCodec.instance);\n deserializers.put(float.class, FloatCodec.instance);\n deserializers.put(Float.class, FloatCodec.instance);\n deserializers.put(double.class, NumberDeserializer.instance);\n deserializers.put(Double.class, NumberDeserializer.instance);\n deserializers.put(boolean.class, BooleanCodec.instance);\n deserializers.put(Boolean.class, BooleanCodec.instance);\n deserializers.put(Class.class, MiscCodec.instance);\n deserializers.put(char[].class, new CharArrayCodec());\n\n deserializers.put(AtomicBoolean.class, BooleanCodec.instance);\n deserializers.put(AtomicInteger.class, IntegerCodec.instance);\n deserializers.put(AtomicLong.class, LongCodec.instance);\n deserializers.put(AtomicReference.class, ReferenceCodec.instance);\n\n deserializers.put(WeakReference.class, ReferenceCodec.instance);\n deserializers.put(SoftReference.class, ReferenceCodec.instance);\n\n deserializers.put(UUID.class, MiscCodec.instance);\n deserializers.put(TimeZone.class, MiscCodec.instance);\n deserializers.put(Locale.class, MiscCodec.instance);\n deserializers.put(Currency.class, MiscCodec.instance);\n\n deserializers.put(Inet4Address.class, MiscCodec.instance);\n deserializers.put(Inet6Address.class, MiscCodec.instance);\n deserializers.put(InetSocketAddress.class, MiscCodec.instance);\n deserializers.put(File.class, MiscCodec.instance);\n deserializers.put(URI.class, MiscCodec.instance);\n deserializers.put(URL.class, MiscCodec.instance);\n deserializers.put(Pattern.class, MiscCodec.instance);\n deserializers.put(Charset.class, MiscCodec.instance);\n deserializers.put(JSONPath.class, MiscCodec.instance);\n deserializers.put(Number.class, NumberDeserializer.instance);\n deserializers.put(AtomicIntegerArray.class, AtomicCodec.instance);\n deserializers.put(AtomicLongArray.class, AtomicCodec.instance);\n deserializers.put(StackTraceElement.class, StackTraceElementDeserializer.instance);\n\n deserializers.put(Serializable.class, JavaObjectDeserializer.instance);\n deserializers.put(Cloneable.class, JavaObjectDeserializer.instance);\n deserializers.put(Comparable.class, JavaObjectDeserializer.instance);\n deserializers.put(Closeable.class, JavaObjectDeserializer.instance);\n\n deserializers.put(JSONPObject.class, new JSONPDeserializer());\n ModuleUtil.callWhenHasJavaSql(initDeserializersWithJavaSql);\n }\n\n private static String[] splitItemsFormProperty(final String property ){\n if (property != null && property.length() > 0) {\n return property.split(\",\");\n }\n return null;\n }\n\n public void configFromPropety(Properties properties) {\n {\n String property = properties.getProperty(DENY_PROPERTY);\n String[] items = splitItemsFormProperty(property);\n addItemsToDeny(items);\n }\n {\n String property = properties.getProperty(AUTOTYPE_ACCEPT);\n String[] items = splitItemsFormProperty(property);\n addItemsToAccept(items);\n }\n {\n String property = properties.getProperty(AUTOTYPE_SUPPORT_PROPERTY);\n if (\"true\".equals(property)) {\n this.autoTypeSupport = true;\n } else if (\"false\".equals(property)) {\n this.autoTypeSupport = false;\n }\n }\n }\n\n private void addItemsToDeny0(final String[] items){\n if (items == null){\n return;\n }\n\n for (int i = 0; i < items.length; ++i) {\n String item = items[i];\n this.addDenyInternal(item);\n }\n }\n\n private void addItemsToDeny(final String[] items){\n if (items == null){\n return;\n }\n\n for (int i = 0; i < items.length; ++i) {\n String item = items[i];\n this.addDeny(item);\n }\n }\n\n private void addItemsToAccept(final String[] items){\n if (items == null){\n return;\n }\n\n for (int i = 0; i < items.length; ++i) {\n String item = items[i];\n this.addAccept(item);\n }\n }\n\n /**\n * @since 1.2.68\n */\n public boolean isSafeMode() {\n return safeMode;\n }\n\n /**\n * @since 1.2.68\n */\n public void setSafeMode(boolean safeMode) {\n this.safeMode = safeMode;\n }\n\n public boolean isAutoTypeSupport() {\n return autoTypeSupport;\n }\n\n public void setAutoTypeSupport(boolean autoTypeSupport) {\n this.autoTypeSupport = autoTypeSupport;\n }\n\n public boolean isAsmEnable() {\n return asmEnable;\n }\n\n public void setAsmEnable(boolean asmEnable) {\n this.asmEnable = asmEnable;\n }\n\n /**\n * @deprecated\n */\n public IdentityHashMap<Type, ObjectDeserializer> getDerializers() {\n return deserializers;\n }\n\n public IdentityHashMap<Type, ObjectDeserializer> getDeserializers() {\n return deserializers;\n }\n\n public ObjectDeserializer getDeserializer(Type type) {\n ObjectDeserializer deserializer = get(type);\n if (deserializer != null) {\n return deserializer;\n }\n\n if (type instanceof Class<?>) {\n return getDeserializer((Class<?>) type, type);\n }\n\n if (type instanceof ParameterizedType) {\n Type rawType = ((ParameterizedType) type).getRawType();\n if (rawType instanceof Class<?>) {\n return getDeserializer((Class<?>) rawType, type);\n } else {\n return getDeserializer(rawType);\n }\n }\n\n if (type instanceof WildcardType) {\n WildcardType wildcardType = (WildcardType) type;\n Type[] upperBounds = wildcardType.getUpperBounds();\n if (upperBounds.length == 1) {\n Type upperBoundType = upperBounds[0];\n return getDeserializer(upperBoundType);\n }\n }\n\n return JavaObjectDeserializer.instance;\n }\n\n public ObjectDeserializer getDeserializer(Class<?> clazz, Type type) {\n ObjectDeserializer deserializer = get(type);\n if (deserializer == null && type instanceof ParameterizedTypeImpl) {\n Type innerType = TypeReference.intern((ParameterizedTypeImpl) type);\n deserializer = get(innerType);\n }\n\n if (deserializer != null) {\n return deserializer;\n }\n\n if (type == null) {\n type = clazz;\n }\n\n deserializer = get(type);\n if (deserializer != null) {\n return deserializer;\n }\n\n {\n JSONType annotation = TypeUtils.getAnnotation(clazz,JSONType.class);\n if (annotation != null) {\n Class<?> mappingTo = annotation.mappingTo();\n if (mappingTo != Void.class) {\n return getDeserializer(mappingTo, mappingTo);\n }\n }\n }\n\n if (type instanceof WildcardType || type instanceof TypeVariable || type instanceof ParameterizedType) {\n deserializer = get(clazz);\n }\n\n if (deserializer != null) {\n return deserializer;\n }\n\n for (Module module : modules) {\n deserializer = module.createDeserializer(this, clazz);\n if (deserializer != null) {\n putDeserializer(type, deserializer);\n return deserializer;\n }\n }\n\n String className = clazz.getName();\n className = className.replace('$', '.');\n\n if (className.startsWith(\"java.awt.\") //\n && AwtCodec.support(clazz)) {\n if (!awtError) {\n String[] names = new String[] {\n \"java.awt.Point\",\n \"java.awt.Font\",\n \"java.awt.Rectangle\",\n \"java.awt.Color\"\n };\n\n try {\n for (String name : names) {\n if (name.equals(className)) {\n putDeserializer(Class.forName(name), deserializer = AwtCodec.instance);\n return deserializer;\n }\n }\n } catch (Throwable e) {\n // skip\n awtError = true;\n }\n\n deserializer = AwtCodec.instance;\n }\n }\n\n if (!jdk8Error) {\n try {\n if (className.startsWith(\"java.time.\")) {\n String[] names = new String[] {\n \"java.time.LocalDateTime\",\n \"java.time.LocalDate\",\n \"java.time.LocalTime\",\n \"java.time.ZonedDateTime\",\n \"java.time.OffsetDateTime\",\n \"java.time.OffsetTime\",\n \"java.time.ZoneOffset\",\n \"java.time.ZoneRegion\",\n \"java.time.ZoneId\",\n \"java.time.Period\",\n \"java.time.Duration\",\n \"java.time.Instant\"\n };\n\n for (String name : names) {\n if (name.equals(className)) {\n putDeserializer(Class.forName(name), deserializer = Jdk8DateCodec.instance);\n return deserializer;\n }\n }\n } else if (className.startsWith(\"java.util.Optional\")) {\n String[] names = new String[] {\n \"java.util.Optional\",\n \"java.util.OptionalDouble\",\n \"java.util.OptionalInt\",\n \"java.util.OptionalLong\"\n };\n for (String name : names) {\n if (name.equals(className)) {\n putDeserializer(Class.forName(name), deserializer = OptionalCodec.instance);\n return deserializer;\n }\n }\n }\n } catch (Throwable e) {\n // skip\n jdk8Error = true;\n }\n }\n\n if (!jodaError) {\n try {\n if (className.startsWith(\"org.joda.time.\")) {\n String[] names = new String[] {\n \"org.joda.time.DateTime\",\n \"org.joda.time.LocalDate\",\n \"org.joda.time.LocalDateTime\",\n \"org.joda.time.LocalTime\",\n \"org.joda.time.Instant\",\n \"org.joda.time.Period\",\n \"org.joda.time.Duration\",\n \"org.joda.time.DateTimeZone\",\n \"org.joda.time.format.DateTimeFormatter\"\n };\n\n for (String name : names) {\n if (name.equals(className)) {\n putDeserializer(Class.forName(name), deserializer = JodaCodec.instance);\n return deserializer;\n }\n }\n }\n } catch (Throwable e) {\n // skip\n jodaError = true;\n }\n }\n\n if ((!guavaError) //\n && className.startsWith(\"com.google.common.collect.\")) {\n try {\n String[] names = new String[] {\n \"com.google.common.collect.HashMultimap\",\n \"com.google.common.collect.LinkedListMultimap\",\n \"com.google.common.collect.LinkedHashMultimap\",\n \"com.google.common.collect.ArrayListMultimap\",\n \"com.google.common.collect.TreeMultimap\"\n };\n\n for (String name : names) {\n if (name.equals(className)) {\n putDeserializer(Class.forName(name), deserializer = GuavaCodec.instance);\n return deserializer;\n }\n }\n } catch (ClassNotFoundException e) {\n // skip\n guavaError = true;\n }\n }\n\n if (className.equals(\"java.nio.ByteBuffer\")) {\n putDeserializer(clazz, deserializer = ByteBufferCodec.instance);\n }\n\n if (className.equals(\"java.nio.file.Path\")) {\n putDeserializer(clazz, deserializer = MiscCodec.instance);\n }\n\n if (clazz == Map.Entry.class) {\n putDeserializer(clazz, deserializer = MiscCodec.instance);\n }\n\n if (className.equals(\"org.javamoney.moneta.Money\")) {\n putDeserializer(clazz, deserializer = MonetaCodec.instance);\n }\n\n final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();\n try {\n for (AutowiredObjectDeserializer autowired : ServiceLoader.load(AutowiredObjectDeserializer.class,\n classLoader)) {\n for (Type forType : autowired.getAutowiredFor()) {\n putDeserializer(forType, autowired);\n }\n }\n } catch (Exception ex) {\n // skip\n }\n\n if (deserializer == null) {\n deserializer = get(type);\n }\n\n if (deserializer != null) {\n return deserializer;\n }\n\n if (clazz.isEnum()) {\n if (jacksonCompatible) {\n Method[] methods = clazz.getMethods();\n for (Method method : methods) {\n if (TypeUtils.isJacksonCreator(method)) {\n deserializer = createJavaBeanDeserializer(clazz, type);\n putDeserializer(type, deserializer);\n return deserializer;\n }\n }\n }\n\n Class mixInType = (Class) JSON.getMixInAnnotations(clazz);\n\n Class<?> deserClass = null;\n JSONType jsonType = TypeUtils.getAnnotation(mixInType != null ? mixInType : clazz, JSONType.class);\n\n if (jsonType != null) {\n deserClass = jsonType.deserializer();\n try {\n deserializer = (ObjectDeserializer) deserClass.newInstance();\n putDeserializer(clazz, deserializer);\n return deserializer;\n } catch (Throwable error) {\n // skip\n }\n }\n\n Method jsonCreatorMethod = null;\n if (mixInType != null) {\n Method mixedCreator = getEnumCreator(mixInType, clazz);\n if (mixedCreator != null) {\n try {\n jsonCreatorMethod = clazz.getMethod(mixedCreator.getName(), mixedCreator.getParameterTypes());\n } catch (Exception e) {\n // skip\n }\n }\n } else {\n jsonCreatorMethod = getEnumCreator(clazz, clazz);\n }\n\n if (jsonCreatorMethod != null) {\n deserializer = new EnumCreatorDeserializer(jsonCreatorMethod);\n putDeserializer(clazz, deserializer);\n return deserializer;\n }\n\n deserializer = getEnumDeserializer(clazz);\n } else if (clazz.isArray()) {\n deserializer = ObjectArrayCodec.instance;\n } else if (clazz == Set.class || clazz == HashSet.class || clazz == Collection.class || clazz == List.class\n || clazz == ArrayList.class) {\n deserializer = CollectionCodec.instance;\n } else if (Collection.class.isAssignableFrom(clazz)) {\n deserializer = CollectionCodec.instance;\n } else if (Map.class.isAssignableFrom(clazz)) {\n deserializer = MapDeserializer.instance;\n } else if (Throwable.class.isAssignableFrom(clazz)) {\n deserializer = new ThrowableDeserializer(this, clazz);\n } else if (PropertyProcessable.class.isAssignableFrom(clazz)) {\n deserializer = new PropertyProcessableDeserializer((Class<PropertyProcessable>) clazz);\n } else if (clazz == InetAddress.class) {\n deserializer = MiscCodec.instance;\n } else {\n deserializer = createJavaBeanDeserializer(clazz, type);\n }\n\n putDeserializer(type, deserializer);\n\n return deserializer;\n }\n\n private static Method getEnumCreator(Class clazz, Class enumClass) {\n Method[] methods = clazz.getMethods();\n Method jsonCreatorMethod = null;\n for (Method method : methods) {\n if (Modifier.isStatic(method.getModifiers())\n && method.getReturnType() == enumClass\n && method.getParameterTypes().length == 1\n ) {\n JSONCreator jsonCreator = method.getAnnotation(JSONCreator.class);\n if (jsonCreator != null) {\n jsonCreatorMethod = method;\n break;\n }\n }\n }\n\n return jsonCreatorMethod;\n }\n\n /**\n * 可以通过重写这个方法,定义自己的枚举反序列化实现\n * @param clazz 转换的类型\n * @return 返回一个枚举的反序列化实现\n * @author zhu.xiaojie\n * @time 2020-4-5\n */\n protected ObjectDeserializer getEnumDeserializer(Class<?> clazz){\n return new EnumDeserializer(clazz);\n }\n\n /**\n *\n * @since 1.2.25\n */\n public void initJavaBeanDeserializers(Class<?>... classes) {\n if (classes == null) {\n return;\n }\n\n for (Class<?> type : classes) {\n if (type == null) {\n continue;\n }\n ObjectDeserializer deserializer = createJavaBeanDeserializer(type, type);\n putDeserializer(type, deserializer);\n }\n }\n\n public ObjectDeserializer createJavaBeanDeserializer(Class<?> clazz, Type type) {\n boolean asmEnable = this.asmEnable & !this.fieldBased;\n if (asmEnable) {\n JSONType jsonType = TypeUtils.getAnnotation(clazz,JSONType.class);\n\n if (jsonType != null) {\n Class<?> deserializerClass = jsonType.deserializer();\n if (deserializerClass != Void.class) {\n try {\n Object deseralizer = deserializerClass.newInstance();\n if (deseralizer instanceof ObjectDeserializer) {\n return (ObjectDeserializer) deseralizer;\n }\n } catch (Throwable e) {\n // skip\n }\n }\n\n asmEnable = jsonType.asm()\n && jsonType.parseFeatures().length == 0;\n }\n\n if (asmEnable) {\n Class<?> superClass = JavaBeanInfo.getBuilderClass(clazz, jsonType);\n if (superClass == null) {\n superClass = clazz;\n }\n\n for (;;) {\n if (!Modifier.isPublic(superClass.getModifiers())) {\n asmEnable = false;\n break;\n }\n\n superClass = superClass.getSuperclass();\n if (superClass == Object.class || superClass == null) {\n break;\n }\n }\n }\n }\n\n if (clazz.getTypeParameters().length != 0) {\n asmEnable = false;\n }\n\n if (asmEnable && asmFactory != null && asmFactory.classLoader.isExternalClass(clazz)) {\n asmEnable = false;\n }\n\n if (asmEnable) {\n asmEnable = ASMUtils.checkName(clazz.getSimpleName());\n }\n\n if (asmEnable) {\n if (clazz.isInterface()) {\n asmEnable = false;\n }\n JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz\n , type\n , propertyNamingStrategy\n ,false\n , TypeUtils.compatibleWithJavaBean\n , jacksonCompatible\n );\n\n if (asmEnable && beanInfo.fields.length > 200) {\n asmEnable = false;\n }\n\n Constructor<?> defaultConstructor = beanInfo.defaultConstructor;\n if (asmEnable && defaultConstructor == null && !clazz.isInterface()) {\n asmEnable = false;\n }\n\n for (FieldInfo fieldInfo : beanInfo.fields) {\n if (fieldInfo.getOnly) {\n asmEnable = false;\n break;\n }\n\n Class<?> fieldClass = fieldInfo.fieldClass;\n if (!Modifier.isPublic(fieldClass.getModifiers())) {\n asmEnable = false;\n break;\n }\n\n if (fieldClass.isMemberClass() && !Modifier.isStatic(fieldClass.getModifiers())) {\n asmEnable = false;\n break;\n }\n\n if (fieldInfo.getMember() != null //\n && !ASMUtils.checkName(fieldInfo.getMember().getName())) {\n asmEnable = false;\n break;\n }\n\n JSONField annotation = fieldInfo.getAnnotation();\n if (annotation != null //\n && ((!ASMUtils.checkName(annotation.name())) //\n || annotation.format().length() != 0 //\n || annotation.deserializeUsing() != Void.class //\n || annotation.parseFeatures().length != 0 //\n || annotation.unwrapped())\n || (fieldInfo.method != null && fieldInfo.method.getParameterTypes().length > 1)) {\n asmEnable = false;\n break;\n }\n\n if (fieldClass.isEnum()) { // EnumDeserializer\n ObjectDeserializer fieldDeser = this.getDeserializer(fieldClass);\n if (!(fieldDeser instanceof EnumDeserializer)) {\n asmEnable = false;\n break;\n }\n }\n }\n }\n\n if (asmEnable) {\n if (clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers())) {\n asmEnable = false;\n }\n }\n\n if (asmEnable) {\n if (TypeUtils.isXmlField(clazz)) {\n asmEnable = false;\n }\n }\n\n if (!asmEnable) {\n return new JavaBeanDeserializer(this, clazz, type);\n }\n\n JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz, type, propertyNamingStrategy);\n try {\n return asmFactory.createJavaBeanDeserializer(this, beanInfo);\n // } catch (VerifyError e) {\n // e.printStackTrace();\n // return new JavaBeanDeserializer(this, clazz, type);\n } catch (NoSuchMethodException ex) {\n return new JavaBeanDeserializer(this, clazz, type);\n } catch (JSONException asmError) {\n return new JavaBeanDeserializer(this, beanInfo);\n } catch (Exception e) {\n throw new JSONException(\"create asm deserializer error, \" + clazz.getName(), e);\n }\n }\n\n public FieldDeserializer createFieldDeserializer(ParserConfig mapping, //\n JavaBeanInfo beanInfo, //\n FieldInfo fieldInfo) {\n Class<?> clazz = beanInfo.clazz;\n Class<?> fieldClass = fieldInfo.fieldClass;\n\n Class<?> deserializeUsing = null;\n JSONField annotation = fieldInfo.getAnnotation();\n if (annotation != null) {\n deserializeUsing = annotation.deserializeUsing();\n if (deserializeUsing == Void.class) {\n deserializeUsing = null;\n }\n }\n\n if (deserializeUsing == null && (fieldClass == List.class || fieldClass == ArrayList.class)) {\n return new ArrayListTypeFieldDeserializer(mapping, clazz, fieldInfo);\n }\n\n return new DefaultFieldDeserializer(mapping, clazz, fieldInfo);\n }\n\n public void putDeserializer(Type type, ObjectDeserializer deserializer) {\n Type mixin = JSON.getMixInAnnotations(type);\n if (mixin != null) {\n IdentityHashMap<Type, ObjectDeserializer> mixInClasses = this.mixInDeserializers.get(type);\n if (mixInClasses == null) {\n //多线程下可能会重复创建,但不影响正确性\n mixInClasses = new IdentityHashMap<Type, ObjectDeserializer>(4);\n this.mixInDeserializers.put(type, mixInClasses);\n }\n mixInClasses.put(mixin, deserializer);\n } else {\n this.deserializers.put(type, deserializer);\n }\n }\n\n public ObjectDeserializer get(Type type) {\n Type mixin = JSON.getMixInAnnotations(type);\n if (null == mixin) {\n return this.deserializers.get(type);\n }\n IdentityHashMap<Type, ObjectDeserializer> mixInClasses = this.mixInDeserializers.get(type);\n if (mixInClasses == null) {\n return null;\n }\n return mixInClasses.get(mixin);\n }\n\n public ObjectDeserializer getDeserializer(FieldInfo fieldInfo) {\n return getDeserializer(fieldInfo.fieldClass, fieldInfo.fieldType);\n }\n\n /**\n * @deprecated internal method, dont call\n */\n public boolean isPrimitive(Class<?> clazz) {\n return isPrimitive2(clazz);\n }\n\n private static Function<Class<?>, Boolean> isPrimitiveFuncation = new Function<Class<?>, Boolean>() {\n public Boolean apply(Class<?> clazz) {\n return clazz == java.sql.Date.class //\n || clazz == java.sql.Time.class //\n || clazz == java.sql.Timestamp.class;\n }\n };\n\n /**\n * @deprecated internal method, dont call\n */\n public static boolean isPrimitive2(final Class<?> clazz) {\n Boolean primitive = clazz.isPrimitive() //\n || clazz == Boolean.class //\n || clazz == Character.class //\n || clazz == Byte.class //\n || clazz == Short.class //\n || clazz == Integer.class //\n || clazz == Long.class //\n || clazz == Float.class //\n || clazz == Double.class //\n || clazz == BigInteger.class //\n || clazz == BigDecimal.class //\n || clazz == String.class //\n || clazz == java.util.Date.class //\n || clazz.isEnum() //\n ;\n if (!primitive) {\n primitive = ModuleUtil.callWhenHasJavaSql(isPrimitiveFuncation, clazz);\n }\n return primitive != null ? primitive : false;\n }\n\n /**\n * fieldName,field ,先生成fieldName的快照,减少之后的findField的轮询\n *\n * @param clazz\n * @param fieldCacheMap :map<fieldName ,Field>\n */\n public static void parserAllFieldToCache(Class<?> clazz,Map</**fieldName*/String , Field> fieldCacheMap){\n Field[] fields = clazz.getDeclaredFields();\n for (Field field : fields) {\n String fieldName = field.getName();\n if (!fieldCacheMap.containsKey(fieldName)) {\n fieldCacheMap.put(fieldName, field);\n }\n }\n if (clazz.getSuperclass() != null && clazz.getSuperclass() != Object.class) {\n parserAllFieldToCache(clazz.getSuperclass(), fieldCacheMap);\n }\n }\n\n public static Field getFieldFromCache(String fieldName, Map<String, Field> fieldCacheMap) {\n Field field = fieldCacheMap.get(fieldName);\n\n if (field == null) {\n field = fieldCacheMap.get(\"_\" + fieldName);\n }\n\n if (field == null) {\n field = fieldCacheMap.get(\"m_\" + fieldName);\n }\n\n if (field == null) {\n char c0 = fieldName.charAt(0);\n if (c0 >= 'a' && c0 <= 'z') {\n char[] chars = fieldName.toCharArray();\n chars[0] -= 32; // lower\n String fieldNameX = new String(chars);\n field = fieldCacheMap.get(fieldNameX);\n }\n\n if (fieldName.length() > 2) {\n char c1 = fieldName.charAt(1);\n if (c0 >= 'a' && c0 <= 'z'\n && c1 >= 'A' && c1 <= 'Z') {\n for (Map.Entry<String, Field> entry : fieldCacheMap.entrySet()) {\n if (fieldName.equalsIgnoreCase(entry.getKey())) {\n field = entry.getValue();\n break;\n }\n }\n }\n }\n }\n\n return field;\n }\n\n public ClassLoader getDefaultClassLoader() {\n return defaultClassLoader;\n }\n\n public void setDefaultClassLoader(ClassLoader defaultClassLoader) {\n this.defaultClassLoader = defaultClassLoader;\n }\n\n public void addDenyInternal(String name) {\n if (name == null || name.length() == 0) {\n return;\n }\n\n long hash = TypeUtils.fnv1a_64(name);\n if (internalDenyHashCodes == null) {\n this.internalDenyHashCodes = new long[] {hash};\n return;\n }\n\n if (Arrays.binarySearch(this.internalDenyHashCodes, hash) >= 0) {\n return;\n }\n\n long[] hashCodes = new long[this.internalDenyHashCodes.length + 1];\n hashCodes[hashCodes.length - 1] = hash;\n System.arraycopy(this.internalDenyHashCodes, 0, hashCodes, 0, this.internalDenyHashCodes.length);\n Arrays.sort(hashCodes);\n this.internalDenyHashCodes = hashCodes;\n }\n\n public void addDeny(String name) {\n if (name == null || name.length() == 0) {\n return;\n }\n\n long hash = TypeUtils.fnv1a_64(name);\n if (Arrays.binarySearch(this.denyHashCodes, hash) >= 0) {\n return;\n }\n\n long[] hashCodes = new long[this.denyHashCodes.length + 1];\n hashCodes[hashCodes.length - 1] = hash;\n System.arraycopy(this.denyHashCodes, 0, hashCodes, 0, this.denyHashCodes.length);\n Arrays.sort(hashCodes);\n this.denyHashCodes = hashCodes;\n }\n\n public void addAccept(String name) {\n if (name == null || name.length() == 0) {\n return;\n }\n\n long hash = TypeUtils.fnv1a_64(name);\n if (Arrays.binarySearch(this.acceptHashCodes, hash) >= 0) {\n return;\n }\n\n long[] hashCodes = new long[this.acceptHashCodes.length + 1];\n hashCodes[hashCodes.length - 1] = hash;\n System.arraycopy(this.acceptHashCodes, 0, hashCodes, 0, this.acceptHashCodes.length);\n Arrays.sort(hashCodes);\n this.acceptHashCodes = hashCodes;\n }\n\n public Class<?> checkAutoType(Class type) {\n if (get(type) != null) {\n return type;\n }\n\n return checkAutoType(type.getName(), null, JSON.DEFAULT_PARSER_FEATURE);\n }\n\n public Class<?> checkAutoType(String typeName, Class<?> expectClass) {\n return checkAutoType(typeName, expectClass, JSON.DEFAULT_PARSER_FEATURE);\n }\n\n public Class<?> checkAutoType(String typeName, Class<?> expectClass, int features) {\n if (typeName == null) {\n return null;\n }\n\n if (autoTypeCheckHandlers != null) {\n for (AutoTypeCheckHandler h : autoTypeCheckHandlers) {\n Class<?> type = h.handler(typeName, expectClass, features);\n if (type != null) {\n return type;\n }\n }\n }\n\n final int safeModeMask = Feature.SafeMode.mask;\n boolean safeMode = this.safeMode\n || (features & safeModeMask) != 0\n || (JSON.DEFAULT_PARSER_FEATURE & safeModeMask) != 0;\n if (safeMode) {\n throw new JSONException(\"safeMode not support autoType : \" + typeName);\n }\n\n if (typeName.length() >= 192 || typeName.length() < 3) {\n throw new JSONException(\"autoType is not support. \" + typeName);\n }\n\n final boolean expectClassFlag;\n if (expectClass == null) {\n expectClassFlag = false;\n } else {\n long expectHash = TypeUtils.fnv1a_64(expectClass.getName());\n if (expectHash == 0x90a25f5baa21529eL\n || expectHash == 0x2d10a5801b9d6136L\n || expectHash == 0xaf586a571e302c6bL\n || expectHash == 0xed007300a7b227c6L\n || expectHash == 0x295c4605fd1eaa95L\n || expectHash == 0x47ef269aadc650b4L\n || expectHash == 0x6439c4dff712ae8bL\n || expectHash == 0xe3dd9875a2dc5283L\n || expectHash == 0xe2a8ddba03e69e0dL\n || expectHash == 0xd734ceb4c3e9d1daL\n ) {\n expectClassFlag = false;\n } else {\n expectClassFlag = true;\n }\n }\n\n String className = typeName.replace('$', '.');\n Class<?> clazz;\n\n final long h1 = (fnv1a_64_magic_hashcode ^ className.charAt(0)) * fnv1a_64_magic_prime;\n if (h1 == 0xaf64164c86024f1aL) { // [\n throw new JSONException(\"autoType is not support. \" + typeName);\n }\n\n if ((h1 ^ className.charAt(className.length() - 1)) * fnv1a_64_magic_prime == 0x9198507b5af98f0L) {\n throw new JSONException(\"autoType is not support. \" + typeName);\n }\n\n final long h3 = (((((fnv1a_64_magic_hashcode ^ className.charAt(0))\n * fnv1a_64_magic_prime)\n ^ className.charAt(1))\n * fnv1a_64_magic_prime)\n ^ className.charAt(2))\n * fnv1a_64_magic_prime;\n\n long fullHash = TypeUtils.fnv1a_64(className);\n boolean internalWhite = Arrays.binarySearch(INTERNAL_WHITELIST_HASHCODES, fullHash) >= 0;\n\n if (internalDenyHashCodes != null) {\n long hash = h3;\n for (int i = 3; i < className.length(); ++i) {\n hash ^= className.charAt(i);\n hash *= fnv1a_64_magic_prime;\n if (Arrays.binarySearch(internalDenyHashCodes, hash) >= 0) {\n throw new JSONException(\"autoType is not support. \" + typeName);\n }\n }\n }\n\n if ((!internalWhite) && (autoTypeSupport || expectClassFlag)) {\n long hash = h3;\n for (int i = 3; i < className.length(); ++i) {\n hash ^= className.charAt(i);\n hash *= fnv1a_64_magic_prime;\n if (Arrays.binarySearch(acceptHashCodes, hash) >= 0) {\n clazz = TypeUtils.loadClass(typeName, defaultClassLoader, true);\n if (clazz != null) {\n return clazz;\n }\n }\n if (Arrays.binarySearch(denyHashCodes, hash) >= 0 && TypeUtils.getClassFromMapping(typeName) == null) {\n if (Arrays.binarySearch(acceptHashCodes, fullHash) >= 0) {\n continue;\n }\n\n throw new JSONException(\"autoType is not support. \" + typeName);\n }\n }\n }\n\n clazz = TypeUtils.getClassFromMapping(typeName);\n\n if (clazz == null) {\n clazz = deserializers.findClass(typeName);\n }\n\n if (expectClass == null && clazz != null && Throwable.class.isAssignableFrom(clazz) && !autoTypeSupport) {\n clazz = null;\n }\n\n if (clazz == null) {\n clazz = typeMapping.get(typeName);\n }\n\n if (internalWhite) {\n clazz = TypeUtils.loadClass(typeName, defaultClassLoader, true);\n }\n\n if (clazz != null) {\n if (expectClass != null\n && clazz != java.util.HashMap.class\n && clazz != java.util.LinkedHashMap.class\n && !expectClass.isAssignableFrom(clazz)) {\n throw new JSONException(\"type not match. \" + typeName + \" -> \" + expectClass.getName());\n }\n\n return clazz;\n }\n\n if (!autoTypeSupport) {\n long hash = h3;\n for (int i = 3; i < className.length(); ++i) {\n char c = className.charAt(i);\n hash ^= c;\n hash *= fnv1a_64_magic_prime;\n\n if (Arrays.binarySearch(denyHashCodes, hash) >= 0) {\n throw new JSONException(\"autoType is not support. \" + typeName);\n }\n\n // white list\n if (Arrays.binarySearch(acceptHashCodes, hash) >= 0) {\n clazz = TypeUtils.loadClass(typeName, defaultClassLoader, true);\n\n if (clazz == null) {\n return expectClass;\n }\n\n if (expectClass != null && expectClass.isAssignableFrom(clazz)) {\n throw new JSONException(\"type not match. \" + typeName + \" -> \" + expectClass.getName());\n }\n\n return clazz;\n }\n }\n }\n\n boolean jsonType = false;\n InputStream is = null;\n try {\n String resource = typeName.replace('.', '/') + \".class\";\n if (defaultClassLoader != null) {\n is = defaultClassLoader.getResourceAsStream(resource);\n } else {\n is = ParserConfig.class.getClassLoader().getResourceAsStream(resource);\n }\n if (is != null) {\n ClassReader classReader = new ClassReader(is, true);\n TypeCollector visitor = new TypeCollector(\"<clinit>\", new Class[0]);\n classReader.accept(visitor);\n jsonType = visitor.hasJsonType();\n }\n } catch (Exception e) {\n // skip\n } finally {\n IOUtils.close(is);\n }\n\n final int mask = Feature.SupportAutoType.mask;\n boolean autoTypeSupport = this.autoTypeSupport\n || (features & mask) != 0\n || (JSON.DEFAULT_PARSER_FEATURE & mask) != 0;\n\n if (autoTypeSupport || jsonType || expectClassFlag) {\n boolean cacheClass = autoTypeSupport || jsonType;\n clazz = TypeUtils.loadClass(typeName, defaultClassLoader, cacheClass);\n }\n\n if (clazz != null) {\n if (jsonType) {\n TypeUtils.addMapping(typeName, clazz);\n return clazz;\n }\n\n if (ClassLoader.class.isAssignableFrom(clazz) // classloader is danger\n || javax.sql.DataSource.class.isAssignableFrom(clazz) // dataSource can load jdbc driver\n || javax.sql.RowSet.class.isAssignableFrom(clazz) //\n ) {\n throw new JSONException(\"autoType is not support. \" + typeName);\n }\n\n if (expectClass != null) {\n if (expectClass.isAssignableFrom(clazz)) {\n TypeUtils.addMapping(typeName, clazz);\n return clazz;\n } else {\n throw new JSONException(\"type not match. \" + typeName + \" -> \" + expectClass.getName());\n }\n }\n\n JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz, clazz, propertyNamingStrategy);\n if (beanInfo.creatorConstructor != null && autoTypeSupport) {\n throw new JSONException(\"autoType is not support. \" + typeName);\n }\n }\n\n if (!autoTypeSupport) {\n if (typeName.endsWith(\"Exception\")) {\n return null;\n }\n\n throw new JSONException(\"autoType is not support. \" + typeName);\n }\n\n if (clazz != null) {\n TypeUtils.addMapping(typeName, clazz);\n }\n\n return clazz;\n }\n\n public void clearDeserializers() {\n this.deserializers.clear();\n this.initDeserializers();\n }\n\n public boolean isJacksonCompatible() {\n return jacksonCompatible;\n }\n\n public void setJacksonCompatible(boolean jacksonCompatible) {\n this.jacksonCompatible = jacksonCompatible;\n }\n\n public void register(String typeName, Class type) {\n typeMapping.putIfAbsent(typeName, type);\n }\n\n public void register(Module module) {\n this.modules.add(module);\n }\n\n public void addAutoTypeCheckHandler(AutoTypeCheckHandler h) {\n List<AutoTypeCheckHandler> autoTypeCheckHandlers = this.autoTypeCheckHandlers;\n if (autoTypeCheckHandlers == null) {\n this.autoTypeCheckHandlers\n = autoTypeCheckHandlers\n = new CopyOnWriteArrayList();\n }\n\n autoTypeCheckHandlers.add(h);\n }\n\n /**\n * @since 1.2.68\n */\n public interface AutoTypeCheckHandler {\n Class<?> handler(String typeName, Class<?> expectClass, int features);\n }\n}\n\n", "input": "", "output": "【漏洞类型】反序列化漏洞(Deserialization Vulnerability)\n【严重性】High\n【漏洞摘要】FastJSON库在反序列化过程中存在AutoType攻击风险。AutoType功能允许通过字符串指定要反序列化的类名,并根据这些类名动态加载类,从而可能导致攻击者通过恶意输入构造反序列化链,执行任意代码。在原始代码中,`checkAutoType`方法中的`Arrays.binarySearch`和`TypeUtils.loadClass`的使用存在潜在风险,因为这些操作依赖于用户输入的类名。\n【关键代码模式】\n- `checkAutoType`方法\n- `TypeUtils.loadClass`方法\n- `Arrays.binarySearch`方法\n- user input affecting class loading\n【修复方法说明】修复方法通过在`checkAutoType`方法中添加对`typeName`是否以`\"Exception\"`或`\"Error\"`结尾的检查来限制反序列化的类类型,从而降低AutoType攻击的风险。这种方法采用黑名单策略(即禁止特定类型的类),增加了代码的安全性。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nmodules/swagger-generator/src/main/java/io/swagger/generator/online/Generator.java\n### 代码内容:\npackage io.swagger.generator.online;\n\nimport com.fasterxml.jackson.databind.JsonNode;\nimport io.swagger.codegen.*;\nimport io.swagger.generator.exception.ApiException;\nimport io.swagger.generator.exception.BadRequestException;\nimport io.swagger.generator.model.GeneratorInput;\nimport io.swagger.generator.model.InputOption;\nimport io.swagger.generator.util.ZipUtil;\nimport io.swagger.models.Swagger;\nimport io.swagger.models.auth.AuthorizationValue;\nimport io.swagger.parser.SwaggerParser;\nimport io.swagger.util.Json;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.File;\nimport java.nio.file.Files;\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\npublic class Generator {\n static Logger LOGGER = LoggerFactory.getLogger(Generator.class);\n\n public static Map<String, CliOption> getOptions(String language) throws ApiException {\n CodegenConfig config = null;\n try {\n config = CodegenConfigLoader.forName(language);\n } catch (Exception e) {\n throw new BadRequestException(String.format(\"Unsupported target %s supplied. %s\",\n language, e));\n }\n Map<String, CliOption> map = new LinkedHashMap<String, CliOption>();\n for (CliOption option : config.cliOptions()) {\n map.put(option.getOpt(), option);\n }\n return map;\n }\n\n public enum Type {\n CLIENT(\"client\"), SERVER(\"server\");\n\n private String name;\n\n Type(String name) {\n this.name = name;\n }\n\n String getTypeName() {\n return name;\n }\n }\n\n public static String generateClient(String language, GeneratorInput opts) throws ApiException {\n return generate(language, opts, Type.CLIENT);\n }\n\n public static String generateServer(String language, GeneratorInput opts) throws ApiException {\n return generate(language, opts, Type.SERVER);\n }\n\n private static String generate(String language, GeneratorInput opts, Type type)\n throws ApiException {\n LOGGER.debug(String.format(\"generate %s for %s\", type.getTypeName(), language));\n if (opts == null) {\n throw new BadRequestException(\"No options were supplied\");\n }\n JsonNode node = opts.getSpec();\n if (node != null && \"{}\".equals(node.toString())) {\n LOGGER.debug(\"ignoring empty spec\");\n node = null;\n }\n Swagger swagger;\n if (node == null) {\n if (opts.getSwaggerUrl() != null) {\n if (opts.getAuthorizationValue() != null) {\n List<AuthorizationValue> authorizationValues =\n new ArrayList<AuthorizationValue>();\n authorizationValues.add(opts.getAuthorizationValue());\n\n swagger =\n new SwaggerParser().read(opts.getSwaggerUrl(), authorizationValues,\n true);\n } else {\n swagger = new SwaggerParser().read(opts.getSwaggerUrl());\n }\n } else {\n throw new BadRequestException(\"No swagger specification was supplied\");\n }\n } else if (opts.getAuthorizationValue() != null) {\n List<AuthorizationValue> authorizationValues = new ArrayList<AuthorizationValue>();\n authorizationValues.add(opts.getAuthorizationValue());\n swagger = new SwaggerParser().read(node, authorizationValues, true);\n } else {\n swagger = new SwaggerParser().read(node, true);\n }\n if (swagger == null) {\n throw new BadRequestException(\"The swagger specification supplied was not valid\");\n }\n\n String destPath = null;\n\n if (opts != null && opts.getOptions() != null) {\n destPath = opts.getOptions().get(\"outputFolder\");\n }\n if (destPath == null) {\n destPath = language + \"-\" + type.getTypeName();\n }\n\n ClientOptInput clientOptInput = new ClientOptInput();\n ClientOpts clientOpts = new ClientOpts();\n String outputFolder = getTmpFolder().getAbsolutePath() + File.separator + destPath;\n String outputFilename = outputFolder + \"-bundle.zip\";\n\n clientOptInput.opts(clientOpts).swagger(swagger);\n\n CodegenConfig codegenConfig = null;\n try {\n codegenConfig = CodegenConfigLoader.forName(language);\n } catch (RuntimeException e) {\n throw new BadRequestException(\"Unsupported target \" + language + \" supplied\");\n }\n\n if (opts.getOptions() != null) {\n codegenConfig.additionalProperties().putAll(opts.getOptions());\n codegenConfig.additionalProperties().put(\"swagger\", swagger);\n }\n\n codegenConfig.setOutputDir(outputFolder);\n\n LOGGER.debug(Json.pretty(clientOpts));\n\n clientOptInput.setConfig(codegenConfig);\n\n try {\n List<File> files = new Codegen().opts(clientOptInput).generate();\n if (files.size() > 0) {\n List<File> filesToAdd = new ArrayList<File>();\n LOGGER.debug(\"adding to \" + outputFolder);\n filesToAdd.add(new File(outputFolder));\n ZipUtil zip = new ZipUtil();\n zip.compressFiles(filesToAdd, outputFilename);\n } else {\n throw new BadRequestException(\n \"A target generation was attempted, but no files were created!\");\n }\n for (File file : files) {\n try {\n file.delete();\n } catch (Exception e) {\n LOGGER.error(\"unable to delete file \" + file.getAbsolutePath());\n }\n }\n try {\n new File(outputFolder).delete();\n } catch (Exception e) {\n LOGGER.error(\"unable to delete output folder \" + outputFolder);\n }\n } catch (Exception e) {\n throw new BadRequestException(\"Unable to build target: \" + e.getMessage());\n }\n return outputFilename;\n }\n\n public static InputOption clientOptions(@SuppressWarnings(\"unused\") String language) {\n return null;\n }\n\n public static InputOption serverOptions(@SuppressWarnings(\"unused\") String language) {\n return null;\n }\n\n protected static File getTmpFolder() {\n try {\n File outputFolder = Files.createTempDirectory(\"codegen-\").toFile();\n outputFolder.deleteOnExit();\n return outputFolder;\n } catch (Exception e) {\n e.printStackTrace();\n return null;\n }\n }\n}\n\n", "input": "", "output": "【漏洞类型】路径遍历漏洞\n【严重性】高\n【漏洞摘要】原始代码中的路径遍历漏洞源于 `getTmpFolder` 方法使用 `File.createTempFile` 创建临时文件夹。`File.createTempFile` 会创建一个临时文件,然后调用 `mkdir` 方法将其转换为文件夹。由于 `File.createTempFile` 会在系统默认临时目录下创建文件,而该目录可能受到用户控制,攻击者可以利用这一点通过构造特殊的文件名来进行路径遍历攻击。具体问题行在 `Generator.java` 文件的第173行:\n```java\nFile outputFolder = File.createTempFile(\"codegen-\", \"-tmp\");\noutputFolder.delete();\noutputFolder.mkdir();\n```\n【关键代码模式】\n- `File.createTempFile`\n- `mkdir`\n- 未验证的临时文件名构造\n【修复方法说明】修复方法使用 `java.nio.file.Files.createTempDirectory` 创建临时文件夹,避免了潜在的路径遍历漏洞。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\napi/src/main/resources/application-local.properties\n### 代码内容:\nserver.port=8080\nlogging.level.org.jooq=INFO\nspring.jooq.sql-dialect=mysql\n# flyway\nspring.flyway.enabled=true\nspring.flyway.baseline-on-migrate=true\nspring.flyway.locations=classpath:db/migration\n# db\ndatabasir.db.url=localhost:3306\ndatabasir.db.username=root\ndatabasir.db.password=123456\ndatabasir.db.driver-directory=drivers\n## 内容2\n### 文件路径:\napi/src/main/resources/application.properties\n### 代码内容:\nserver.port=8080\n# datasource\nspring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver\nspring.datasource.username=${databasir.db.username}\nspring.datasource.password=${databasir.db.password}\nspring.datasource.url=jdbc:mysql://${databasir.db.url}/${databasir.db.name:databasir}\n# jooq\nspring.jooq.sql-dialect=mysql\n# flyway\nspring.flyway.enabled=true\nspring.flyway.baseline-on-migrate=true\nspring.flyway.locations=classpath:db/migration\n# driver directory\ndatabasir.db.driver-directory=drivers\n## 内容3\n### 文件路径:\ncore/src/main/java/com/databasir/core/domain/DomainErrors.java\n### 代码内容:\npackage com.databasir.core.domain;\n\nimport com.databasir.common.DatabasirErrors;\nimport com.databasir.common.DatabasirException;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\n@RequiredArgsConstructor\n@Getter\npublic enum DomainErrors implements DatabasirErrors {\n REFRESH_TOKEN_EXPIRED(\"X_0001\", \"refresh token expired\"),\n INVALID_REFRESH_TOKEN_OPERATION(\"X_0002\", \"invalid refresh token operation\"),\n NETWORK_ERROR(\"X_0003\", \"网络似乎不稳定,请稍后再试\"),\n INVALID_ACCESS_TOKEN(\"X_0004\", \"无效的 access token\"),\n\n NOT_SUPPORT_DATABASE_TYPE(\"A_10000\", \"不支持的数据库类型, 请检查项目配置\"),\n PROJECT_NOT_FOUND(\"A_10001\", \"项目不存在\"),\n DATABASE_META_NOT_FOUND(\"A_10002\", \"获取数据库信息失败\"),\n CONNECT_DATABASE_FAILED(\"A_10003\", \"连接数据库失败,请检查连接配置\"),\n GROUP_OWNER_MUST_NOT_BE_EMPTY(\"A_10004\", \"请至少指定一个分组组长\"),\n PASSWORD_MUST_NOT_BE_BLANK(\"A_10005\", \"密码不能为空\"),\n USERNAME_OR_EMAIL_DUPLICATE(\"A_10006\", \"用户名或邮箱已存在\"),\n USER_ROLE_DUPLICATE(\"A_10007\", \"用户角色已存在\"),\n PROJECT_NAME_DUPLICATE(\"A_10008\", \"项目名称已被占用\"),\n CANNOT_UPDATE_SELF_ROLE(\"A_10009\", \"无法对自己执行角色变更的操作\"),\n UPDATE_PASSWORD_CONFIRM_FAILED(\"A_10010\", \"两次密码输入不一致\"),\n ORIGIN_PASSWORD_NOT_CORRECT(\"A_10011\", \"原密码不正确\"),\n INVALID_CRON_EXPRESSION(\"A_10012\", \"不合法的 cron 表达式\"),\n REGISTRATION_ID_DUPLICATE(\"A_10013\", \"应用注册 ID 不能重复\"),\n REGISTRATION_ID_NOT_FOUND(\"A_10014\", \"应用 ID 不存在\"),\n MISS_REQUIRED_PARAMETERS(\"A_10015\", \"缺少必填参数\"),\n DATABASE_TYPE_NAME_DUPLICATE(\"A_10016\", \"数据库类型名已存在\"),\n MUST_NOT_MODIFY_SYSTEM_DEFAULT_DATABASE_TYPE(\"A_10017\", \"禁止修改系统默认数据库类型\"),\n DOWNLOAD_DRIVER_ERROR(\"A_10018\", \"驱动下载失败\"),\n INVALID_DATABASE_TYPE_URL_PATTERN(\"A_10019\", \"不合法的 url pattern\"),\n DOCUMENT_VERSION_IS_INVALID(\"A_10020\", \"文档版本不合法\"),\n CANNOT_UPDATE_SELF_ENABLED_STATUS(\"A_10021\", \"无法对自己执行启用禁用操作\"),\n MOCK_DATA_SCRIPT_MUST_NOT_BE_BLANK(\"A_10022\", \"脚本内容不能为空\"),\n TABLE_META_NOT_FOUND(\"A_10023\", \"不存在的数据库表\"),\n DEPENDENT_COLUMN_NAME_MUST_NOT_BE_BLANK(\"A_10024\", \"必须指定依赖的字段\"),\n DEPENDENT_REF_MUST_NOT_BE_BLANK(\"A_10025\", \"请选择关联表和字段\"),\n MUST_NOT_REF_SELF(\"A_10026\", \"不能引用自身\"),\n CIRCLE_REFERENCE(\"A_10027\", \"检查到循环引用\"),\n DUPLICATE_COLUMN(\"A_10028\", \"重复的列\"),\n INVALID_MOCK_DATA_SCRIPT(\"A_10029\", \"不合法的表达式\"),\n CANNOT_DELETE_SELF(\"A_10030\", \"无法对自己执行删除账号操作\"),\n DRIVER_CLASS_NAME_OBTAIN_ERROR(\"A_10031\", \"获取驱动类名失败\"),\n ;\n\n private final String errCode;\n\n private final String errMessage;\n\n public DatabasirException exception() {\n return new DatabasirException(this);\n }\n\n public DatabasirException exception(Throwable origin) {\n return new DatabasirException(this, origin);\n }\n\n public DatabasirException exception(String message, Throwable origin) {\n return new DatabasirException(this, message, origin);\n }\n\n public DatabasirException exception(String s) {\n return exception(s, null);\n }\n}\n\n## 内容4\n### 文件路径:\ncore/src/main/java/com/databasir/core/domain/database/service/DatabaseTypeService.java\n### 代码内容:\npackage com.databasir.core.domain.database.service;\n\nimport com.databasir.core.domain.DomainErrors;\nimport com.databasir.core.domain.database.converter.DatabaseTypePojoConverter;\nimport com.databasir.core.domain.database.data.*;\nimport com.databasir.core.infrastructure.connection.DatabaseTypes;\nimport com.databasir.core.infrastructure.driver.DriverResources;\nimport com.databasir.dao.impl.DatabaseTypeDao;\nimport com.databasir.dao.impl.ProjectDao;\nimport com.databasir.dao.tables.pojos.DatabaseTypePojo;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.dao.DuplicateKeyException;\nimport org.springframework.data.domain.Page;\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\n\n@Service\n@RequiredArgsConstructor\n@Slf4j\npublic class DatabaseTypeService {\n\n private final DriverResources driverResources;\n\n private final DatabaseTypeDao databaseTypeDao;\n\n private final ProjectDao projectDao;\n\n private final DatabaseTypePojoConverter databaseTypePojoConverter;\n\n public Integer create(DatabaseTypeCreateRequest request) {\n DatabaseTypePojo pojo = databaseTypePojoConverter.of(request);\n try {\n return databaseTypeDao.insertAndReturnId(pojo);\n } catch (DuplicateKeyException e) {\n throw DomainErrors.DATABASE_TYPE_NAME_DUPLICATE.exception();\n }\n }\n\n @Transactional\n public void update(DatabaseTypeUpdateRequest request) {\n databaseTypeDao.selectOptionalById(request.getId()).ifPresent(data -> {\n if (DatabaseTypes.has(data.getDatabaseType())) {\n throw DomainErrors.MUST_NOT_MODIFY_SYSTEM_DEFAULT_DATABASE_TYPE.exception();\n }\n\n DatabaseTypePojo pojo = databaseTypePojoConverter.of(request);\n try {\n databaseTypeDao.updateById(pojo);\n } catch (DuplicateKeyException e) {\n throw DomainErrors.DATABASE_TYPE_NAME_DUPLICATE.exception();\n }\n\n // 名称修改,下载地址修改需要删除原有的 driver\n if (!Objects.equals(request.getDatabaseType(), data.getDatabaseType())\n || !Objects.equals(request.getJdbcDriverFileUrl(), data.getJdbcDriverFileUrl())) {\n driverResources.delete(data.getDatabaseType());\n }\n });\n\n }\n\n public void deleteById(Integer id) {\n databaseTypeDao.selectOptionalById(id).ifPresent(data -> {\n if (DatabaseTypes.has(data.getDatabaseType())) {\n throw DomainErrors.MUST_NOT_MODIFY_SYSTEM_DEFAULT_DATABASE_TYPE.exception();\n }\n databaseTypeDao.deleteById(id);\n driverResources.delete(data.getDatabaseType());\n });\n }\n\n public Page<DatabaseTypePageResponse> findByPage(Pageable page,\n DatabaseTypePageCondition condition) {\n Page<DatabaseTypePojo> pageData = databaseTypeDao.selectByPage(page, condition.toCondition());\n List<String> databaseTypes = pageData.map(DatabaseTypePojo::getDatabaseType).toList();\n Map<String, Integer> projectCountMapByDatabaseType = projectDao.countByDatabaseTypes(databaseTypes);\n return pageData\n .map(data -> {\n Integer count = projectCountMapByDatabaseType.getOrDefault(data.getDatabaseType(), 0);\n return databaseTypePojoConverter.toPageResponse(data, count);\n });\n }\n\n public List<DatabaseTypeSimpleResponse> listSimpleDatabaseTypes() {\n return databaseTypeDao.selectAll()\n .stream()\n .map(type -> {\n DatabaseTypeSimpleResponse response = new DatabaseTypeSimpleResponse();\n response.setDatabaseType(type.getDatabaseType());\n response.setUrlPattern(type.getUrlPattern());\n response.setDescription(type.getDescription());\n response.setJdbcProtocol(type.getJdbcProtocol());\n return response;\n })\n .collect(Collectors.toList());\n }\n\n public Optional<DatabaseTypeDetailResponse> selectOne(Integer id) {\n return databaseTypeDao.selectOptionalById(id)\n .map(databaseTypePojoConverter::toDetailResponse);\n }\n\n public String resolveDriverClassName(DriverClassNameResolveRequest request) {\n return driverResources.resolveSqlDriverNameFromJar(request.getJdbcDriverFileUrl());\n }\n\n}\n\n## 内容5\n### 文件路径:\ncore/src/main/java/com/databasir/core/infrastructure/connection/CustomDatabaseConnectionFactory.java\n### 代码内容:\npackage com.databasir.core.infrastructure.connection;\n\nimport com.databasir.core.domain.DomainErrors;\nimport com.databasir.core.infrastructure.driver.DriverResources;\nimport com.databasir.dao.impl.DatabaseTypeDao;\nimport com.databasir.dao.tables.pojos.DatabaseTypePojo;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.stereotype.Component;\n\nimport java.io.File;\nimport java.lang.reflect.InvocationTargetException;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.sql.Connection;\nimport java.sql.Driver;\nimport java.sql.SQLException;\nimport java.util.Properties;\n\n@Component\n@RequiredArgsConstructor\n@Slf4j\n@Order\npublic class CustomDatabaseConnectionFactory implements DatabaseConnectionFactory {\n\n private final DatabaseTypeDao databaseTypeDao;\n\n private final DriverResources driverResources;\n\n @Override\n public boolean support(String databaseType) {\n return databaseTypeDao.existsByDatabaseType(databaseType);\n }\n\n @Override\n public Connection getConnection(Context context) throws SQLException {\n DatabaseTypePojo type = databaseTypeDao.selectByDatabaseType(context.getDatabaseType());\n File driverFile = driverResources.loadOrDownload(context.getDatabaseType(), type.getJdbcDriverFileUrl());\n URLClassLoader loader = null;\n try {\n loader = new URLClassLoader(\n new URL[]{\n driverFile.toURI().toURL()\n },\n this.getClass().getClassLoader()\n );\n } catch (MalformedURLException e) {\n log.error(\"load driver error \" + context, e);\n throw DomainErrors.CONNECT_DATABASE_FAILED.exception(e.getMessage());\n }\n // retrieve the driver class\n\n Class<?> clazz = null;\n Driver driver = null;\n try {\n clazz = Class.forName(type.getJdbcDriverClassName(), true, loader);\n driver = (Driver) clazz.getConstructor().newInstance();\n } catch (ClassNotFoundException e) {\n log.error(\"init driver error\", e);\n throw DomainErrors.CONNECT_DATABASE_FAILED.exception(\"驱动初始化异常, 请检查 Driver name:\" + e.getMessage());\n } catch (InvocationTargetException\n | InstantiationException\n | IllegalAccessException\n | NoSuchMethodException e) {\n log.error(\"init driver error\", e);\n throw DomainErrors.CONNECT_DATABASE_FAILED.exception(\"驱动初始化异常:\" + e.getMessage());\n }\n\n String urlPattern = type.getUrlPattern();\n String jdbcUrl = urlPattern.replace(\"{{jdbc.protocol}}\", type.getJdbcProtocol())\n .replace(\"{{db.url}}\", context.getUrl())\n .replace(\"{{db.name}}\", context.getDatabaseName())\n .replace(\"{{db.schema}}\", context.getSchemaName());\n Properties info = new Properties();\n info.put(\"user\", context.getUsername());\n info.put(\"password\", context.getPassword());\n return driver.connect(jdbcUrl, info);\n }\n\n}\n\n## 内容6\n### 文件路径:\ncore/src/main/java/com/databasir/core/infrastructure/driver/DriverResources.java\n### 代码内容:\npackage com.databasir.core.infrastructure.driver;\n\nimport com.databasir.core.domain.DomainErrors;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.io.IOUtils;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.StreamUtils;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.io.*;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.UUID;\nimport java.util.jar.JarFile;\n\n@Component\n@Slf4j\n@RequiredArgsConstructor\npublic class DriverResources {\n\n @Value(\"${databasir.db.driver-directory}\")\n private String driverBaseDirectory;\n\n private final RestTemplate restTemplate;\n\n public void delete(String databaseType) {\n Path path = Paths.get(driverFilePath(driverBaseDirectory, databaseType));\n try {\n Files.deleteIfExists(path);\n } catch (IOException e) {\n log.error(\"delete driver error \" + databaseType, e);\n }\n }\n\n public String resolveSqlDriverNameFromJar(String driverFileUrl) {\n String tempFilePath = \"temp/\" + UUID.randomUUID() + \".jar\";\n File driverFile = doDownload(driverFileUrl, tempFilePath);\n String className = doResolveSqlDriverNameFromJar(driverFile);\n try {\n Files.deleteIfExists(driverFile.toPath());\n } catch (IOException e) {\n log.error(\"delete driver error \" + tempFilePath, e);\n }\n return className;\n }\n\n public File loadOrDownload(String databaseType, String driverFileUrl) {\n String filePath = driverFilePath(driverBaseDirectory, databaseType);\n Path path = Path.of(filePath);\n if (Files.exists(path)) {\n // ignore\n log.debug(\"{} already exists, ignore download from {}\", filePath, driverFileUrl);\n return path.toFile();\n }\n return this.doDownload(driverFileUrl, filePath);\n }\n\n private File doDownload(String driverFileUrl, String filePath) {\n Path path = Path.of(filePath);\n\n // create parent directory\n if (Files.notExists(path)) {\n path.getParent().toFile().mkdirs();\n try {\n Files.createFile(path);\n } catch (IOException e) {\n log.error(\"create file error \" + filePath, e);\n throw DomainErrors.DOWNLOAD_DRIVER_ERROR.exception(e.getMessage());\n }\n }\n\n // download\n try {\n return restTemplate.execute(driverFileUrl, HttpMethod.GET, null, response -> {\n if (response.getStatusCode().is2xxSuccessful()) {\n File file = path.toFile();\n FileOutputStream out = new FileOutputStream(file);\n StreamUtils.copy(response.getBody(), out);\n IOUtils.closeQuietly(out, ex -> log.error(\"close file error\", ex));\n log.info(\"{} download success \", filePath);\n return file;\n } else {\n log.error(\"{} download error from {}: {} \", filePath, driverFileUrl, response);\n throw DomainErrors.DOWNLOAD_DRIVER_ERROR.exception(\"驱动下载失败:\"\n + response.getStatusCode()\n + \", \"\n + response.getStatusText());\n }\n });\n } catch (IllegalArgumentException e) {\n log.error(filePath + \" download driver error\", e);\n throw DomainErrors.DOWNLOAD_DRIVER_ERROR.exception(e.getMessage());\n }\n }\n\n private String doResolveSqlDriverNameFromJar(File driverFile) {\n JarFile jarFile = null;\n try {\n jarFile = new JarFile(driverFile);\n } catch (IOException e) {\n log.error(\"resolve driver class name error\", e);\n throw DomainErrors.DRIVER_CLASS_NAME_OBTAIN_ERROR.exception(e.getMessage());\n }\n\n final JarFile driverJar = jarFile;\n String driverClassName = jarFile.stream()\n .filter(entry -> entry.getName().contains(\"META-INF/services/java.sql.Driver\"))\n .findFirst()\n .map(entry -> {\n InputStream stream = null;\n BufferedReader reader = null;\n try {\n stream = driverJar.getInputStream(entry);\n reader = new BufferedReader(new InputStreamReader(stream));\n return reader.readLine();\n } catch (IOException e) {\n log.error(\"resolve driver class name error\", e);\n throw DomainErrors.DRIVER_CLASS_NAME_OBTAIN_ERROR.exception(e.getMessage());\n } finally {\n IOUtils.closeQuietly(reader, ex -> log.error(\"close reader error\", ex));\n }\n })\n .orElseThrow(DomainErrors.DRIVER_CLASS_NAME_OBTAIN_ERROR::exception);\n IOUtils.closeQuietly(jarFile, ex -> log.error(\"close jar file error\", ex));\n return driverClassName;\n }\n\n private String driverFilePath(String baseDir, String databaseType) {\n String fileName = databaseType + \".jar\";\n String filePath;\n if (baseDir.endsWith(File.separator)) {\n filePath = baseDir + fileName;\n } else {\n filePath = baseDir + File.separator + fileName;\n }\n return filePath;\n }\n}\n\n## 内容7\n### 文件路径:\ncore/src/main/java/com/databasir/core/infrastructure/jwt/JwtTokens.java\n### 代码内容:\npackage com.databasir.core.infrastructure.jwt;\n\nimport com.auth0.jwt.JWT;\nimport com.auth0.jwt.algorithms.Algorithm;\nimport com.auth0.jwt.exceptions.JWTVerificationException;\nimport com.auth0.jwt.interfaces.JWTVerifier;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport java.time.Instant;\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\nimport java.util.Date;\n\n@Component\n@Slf4j\npublic class JwtTokens {\n\n // 15 minutes\n private static final long ACCESS_EXPIRE_TIME = 1000 * 60 * 15;\n\n public static final String TOKEN_PREFIX = \"Bearer \";\n\n private static final String ISSUER = \"Databasir\";\n\n private static final String SECRET = \"Databasir2022\";\n\n public String accessToken(String username) {\n Algorithm algorithm = Algorithm.HMAC256(SECRET);\n\n return JWT.create()\n .withExpiresAt(new Date(new Date().getTime() + ACCESS_EXPIRE_TIME))\n .withIssuer(ISSUER)\n .withClaim(\"username\", username)\n .sign(algorithm);\n }\n\n public boolean verify(String token) {\n JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET))\n .withIssuer(ISSUER)\n .build();\n try {\n verifier.verify(token);\n return true;\n } catch (JWTVerificationException e) {\n log.warn(\"verify jwt token failed \" + e.getMessage());\n return false;\n }\n }\n\n public String getUsername(String token) {\n return JWT.decode(token).getClaim(\"username\").asString();\n }\n\n public LocalDateTime expireAt(String token) {\n long time = JWT.decode(token).getExpiresAt().getTime();\n return Instant.ofEpochMilli(time)\n .atZone(ZoneId.systemDefault())\n .toLocalDateTime();\n }\n}\n\n", "input": "", "output": "【漏洞类型】硬编码的密钥和敏感信息泄露\n【严重性】High\n【漏洞摘要】原始代码中存在硬编码的密钥和敏感信息泄露问题。具体表现为在`JwtTokens.java`文件中使用硬编码的`SECRET`值(\"Databasir2022\"),并且在`application-local.properties`和`application.properties`文件中未对数据库驱动目录进行安全验证,可能导致路径遍历攻击或文件覆盖问题。\n【关键代码模式】硬编码的密钥、路径遍历漏洞、文件操作、数据库连接、未验证的用户输入。\n【修复方法说明】修复方法通过将硬编码的密钥改为从配置文件中读取,并且在处理数据库驱动时增加了文件验证和路径安全措施,以防止路径遍历攻击和文件覆盖问题。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nnpm-user-validate.js\n### 代码内容:\nexports.email = email\nexports.pw = pw\nexports.username = username\nvar requirements = exports.requirements = {\n username: {\n length: 'Name length must be less than or equal to 214 characters long',\n lowerCase: 'Name must be lowercase',\n urlSafe: 'Name may not contain non-url-safe chars',\n dot: 'Name may not start with \".\"',\n illegal: 'Name may not contain illegal character'\n },\n password: {},\n email: {\n valid: 'Email must be an email address'\n }\n}\n\nvar illegalCharacterRe = new RegExp('([' + [\n \"'\"\n].join() + '])')\n\nfunction username (un) {\n if (un !== un.toLowerCase()) {\n return new Error(requirements.username.lowerCase)\n }\n\n if (un !== encodeURIComponent(un)) {\n return new Error(requirements.username.urlSafe)\n }\n\n if (un.charAt(0) === '.') {\n return new Error(requirements.username.dot)\n }\n\n if (un.length > 214) {\n return new Error(requirements.username.length)\n }\n\n var illegal = un.match(illegalCharacterRe)\n if (illegal) {\n return new Error(requirements.username.illegal + ' \"' + illegal[0] + '\"')\n }\n\n return null\n}\n\nfunction email (em) {\n if (!em.match(/^.+@.+\\..+$/)) {\n return new Error(requirements.email.valid)\n }\n\n return null\n}\n\nfunction pw (pw) {\n return null\n}\n\n", "input": "", "output": "【漏洞类型】业务逻辑安全漏洞\n\n【严重性】Medium\n\n【漏洞摘要】\n\n原始代码中的`email`函数存在一个业务逻辑安全漏洞,因为它仅使用正则表达式来验证电子邮件地址的格式,而没有限制电子邮件地址的长度。电子邮件地址的最大长度为254个字符,而原始代码中并未对此进行检查。这可能导致在处理过长的电子邮件地址时出现潜在的安全问题或数据存储问题。\n\n【关键代码模式】\n\n- 正则表达式验证:`if (!em.match(/^.+@.+\\..+$/)) {` \n- 电子邮件验证函数:`function email (em) {`\n- 未验证的用户输入:`em`作为函数参数未进行长度检查。\n\n【修复方法说明】\n\n修复方法通过在`email`函数中添加对电子邮件地址长度的检查,解决了潜在的业务逻辑安全漏洞。具体修复代码为:\n\n```javascript\nif (em.length > 254) {\n return new Error(requirements.email.length)\n}\n```\n\n最佳实践或安全编码技术:应始终对用户输入进行长度、格式和内容的验证,以防止潜在的安全风险和数据存储问题。对于电子邮件地址,不仅需要验证其格式,还应限制其最大长度。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\njava/code/webapp/WEB-INF/pages/admin/multiorg/sat_org_users.jsp\n### 代码内容:\n<%@ taglib uri=\"http://java.sun.com/jsp/jstl/core\" prefix=\"c\" %>\n<%@ taglib uri=\"http://rhn.redhat.com/rhn\" prefix=\"rhn\" %>\n<%@ taglib uri=\"http://rhn.redhat.com/tags/list\" prefix=\"rl\" %>\n<%@ taglib uri=\"http://struts.apache.org/tags-bean\" prefix=\"bean\" %>\n<%@ taglib uri=\"http://struts.apache.org/tags-html\" prefix=\"html\" %>\n\n<html>\n<body>\n<rhn:toolbar base=\"h1\" icon=\"header-organisation\"\n miscUrl=\"${url}\"\n miscAcl=\"user_role(org_admin)\"\n miscText=\"${text}\"\n miscImg=\"${img}\"\n miscAlt=\"${text}\"\n imgAlt=\"users.jsp.imgAlt\">\n <bean:message key=\"org.allusers.title\" />\n</rhn:toolbar>\n\n<rhn:dialogmenu mindepth=\"0\" maxdepth=\"1\" definition=\"/WEB-INF/nav/admin_user.xml\" renderer=\"com.redhat.rhn.frontend.nav.DialognavRenderer\" />\n\n<div class=\"page-summary\">\n<p>\n <bean:message key=\"org.allusers.summary\" arg0=\"${orgName}\" />\n</p>\n</div>\n<c:set var=\"pageList\" value=\"${requestScope.pageList}\" />\n\n<div>\n<rl:listset name=\"orgListSet\">\n<rhn:csrf />\n<rhn:submitted />\n<!-- Start of org list -->\n<rl:list dataset=\"pageList\"\n width=\"100%\"\n name=\"userList\"\n styleclass=\"list\"\n filter=\"com.redhat.rhn.frontend.action.multiorg.UserListFilter\"\n emptykey=\"org.jsp.noUsers\">\n\n <!-- Username column -->\n <rl:column bound=\"false\"\n sortable=\"true\"\n headerkey=\"username.nopunc.displayname\"\n sortattr=\"login\">\n <c:choose>\n <c:when test=\"${(userOrgId == current.orgId) and (canModify == 1)}\">\n <c:out value=\"<a href=\\\"/rhn/users/UserDetails.do?uid=${current.id}\\\">${current.login}</a>\" escapeXml=\"false\" />\n </c:when>\n <c:otherwise>\n <c:out value=\"${current.login}\" />\n </c:otherwise>\n </c:choose>\n </rl:column>\n <rl:column bound=\"false\"\n sortable=\"false\"\n headerkey=\"realname.displayname\"\n attr=\"userLastName\">\n <c:out value=\"<a href=\\\"mailto:${current.address}\\\">${current.userLastName}, ${current.userFirstName}</a>\" escapeXml=\"true\"/>\n </rl:column>\n <rl:column bound=\"false\"\n sortable=\"false\"\n headerkey=\"org.displayname\"\n attr=\"orgName\">\n <c:out value=\"${current.orgName}\" />\n </rl:column>\n <rl:column bound=\"false\"\n sortable=\"false\"\n headerkey=\"orgadmin.displayname\"\n attr=\"orgAdmin\">\n <c:choose>\n <c:when test=\"${current.orgAdmin == 1}\">\n <rhn:icon type=\"item-enabled\" />\n </c:when>\n <c:otherwise>\n <rhn:icon type=\"item-disabled\" />\n </c:otherwise>\n </c:choose>\n </rl:column>\n <rl:column bound=\"false\"\n sortable=\"false\"\n headerkey=\"satadmin.displayname\"\n attr=\"orgAdmin\">\n <c:choose>\n <c:when test=\"${current.satAdmin == 1}\">\n <a href=\"/rhn/admin/multiorg/ToggleSatAdmin.do?uid=${current.id}\">\n <rhn:icon type=\"item-enabled\" />\n </a>\n </c:when>\n <c:otherwise>\n <a href=\"/rhn/admin/multiorg/ToggleSatAdmin.do?uid=${current.id}\">\n <rhn:icon type=\"item-disabled\" />\n </a>\n </c:otherwise>\n </c:choose>\n </rl:column>\n\n</rl:list>\n\n</rl:listset>\n<span class=\"small-text\">\n <bean:message key=\"satusers.tip\"/>\n</span>\n\n</div>\n\n</body>\n</html>\n\n\n", "input": "", "output": "【漏洞类型】跨站脚本攻击(XSS)\n【严重性】中等\n【漏洞摘要】这是一个跨站脚本攻击(XSS)漏洞。具体表现为在`<rl:column>`标签内的链接中,`<c:out value=\"${current.userLastName}, ${current.userFirstName}\" escapeXml=\"true\"/>`用于输出用户的名字和姓氏,但`<c:out value=\"<a href=\\\"mailto:${current.address}\\\">\" escapeXml=\"false\" />`和`<c:out value=\"</a>\" escapeXml=\"false\" />`没有对用户提供的`current.address`进行转义。如果`current.address`中包含恶意脚本,攻击者可以通过构造特制的电子邮件地址来执行脚本,从而在用户的浏览器中注入恶意代码。\n【关键代码模式】`<c:out>`标签用于输出内容,特别是`escapeXml=\"false\"`时会禁用XML转义,可能导致XSS漏洞。`mailto:`协议和用户提供的输入数据组合使用时需要特别注意。\n【修复方法说明】修复方法通过将`<a>`标签的开始和结束部分分别用`<c:out>`标签输出,确保只有用户的名字和姓氏被转义,从而防止恶意脚本的注入。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\napplications/dashboard/controllers/class.entrycontroller.php\n### 代码内容:\n<?php if (!defined('APPLICATION')) exit();\n\nclass EntryController extends Gdn_Controller {\n \n // Make sure the database class is loaded (class.controller.php takes care of this).\n public $Uses = array('Database', 'Form', 'UserModel');\n\tconst UsernameError = 'Username can only contain letters, numbers, underscores, and must be between 3 and 20 characters long.';\n \n public function Auth($AuthenticationSchemeAlias = 'default') {\n $this->EventArguments['AuthenticationSchemeAlias'] = $AuthenticationSchemeAlias;\n $this->FireEvent('BeforeAuth');\n \n // Allow hijacking auth type\n $AuthenticationSchemeAlias = $this->EventArguments['AuthenticationSchemeAlias'];\n \n try {\n $Authenticator = Gdn::Authenticator()->AuthenticateWith($AuthenticationSchemeAlias);\n } catch (Exception $e) {\n $Authenticator = Gdn::Authenticator()->AuthenticateWith('default');\n }\n \n // Set up controller\n $this->View = 'auth/'.$Authenticator->GetAuthenticationSchemeAlias();\n $this->Form->SetModel($this->UserModel);\n $this->Form->AddHidden('ClientHour', date('G', time())); // Use the server's current hour as a default\n $this->Form->AddHidden('Target', GetIncomingValue('Target', ''));\n\n // Import authenticator data source\n switch ($Authenticator->DataSourceType()) {\n case Gdn_Authenticator::DATA_FORM:\n $Authenticator->FetchData($this->Form);\n break;\n \n case Gdn_Authenticator::DATA_REQUEST:\n case Gdn_Authenticator::DATA_COOKIE:\n $Authenticator->FetchData(Gdn::Request());\n break;\n }\n \n // By default, just render the view\n $Reaction = Gdn_Authenticator::REACT_RENDER;\n \n // Where are we in the process? Still need to gather (render view) or are we validating?\n $AuthenticationStep = $Authenticator->CurrentStep();\n \n switch ($AuthenticationStep) {\n \n // User is already logged in\n case Gdn_Authenticator::MODE_REPEAT:\n $Reaction = $Authenticator->RepeatResponse();\n break;\n \n // Not enough information to perform authentication, render input form\n case Gdn_Authenticator::MODE_GATHER:\n $this->AddJsFile('entry.js');\n $Reaction = $Authenticator->LoginResponse();\n\t\t\t\tif ($this->Form->IsPostBack())\n\t\t\t\t\t$this->Form->AddError('ErrorCredentials');\n break;\n \n // All information is present, authenticate\n case Gdn_Authenticator::MODE_VALIDATE:\n \n // Attempt to authenticate.\n try {\n $AuthenticationResponse = $Authenticator->Authenticate();\n Gdn::Authenticator()->Trigger($AuthenticationResponse);\n switch ($AuthenticationResponse) {\n case Gdn_Authenticator::AUTH_PERMISSION:\n $this->Form->AddError('ErrorPermission');\n $Reaction = $Authenticator->FailedResponse();\n break;\n\n case Gdn_Authenticator::AUTH_DENIED:\n $this->Form->AddError('ErrorCredentials');\n $Reaction = $Authenticator->FailedResponse();\n break;\n\n case Gdn_Authenticator::AUTH_INSUFFICIENT:\n // Unable to comply with auth request, more information is needed from user.\n $this->Form->AddError('ErrorInsufficient');\n $Reaction = $Authenticator->FailedResponse();\n break;\n\n case Gdn_Authenticator::AUTH_PARTIAL:\n // Partial auth completed.\n $Reaction = $Authenticator->PartialResponse();\n break;\n\n case Gdn_Authenticator::AUTH_SUCCESS:\n default: \n // Full auth completed.\n $UserID = $AuthenticationResponse;\n $Reaction = $Authenticator->SuccessResponse();\n }\n } catch (Exception $Ex) {\n $this->Form->AddError($Ex);\n }\n break;\n \n case Gdn_Authenticator::MODE_NOAUTH:\n $Reaction = Gdn_Authenticator::REACT_REDIRECT;\n break;\n }\n \n // AddActivity($AuthenticatedUserID, 'SignIn');\n switch ($Reaction) {\n \n case Gdn_Authenticator::REACT_RENDER:\n // Do nothing (render the view)\n break;\n \n case Gdn_Authenticator::REACT_EXIT:\n exit();\n break;\n \n case Gdn_Authenticator::REACT_REMOTE:\n // Let the authenticator handle generating output, using a blank slate\n $this->_DeliveryType= DELIVERY_TYPE_VIEW;\n \n exit;\n break;\n \n case Gdn_Authenticator::REACT_REDIRECT:\n default:\n \n if (is_string($Reaction))\n $Route = $Reaction;\n else\n $Route = $this->RedirectTo();\n \n if ($this->_DeliveryType != DELIVERY_TYPE_ALL) {\n $this->RedirectUrl = Url($Route);\n } else {\n \n if ($Route !== FALSE) {\n Redirect($Route);\n } else {\n Redirect(Gdn::Router()->GetDestination('DefaultController'));\n }\n }\n break;\n }\n \n $this->SetData('SendWhere', \"/entry/auth/{$AuthenticationSchemeAlias}\");\n $this->Render();\n }\n \n public function Index() {\n $this->SignIn();\n }\n \n public function Password() {\n $this->Auth('password');\n }\n \n public function SignIn() {\n $this->FireEvent(\"SignIn\");\n $this->Auth('default');\n }\n\n public function SignOut($TransientKey = \"\") {\n $SessionAuthenticator = Gdn::Session()->GetPreference('Authenticator');\n $AuthenticationScheme = ($SessionAuthenticator) ? $SessionAuthenticator : 'default';\n\n try {\n $Authenticator = Gdn::Authenticator()->GetAuthenticator($AuthenticationScheme);\n } catch (Exception $e) {\n $Authenticator = Gdn::Authenticator()->GetAuthenticator();\n }\n \n $this->FireEvent(\"SignOut\");\n $this->Leave($AuthenticationScheme, $TransientKey);\n }\n \n /** A version of signin that supports multiple authentication methods.\n * This method should replace EntryController::SignIn() eventually.\n *\n * @param string $Method\n */\n public function SignIn2($SelectedMethod = FALSE) {\n // Set up the initial methods. Other methods can be added by plugins.\n $Methods = array(\n 'password' => array('Label' => 'Vanilla', 'Url' => Url('/entry/auth/password'), 'ViewLocation' => $this->FetchViewLocation('SignIn'))\n );\n\n // Make sure a valid method is chosen.\n if (!array_key_exists((string)$SelectedMethod, $Methods)) {\n $SelectedMethod = array_shift(array_keys($Methods));\n }\n \n $this->SetData('SelectedMethod', $SelectedMethod);\n $this->SetData('Methods', $Methods);\n\n return $this->Render();\n }\n \n public function Handshake($AuthenticationSchemeAlias = 'default') {\n \n try {\n // Don't show anything if handshaking not turned on by an authenticator\n \t\tif (!Gdn::Authenticator()->CanHandshake())\n \t\t\tthrow new Exception();\n \n // Try to load the authenticator\n $Authenticator = Gdn::Authenticator()->AuthenticateWith($AuthenticationSchemeAlias);\n \n // Try to grab the authenticator data\n $Payload = $Authenticator->GetHandshake();\n if ($Payload === FALSE) {\n Gdn::Request()->WithURI('dashboard/entry/auth/password');\n return Gdn::Dispatcher()->Dispatch();\n }\n } catch (Exception $e) {\n Gdn::Request()->WithURI('/entry/signin');\n return Gdn::Dispatcher()->Dispatch();\n }\n \n $UserInfo = array(\n 'UserKey' => $Authenticator->GetUserKeyFromHandshake($Payload),\n 'ConsumerKey' => $Authenticator->GetProviderKeyFromHandshake($Payload),\n 'TokenKey' => $Authenticator->GetTokenKeyFromHandshake($Payload),\n 'UserName' => $Authenticator->GetUserNameFromHandshake($Payload),\n 'UserEmail' => $Authenticator->GetUserEmailFromHandshake($Payload)\n );\n \n // Manual user sync is disabled. No hand holding will occur for users.\n $SyncScreen = C('Garden.Authenticator.SyncScreen', 'on');\n switch ($SyncScreen) {\n case 'on':\n \n $this->SyncScreen($Authenticator, $UserInfo, $Payload);\n \n break;\n \n case 'off':\n case 'smart':\n $UserID = $this->UserModel->Synchronize($UserInfo['UserKey'], array(\n 'Name' => $UserInfo['UserName'],\n 'Email' => $UserInfo['UserEmail']\n ));\n \n if ($UserID > 0) {\n // Account created successfully.\n \n // Finalize the link between the forum user and the foreign userkey\n $Authenticator->Finalize($UserInfo['UserKey'], $UserID, $UserInfo['ConsumerKey'], $UserInfo['TokenKey'], $Payload);\n \n /// ... and redirect them appropriately\n $Route = $this->RedirectTo();\n if ($Route !== FALSE)\n Redirect($Route);\n else\n Redirect('/');\n \n } else {\n // Account not created.\n if ($SyncScreen == 'smart') {\n \n $this->StatusMessage = T('There is already an account in this forum using your email address. Please create a new account, or enter the credentials for the existing account.');\n $this->SyncScreen($Authenticator, $UserInfo, $Payload);\n \n } else {\n \n // Set the memory cookie to allow signinloopback to shortcircuit remote query.\n $CookiePayload = array(\n 'Sync' => 'Failed'\n );\n $SerializedCookiePayload = Gdn_Format::Serialize($CookiePayload);\n $Authenticator->Remember($UserInfo['ConsumerKey'], $SerializedCookiePayload);\n \n // This resets vanilla's internal \"where am I\" to the homepage. Needed.\n Gdn::Request()->WithRoute('DefaultController');\n $this->SelfUrl = Gdn::Request()->Path();\n \n $this->View = 'syncfailed';\n $this->ProviderSite = $Authenticator->GetProviderUrl();\n $this->Render();\n }\n \n }\n break;\n \n }\n }\n \n public function SyncScreen($Authenticator, $UserInfo, $Payload) {\n $this->AddJsFile('entry.js');\n $this->View = 'handshake';\n $this->HandshakeScheme = $Authenticator->GetAuthenticationSchemeAlias();\n $this->Form->SetModel($this->UserModel);\n $this->Form->AddHidden('ClientHour', date('G', time())); // Use the server's current hour as a default\n $this->Form->AddHidden('Target', GetIncomingValue('Target', '/'));\n \n $PreservedKeys = array(\n 'UserKey', 'Token', 'Consumer', 'Email', 'Name', 'Gender', 'HourOffset'\n );\n $UserID = 0;\n $Target = GetIncomingValue('Target', '/');\n \n if ($this->Form->IsPostBack() === TRUE) {\n \n $FormValues = $this->Form->FormValues();\n if (ArrayValue('StopLinking', $FormValues)) {\n \n $Authenticator->DeleteCookie();\n Gdn::Request()->WithRoute('DefaultController');\n return Gdn::Dispatcher()->Dispatch();\n \n } elseif (ArrayValue('NewAccount', $FormValues)) {\n \n // Try and synchronize the user with the new username/email.\n $FormValues['Name'] = $FormValues['NewName'];\n $FormValues['Email'] = $FormValues['NewEmail'];\n $UserID = $this->UserModel->Synchronize($UserInfo['UserKey'], $FormValues);\n $this->Form->SetValidationResults($this->UserModel->ValidationResults());\n \n } else {\n \n // Try and sign the user in.\n $PasswordAuthenticator = Gdn::Authenticator()->AuthenticateWith('password');\n $PasswordAuthenticator->HookDataField('Email', 'SignInEmail');\n $PasswordAuthenticator->HookDataField('Password', 'SignInPassword');\n $PasswordAuthenticator->FetchData($this->Form);\n \n $UserID = $PasswordAuthenticator->Authenticate();\n \n if ($UserID < 0) {\n $this->Form->AddError('ErrorPermission');\n } else if ($UserID == 0) {\n $this->Form->AddError('ErrorCredentials');\n }\n \n if ($UserID > 0) {\n $Data = $FormValues;\n $Data['UserID'] = $UserID;\n $Data['Email'] = ArrayValue('SignInEmail', $FormValues, '');\n $UserID = $this->UserModel->Synchronize($UserInfo['UserKey'], $Data);\n }\n }\n \n if ($UserID > 0) {\n // The user has been created successfully, so sign in now\n \n // Finalize the link between the forum user and the foreign userkey\n $Authenticator->Finalize($UserInfo['UserKey'], $UserID, $UserInfo['ConsumerKey'], $UserInfo['TokenKey'], $Payload);\n \n /// ... and redirect them appropriately\n $Route = $this->RedirectTo();\n if ($Route !== FALSE)\n Redirect($Route);\n } else {\n // Add the hidden inputs back into the form.\n foreach($FormValues as $Key => $Value) {\n if (in_array($Key, $PreservedKeys))\n $this->Form->AddHidden($Key, $Value);\n }\n }\n } else {\n $Id = Gdn::Authenticator()->GetIdentity(TRUE);\n if ($Id > 0) {\n // The user is signed in so we can just go back to the homepage.\n Redirect($Target);\n }\n \n $Name = $UserInfo['UserName'];\n $Email = $UserInfo['UserEmail'];\n \n // Set the defaults for a new user.\n $this->Form->SetFormValue('NewName', $Name);\n $this->Form->SetFormValue('NewEmail', $Email);\n \n // Set the default for the login.\n $this->Form->SetFormValue('SignInEmail', $Email);\n $this->Form->SetFormValue('Handshake', 'NEW');\n \n // Add the handshake data as hidden fields.\n $this->Form->AddHidden('Name', $Name);\n $this->Form->AddHidden('Email', $Email);\n $this->Form->AddHidden('UserKey', $UserInfo['UserKey']);\n $this->Form->AddHidden('Token', $UserInfo['TokenKey']);\n $this->Form->AddHidden('Consumer', $UserInfo['ConsumerKey']);\n \n }\n \n $this->SetData('Name', ArrayValue('Name', $this->Form->HiddenInputs));\n $this->SetData('Email', ArrayValue('Email', $this->Form->HiddenInputs));\n \n $this->Render();\n }\n \n /**\n * Calls the appropriate registration method based on the configuration setting.\n */\n public function Register($InvitationCode = '') {\n $this->Form->SetModel($this->UserModel);\n\n // Define gender dropdown options\n $this->GenderOptions = array(\n 'm' => T('Male'),\n 'f' => T('Female')\n );\n\n // Make sure that the hour offset for new users gets defined when their account is created\n $this->AddJsFile('entry.js');\n \n $this->Form->AddHidden('ClientHour', date('G', time())); // Use the server's current hour as a default\n $this->Form->AddHidden('Target', GetIncomingValue('Target', ''));\n\n $RegistrationMethod = $this->_RegistrationView();\n $this->View = $RegistrationMethod;\n $this->$RegistrationMethod($InvitationCode);\n }\n \n protected function _RegistrationView() {\n $RegistrationMethod = Gdn::Config('Garden.Registration.Method');\n if (!in_array($RegistrationMethod, array('Closed', 'Basic','Captcha','Approval','Invitation')))\n $RegistrationMethod = 'Basic';\n \n return 'Register'.$RegistrationMethod;\n }\n \n private function RegisterApproval() {\n // If the form has been posted back...\n if ($this->Form->IsPostBack()) {\n // Add validation rules that are not enforced by the model\n $this->UserModel->DefineSchema();\n $this->UserModel->Validation->ApplyRule('Name', 'Username', self::UsernameError);\n $this->UserModel->Validation->ApplyRule('TermsOfService', 'Required', 'You must agree to the terms of service.');\n $this->UserModel->Validation->ApplyRule('Password', 'Required');\n $this->UserModel->Validation->ApplyRule('Password', 'Match');\n $this->UserModel->Validation->ApplyRule('DiscoveryText', 'Required', 'Tell us why you want to join!');\n // $this->UserModel->Validation->ApplyRule('DateOfBirth', 'MinimumAge');\n \n if (!$this->UserModel->InsertForApproval($this->Form->FormValues()))\n $this->Form->SetValidationResults($this->UserModel->ValidationResults());\n else\n $this->View = \"RegisterThanks\"; // Tell the user their application will be reviewed by an administrator.\n }\n $this->Render();\n }\n \n private function RegisterBasic() {\n if ($this->Form->IsPostBack() === TRUE) {\n // Add validation rules that are not enforced by the model\n $this->UserModel->DefineSchema();\n $this->UserModel->Validation->ApplyRule('Name', 'Username', self::UsernameError);\n $this->UserModel->Validation->ApplyRule('TermsOfService', 'Required', 'You must agree to the terms of service.');\n $this->UserModel->Validation->ApplyRule('Password', 'Required');\n $this->UserModel->Validation->ApplyRule('Password', 'Match');\n // $this->UserModel->Validation->ApplyRule('DateOfBirth', 'MinimumAge');\n \n if (!$this->UserModel->InsertForBasic($this->Form->FormValues())) {\n $this->Form->SetValidationResults($this->UserModel->ValidationResults());\n } else {\n // The user has been created successfully, so sign in now\n $Authenticator = Gdn::Authenticator()->AuthenticateWith('password');\n $Authenticator->FetchData($this->Form);\n $AuthUserID = $Authenticator->Authenticate();\n \n // ... and redirect them appropriately\n $Route = $this->RedirectTo();\n if ($this->_DeliveryType != DELIVERY_TYPE_ALL) {\n $this->RedirectUrl = Url($Route);\n } else {\n if ($Route !== FALSE)\n Redirect($Route);\n }\n }\n }\n $this->Render();\n }\n \n private function RegisterCaptcha() {\n include(CombinePaths(array(PATH_LIBRARY, 'vendors/recaptcha', 'functions.recaptchalib.php')));\n if ($this->Form->IsPostBack() === TRUE) {\n // Add validation rules that are not enforced by the model\n $this->UserModel->DefineSchema();\n $this->UserModel->Validation->ApplyRule('Name', 'Username', self::UsernameError);\n $this->UserModel->Validation->ApplyRule('TermsOfService', 'Required', 'You must agree to the terms of service.');\n $this->UserModel->Validation->ApplyRule('Password', 'Required');\n $this->UserModel->Validation->ApplyRule('Password', 'Match');\n // $this->UserModel->Validation->ApplyRule('DateOfBirth', 'MinimumAge');\n \n if (!$this->UserModel->InsertForBasic($this->Form->FormValues())) {\n $this->Form->SetValidationResults($this->UserModel->ValidationResults());\n if($this->_DeliveryType != DELIVERY_TYPE_ALL) {\n $this->_DeliveryType = DELIVERY_TYPE_MESSAGE;\n }\n } else {\n // The user has been created successfully, so sign in now\n $Authenticator = Gdn::Authenticator()->AuthenticateWith('password');\n $Authenticator->FetchData($this->Form);\n $AuthUserID = $Authenticator->Authenticate();\n \n // ... and redirect them appropriately\n $Route = $this->RedirectTo();\n if ($this->_DeliveryType != DELIVERY_TYPE_ALL) {\n $this->RedirectUrl = Url($Route);\n } else {\n if ($Route !== FALSE)\n Redirect($Route);\n }\n }\n }\n $this->Render();\n }\n \n private function RegisterClosed() {\n $this->Render();\n }\n \n private function RegisterInvitation($InvitationCode) {\n if ($this->Form->IsPostBack() === TRUE) {\n $this->InvitationCode = $this->Form->GetValue('InvitationCode');\n // Add validation rules that are not enforced by the model\n $this->UserModel->DefineSchema();\n $this->UserModel->Validation->ApplyRule('Name', 'Username', self::UsernameError);\n $this->UserModel->Validation->ApplyRule('TermsOfService', 'Required', 'You must agree to the terms of service.');\n $this->UserModel->Validation->ApplyRule('Password', 'Required');\n $this->UserModel->Validation->ApplyRule('Password', 'Match');\n // $this->UserModel->Validation->ApplyRule('DateOfBirth', 'MinimumAge');\n \n if (!$this->UserModel->InsertForInvite($this->Form->FormValues())) {\n $this->Form->SetValidationResults($this->UserModel->ValidationResults());\n } else {\n // The user has been created successfully, so sign in now\n $Authenticator = Gdn::Authenticator()->AuthenticateWith('password');\n $Authenticator->FetchData($this->Form);\n $AuthUserID = $Authenticator->Authenticate();\n \n // ... and redirect them appropriately\n $Route = $this->RedirectTo();\n if ($this->_DeliveryType != DELIVERY_TYPE_ALL) {\n $this->RedirectUrl = Url($Route);\n } else {\n if ($Route !== FALSE)\n Redirect($Route);\n }\n }\n } else {\n $this->InvitationCode = $InvitationCode;\n }\n $this->Render(); \n }\n \n public function PasswordRequest() {\n $this->Form->SetModel($this->UserModel);\n if ($this->Form->IsPostBack() === TRUE) {\n\n if ($this->Form->ValidateModel() == 0) {\n try {\n if (!$this->UserModel->PasswordRequest($this->Form->GetFormValue('Email', ''))) {\n $this->Form->AddError(\"Couldn't find an account associated with that email address.\");\n }\n } catch (Exception $ex) {\n $this->Form->AddError($ex->getMessage());\n }\n if ($this->Form->ErrorCount() == 0) {\n $this->Form->AddError('Success!');\n $this->View = 'passwordrequestsent';\n }\n } else {\n if ($this->Form->ErrorCount() == 0)\n $this->Form->AddError('That email address was not found.');\n }\n }\n $this->Render();\n }\n\n public function PasswordReset($UserID = '', $PasswordResetKey = '') {\n if (!is_numeric($UserID)\n || $PasswordResetKey == ''\n || $this->UserModel->GetAttribute($UserID, 'PasswordResetKey', '') != $PasswordResetKey\n ) $this->Form->AddError('Failed to authenticate your password reset request. Try using the reset request form again.');\n \n if ($this->Form->ErrorCount() == 0\n && $this->Form->IsPostBack() === TRUE\n ) {\n $Password = $this->Form->GetFormValue('Password', '');\n $Confirm = $this->Form->GetFormValue('Confirm', '');\n if ($Password == '')\n $this->Form->AddError('Your new password is invalid');\n else if ($Password != $Confirm)\n $this->Form->AddError('Your passwords did not match.');\n\n if ($this->Form->ErrorCount() == 0) {\n $User = $this->UserModel->PasswordReset($UserID, $Password);\n $Authenticator = Gdn::Authenticator()->AuthenticateWith('password');\n $Authenticator->FetchData($Authenticator, array('Email' => $User->Email, 'Password' => $Password, 'RememberMe' => FALSE));\n $AuthUserID = $Authenticator->Authenticate();\n\t\t\t\tRedirect('/');\n }\n }\n $this->Render();\n }\n\n public function EmailConfirm($UserID = '', $EmailKey = '') {\n if (!is_numeric($UserID) || $EmailKey != $this->UserModel->GetAttribute($UserID, 'EmailKey', '')) {\n $this->Form->AddError(T('Couldn\\'t confirm email.',\n 'We couldn\\'t confirm your email. Check the link in the email we sent you or try sending another confirmation email.'));\n }\n\n if ($this->Form->ErrorCount() == 0) {\n \n\n }\n $this->Render();\n }\n\n public function Leave($AuthenticationSchemeAlias = 'default', $TransientKey = '') {\n $this->EventArguments['AuthenticationSchemeAlias'] = $AuthenticationSchemeAlias;\n $this->FireEvent('BeforeLeave');\n \n // Allow hijacking deauth type\n $AuthenticationSchemeAlias = $this->EventArguments['AuthenticationSchemeAlias'];\n \n try {\n $Authenticator = Gdn::Authenticator()->AuthenticateWith($AuthenticationSchemeAlias);\n } catch (Exception $e) {\n $Authenticator = Gdn::Authenticator()->AuthenticateWith('default');\n }\n \n // Only sign the user out if this is an authenticated postback! Start off pessimistic\n $this->Leaving = FALSE;\n $Result = Gdn_Authenticator::REACT_RENDER;\n \n // Build these before doing anything desctructive as they are supposed to have user context\n $LogoutResponse = $Authenticator->LogoutResponse();\n $LoginResponse = $Authenticator->LoginResponse();\n \n $AuthenticatedPostbackRequired = $Authenticator->RequireLogoutTransientKey();\n if (!$AuthenticatedPostbackRequired || Gdn::Session()->ValidateTransientKey($TransientKey)) {\n $Result = $Authenticator->DeAuthenticate();\n $this->Leaving = TRUE;\n }\n \n if ($Result == Gdn_Authenticator::AUTH_SUCCESS) {\n $this->View = 'leave';\n $Reaction = $LogoutResponse;\n } else {\n $this->View = 'auth/'.$Authenticator->GetAuthenticationSchemeAlias();\n $Reaction = $LoginResponse;\n }\n \n switch ($Reaction) {\n case Gdn_Authenticator::REACT_RENDER:\n \n break;\n\n case Gdn_Authenticator::REACT_EXIT:\n exit();\n break;\n\n case Gdn_Authenticator::REACT_REMOTE:\n // Render the view, but set the delivery type to VIEW\n $this->_DeliveryType= DELIVERY_TYPE_VIEW;\n break;\n\n case Gdn_Authenticator::REACT_REDIRECT:\n default:\n // If we're just told to redirect, but not where... try to figure out somewhere that makes sense.\n if ($Reaction == Gdn_Authenticator::REACT_REDIRECT) {\n $Route = '/';\n $Target = GetIncomingValue('Target', NULL);\n if (!is_null($Target)) \n $Route = $Target;\n } else {\n $Route = $Reaction;\n }\n \n if ($this->_DeliveryType != DELIVERY_TYPE_ALL) {\n $this->RedirectUrl = Url($Route);\n } else {\n if ($Route !== FALSE) {\n Redirect($Route);\n } else {\n Redirect(Gdn::Router()->GetDestination('DefaultController'));\n }\n }\n break;\n }\n \n $this->Render();\n }\n \n public function RedirectTo() {\n $IncomingTarget = $this->Form->GetValue('Target', '');\n return $IncomingTarget == '' ? Gdn::Router()->GetDestination('DefaultController') : $IncomingTarget;\n }\n \n public function Initialize() {\n $this->Head = new HeadModule($this);\n $this->AddJsFile('jquery.js');\n $this->AddJsFile('jquery.livequery.js');\n $this->AddJsFile('jquery.form.js');\n $this->AddJsFile('jquery.popup.js');\n $this->AddJsFile('jquery.gardenhandleajaxform.js');\n $this->AddJsFile('global.js');\n \n $this->AddCssFile('style.css');\n parent::Initialize();\n }\n\n}\n\n## 内容2\n### 文件路径:\nlibrary/core/class.dispatcher.php\n### 代码内容:\n<?php if (!defined('APPLICATION')) exit();\n\n/**\n * Dispatcher handles all requests.\n *\n * @author Mark O'Sullivan\n * @copyright 2003 Mark O'Sullivan\n * @license http://www.opensource.org/licenses/gpl-2.0.php GPL\n * @package Garden\n * @version @@GARDEN-VERSION@@\n * @namespace Garden.Core\n */\n\nclass Gdn_Dispatcher extends Gdn_Pluggable {\n\n /**\n * An array of folders within the application that are OK to search through\n * for controllers. This property is filled by the applications array\n * located in /conf/applications.php and included in /bootstrap.php\n *\n * @var array\n */\n private $_EnabledApplicationFolders;\n\n /**\n * An associative array of ApplicationName => ApplicationFolder. This\n * property is filled by the applications array located in\n * /conf/applications.php and included in /bootstrap.php\n *\n * @var array\n */\n private $_EnabledApplications;\n\n /**\n * The currently requested url (defined in _AnalyzeRequest)\n *\n * @var string\n */\n public $Request;\n\n /**\n * The name of the application folder that contains the controller that has\n * been requested.\n *\n * @var string\n */\n private $_ApplicationFolder;\n\n /**\n * An associative collection of AssetName => Strings that will get passed\n * into the controller once it has been instantiated.\n *\n * @var array\n */\n private $_AssetCollection;\n\n /**\n * The name of the controller folder that contains the controller that has\n * been requested.\n *\n * @var string\n */\n private $_ControllerFolder;\n\n /**\n * The name of the controller to be dispatched.\n *\n * @var string\n */\n private $_ControllerName;\n\n /**\n * The method of the controller to be called.\n *\n * @var string\n */\n private $_ControllerMethod;\n\n /**\n * Any query string arguments supplied to the controller method.\n *\n * @var string\n */\n private $_ControllerMethodArgs = array();\n\n /**\n * An associative collection of variables that will get passed into the\n * controller as properties once it has been instantiated.\n *\n * @var array\n */\n private $_PropertyCollection;\n\n /**\n * Defined by the url of the request: SYNDICATION_RSS, SYNDICATION_ATOM, or\n * SYNDICATION_NONE (default).\n *\n * @var string\n */\n private $_SyndicationMethod;\n\n /**\n * Class constructor.\n */\n public function __construct() {\n parent::__construct();\n $this->_EnabledApplicationFolders = array();\n $this->Request = '';\n $this->_ApplicationFolder = '';\n $this->_AssetCollection = array();\n $this->_ControllerFolder = '';\n $this->_ControllerName = '';\n $this->_ControllerMethod = '';\n $this->_ControllerMethodArgs = array();\n $this->_PropertyCollection = array();\n }\n \n public function Cleanup() {\n // Destruct the db connection;\n $Database = Gdn::Database();\n if($Database != null)\n $Database->CloseConnection();\n }\n\n /**\n * Return the properly formatted controller class name.\n */\n public function ControllerName() {\n return $this->_ControllerName.'Controller';\n }\n \n public function Application() {\n return $this->_ApplicationFolder;\n }\n \n public function Controller() {\n return $this->_ControllerName;\n }\n \n public function ControllerMethod() {\n return $this->_ControllerMethod;\n }\n \n public function ControllerArguments() {\n return $this->_ControllerMethodArgs;\n }\n\n /**\n * Analyzes the supplied query string and decides how to dispatch the request.\n */\n public function Dispatch($ImportRequest = NULL, $Permanent = TRUE) {\n if ($ImportRequest && is_string($ImportRequest))\n $ImportRequest = Gdn_Request::Create()->FromEnvironment()->WithURI($ImportRequest);\n \n if (is_a($ImportRequest, 'Gdn_Request') && $Permanent) {\n Gdn::Request($ImportRequest);\n }\n \n $Request = is_a($ImportRequest, 'Gdn_Request') ? $ImportRequest : Gdn::Request();\n \n if (Gdn::Config('Garden.UpdateMode', FALSE)) {\n if (!Gdn::Session()->CheckPermission('Garden.Settings.GlobalPrivs')) {\n // Updatemode, and this user is not root admin\n $Request->WithURI(Gdn::Router()->GetDestination('UpdateMode'));\n }\n }\n \n $this->FireEvent('BeforeDispatch');\n $this->_AnalyzeRequest($Request);\n \n // Send user to login page if this is a private community\n if (C('Garden.PrivateCommunity') && $this->ControllerName() != 'EntryController' && !Gdn::Session()->IsValid()) {\n Redirect(Gdn::Authenticator()->SignInUrl($this->Request));\n exit();\n }\n \n /*\n echo \"<br />Gdn::Request thinks: \".Gdn::Request()->Path();\n echo \"<br />Gdn::Request also suggests: output=\".Gdn::Request()->OutputFormat().\", filename=\".Gdn::Request()->Filename();\n echo '<br />Request: '.$this->Request; \n echo '<br />App folder: '.$this->_ApplicationFolder;\n echo '<br />Controller folder: '.$this->_ControllerFolder;\n echo '<br />ControllerName: '.$this->_ControllerName;\n echo '<br />ControllerMethod: '.$this->_ControllerMethod;\n */\n\n $ControllerName = $this->ControllerName();\n if ($ControllerName != '' && class_exists($ControllerName)) {\n // Create it and call the appropriate method/action\n $Controller = new $ControllerName();\n\n // Pass along any assets\n if (is_array($this->_AssetCollection)) {\n foreach ($this->_AssetCollection as $AssetName => $Assets) {\n foreach ($Assets as $Asset) {\n $Controller->AddAsset($AssetName, $Asset);\n }\n }\n }\n\n // Instantiate Imported & Uses classes\n $Controller->GetImports();\n\n // Pass in the syndication method\n $Controller->SyndicationMethod = $this->_SyndicationMethod;\n\n // Pass along the request\n $Controller->SelfUrl = $this->Request;\n\n // Pass along any objects\n foreach($this->_PropertyCollection as $Name => $Mixed) {\n $Controller->$Name = $Mixed;\n }\n\n // Set up a default controller method in case one isn't defined.\n $ControllerMethod = str_replace('_', '', $this->_ControllerMethod);\n $Controller->OriginalRequestMethod = $ControllerMethod;\n \n // Take enabled plugins into account, as well\n $PluginManagerHasReplacementMethod = Gdn::PluginManager()->HasNewMethod($this->ControllerName(), $this->_ControllerMethod);\n if (!$PluginManagerHasReplacementMethod && ($this->_ControllerMethod == '' || !method_exists($Controller, $ControllerMethod))) {\n // Check to see if there is an 'x' version of the method.\n if (method_exists($Controller, 'x'.$ControllerMethod)) {\n // $PluginManagerHasReplacementMethod = TRUE;\n $ControllerMethod = 'x'.$ControllerMethod;\n } else {\n if ($this->_ControllerMethod != '')\n array_unshift($this->_ControllerMethodArgs, $this->_ControllerMethod);\n \n $this->_ControllerMethod = 'Index';\n $ControllerMethod = 'Index';\n \n $PluginManagerHasReplacementMethod = Gdn::PluginManager()->HasNewMethod($this->ControllerName(), $this->_ControllerMethod);\n }\n }\n // Pass in the querystring values\n $Controller->ApplicationFolder = $this->_ApplicationFolder;\n $Controller->Application = $this->EnabledApplication();\n $Controller->ControllerFolder = $this->_ControllerFolder;\n $Controller->RequestMethod = $this->_ControllerMethod;\n $Controller->RequestArgs = $this->_ControllerMethodArgs;\n $Controller->Request = $Request;\n $Controller->DeliveryType($Request->GetValue('DeliveryType', ''));\n $Controller->DeliveryMethod($Request->GetValue('DeliveryMethod', ''));\n\n // Set special controller method options for REST APIs.\n $this->_SetControllerMethod($Controller);\n \n $Controller->Initialize();\n\n // Call the requested method on the controller - error out if not defined.\n if ($PluginManagerHasReplacementMethod || method_exists($Controller, $ControllerMethod)) {\n // call_user_func_array is too slow!!\n //call_user_func_array(array($Controller, $ControllerMethod), $this->_ControllerMethodArgs);\n \n if ($PluginManagerHasReplacementMethod) {\n Gdn::PluginManager()->CallNewMethod($Controller, $Controller->ControllerName, $ControllerMethod);\n } else { \n $Args = $this->_ControllerMethodArgs;\n $Count = count($Args);\n\n try {\n call_user_func_array(array($Controller, $ControllerMethod), $Args);\n } catch (Exception $Ex) {\n $Controller->RenderException($Ex);\n exit();\n }\n }\n } else {\n Gdn::Request()->WithRoute('Default404');\n return $this->Dispatch();\n }\n }\n }\n \n /**\n * Undocumented method.\n *\n * @param string $EnabledApplications\n * @todo Method EnabledApplicationFolders() and $EnabledApplications needs descriptions.\n */\n public function EnabledApplicationFolders($EnabledApplications = '') {\n if ($EnabledApplications != '' && count($this->_EnabledApplicationFolders) == 0) {\n $this->_EnabledApplications = $EnabledApplications;\n $this->_EnabledApplicationFolders = array_values($EnabledApplications);\n }\n return $this->_EnabledApplicationFolders;\n }\n\n /**\n * Returns the name of the enabled application based on $ApplicationFolder.\n *\n * @param string The application folder related to the application name you want to return.\n */\n public function EnabledApplication($ApplicationFolder = '') {\n if ($ApplicationFolder == '')\n $ApplicationFolder = $this->_ApplicationFolder;\n\n $EnabledApplication = array_keys($this->_EnabledApplications, $ApplicationFolder);\n $EnabledApplication = count($EnabledApplication) > 0 ? $EnabledApplication[0] : '';\n $this->EventArguments['EnabledApplication'] = $EnabledApplication;\n $this->FireEvent('AfterEnabledApplication');\n return $EnabledApplication;\n }\n\n /**\n * Allows the passing of a string to the controller's asset collection.\n *\n * @param string $AssetName The name of the asset collection to add the string to.\n * @param mixed $Asset The string asset to be added. The asset can be one of two things.\n * - <b>string</b>: The string will be rendered to the page.\n * - <b>Gdn_IModule</b>: The Gdn_IModule::Render() method will be called when the asset is rendered.\n */\n public function PassAsset($AssetName, $Asset) {\n $this->_AssetCollection[$AssetName][] = $Asset;\n }\n\n /**\n * Allows the passing of any variable to the controller as a property.\n *\n * @param string $Name The name of the property to assign the variable to.\n * @param mixed $Mixed The variable to be passed as a property of the controller.\n */\n public function PassProperty($Name, $Mixed) {\n $this->_PropertyCollection[$Name] = $Mixed;\n }\n\n /**\n * Parses the query string looking for supplied request parameters. Places\n * anything useful into this object's Controller properties.\n *\n * @param int $FolderDepth\n * @todo $folderDepth needs a description.\n */\n protected function _AnalyzeRequest(&$Request, $FolderDepth = 1) {\n // Here is the basic format of a request:\n // [/application]/controller[/method[.json|.xml]]/argn|argn=valn\n\n // Here are some examples of what this method could/would receive:\n // /application/controller/method/argn\n // /controller/method/argn\n // /application/controller/argn\n // /controller/argn\n // /controller\n\n // Clear the slate\n $this->_ApplicationFolder = '';\n $this->_ControllerFolder = '';\n $this->_ControllerName = '';\n $this->_ControllerMethod = 'index';\n $this->_ControllerMethodArgs = array();\n $this->Request = $Request->Path();\n\n $PathAndQuery = $Request->PathAndQuery();\n $MatchRoute = Gdn::Router()->MatchRoute($PathAndQuery);\n\n // We have a route. Take action.\n if ($MatchRoute !== FALSE) {\n switch ($MatchRoute['Type']) {\n case 'Internal':\n $Request->PathAndQuery($MatchRoute['FinalDestination']);\n $this->Request = $MatchRoute['FinalDestination'];\n break;\n\n case 'Temporary':\n Header( \"HTTP/1.1 302 Moved Temporarily\" );\n Header( \"Location: \".$MatchRoute['FinalDestination'] );\n exit();\n break;\n\n case 'Permanent':\n Header( \"HTTP/1.1 301 Moved Permanently\" );\n Header( \"Location: \".$MatchRoute['FinalDestination'] );\n exit();\n break;\n\n case 'NotFound':\n Header( \"HTTP/1.1 404 Not Found\" );\n $this->Request = $MatchRoute['FinalDestination'];\n break;\n }\n }\n\n switch ($Request->OutputFormat()) {\n case 'rss':\n $this->_SyndicationMethod = SYNDICATION_RSS;\n break;\n case 'atom':\n $this->_SyndicationMethod = SYNDICATION_ATOM;\n break;\n case 'default':\n default:\n $this->_SyndicationMethod = SYNDICATION_NONE;\n break;\n }\n\n if ($this->Request == '')\n {\n $DefaultController = Gdn::Router()->GetRoute('DefaultController');\n $this->Request = $DefaultController['Destination'];\n }\n \n $Parts = explode('/', $this->Request);\n $Length = count($Parts);\n if ($Length == 1 || $FolderDepth <= 0) {\n $FolderDepth = 0;\n $this->_ControllerName = $Parts[0];\n $this->_MapParts($Parts, 0);\n $this->_FetchController(TRUE); // Throw an error if this fails because there's nothing else to check\n } else if ($Length == 2) {\n // Force a depth of 1 because only one of the two url parts can be a folder.\n $FolderDepth = 1;\n }\n if ($FolderDepth == 2) {\n $this->_ApplicationFolder = $Parts[0];\n $this->_ControllerFolder = $Parts[1];\n $this->_MapParts($Parts, 2);\n\n if (!$this->_FetchController()) {\n // echo '<div>Failed. AppFolder: '.$this->_ApplicationFolder.'; Cont Folder: '.$this->_ControllerFolder.'; Cont: '.$this->_ControllerName.';</div>';\n $this->_AnalyzeRequest($Request, 1);\n }\n\n } else if ($FolderDepth == 1) {\n // Try the application folder first\n $Found = FALSE;\n if (in_array($Parts[0], $this->EnabledApplicationFolders())) {\n // Check to see if the first part is an application\n $this->_ApplicationFolder = $Parts[0];\n $this->_MapParts($Parts, 1);\n $Found = $this->_FetchController();\n }\n if (!$Found) {\n // echo '<div>Failed. AppFolder: '.$this->_ApplicationFolder.'; Cont Folder: '.$this->_ControllerFolder.'; Cont: '.$this->_ControllerName.';</div>';\n // Check to see if the first part is a controller folder\n $this->_ApplicationFolder = '';\n $this->_ControllerFolder = $Parts[0];\n $this->_MapParts($Parts, 1);\n if (!$this->_FetchController()) {\n // echo '<div>Failed. AppFolder: '.$this->_ApplicationFolder.'; Cont Folder: '.$this->_ControllerFolder.'; Cont: '.$this->_ControllerName.';</div>';\n $this->_AnalyzeRequest($Request, 0);\n }\n }\n }\n }\n\n /**\n * Searches through the /cache/controller_mappings.php file for the requested\n * controller. If it doesn't find it, it searches through the entire\n * application's folders for the requested controller. If it finds the\n * controller, it adds the mapping to /cache/controller_mappings.php so it\n * won't need to search again. If it doesn't find the controller file\n * anywhere, it throws a fatal error.\n *\n * @param boolean $ThrowErrorOnFailure\n * @todo $ThrowErrorOnFailure needs a description.\n */\n private function _FetchController($ThrowErrorOnFailure = FALSE) {\n $ControllerWhiteList = $this->EnabledApplicationFolders();\n // Don't include it if it's already been included\n if (!class_exists($this->ControllerName())) {\n $PathParts = array('controllers');\n if ($this->_ControllerFolder != '')\n $PathParts[] = $this->_ControllerFolder;\n\n $PathParts[] = 'class.'.strtolower($this->_ControllerName).'controller.php';\n $ControllerFileName = CombinePaths($PathParts);\n\n // Limit the white list to the specified application folder if it was in the request\n if ($this->_ApplicationFolder != '' && InArrayI($this->_ApplicationFolder, $ControllerWhiteList))\n $ControllerWhiteList = array($this->_ApplicationFolder);\n\n $ControllerPath = Gdn_FileSystem::FindByMapping('controller', PATH_APPLICATIONS, $ControllerWhiteList, $ControllerFileName);\n if ($ControllerPath !== FALSE) {\n // Strip the \"Application Folder\" from the controller path (this is\n // used by the controller for various purposes. ie. knowing which\n // application to search in for a view file).\n $this->_ApplicationFolder = explode(DS, str_replace(PATH_APPLICATIONS . DS, '', $ControllerPath));\n $this->_ApplicationFolder = $this->_ApplicationFolder[0];\n $AppControllerName = ucfirst(strtolower($this->_ApplicationFolder)).'Controller';\n\n // Load the application's master controller\n if (!class_exists($AppControllerName))\n require_once(CombinePaths(array(PATH_APPLICATIONS, $this->_ApplicationFolder, 'controllers', 'class.'.strtolower($this->_ApplicationFolder).'controller.php')));\n\n // Now load the library (no need to check for existence - couldn't\n // have made it here if it didn't exist).\n require_once($ControllerPath);\n }\n }\n if (!class_exists($this->ControllerName())) {\n if ($ThrowErrorOnFailure === TRUE) {\n if (ForceBool(Gdn::Config('Garden.Debug'))) {\n trigger_error(ErrorMessage('Controller not found: '.$this->ControllerName(), 'Dispatcher', '_FetchController'), E_USER_ERROR);\n } else {\n $MissingRoute = Gdn::Router()->GetRoute('Default404');\n \n // Return a 404 message\n list($this->_ApplicationFolder, $this->_ControllerName, $this->_ControllerMethod) = explode('/', $MissingRoute['Destination']);\n $ControllerFileName = CombinePaths(array('controllers', 'class.' . strtolower($this->_ControllerName) . 'controller.php'));\n $ControllerPath = Gdn_FileSystem::FindByMapping('controller', PATH_APPLICATIONS, $ControllerWhiteList, $ControllerFileName);\n $this->_ApplicationFolder = explode(DS, str_replace(PATH_APPLICATIONS . DS, '', $ControllerPath));\n $this->_ApplicationFolder = $this->_ApplicationFolder[0];\n require_once(CombinePaths(array(PATH_APPLICATIONS, $this->_ApplicationFolder, 'controllers', 'class.'.strtolower($this->_ApplicationFolder).'controller.php')));\n require_once($ControllerPath);\n }\n }\n return FALSE;\n } else {\n return TRUE;\n }\n }\n \n /**\n * An internal method used to map parts of the request to various properties\n * of this object that represent the controller, controller method, and\n * controller method arguments.\n *\n * @param array $Parts An array of parts of the request.\n * @param int $ControllerKey An integer representing the key of the controller in the $Parts array.\n */\n private function _MapParts($Parts, $ControllerKey) {\n $Length = count($Parts);\n if ($Length > $ControllerKey)\n $this->_ControllerName = ucfirst(strtolower($Parts[$ControllerKey]));\n\n if ($Length > $ControllerKey + 1)\n $this->_ControllerMethod = $Parts[$ControllerKey + 1];\n\n if ($Length > $ControllerKey + 2) {\n for ($i = $ControllerKey + 2; $i < $Length; ++$i) {\n if ($Parts[$i] != '')\n $this->_ControllerMethodArgs[] = $Parts[$i];\n\n }\n }\n }\n\n /**\n * Set up the dispatch defaults.\n */\n protected function _SetControllerMethod($Controller) {\n // Start with the controller name.\n $ControllerName = $Controller->ControllerName;\n if (StringEndsWith($ControllerName, 'controller', TRUE))\n $ControllerName = substr($ControllerName, 0, -10);\n\n // Figure out the method from the api.\n $PathParts = explode('/', trim($Controller->Request->Path(), '/'));\n\n foreach ($PathParts as $Part) {\n $Curr = array_shift($PathParts);\n if (strcasecmp($Curr, $ControllerName) == 0) {\n break;\n }\n }\n\n // We've gotten rid of the controller so the next part is the method.\n if (count($PathParts) == 0)\n return FALSE;\n $MethodName = array_shift($PathParts);\n $DeliveryMethod = strstr($MethodName, '.');\n\n if ($DeliveryMethod !== FALSE) {\n $DeliveryMethod = strtoupper(trim($DeliveryMethod, '.'));\n switch ($DeliveryMethod) {\n case DELIVERY_METHOD_JSON:\n case DELIVERY_METHOD_XML:\n // The extension was recognized.\n $Controller->DeliveryMethod($DeliveryMethod);\n $MethodName = ucfirst(substr($MethodName, 0, -(strlen($DeliveryMethod) + 1)));\n $Controller->DeliveryType(DELIVERY_TYPE_DATA);\n $Controller->RequestMethod = $MethodName;\n $Controller->OriginalRequestMethod = strtolower($MethodName);\n\n if (!method_exists($Controller, $MethodName)) {\n throw new Exception(sprintf(T('Method does not exist: %s.'), get_class($Controller).'::'.$MethodName.'()'), 404);\n }\n\n $Meth = new ReflectionMethod($Controller, $MethodName);\n $MethParams = $Meth->getParameters();\n $Params = array();\n $Get = array_change_key_case($Controller->Request->GetRequestArguments(Gdn_Request::INPUT_GET));\n $MissingParams = array();\n\n // Set all of the parameters.\n foreach ($MethParams as $Index => $MethParam) {\n $ParamName = strtolower($MethParam->getName());\n\n if (isset($PathParts[$Index]))\n $Params[] = $PathParts[$Index];\n elseif (isset($Get[$ParamName]))\n $Params[] = $Get[$ParamName];\n elseif ($MethParam->isDefaultValueAvailable())\n $Params[] = $MethParam->getDefaultValue();\n else\n $MissingParams[] = \"$Index: $ParamName\";\n }\n\n if (count($MissingParams) > 0)\n throw new Exception(sprintf(T('Missing required parameters: %s.'), implode(', ', $MissingParams)), 400);\n\n // Call the method.\n //call_user_func_array(array($Controller, $MethodName), $Params);\n $this->_ControllerMethodArgs = $Params;\n\n break;\n default:\n return FALSE;\n }\n } else {\n return FALSE;\n }\n return TRUE;\n }\n}\n", "input": "", "output": "【漏洞类型】跨站脚本攻击(XSS)\n【严重性】High\n【漏洞摘要】此代码存在一个跨站脚本攻击(XSS)漏洞,攻击者可以通过构造恶意的URL中的文件名(`$Request->Path()`)来执行任意客户端代码。具体表现为,原始代码在`EntryController::Handshake()`和`Dispatcher::_AnalyzeRequest()`中使用了未验证的用户输入作为`$this->SelfUrl`和`$this->Request`的值。这样,恶意用户可以注入JavaScript代码到URL中,从而在用户的浏览器中执行。\n【关键代码模式】与此漏洞相关的关键代码模式包括:\n- 使用用户输入的`$Request->Path()`来设置`$this->SelfUrl`和`$this->Request`。\n- `$this->SelfUrl = Gdn::Request()->Path();`\n- `$this->Request = $Request->Path();`\n【修复方法说明】修复方法通过将`$Request->Path()`替换为`Url('')`来解决问题,确保不会直接使用用户输入的URL路径进行渲染。这遵循了最佳实践,即对用户输入进行适当的验证和编码,以防止XSS攻击。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nxwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-api/src/main/java/org/xwiki/rendering/async/internal/block/BlockAsyncRendererConfiguration.java\n### 代码内容:\n/*\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n */\npackage org.xwiki.rendering.async.internal.block;\n\nimport java.lang.reflect.Type;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.xwiki.component.descriptor.ComponentRole;\nimport org.xwiki.component.descriptor.DefaultComponentRole;\nimport org.xwiki.model.reference.EntityReference;\nimport org.xwiki.rendering.async.internal.AsyncRendererConfiguration;\nimport org.xwiki.rendering.block.Block;\nimport org.xwiki.rendering.syntax.Syntax;\nimport org.xwiki.rendering.transformation.TransformationContext;\n\n/**\n * Configuration to pass to {@link BlockAsyncRendererExecutor}.\n * \n * @version $Id$\n * @since 10.10RC1\n */\npublic class BlockAsyncRendererConfiguration extends AsyncRendererConfiguration\n{\n private List<String> id;\n\n private Block block;\n\n private boolean asyncAllowed;\n\n private boolean cacheAllowed;\n\n private Syntax defaultSyntax;\n\n private String transformationId;\n\n private Syntax targetSyntax;\n\n private boolean inline;\n\n private BlockAsyncRendererDecorator decorator;\n\n private Set<EntityReference> references;\n\n private Set<ComponentRole<?>> roles;\n\n /**\n * @param idElements the id used as prefix (concatenated with contextual information) for the actual job identifier\n * @param block the block to transform\n */\n public BlockAsyncRendererConfiguration(List<?> idElements, Block block)\n {\n this.id = new ArrayList<>(idElements.size());\n addElements(idElements);\n this.block = block;\n\n // Enabled by default\n this.asyncAllowed = true;\n }\n\n private void addElements(Iterable<?> elements)\n {\n for (Object element : elements) {\n if (element instanceof Iterable) {\n addElements((Iterable<?>) element);\n } else {\n this.id.add(element != null ? element.toString() : null);\n }\n }\n }\n\n /**\n * @return the id\n */\n public List<String> getId()\n {\n return this.id;\n }\n\n /**\n * @param id the id to set\n */\n public void setId(List<String> id)\n {\n this.id = id;\n }\n\n /**\n * @return the block\n */\n public Block getBlock()\n {\n return this.block;\n }\n\n /**\n * @return true if the execution should be asynchronous if possible\n */\n public boolean isAsyncAllowed()\n {\n return this.asyncAllowed;\n }\n\n /**\n * @param asyncAllowed true if the execution should be asynchronous if possible\n */\n public void setAsyncAllowed(boolean asyncAllowed)\n {\n this.asyncAllowed = asyncAllowed;\n }\n\n /**\n * @return true if the result cache be reused several times\n */\n public boolean isCacheAllowed()\n {\n return cacheAllowed;\n }\n\n /**\n * @param cacheAllowed true if the result cache be reused several times\n */\n public void setCacheAllowed(boolean cacheAllowed)\n {\n this.cacheAllowed = cacheAllowed;\n }\n\n /**\n * @return the default syntax\n */\n public Syntax getDefaultSyntax()\n {\n return this.defaultSyntax;\n }\n\n /**\n * @param defaultSyntax the default syntax\n */\n public void setDefaultSyntax(Syntax defaultSyntax)\n {\n this.defaultSyntax = defaultSyntax;\n }\n\n /**\n * @return the transformation id to set in the {@link TransformationContext}\n */\n public String getTransformationId()\n {\n return this.transformationId;\n }\n\n /**\n * @param transformationId the transformation id to set in the {@link TransformationContext}\n */\n public void setTransformationId(String transformationId)\n {\n this.transformationId = transformationId;\n }\n\n /**\n * @return the target Syntax\n */\n public Syntax getTargetSyntax()\n {\n return this.targetSyntax;\n }\n\n /**\n * @param targetSyntax the target syntax\n */\n public void setTargetSyntax(Syntax targetSyntax)\n {\n this.targetSyntax = targetSyntax;\n }\n\n /**\n * @return true if the rendering should be done in an inline context\n */\n public boolean isInline()\n {\n return this.inline;\n }\n\n /**\n * @param inline true if the rendering should be done in an inline context\n */\n public void setInline(boolean inline)\n {\n this.inline = inline;\n }\n\n /**\n * @return the decorator\n */\n public BlockAsyncRendererDecorator getDecorator()\n {\n return this.decorator;\n }\n\n /**\n * @param decorator the decorator\n */\n public void setDecorator(BlockAsyncRendererDecorator decorator)\n {\n this.decorator = decorator;\n }\n\n /**\n * @return the references involved in the rendering (they will be used to invalidate the cache when one of those\n * entities is modified). More can be injected by the {@link Block}s trough script macros.\n */\n public Set<EntityReference> getReferences()\n {\n return this.references != null ? this.references : Collections.emptySet();\n }\n\n /**\n * @param references the references involved in the rendering (they will be used to invalidate the cache when one of\n * those entities is modified). More can be injected by the {@link Block}s trough script macros.\n */\n public void setReferences(Set<EntityReference> references)\n {\n this.references = new HashSet<>(references);\n }\n\n /**\n * @param reference the reference to add\n */\n public void useEntity(EntityReference reference)\n {\n if (this.references == null) {\n this.references = new HashSet<>();\n }\n\n this.references.add(reference);\n }\n\n /**\n * @return the components involved in the rendering (they will be used to invalidate the cache when one of those\n * component is modified). More can be injected by the {@link Block}s trough script macros.\n */\n public Set<ComponentRole<?>> getRoles()\n {\n return this.roles != null ? this.roles : Collections.emptySet();\n }\n\n /**\n * @param roles involved in the rendering (they will be used to invalidate the cache when one of those component is\n * modified). More can be injected by the {@link Block}s trough script macros.\n */\n public void setRoles(Set<ComponentRole<?>> roles)\n {\n this.roles = roles;\n }\n\n /**\n * Indicate that the execution manipulate components of the passed type and the result will need to be removed from\n * the cache if any is unregistered or a new one registered.\n * \n * @param roleType the type of the component role\n */\n public void useComponent(Type roleType)\n {\n if (this.roles == null) {\n this.roles = new HashSet<>();\n }\n\n this.roles.add(new DefaultComponentRole<>(roleType, null));\n }\n\n /**\n * Indicate that the execution manipulate component wit the passed type and hint and the result will need to be\n * removed from the cache if it's registered or unregistered.\n * \n * @param roleType the type of the component role\n * @param roleHint the hint of the component\n */\n public void useComponent(Type roleType, String roleHint)\n {\n if (this.roles == null) {\n this.roles = new HashSet<>();\n }\n\n this.roles.add(new DefaultComponentRole<>(roleType, roleHint));\n }\n}\n\n## 内容2\n### 文件路径:\nxwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-api/src/main/java/org/xwiki/rendering/async/internal/block/DefaultBlockAsyncRenderer.java\n### 代码内容:\n/*\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n */\npackage org.xwiki.rendering.async.internal.block;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport javax.inject.Inject;\n\nimport org.xwiki.component.annotation.Component;\nimport org.xwiki.component.descriptor.ComponentRole;\nimport org.xwiki.model.reference.EntityReference;\nimport org.xwiki.rendering.RenderingException;\nimport org.xwiki.rendering.async.AsyncContext;\nimport org.xwiki.rendering.block.Block;\nimport org.xwiki.rendering.block.CompositeBlock;\nimport org.xwiki.rendering.block.MetaDataBlock;\nimport org.xwiki.rendering.block.XDOM;\nimport org.xwiki.rendering.syntax.Syntax;\nimport org.xwiki.rendering.transformation.TransformationContext;\nimport org.xwiki.rendering.util.ErrorBlockGenerator;\nimport org.xwiki.rendering.util.ParserUtils;\n\n/**\n * Helper to execute Block based asynchronous renderer.\n * \n * @version $Id$\n * @since 10.10RC1\n */\n@Component(roles = DefaultBlockAsyncRenderer.class)\npublic class DefaultBlockAsyncRenderer extends AbstractBlockAsyncRenderer\n{\n private static final String TM_FAILEDASYNC = \"rendering.async.error.failed\";\n\n private static final ParserUtils PARSERUTILS = new ParserUtils();\n\n @Inject\n private AsyncContext asyncContext;\n\n @Inject\n private ErrorBlockGenerator errorBlockGenerator;\n\n private BlockAsyncRendererConfiguration configuration;\n\n /**\n * @param configuration the configuration of the renderer\n */\n public void initialize(BlockAsyncRendererConfiguration configuration)\n {\n this.configuration = configuration;\n }\n\n @Override\n public List<String> getId()\n {\n return this.configuration.getId();\n }\n\n @Override\n public boolean isAsyncAllowed()\n {\n return this.configuration.isAsyncAllowed();\n }\n\n @Override\n public boolean isCacheAllowed()\n {\n return this.configuration.isCacheAllowed();\n }\n\n @Override\n public boolean isInline()\n {\n return this.configuration.isInline();\n }\n\n @Override\n public Syntax getTargetSyntax()\n {\n return this.configuration.getTargetSyntax();\n }\n\n @Override\n public Block execute(boolean async, boolean cached) throws RenderingException\n {\n Block resultBlock;\n\n try {\n // Register the known involved references and components\n for (EntityReference reference : this.configuration.getReferences()) {\n this.asyncContext.useEntity(reference);\n }\n for (ComponentRole<?> role : this.configuration.getRoles()) {\n this.asyncContext.useComponent(role.getRoleType(), role.getRoleHint());\n }\n\n // Get the block to transform\n Block block = this.configuration.getBlock();\n\n // Make sure the source is inline if needed\n if (this.configuration.isInline()) {\n block = PARSERUTILS.convertToInline(block, true);\n }\n\n // Create a XDOM instance for the transformation context\n XDOM xdom;\n if (block instanceof XDOM) {\n xdom = (XDOM) block;\n } else {\n Block rootBlock = block.getRoot();\n\n if (rootBlock instanceof XDOM) {\n xdom = (XDOM) rootBlock;\n } else {\n xdom = new XDOM(Collections.singletonList(rootBlock));\n }\n }\n\n ///////////////////////////////////////\n // Transformations\n\n resultBlock = tranform(xdom, block);\n } catch (Exception e) {\n // Display the error in the result\n resultBlock = new CompositeBlock(this.errorBlockGenerator.generateErrorBlocks(this.configuration.isInline(),\n TM_FAILEDASYNC, \"Failed to execute asynchronous content\", null, e));\n }\n\n return resultBlock;\n }\n\n private Block tranform(XDOM xdom, Block block) throws RenderingException\n {\n TransformationContext transformationContext =\n new TransformationContext(xdom, this.configuration.getDefaultSyntax(), false);\n transformationContext.setTargetSyntax(this.configuration.getTargetSyntax());\n transformationContext.setId(this.configuration.getTransformationId());\n\n transform(block, transformationContext);\n\n // The result is often inserted in a bigger content so we remove the XDOM around it\n if (block instanceof XDOM) {\n return new MetaDataBlock(block.getChildren(), ((XDOM) block).getMetaData());\n }\n\n return block;\n }\n}\n\n## 内容3\n### 文件路径:\nxwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-macro/pom.xml\n### 代码内容:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n-->\n\n<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/maven-v4_0_0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n <parent>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-rendering-async</artifactId>\n <version>14.8-SNAPSHOT</version>\n </parent>\n <artifactId>xwiki-platform-rendering-async-macro</artifactId>\n <name>XWiki Platform - Rendering - Async - Macro</name>\n <packaging>jar</packaging>\n <description>XWiki Platform - Rendering - Async - Macro</description>\n <properties>\n <xwiki.jacoco.instructionRatio>0.00</xwiki.jacoco.instructionRatio>\n </properties>\n <dependencies>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-rendering-async-default</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-bridge</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-rendering-xwiki</artifactId>\n <version>${project.version}</version>\n </dependency>\n\n <!-- Testing dependencies -->\n <dependency>\n <groupId>org.xwiki.rendering</groupId>\n <artifactId>xwiki-rendering-syntax-xwiki21</artifactId>\n <version>${rendering.version}</version>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.xwiki.rendering</groupId>\n <artifactId>xwiki-rendering-syntax-event</artifactId>\n <version>${rendering.version}</version>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.xwiki.rendering</groupId>\n <artifactId>xwiki-rendering-test</artifactId>\n <version>${rendering.version}</version>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-job-default</artifactId>\n <version>${commons.version}</version>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>javax.servlet</groupId>\n <artifactId>javax.servlet-api</artifactId>\n <scope>test</scope>\n </dependency>\n </dependencies>\n <build>\n <plugins>\n <plugin>\n <groupId>org.apache.maven.plugins</groupId>\n <artifactId>maven-checkstyle-plugin</artifactId>\n <executions>\n <execution>\n <!-- Specify the \"default\" execution id so that the \"blocker\" one is always executed -->\n <id>default</id>\n <configuration>\n <excludes>\n org/xwiki/rendering/internal/macro/display/DisplayMacro.java\n </excludes>\n </configuration>\n </execution>\n </executions>\n </plugin>\n </plugins>\n </build>\n</project>\n\n## 内容4\n### 文件路径:\nxwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-macro/src/main/java/org/xwiki/rendering/async/internal/AsyncMacro.java\n### 代码内容:\n/*\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n */\npackage org.xwiki.rendering.async.internal;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\n\nimport javax.inject.Inject;\nimport javax.inject.Named;\nimport javax.inject.Singleton;\n\nimport org.xwiki.bridge.DocumentAccessBridge;\nimport org.xwiki.component.annotation.Component;\nimport org.xwiki.model.reference.DocumentReference;\nimport org.xwiki.model.reference.DocumentReferenceResolver;\nimport org.xwiki.rendering.async.AsyncContext;\nimport org.xwiki.rendering.async.AsyncMacroParameters;\nimport org.xwiki.rendering.async.internal.block.BlockAsyncRendererConfiguration;\nimport org.xwiki.rendering.async.internal.block.BlockAsyncRendererExecutor;\nimport org.xwiki.rendering.block.Block;\nimport org.xwiki.rendering.block.Block.Axes;\nimport org.xwiki.rendering.block.MacroBlock;\nimport org.xwiki.rendering.block.MetaDataBlock;\nimport org.xwiki.rendering.block.XDOM;\nimport org.xwiki.rendering.block.match.MetadataBlockMatcher;\nimport org.xwiki.rendering.listener.MetaData;\nimport org.xwiki.rendering.macro.AbstractMacro;\nimport org.xwiki.rendering.macro.MacroContentParser;\nimport org.xwiki.rendering.macro.MacroExecutionException;\nimport org.xwiki.rendering.macro.descriptor.DefaultContentDescriptor;\nimport org.xwiki.rendering.transformation.MacroTransformationContext;\nimport org.xwiki.rendering.transformation.RenderingContext;\n\n/**\n * Asynchronous and cached execution of wiki content.\n * \n * @version $Id$\n * @since 11.6RC1\n */\n@Component\n@Named(\"async\")\n@Singleton\npublic class AsyncMacro extends AbstractMacro<AsyncMacroParameters>\n{\n /**\n * The description of the macro.\n */\n private static final String DESCRIPTION = \"Asynchronous and cached execution of wiki content.\";\n\n @Inject\n private BlockAsyncRendererExecutor executor;\n\n @Inject\n private MacroContentParser parser;\n\n @Inject\n private DocumentAccessBridge documentAccessBridge;\n\n @Inject\n private DocumentReferenceResolver<String> resolver;\n\n @Inject\n private AsyncContext asyncContext;\n\n @Inject\n private RenderingContext renderingContext;\n\n /**\n * Default constructor.\n */\n public AsyncMacro()\n {\n super(\"Async\", DESCRIPTION, new DefaultContentDescriptor(\"Content to execute\", true, Block.LIST_BLOCK_TYPE),\n AsyncMacroParameters.class);\n\n setDefaultCategories(Set.of(DEFAULT_CATEGORY_CONTENT));\n }\n\n @Override\n public boolean supportsInlineMode()\n {\n return true;\n }\n\n private String getCurrentSource(MacroTransformationContext context)\n {\n String currentSource = null;\n\n if (context != null) {\n currentSource =\n context.getTransformationContext() != null ? context.getTransformationContext().getId() : null;\n\n MacroBlock currentMacroBlock = context.getCurrentMacroBlock();\n\n if (currentMacroBlock != null) {\n MetaDataBlock metaDataBlock =\n currentMacroBlock.getFirstBlock(new MetadataBlockMatcher(MetaData.SOURCE), Axes.ANCESTOR_OR_SELF);\n\n if (metaDataBlock != null) {\n currentSource = (String) metaDataBlock.getMetaData().getMetaData(MetaData.SOURCE);\n }\n }\n }\n\n return currentSource;\n }\n\n @Override\n public List<Block> execute(AsyncMacroParameters parameters, String content, MacroTransformationContext context)\n throws MacroExecutionException\n {\n XDOM xdom = this.parser.parse(content, context, false, context.isInline());\n\n if (xdom.getChildren().isEmpty()) {\n return Collections.emptyList();\n }\n\n String source = getCurrentSource(context);\n\n List<Object> idElements = new ArrayList<>(4);\n\n idElements.add(\"async\");\n idElements.add(\"macro\");\n\n // Generate the id if not provided\n if (parameters.getId() != null) {\n idElements.addAll(parameters.getId());\n } else {\n // Add the source reference in the id\n if (source != null) {\n idElements.add(source);\n }\n // Add the macro index in the id\n long index = context.getXDOM().indexOf(context.getCurrentMacroBlock());\n if (index != -1) {\n idElements.add(index);\n }\n }\n\n BlockAsyncRendererConfiguration configuration = new BlockAsyncRendererConfiguration(idElements, xdom);\n\n // Set author\n if (source != null) {\n DocumentReference sourceReference = this.resolver.resolve(source);\n configuration.setSecureReference(sourceReference, this.documentAccessBridge.getCurrentAuthorReference());\n\n // Invalidate the cache when the document containing the macro call is modified\n configuration.useEntity(sourceReference);\n }\n\n // Indicate if the result should be inline or not\n configuration.setInline(context.isInline());\n\n // Enable/disable async\n configuration.setAsyncAllowed(parameters.isAsync());\n // Enable/disable caching\n configuration.setCacheAllowed(parameters.isCached());\n // Indicate context entries\n configuration.setContextEntries(parameters.getContext());\n\n // Indicate the syntax of the content\n configuration.setDefaultSyntax(this.parser.getCurrentSyntax(context));\n\n // Indicate the target syntax\n configuration.setTargetSyntax(this.renderingContext.getTargetSyntax());\n\n // Set the transformation id\n configuration.setTransformationId(context.getTransformationContext().getId());\n\n try {\n Block result = this.executor.execute(configuration);\n\n // Indicate the content is not transformed if the current execution is not async\n if (!parameters.isAsync() || !this.asyncContext.isEnabled()) {\n result = new MetaDataBlock(Collections.singletonList(result), this.getNonGeneratedContentMetaData());\n }\n\n return Collections.singletonList(result);\n } catch (Exception e) {\n throw new MacroExecutionException(\"Failed start the execution of the macro\", e);\n }\n }\n}\n\n## 内容5\n### 文件路径:\nxwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/pom.xml\n### 代码内容:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n-->\n\n<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/maven-v4_0_0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n <parent>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-rendering-macros</artifactId>\n <version>14.8-SNAPSHOT</version>\n </parent>\n <artifactId>xwiki-platform-rendering-macro-context</artifactId>\n <name>XWiki Platform - Rendering - Macro - Context</name>\n <description>Executes content with another document set as the current document</description>\n <properties>\n <xwiki.jacoco.instructionRatio>0.91</xwiki.jacoco.instructionRatio>\n <!-- Name to display by the Extension Manager -->\n <xwiki.extension.name>Context Macro</xwiki.extension.name>\n </properties>\n <dependencies>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-bridge</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-rendering-transformation-macro</artifactId>\n <version>${project.version}</version>\n <scope>runtime</scope>\n </dependency>\n <!-- Test Dependencies -->\n <dependency>\n <groupId>org.xwiki.rendering</groupId>\n <artifactId>xwiki-rendering-macro-toc</artifactId>\n <version>${rendering.version}</version>\n <scope>test</scope>\n </dependency>\n </dependencies>\n</project>\n\n## 内容6\n### 文件路径:\nxwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/main/java/org/xwiki/rendering/internal/macro/context/ContextMacro.java\n### 代码内容:\n/*\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n */\npackage org.xwiki.rendering.internal.macro.context;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.inject.Inject;\nimport javax.inject.Named;\nimport javax.inject.Singleton;\n\nimport org.xwiki.bridge.DocumentAccessBridge;\nimport org.xwiki.bridge.DocumentModelBridge;\nimport org.xwiki.component.annotation.Component;\nimport org.xwiki.model.reference.DocumentReference;\nimport org.xwiki.model.reference.DocumentReferenceResolver;\nimport org.xwiki.rendering.block.Block;\nimport org.xwiki.rendering.block.MetaDataBlock;\nimport org.xwiki.rendering.block.XDOM;\nimport org.xwiki.rendering.listener.MetaData;\nimport org.xwiki.rendering.macro.AbstractMacro;\nimport org.xwiki.rendering.macro.MacroContentParser;\nimport org.xwiki.rendering.macro.MacroExecutionException;\nimport org.xwiki.rendering.macro.context.ContextMacroParameters;\nimport org.xwiki.rendering.macro.context.TransformationContextMode;\nimport org.xwiki.rendering.macro.descriptor.DefaultContentDescriptor;\nimport org.xwiki.rendering.transformation.MacroTransformationContext;\nimport org.xwiki.rendering.transformation.TransformationContext;\nimport org.xwiki.rendering.transformation.TransformationManager;\n\n/**\n * Execute the macro's content in the context of another document's reference.\n * \n * @version $Id$\n * @since 3.0M1\n */\n@Component\n@Named(\"context\")\n@Singleton\npublic class ContextMacro extends AbstractMacro<ContextMacroParameters>\n{\n /**\n * The description of the macro.\n */\n private static final String DESCRIPTION = \"Executes content in the context of the passed document\";\n\n /**\n * The description of the macro content.\n */\n private static final String CONTENT_DESCRIPTION = \"The content to execute\";\n\n /**\n * Used to set the current document in the context (old way) and check rights.\n */\n @Inject\n private DocumentAccessBridge documentAccessBridge;\n\n /**\n * The parser used to parse macro content.\n */\n @Inject\n private MacroContentParser contentParser;\n\n /**\n * Used to transform document links into absolute references.\n */\n @Inject\n @Named(\"macro\")\n private DocumentReferenceResolver<String> macroDocumentReferenceResolver;\n\n @Inject\n private TransformationManager transformationManager;\n\n /**\n * Create and initialize the descriptor of the macro.\n */\n public ContextMacro()\n {\n super(\"Context\", DESCRIPTION, new DefaultContentDescriptor(CONTENT_DESCRIPTION, true, Block.LIST_BLOCK_TYPE),\n ContextMacroParameters.class);\n\n // The Context macro must execute early since it can contain include macros which can bring stuff like headings\n // for other macros (TOC macro, etc). Make it the same priority as the Include macro.\n setPriority(10);\n setDefaultCategories(Set.of(DEFAULT_CATEGORY_DEVELOPMENT));\n }\n\n @Override\n public boolean supportsInlineMode()\n {\n return true;\n }\n\n @Override\n public List<Block> execute(ContextMacroParameters parameters, String content, MacroTransformationContext context)\n throws MacroExecutionException\n {\n if (parameters.getDocument() == null) {\n throw new MacroExecutionException(\"You must specify a 'document' parameter pointing to the document to \"\n + \"set in the context as the current document.\");\n }\n\n DocumentReference referencedDocReference =\n this.macroDocumentReferenceResolver.resolve(parameters.getDocument(), context.getCurrentMacroBlock());\n\n boolean currentContextHasProgrammingRights = this.documentAccessBridge.hasProgrammingRights();\n\n List<Block> result;\n try {\n Map<String, Object> backupObjects = new HashMap<>();\n try {\n this.documentAccessBridge.pushDocumentInContext(backupObjects, referencedDocReference);\n\n // The current document is now the passed document. Check for programming rights for it. If it has\n // programming rights then the initial current document also needs programming right, else throw an\n // error since it would be a security breach otherwise.\n if (this.documentAccessBridge.hasProgrammingRights() && !currentContextHasProgrammingRights) {\n throw new MacroExecutionException(\"Current document must have programming rights since the \"\n + \"context document provided [\" + parameters.getDocument() + \"] has programming rights.\");\n }\n\n MetaData metadata = new MetaData();\n metadata.addMetaData(MetaData.SOURCE, parameters.getDocument());\n metadata.addMetaData(MetaData.BASE, parameters.getDocument());\n\n XDOM xdom = this.contentParser.parse(content, context, false, metadata, false);\n\n // Configure the Transformation Context depending on the mode asked.\n if (parameters.getTransformationContext() == TransformationContextMode.DOCUMENT\n || parameters.getTransformationContext() == TransformationContextMode.TRANSFORMATIONS)\n {\n // Apply the transformations but with a Transformation Context having the XDOM of the passed\n // document so that macros execute on the passed document's XDOM (e.g. the TOC macro will generate\n // the toc for the passed document instead of the current document).\n DocumentModelBridge referencedDoc =\n this.documentAccessBridge.getTranslatedDocumentInstance(referencedDocReference);\n XDOM referencedXDOM = referencedDoc.getXDOM();\n\n if (parameters.getTransformationContext() == TransformationContextMode.TRANSFORMATIONS) {\n // Get the XDOM from the referenced doc but with Transformations applied so that all macro are\n // executed and contribute XDOM elements.\n // IMPORTANT: This can be dangerous since it means executing macros, and thus also script macros\n // defined in the referenced document. To be used with caution.\n TransformationContext referencedTxContext =\n new TransformationContext(referencedXDOM, referencedDoc.getSyntax());\n this.transformationManager.performTransformations(referencedXDOM, referencedTxContext);\n }\n\n // Now execute transformation on the context macro content but with the referenced XDOM in the\n // Transformation context!\n TransformationContext txContext =\n new TransformationContext(referencedXDOM, referencedDoc.getSyntax());\n this.transformationManager.performTransformations(xdom, txContext);\n }\n\n // Keep metadata so that the result stay associated to context properties when inserted in the parent\n // XDOM\n result = Arrays.asList((Block) new MetaDataBlock(xdom.getChildren(), xdom.getMetaData()));\n\n } finally {\n this.documentAccessBridge.popDocumentFromContext(backupObjects);\n }\n } catch (Exception e) {\n if (e instanceof MacroExecutionException) {\n throw (MacroExecutionException) e;\n } else {\n throw new MacroExecutionException(\n String.format(\"Failed to render page in the context of [%s]\", referencedDocReference), e);\n }\n }\n\n return result;\n }\n}\n\n", "input": "", "output": "【漏洞类型】未验证的用户输入\n【严重性】High\n【漏洞摘要】此代码中存在未验证用户输入的安全漏洞。在`AsyncMacro`类的`execute`方法中,`parameters.getDocument()`参数直接用于解析和执行宏内容,而没有进行任何访问控制检查。如果攻击者能够控制`parameters.getDocument()`的值,他们可能会执行在他们有权访问的文档中定义的宏,包括潜在的恶意脚本宏。这可能导致未经授权的访问或执行。\n【关键代码模式】\n- `parameters.getDocument()`\n- `this.documentAccessBridge.pushDocumentInContext(backupObjects, referencedDocReference)`\n- `this.transformationManager.performTransformations(xdom, txContext)`\n\n这些代码模式和关键词表明了未验证用户输入的问题,特别是在文档引用的解析和上下文切换过程中。\n【修复方法说明】通过引入`AuthorizationManager`进行访问控制检查,确保只有具有适当权限的用户才能执行特定文档中的宏内容。这解决了未验证用户输入的安全漏洞,提高了系统的安全性。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nREADME.md\n### 代码内容:\n\n\n[](https://api.reuse.software/info/github.com/SAP/less-openui5)\n[](https://travis-ci.org/SAP/less-openui5)\n[](https://www.npmjs.org/package/less-openui5)\n\n# less-openui5\n\n> Build OpenUI5 themes with [Less.js](http://lesscss.org).\n\n## Install\n\n```\nnpm install less-openui5\n```\n\n## Usage\n\n```js\nvar lessOpenUI5 = require('less-openui5');\n\n// Create a builder instance\nvar builder = new lessOpenUI5.Builder();\n\n// Build a theme\nbuilder.build({\n lessInput: '@var: #ffffff; .class { color: @var; float: left }'\n})\n.then(function(result) {\n\n console.log(result.css); // => regular css\n /*\n .class {\n color: #ffffff;\n float: left;\n }\n */\n\n console.log(result.cssRtl); // => mirrored css for right-to-left support\n /*\n .class {\n color: #ffffff;\n float: right;\n }\n */\n\n console.log(result.variables); // => less variables with their values (only global ones)\n /*\n { var: \"#ffffff\" }\n */\n\n console.log(result.imports); // => paths to files imported via @import directives\n /*\n []\n */\n\n // Clear builder cache when finished to cleanup memory\n builder.clearCache();\n\n});\n```\n\n## API\n\n### new Builder([options])\n\nCreates a new `Builder` instance.\n\nIt caches build results to only rebuild a theme when related files have been changed. \nThis is mainly relevant when building themes as part of a server middleware like [`connect-openui5`](https://github.com/SAP/connect-openui5).\n\n#### options\n\n##### fs\nType: [Node.js `fs` interface](https://nodejs.org/api/fs.html)\n\nBy default, all file system access is done via the standard Node.js file system (\"fs\"). With this option an alternative file system can be passed and will be used for all file system operations.\n\n### .build(options)\nReturns a Promise resolving with a [`result`](#result) object.\n\n#### options\n\n##### lessInput\n\n*Required (either `lessInput` or `lessInputPath`, not both)* \nType: `string`\n\nInput less content.\n\n##### lessInputPath\n\n*Required (either `lessInput` or `lessInputPath`, not both)* \nType: `string`\n\nPath to input less file. \nWhen `rootPaths` is given this must be a relative path inside one of the provided `rootPaths`, otherwise just a regular filesystem path.\n\n##### rtl\n\nType: `boolean` \nDefault: `true`\n\nCreate mirrored css for right-to-left support.\n\n##### cssVariables\n\nType `boolean` \nDefault: `false`\n\nCreate files for experimental CSS Variables support ([`cssSkeleton`](#cssSkeleton), [`cssSkeletonRtl`](#cssSkeletonRtl), [`cssVariablesSource`](#cssVariablesSource), [`cssVariables`](#cssVariables-1)).\n\n##### rootPaths\n\nType: `array` of `string`\n\nRoot paths to use for import directives.\n\nThis option differs from the less `compiler.paths` option. \nIt is useful if less files are located in separate folders but referenced as they would all be in one. \n\n###### Example\n\n```js\nrootPaths: [ './lib1', './lib2' ]\n```\n\nFolder structure\n\n```\nlib1\n my\n themes\n foo\n foo.less\nlib2\n my\n themes\n bar\n bar.less\n```\n`lib2/my/themes/bar/bar.less`\n```css\n@import \"../foo/foo.less\"; /* lib1/my/themes/foo/foo.less will be imported */\n```\n\n##### parser\n\nType: `object`\n\nOptions for the [less](http://lesscss.org) parser (`less.Parser`). \n**Note:** Default of `relativeUrls` option is changed from `false` to `true`.\n\n##### compiler\n\nType `object`\n\nOptions for the [less](http://lesscss.org) compiler (`tree.toCss`).\n\n**Note:** The options `sourceMap` and `cleancss` are not supported. An exception will be thrown when used.\n\n##### library.name\n\nType `string`\n\nDot-separated name of the corresponding library. \nIt will be used to inline the `variables` JSON as data-uri which can be retrieved at runtime.\n\n##### scope\n\nType `object`\n\nScope options to be set when not using a .theming file. The available settings are:\n* `selector`\n* `embeddedFilePath`\n* `embeddedCompareFilePath`\n* `baseFilePath`\n\nThose settings correspond to the aScopes property of the .theming file. When using this options all four settings have to be set.\n\nExample:\n```js\nscope: {\n selector: \"scopeSelector\",\n embeddedFilePath: \"src/themes/theme_to_be_embedded.less\",\n embeddedCompareFilePath: \"src/themes/basetheme_to_compare_embedded_theme_to.less\",\n baseFilePath: \"src/themes/basetheme_to_compare_embedded_theme_to.less\"\n}\n```\n\n#### result\n\n##### css\n\nType: `string`\n\nRegular css output.\n\n##### cssRtl\n\nType: `string`\n\nMirrored css for right-to-left support (if rtl option was enabled).\n\n##### variables\n\nType: `object`\n\nKey-value map of global less variables (without @ prefix) defined by files within the same folder as the input file. This excludes variables imported from a different path, as those are usually shared across multiple themes.\n\n##### allVariables\n\nType: `object`\n\nKey-value map of all global less variables (without @ prefix).\n\n##### imports\n\nType: `array`\n\nPaths to files imported via import directives.\n\n##### cssSkeleton\n\nType: `string` \nOnly available when [`options.cssVariables`](#cssVariables) is set to `true`\n\nCSS with references to CSS Variables.\n\n##### cssSkeletonRtl\n\nType: `string` \nOnly available when [`options.cssVariables`](#cssVariables) and [`options.rtl`](#rtl) are set to `true`\n\nMirrored (right-to-left) CSS with references to CSS Variables (if `rtl` option was enabled).\n\n##### cssVariablesSource\n\nType: `string` \nOnly available when [`options.cssVariables`](#cssVariables) is set to `true`\n\nSource file used to compile the `cssVariables` output.\n\n##### cssVariables\n\nType: `string` \nOnly available when [`options.cssVariables`](#cssVariables) is set to `true`\n\nDefinition of CSS Variables based on LESS variables.\n\n### .clearCache()\nClears all cached build results. \nUse this method to prevent high memory consumption when building many themes within the same process.\n\n## Contributing\nSee [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## Release History\n\nSee [CHANGELOG.md](CHANGELOG.md).\n\n## 内容2\n### 文件路径:\nlib/thirdparty/less/README.md\n### 代码内容:\n# less.js\n\nThis folder contains the `lib/less` sub-folder of [v1.6.3](https://github.com/less/less.js/tree/v1.6.3/lib/less) of the [less.js project](https://github.com/less/less.js) with commit [`ccd8ebbfdfa300b6e748e8d7c12e3dbb0efd8371`](https://github.com/less/less.js/commit/ccd8ebbfdfa300b6e748e8d7c12e3dbb0efd8371) applied on top of it to resolve https://github.com/SAP/less-openui5/issues/24.\n\nThe files `browser.js` and `rhino.js` have been removed, as they are not relevant for the Node.js implementation.\n\n## 内容3\n### 文件路径:\nlib/thirdparty/less/env.js\n### 代码内容:\n(function (tree) {\n\n var parseCopyProperties = [\n 'paths', // option - unmodified - paths to search for imports on\n 'optimization', // option - optimization level (for the chunker)\n 'files', // list of files that have been imported, used for import-once\n 'contents', // map - filename to contents of all the files\n 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore\n 'relativeUrls', // option - whether to adjust URL's to be relative\n 'rootpath', // option - rootpath to append to URL's\n 'strictImports', // option -\n 'insecure', // option - whether to allow imports from insecure ssl hosts\n 'dumpLineNumbers', // option - whether to dump line numbers\n 'compress', // option - whether to compress\n 'processImports', // option - whether to process imports. if false then imports will not be imported\n 'syncImport', // option - whether to import synchronously\n 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true\n 'mime', // browser only - mime type for sheet import\n 'useFileCache', // browser only - whether to use the per file session cache\n 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc.\n ];\n\n //currentFileInfo = {\n // 'relativeUrls' - option - whether to adjust URL's to be relative\n // 'filename' - full resolved filename of current file\n // 'rootpath' - path to append to normal URLs for this node\n // 'currentDirectory' - path to the current file, absolute\n // 'rootFilename' - filename of the base file\n // 'entryPath' - absolute path to the entry file\n // 'reference' - whether the file should not be output and only output parts that are referenced\n\n tree.parseEnv = function(options) {\n copyFromOriginal(options, this, parseCopyProperties);\n\n if (!this.contents) { this.contents = {}; }\n if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; }\n if (!this.files) { this.files = {}; }\n\n if (!this.currentFileInfo) {\n var filename = (options && options.filename) || \"input\";\n var entryPath = filename.replace(/[^\\/\\\\]*$/, \"\");\n if (options) {\n options.filename = null;\n }\n this.currentFileInfo = {\n filename: filename,\n relativeUrls: this.relativeUrls,\n rootpath: (options && options.rootpath) || \"\",\n currentDirectory: entryPath,\n entryPath: entryPath,\n rootFilename: filename\n };\n }\n };\n\n var evalCopyProperties = [\n 'silent', // whether to swallow errors and warnings\n 'verbose', // whether to log more activity\n 'compress', // whether to compress\n 'yuicompress', // whether to compress with the outside tool yui compressor\n 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri)\n 'strictMath', // whether math has to be within parenthesis\n 'strictUnits', // whether units need to evaluate correctly\n 'cleancss', // whether to compress with clean-css\n 'sourceMap', // whether to output a source map\n 'importMultiple', // whether we are currently importing multiple copies\n 'urlArgs' // whether to add args into url tokens\n ];\n\n tree.evalEnv = function(options, frames) {\n copyFromOriginal(options, this, evalCopyProperties);\n\n this.frames = frames || [];\n };\n\n tree.evalEnv.prototype.inParenthesis = function () {\n if (!this.parensStack) {\n this.parensStack = [];\n }\n this.parensStack.push(true);\n };\n\n tree.evalEnv.prototype.outOfParenthesis = function () {\n this.parensStack.pop();\n };\n\n tree.evalEnv.prototype.isMathOn = function () {\n return this.strictMath ? (this.parensStack && this.parensStack.length) : true;\n };\n\n tree.evalEnv.prototype.isPathRelative = function (path) {\n return !/^(?:[a-z-]+:|\\/)/.test(path);\n };\n\n tree.evalEnv.prototype.normalizePath = function( path ) {\n var\n segments = path.split(\"/\").reverse(),\n segment;\n\n path = [];\n while (segments.length !== 0 ) {\n segment = segments.pop();\n switch( segment ) {\n case \".\":\n break;\n case \"..\":\n if ((path.length === 0) || (path[path.length - 1] === \"..\")) {\n path.push( segment );\n } else {\n path.pop();\n }\n break;\n default:\n path.push( segment );\n break;\n }\n }\n\n return path.join(\"/\");\n };\n\n //todo - do the same for the toCSS env\n //tree.toCSSEnv = function (options) {\n //};\n\n var copyFromOriginal = function(original, destination, propertiesToCopy) {\n if (!original) { return; }\n\n for(var i = 0; i < propertiesToCopy.length; i++) {\n if (original.hasOwnProperty(propertiesToCopy[i])) {\n destination[propertiesToCopy[i]] = original[propertiesToCopy[i]];\n }\n }\n };\n\n})(require('./tree'));\n\n## 内容4\n### 文件路径:\nlib/thirdparty/less/functions.js\n### 代码内容:\n(function (tree) {\n\ntree.functions = {\n rgb: function (r, g, b) {\n return this.rgba(r, g, b, 1.0);\n },\n rgba: function (r, g, b, a) {\n var rgb = [r, g, b].map(function (c) { return scaled(c, 255); });\n a = number(a);\n return new(tree.Color)(rgb, a);\n },\n hsl: function (h, s, l) {\n return this.hsla(h, s, l, 1.0);\n },\n hsla: function (h, s, l, a) {\n function hue(h) {\n h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);\n if (h * 6 < 1) { return m1 + (m2 - m1) * h * 6; }\n else if (h * 2 < 1) { return m2; }\n else if (h * 3 < 2) { return m1 + (m2 - m1) * (2/3 - h) * 6; }\n else { return m1; }\n }\n\n h = (number(h) % 360) / 360;\n s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a));\n\n var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;\n var m1 = l * 2 - m2;\n\n return this.rgba(hue(h + 1/3) * 255,\n hue(h) * 255,\n hue(h - 1/3) * 255,\n a);\n },\n\n hsv: function(h, s, v) {\n return this.hsva(h, s, v, 1.0);\n },\n\n hsva: function(h, s, v, a) {\n h = ((number(h) % 360) / 360) * 360;\n s = number(s); v = number(v); a = number(a);\n\n var i, f;\n i = Math.floor((h / 60) % 6);\n f = (h / 60) - i;\n\n var vs = [v,\n v * (1 - s),\n v * (1 - f * s),\n v * (1 - (1 - f) * s)];\n var perm = [[0, 3, 1],\n [2, 0, 1],\n [1, 0, 3],\n [1, 2, 0],\n [3, 1, 0],\n [0, 1, 2]];\n\n return this.rgba(vs[perm[i][0]] * 255,\n vs[perm[i][1]] * 255,\n vs[perm[i][2]] * 255,\n a);\n },\n\n hue: function (color) {\n return new(tree.Dimension)(Math.round(color.toHSL().h));\n },\n saturation: function (color) {\n return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%');\n },\n lightness: function (color) {\n return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%');\n },\n hsvhue: function(color) {\n return new(tree.Dimension)(Math.round(color.toHSV().h));\n },\n hsvsaturation: function (color) {\n return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%');\n },\n hsvvalue: function (color) {\n return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%');\n },\n red: function (color) {\n return new(tree.Dimension)(color.rgb[0]);\n },\n green: function (color) {\n return new(tree.Dimension)(color.rgb[1]);\n },\n blue: function (color) {\n return new(tree.Dimension)(color.rgb[2]);\n },\n alpha: function (color) {\n return new(tree.Dimension)(color.toHSL().a);\n },\n luma: function (color) {\n return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%');\n },\n saturate: function (color, amount) {\n // filter: saturate(3.2);\n // should be kept as is, so check for color\n if (!color.rgb) {\n return null;\n }\n var hsl = color.toHSL();\n\n hsl.s += amount.value / 100;\n hsl.s = clamp(hsl.s);\n return hsla(hsl);\n },\n desaturate: function (color, amount) {\n var hsl = color.toHSL();\n\n hsl.s -= amount.value / 100;\n hsl.s = clamp(hsl.s);\n return hsla(hsl);\n },\n lighten: function (color, amount) {\n var hsl = color.toHSL();\n\n hsl.l += amount.value / 100;\n hsl.l = clamp(hsl.l);\n return hsla(hsl);\n },\n darken: function (color, amount) {\n var hsl = color.toHSL();\n\n hsl.l -= amount.value / 100;\n hsl.l = clamp(hsl.l);\n return hsla(hsl);\n },\n fadein: function (color, amount) {\n var hsl = color.toHSL();\n\n hsl.a += amount.value / 100;\n hsl.a = clamp(hsl.a);\n return hsla(hsl);\n },\n fadeout: function (color, amount) {\n var hsl = color.toHSL();\n\n hsl.a -= amount.value / 100;\n hsl.a = clamp(hsl.a);\n return hsla(hsl);\n },\n fade: function (color, amount) {\n var hsl = color.toHSL();\n\n hsl.a = amount.value / 100;\n hsl.a = clamp(hsl.a);\n return hsla(hsl);\n },\n spin: function (color, amount) {\n var hsl = color.toHSL();\n var hue = (hsl.h + amount.value) % 360;\n\n hsl.h = hue < 0 ? 360 + hue : hue;\n\n return hsla(hsl);\n },\n //\n // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein\n // http://sass-lang.com\n //\n mix: function (color1, color2, weight) {\n if (!weight) {\n weight = new(tree.Dimension)(50);\n }\n var p = weight.value / 100.0;\n var w = p * 2 - 1;\n var a = color1.toHSL().a - color2.toHSL().a;\n\n var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;\n var w2 = 1 - w1;\n\n var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2,\n color1.rgb[1] * w1 + color2.rgb[1] * w2,\n color1.rgb[2] * w1 + color2.rgb[2] * w2];\n\n var alpha = color1.alpha * p + color2.alpha * (1 - p);\n\n return new(tree.Color)(rgb, alpha);\n },\n greyscale: function (color) {\n return this.desaturate(color, new(tree.Dimension)(100));\n },\n contrast: function (color, dark, light, threshold) {\n // filter: contrast(3.2);\n // should be kept as is, so check for color\n if (!color.rgb) {\n return null;\n }\n if (typeof light === 'undefined') {\n light = this.rgba(255, 255, 255, 1.0);\n }\n if (typeof dark === 'undefined') {\n dark = this.rgba(0, 0, 0, 1.0);\n }\n //Figure out which is actually light and dark!\n if (dark.luma() > light.luma()) {\n var t = light;\n light = dark;\n dark = t;\n }\n if (typeof threshold === 'undefined') {\n threshold = 0.43;\n } else {\n threshold = number(threshold);\n }\n if (color.luma() < threshold) {\n return light;\n } else {\n return dark;\n }\n },\n e: function (str) {\n return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str);\n },\n escape: function (str) {\n return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, \"%3D\").replace(/:/g, \"%3A\").replace(/#/g, \"%23\").replace(/;/g, \"%3B\").replace(/\\(/g, \"%28\").replace(/\\)/g, \"%29\"));\n },\n '%': function (quoted /* arg, arg, ...*/) {\n var args = Array.prototype.slice.call(arguments, 1),\n str = quoted.value;\n\n for (var i = 0; i < args.length; i++) {\n /*jshint loopfunc:true */\n str = str.replace(/%[sda]/i, function(token) {\n var value = token.match(/s/i) ? args[i].value : args[i].toCSS();\n return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value;\n });\n }\n str = str.replace(/%%/g, '%');\n return new(tree.Quoted)('\"' + str + '\"', str);\n },\n unit: function (val, unit) {\n if(!(val instanceof tree.Dimension)) {\n throw { type: \"Argument\", message: \"the first argument to unit must be a number\" + (val instanceof tree.Operation ? \". Have you forgotten parenthesis?\" : \"\") };\n }\n return new(tree.Dimension)(val.value, unit ? unit.toCSS() : \"\");\n },\n convert: function (val, unit) {\n return val.convertTo(unit.value);\n },\n round: function (n, f) {\n var fraction = typeof(f) === \"undefined\" ? 0 : f.value;\n return _math(function(num) { return num.toFixed(fraction); }, null, n);\n },\n pi: function () {\n return new(tree.Dimension)(Math.PI);\n },\n mod: function(a, b) {\n return new(tree.Dimension)(a.value % b.value, a.unit);\n },\n pow: function(x, y) {\n if (typeof x === \"number\" && typeof y === \"number\") {\n x = new(tree.Dimension)(x);\n y = new(tree.Dimension)(y);\n } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) {\n throw { type: \"Argument\", message: \"arguments must be numbers\" };\n }\n\n return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit);\n },\n _minmax: function (isMin, args) {\n args = Array.prototype.slice.call(args);\n switch(args.length) {\n case 0: throw { type: \"Argument\", message: \"one or more arguments required\" };\n case 1: return args[0];\n }\n var i, j, current, currentUnified, referenceUnified, unit,\n order = [], // elems only contains original argument values.\n values = {}; // key is the unit.toString() for unified tree.Dimension values,\n // value is the index into the order array.\n for (i = 0; i < args.length; i++) {\n current = args[i];\n if (!(current instanceof tree.Dimension)) {\n order.push(current);\n continue;\n }\n currentUnified = current.unify();\n unit = currentUnified.unit.toString();\n j = values[unit];\n if (j === undefined) {\n values[unit] = order.length;\n order.push(current);\n continue;\n }\n referenceUnified = order[j].unify();\n if ( isMin && currentUnified.value < referenceUnified.value ||\n !isMin && currentUnified.value > referenceUnified.value) {\n order[j] = current;\n }\n }\n if (order.length == 1) {\n return order[0];\n }\n args = order.map(function (a) { return a.toCSS(this.env); })\n .join(this.env.compress ? \",\" : \", \");\n return new(tree.Anonymous)((isMin ? \"min\" : \"max\") + \"(\" + args + \")\");\n },\n min: function () {\n return this._minmax(true, arguments);\n },\n max: function () {\n return this._minmax(false, arguments);\n },\n argb: function (color) {\n return new(tree.Anonymous)(color.toARGB());\n },\n percentage: function (n) {\n return new(tree.Dimension)(n.value * 100, '%');\n },\n color: function (n) {\n if (n instanceof tree.Quoted) {\n var colorCandidate = n.value,\n returnColor;\n returnColor = tree.Color.fromKeyword(colorCandidate);\n if (returnColor) {\n return returnColor;\n }\n if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) {\n return new(tree.Color)(colorCandidate.slice(1));\n }\n throw { type: \"Argument\", message: \"argument must be a color keyword or 3/6 digit hex e.g. #FFF\" };\n } else {\n throw { type: \"Argument\", message: \"argument must be a string\" };\n }\n },\n iscolor: function (n) {\n return this._isa(n, tree.Color);\n },\n isnumber: function (n) {\n return this._isa(n, tree.Dimension);\n },\n isstring: function (n) {\n return this._isa(n, tree.Quoted);\n },\n iskeyword: function (n) {\n return this._isa(n, tree.Keyword);\n },\n isurl: function (n) {\n return this._isa(n, tree.URL);\n },\n ispixel: function (n) {\n return this.isunit(n, 'px');\n },\n ispercentage: function (n) {\n return this.isunit(n, '%');\n },\n isem: function (n) {\n return this.isunit(n, 'em');\n },\n isunit: function (n, unit) {\n return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False;\n },\n _isa: function (n, Type) {\n return (n instanceof Type) ? tree.True : tree.False;\n },\n tint: function(color, amount) {\n return this.mix(this.rgb(255,255,255), color, amount);\n },\n shade: function(color, amount) {\n return this.mix(this.rgb(0, 0, 0), color, amount);\n }, \n extract: function(values, index) {\n index = index.value - 1; // (1-based index) \n // handle non-array values as an array of length 1\n // return 'undefined' if index is invalid\n return Array.isArray(values.value) \n ? values.value[index] : Array(values)[index];\n },\n length: function(values) {\n var n = Array.isArray(values.value) ? values.value.length : 1;\n return new tree.Dimension(n);\n },\n\n \"data-uri\": function(mimetypeNode, filePathNode) {\n\n if (typeof window !== 'undefined') {\n return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env);\n }\n\n var mimetype = mimetypeNode.value;\n var filePath = (filePathNode && filePathNode.value);\n\n var fs = require('fs'),\n path = require('path'),\n useBase64 = false;\n\n if (arguments.length < 2) {\n filePath = mimetype;\n }\n\n if (this.env.isPathRelative(filePath)) {\n if (this.currentFileInfo.relativeUrls) {\n filePath = path.join(this.currentFileInfo.currentDirectory, filePath);\n } else {\n filePath = path.join(this.currentFileInfo.entryPath, filePath);\n }\n }\n\n // detect the mimetype if not given\n if (arguments.length < 2) {\n var mime;\n try {\n mime = require('mime');\n } catch (ex) {\n mime = tree._mime;\n }\n\n mimetype = mime.lookup(filePath);\n\n // use base 64 unless it's an ASCII or UTF-8 format\n var charset = mime.charsets.lookup(mimetype);\n useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;\n if (useBase64) { mimetype += ';base64'; }\n }\n else {\n useBase64 = /;base64$/.test(mimetype);\n }\n\n var buf = fs.readFileSync(filePath);\n\n // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded\n // and the --ieCompat flag is enabled, return a normal url() instead.\n var DATA_URI_MAX_KB = 32,\n fileSizeInKB = parseInt((buf.length / 1024), 10);\n if (fileSizeInKB >= DATA_URI_MAX_KB) {\n\n if (this.env.ieCompat !== false) {\n if (!this.env.silent) {\n console.warn(\"Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!\", filePath, fileSizeInKB, DATA_URI_MAX_KB);\n }\n\n return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env);\n }\n }\n\n buf = useBase64 ? buf.toString('base64')\n : encodeURIComponent(buf);\n\n var uri = \"\\\"data:\" + mimetype + ',' + buf + \"\\\"\";\n return new(tree.URL)(new(tree.Anonymous)(uri));\n },\n\n \"svg-gradient\": function(direction) {\n\n function throwArgumentDescriptor() {\n throw { type: \"Argument\", message: \"svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]\" };\n }\n\n if (arguments.length < 3) {\n throwArgumentDescriptor();\n }\n var stops = Array.prototype.slice.call(arguments, 1),\n gradientDirectionSvg,\n gradientType = \"linear\",\n rectangleDimension = 'x=\"0\" y=\"0\" width=\"1\" height=\"1\"',\n useBase64 = true,\n renderEnv = {compress: false},\n returner,\n directionValue = direction.toCSS(renderEnv),\n i, color, position, positionValue, alpha;\n\n switch (directionValue) {\n case \"to bottom\":\n gradientDirectionSvg = 'x1=\"0%\" y1=\"0%\" x2=\"0%\" y2=\"100%\"';\n break;\n case \"to right\":\n gradientDirectionSvg = 'x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"0%\"';\n break;\n case \"to bottom right\":\n gradientDirectionSvg = 'x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\"';\n break;\n case \"to top right\":\n gradientDirectionSvg = 'x1=\"0%\" y1=\"100%\" x2=\"100%\" y2=\"0%\"';\n break;\n case \"ellipse\":\n case \"ellipse at center\":\n gradientType = \"radial\";\n gradientDirectionSvg = 'cx=\"50%\" cy=\"50%\" r=\"75%\"';\n rectangleDimension = 'x=\"-50\" y=\"-50\" width=\"101\" height=\"101\"';\n break;\n default:\n throw { type: \"Argument\", message: \"svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'\" };\n }\n returner = '<?xml version=\"1.0\" ?>' +\n '<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"100%\" height=\"100%\" viewBox=\"0 0 1 1\" preserveAspectRatio=\"none\">' +\n '<' + gradientType + 'Gradient id=\"gradient\" gradientUnits=\"userSpaceOnUse\" ' + gradientDirectionSvg + '>';\n\n for (i = 0; i < stops.length; i+= 1) {\n if (stops[i].value) {\n color = stops[i].value[0];\n position = stops[i].value[1];\n } else {\n color = stops[i];\n position = undefined;\n }\n\n if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) {\n throwArgumentDescriptor();\n }\n positionValue = position ? position.toCSS(renderEnv) : i === 0 ? \"0%\" : \"100%\";\n alpha = color.alpha;\n returner += '<stop offset=\"' + positionValue + '\" stop-color=\"' + color.toRGB() + '\"' + (alpha < 1 ? ' stop-opacity=\"' + alpha + '\"' : '') + '/>';\n }\n returner += '</' + gradientType + 'Gradient>' +\n '<rect ' + rectangleDimension + ' fill=\"url(#gradient)\" /></svg>';\n\n if (useBase64) {\n try {\n returner = require('./encoder').encodeBase64(returner); // TODO browser implementation\n } catch(e) {\n useBase64 = false;\n }\n }\n\n returner = \"'data:image/svg+xml\" + (useBase64 ? \";base64\" : \"\") + \",\" + returner + \"'\";\n return new(tree.URL)(new(tree.Anonymous)(returner));\n }\n};\n\n// these static methods are used as a fallback when the optional 'mime' dependency is missing\ntree._mime = {\n // this map is intentionally incomplete\n // if you want more, install 'mime' dep\n _types: {\n '.htm' : 'text/html',\n '.html': 'text/html',\n '.gif' : 'image/gif',\n '.jpg' : 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.png' : 'image/png'\n },\n lookup: function (filepath) {\n var ext = require('path').extname(filepath),\n type = tree._mime._types[ext];\n if (type === undefined) {\n throw new Error('Optional dependency \"mime\" is required for ' + ext);\n }\n return type;\n },\n charsets: {\n lookup: function (type) {\n // assumes all text types are UTF-8\n return type && (/^text\\//).test(type) ? 'UTF-8' : '';\n }\n }\n};\n\n// Math\n\nvar mathFunctions = {\n // name, unit\n ceil: null, \n floor: null, \n sqrt: null, \n abs: null,\n tan: \"\", \n sin: \"\", \n cos: \"\",\n atan: \"rad\", \n asin: \"rad\", \n acos: \"rad\"\n};\n\nfunction _math(fn, unit, n) {\n if (!(n instanceof tree.Dimension)) {\n throw { type: \"Argument\", message: \"argument must be a number\" };\n }\n if (unit == null) {\n unit = n.unit;\n } else {\n n = n.unify();\n }\n return new(tree.Dimension)(fn(parseFloat(n.value)), unit);\n}\n\n// ~ End of Math\n\n// Color Blending\n// ref: http://www.w3.org/TR/compositing-1\n\nfunction colorBlend(mode, color1, color2) {\n var ab = color1.alpha, cb, // backdrop\n as = color2.alpha, cs, // source\n ar, cr, r = []; // result\n \n ar = as + ab * (1 - as);\n for (var i = 0; i < 3; i++) {\n cb = color1.rgb[i] / 255;\n cs = color2.rgb[i] / 255;\n cr = mode(cb, cs);\n if (ar) {\n cr = (as * cs + ab * (cb \n - as * (cb + cs - cr))) / ar;\n }\n r[i] = cr * 255;\n }\n \n return new(tree.Color)(r, ar);\n}\n\nvar colorBlendMode = {\n multiply: function(cb, cs) {\n return cb * cs;\n },\n screen: function(cb, cs) {\n return cb + cs - cb * cs;\n }, \n overlay: function(cb, cs) {\n cb *= 2;\n return (cb <= 1)\n ? colorBlendMode.multiply(cb, cs)\n : colorBlendMode.screen(cb - 1, cs);\n },\n softlight: function(cb, cs) {\n var d = 1, e = cb;\n if (cs > 0.5) {\n e = 1;\n d = (cb > 0.25) ? Math.sqrt(cb)\n : ((16 * cb - 12) * cb + 4) * cb;\n } \n return cb - (1 - 2 * cs) * e * (d - cb);\n },\n hardlight: function(cb, cs) {\n return colorBlendMode.overlay(cs, cb);\n },\n difference: function(cb, cs) {\n return Math.abs(cb - cs);\n },\n exclusion: function(cb, cs) {\n return cb + cs - 2 * cb * cs;\n },\n\n // non-w3c functions:\n average: function(cb, cs) {\n return (cb + cs) / 2;\n },\n negation: function(cb, cs) {\n return 1 - Math.abs(cb + cs - 1);\n }\n};\n\n// ~ End of Color Blending\n\ntree.defaultFunc = {\n eval: function () {\n var v = this.value_, e = this.error_;\n if (e) {\n throw e;\n }\n if (v != null) {\n return v ? tree.True : tree.False;\n }\n },\n value: function (v) {\n this.value_ = v;\n },\n error: function (e) {\n this.error_ = e;\n },\n reset: function () {\n this.value_ = this.error_ = null;\n }\n};\n\nfunction initFunctions() {\n var f, tf = tree.functions;\n \n // math\n for (f in mathFunctions) {\n if (mathFunctions.hasOwnProperty(f)) {\n tf[f] = _math.bind(null, Math[f], mathFunctions[f]);\n }\n }\n \n // color blending\n for (f in colorBlendMode) {\n if (colorBlendMode.hasOwnProperty(f)) {\n tf[f] = colorBlend.bind(null, colorBlendMode[f]);\n }\n }\n \n // default\n f = tree.defaultFunc;\n tf[\"default\"] = f.eval.bind(f);\n \n} initFunctions();\n\nfunction hsla(color) {\n return tree.functions.hsla(color.h, color.s, color.l, color.a);\n}\n\nfunction scaled(n, size) {\n if (n instanceof tree.Dimension && n.unit.is('%')) {\n return parseFloat(n.value * size / 100);\n } else {\n return number(n);\n }\n}\n\nfunction number(n) {\n if (n instanceof tree.Dimension) {\n return parseFloat(n.unit.is('%') ? n.value / 100 : n.value);\n } else if (typeof(n) === 'number') {\n return n;\n } else {\n throw {\n error: \"RuntimeError\",\n message: \"color functions take numbers as parameters\"\n };\n }\n}\n\nfunction clamp(val) {\n return Math.min(1, Math.max(0, val));\n}\n\ntree.fround = function(env, value) {\n var p;\n if (env && (env.numPrecision != null)) {\n p = Math.pow(10, env.numPrecision);\n return Math.round(value * p) / p;\n } else {\n return value;\n }\n};\n\ntree.functionCall = function(env, currentFileInfo) {\n this.env = env;\n this.currentFileInfo = currentFileInfo;\n};\n\ntree.functionCall.prototype = tree.functions;\n\n})(require('./tree'));\n\n## 内容5\n### 文件路径:\nlib/thirdparty/less/index.js\n### 代码内容:\nvar path = require('path'),\n url = require('url'),\n request,\n fs = require('fs');\n\nvar less = {\n version: [1, 6, 3],\n Parser: require('./parser').Parser,\n tree: require('./tree'),\n render: function (input, options, callback) {\n options = options || {};\n\n if (typeof(options) === 'function') {\n callback = options;\n options = {};\n }\n\n var parser = new(less.Parser)(options),\n ee;\n\n if (callback) {\n parser.parse(input, function (e, root) {\n try { callback(e, root && root.toCSS && root.toCSS(options)); } \n catch (err) { callback(err); }\n });\n } else {\n ee = new (require('events').EventEmitter)();\n\n process.nextTick(function () {\n parser.parse(input, function (e, root) {\n if (e) { return ee.emit('error', e); }\n try { ee.emit('success', root.toCSS(options)); } \n catch (err) { ee.emit('error', err); }\n });\n });\n return ee;\n }\n },\n formatError: function(ctx, options) {\n options = options || {};\n\n var message = \"\";\n var extract = ctx.extract;\n var error = [];\n var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; };\n\n // only output a stack if it isn't a less error\n if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); }\n\n if (!ctx.hasOwnProperty('index') || !extract) {\n return ctx.stack || ctx.message;\n }\n\n if (typeof(extract[0]) === 'string') {\n error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey'));\n }\n\n if (typeof(extract[1]) === 'string') {\n var errorTxt = ctx.line + ' ';\n if (extract[1]) {\n errorTxt += extract[1].slice(0, ctx.column) +\n stylize(stylize(stylize(extract[1][ctx.column], 'bold') +\n extract[1].slice(ctx.column + 1), 'red'), 'inverse');\n }\n error.push(errorTxt);\n }\n\n if (typeof(extract[2]) === 'string') {\n error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey'));\n }\n error = error.join('\\n') + stylize('', 'reset') + '\\n';\n\n message += stylize(ctx.type + 'Error: ' + ctx.message, 'red');\n if (ctx.filename) {\n message += stylize(' in ', 'red') + ctx.filename +\n stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey');\n }\n\n message += '\\n' + error;\n\n if (ctx.callLine) {\n message += stylize('from ', 'red') + (ctx.filename || '') + '/n';\n message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n';\n }\n\n return message;\n },\n writeError: function (ctx, options) {\n options = options || {};\n if (options.silent) { return; }\n console.error(less.formatError(ctx, options));\n }\n};\n\nrequire('./tree/color');\nrequire('./tree/directive');\nrequire('./tree/operation');\nrequire('./tree/dimension');\nrequire('./tree/keyword');\nrequire('./tree/variable');\nrequire('./tree/ruleset');\nrequire('./tree/element');\nrequire('./tree/selector');\nrequire('./tree/quoted');\nrequire('./tree/expression');\nrequire('./tree/rule');\nrequire('./tree/call');\nrequire('./tree/url');\nrequire('./tree/alpha');\nrequire('./tree/import');\nrequire('./tree/mixin');\nrequire('./tree/comment');\nrequire('./tree/anonymous');\nrequire('./tree/value');\nrequire('./tree/javascript');\nrequire('./tree/assignment');\nrequire('./tree/condition');\nrequire('./tree/paren');\nrequire('./tree/media');\nrequire('./tree/unicode-descriptor');\nrequire('./tree/negative');\nrequire('./tree/extend');\n\nvar isUrlRe = /^(?:https?:)?\\/\\//i;\n\nless.Parser.fileLoader = function (file, currentFileInfo, callback, env) {\n var pathname, dirname, data,\n newFileInfo = {\n relativeUrls: env.relativeUrls,\n entryPath: currentFileInfo.entryPath,\n rootpath: currentFileInfo.rootpath,\n rootFilename: currentFileInfo.rootFilename\n };\n\n function handleDataAndCallCallback(data) {\n var j = file.lastIndexOf('/');\n\n // Pass on an updated rootpath if path of imported file is relative and file \n // is in a (sub|sup) directory\n // \n // Examples: \n // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/',\n // then rootpath should become 'less/module/nav/'\n // - If path of imported file is '../mixins.less' and rootpath is 'less/', \n // then rootpath should become 'less/../'\n if(newFileInfo.relativeUrls && !/^(?:[a-z-]+:|\\/)/.test(file) && j != -1) {\n var relativeSubDirectory = file.slice(0, j+1);\n newFileInfo.rootpath = newFileInfo.rootpath + relativeSubDirectory; // append (sub|sup) directory path of imported file\n }\n newFileInfo.currentDirectory = pathname.replace(/[^\\\\\\/]*$/, \"\");\n newFileInfo.filename = pathname;\n\n callback(null, data, pathname, newFileInfo);\n }\n\n var isUrl = isUrlRe.test( file );\n if (isUrl || isUrlRe.test(currentFileInfo.currentDirectory)) {\n /* BEGIN MODIFICATION */\n // Removed support for import over http(s) to get rid of 'request' dependency\n callback({\n type: 'File',\n message: 'Import over http(s) is not supported anymore since less-openui5 v0.9.0\\n' +\n 'Use the Builder \\'fs\\' option to provide an interface that also handles http(s) resources.\\n'\n });\n return;\n /* END MODIFICATION */\n } else {\n\n var paths = [currentFileInfo.currentDirectory].concat(env.paths);\n paths.push('.');\n\n if (env.syncImport) {\n for (var i = 0; i < paths.length; i++) {\n try {\n pathname = path.join(paths[i], file);\n fs.statSync(pathname);\n break;\n } catch (e) {\n pathname = null;\n }\n }\n\n if (!pathname) {\n callback({ type: 'File', message: \"'\" + file + \"' wasn't found\" });\n return;\n }\n\n try {\n data = fs.readFileSync(pathname, 'utf-8');\n handleDataAndCallCallback(data);\n } catch (e) {\n callback(e);\n }\n } else {\n (function tryPathIndex(i) {\n if (i < paths.length) {\n pathname = path.join(paths[i], file);\n fs.stat(pathname, function (err) {\n if (err) {\n tryPathIndex(i + 1);\n } else {\n fs.readFile(pathname, 'utf-8', function(e, data) {\n if (e) { callback(e); }\n handleDataAndCallCallback(data);\n });\n }\n });\n } else {\n callback({ type: 'File', message: \"'\" + file + \"' wasn't found\" });\n }\n }(0));\n }\n }\n};\n\nrequire('./env');\nrequire('./functions');\nrequire('./colors');\nrequire('./visitor.js');\nrequire('./import-visitor.js');\nrequire('./extend-visitor.js');\nrequire('./join-selector-visitor.js');\nrequire('./to-css-visitor.js');\nrequire('./source-map-output.js');\n\nfor (var k in less) { if (less.hasOwnProperty(k)) { exports[k] = less[k]; }}\n\n## 内容6\n### 文件路径:\nlib/thirdparty/less/lessc_helper.js\n### 代码内容:\n// lessc_helper.js\n//\n// helper functions for lessc\nvar lessc_helper = {\n\n //Stylize a string\n stylize : function(str, style) {\n var styles = {\n 'reset' : [0, 0],\n 'bold' : [1, 22],\n 'inverse' : [7, 27],\n 'underline' : [4, 24],\n 'yellow' : [33, 39],\n 'green' : [32, 39],\n 'red' : [31, 39],\n 'grey' : [90, 39]\n };\n return '\\033[' + styles[style][0] + 'm' + str +\n '\\033[' + styles[style][1] + 'm';\n },\n\n //Print command line options\n printUsage: function() {\n console.log(\"usage: lessc [option option=parameter ...] <source> [destination]\");\n console.log(\"\");\n console.log(\"If source is set to `-' (dash or hyphen-minus), input is read from stdin.\");\n console.log(\"\");\n console.log(\"options:\");\n console.log(\" -h, --help Print help (this message) and exit.\");\n console.log(\" --include-path=PATHS Set include paths. Separated by `:'. Use `;' on Windows.\");\n console.log(\" -M, --depends Output a makefile import dependency list to stdout\");\n console.log(\" --no-color Disable colorized output.\");\n console.log(\" --no-ie-compat Disable IE compatibility checks.\");\n console.log(\" --no-js Disable JavaScript in less files\");\n console.log(\" -l, --lint Syntax check only (lint).\");\n console.log(\" -s, --silent Suppress output of error messages.\");\n console.log(\" --strict-imports Force evaluation of imports.\");\n console.log(\" --insecure Allow imports from insecure https hosts.\");\n console.log(\" -v, --version Print version number and exit.\");\n console.log(\" -x, --compress Compress output by removing some whitespaces.\");\n console.log(\" --clean-css Compress output using clean-css\");\n console.log(\" --clean-option=opt:val Pass an option to clean css, using CLI arguments from \");\n console.log(\" https://github.com/GoalSmashers/clean-css e.g.\");\n console.log(\" --clean-option=--selectors-merge-mode:ie8\");\n console.log(\" and to switch on advanced use --clean-option=--advanced\");\n console.log(\" --source-map[=FILENAME] Outputs a v3 sourcemap to the filename (or output filename.map)\");\n console.log(\" --source-map-rootpath=X adds this path onto the sourcemap filename and less file paths\");\n console.log(\" --source-map-basepath=X Sets sourcemap base path, defaults to current working directory.\");\n console.log(\" --source-map-less-inline puts the less files into the map instead of referencing them\");\n console.log(\" --source-map-map-inline puts the map (and any less files) into the output css file\");\n console.log(\" --source-map-url=URL the complete url and filename put in the less file\");\n console.log(\" -rp, --rootpath=URL Set rootpath for url rewriting in relative imports and urls.\");\n console.log(\" Works with or without the relative-urls option.\");\n console.log(\" -ru, --relative-urls re-write relative urls to the base less file.\");\n console.log(\" -sm=on|off Turn on or off strict math, where in strict mode, math\");\n console.log(\" --strict-math=on|off requires brackets. This option may default to on and then\");\n console.log(\" be removed in the future.\");\n console.log(\" -su=on|off Allow mixed units, e.g. 1px+1em or 1px*1px which have units\");\n console.log(\" --strict-units=on|off that cannot be represented.\");\n console.log(\" --global-var='VAR=VALUE' Defines a variable that can be referenced by the file.\");\n console.log(\" --modify-var='VAR=VALUE' Modifies a variable already declared in the file.\");\n console.log(\"\");\n console.log(\"-------------------------- Deprecated ----------------\");\n console.log(\" -O0, -O1, -O2 Set the parser's optimization level. The lower\");\n console.log(\" the number, the less nodes it will create in the\");\n console.log(\" tree. This could matter for debugging, or if you\");\n console.log(\" want to access the individual nodes in the tree.\");\n console.log(\" --line-numbers=TYPE Outputs filename and line numbers.\");\n console.log(\" TYPE can be either 'comments', which will output\");\n console.log(\" the debug info within comments, 'mediaquery'\");\n console.log(\" that will output the information within a fake\");\n console.log(\" media query which is compatible with the SASS\");\n console.log(\" format, and 'all' which will do both.\");\n console.log(\" --verbose Be verbose.\");\n console.log(\"\");\n console.log(\"Report bugs to: http://github.com/less/less.js/issues\");\n console.log(\"Home page: <http://lesscss.org/>\");\n }\n};\n\n// Exports helper functions\nfor (var h in lessc_helper) { if (lessc_helper.hasOwnProperty(h)) { exports[h] = lessc_helper[h]; }}\n\n## 内容7\n### 文件路径:\nlib/thirdparty/less/parser.js\n### 代码内容:\nvar less, tree;\n\n// Node.js does not have a header file added which defines less\nif (less === undefined) {\n less = exports;\n tree = require('./tree');\n less.mode = 'node';\n}\n//\n// less.js - parser\n//\n// A relatively straight-forward predictive parser.\n// There is no tokenization/lexing stage, the input is parsed\n// in one sweep.\n//\n// To make the parser fast enough to run in the browser, several\n// optimization had to be made:\n//\n// - Matching and slicing on a huge input is often cause of slowdowns.\n// The solution is to chunkify the input into smaller strings.\n// The chunks are stored in the `chunks` var,\n// `j` holds the current chunk index, and `currentPos` holds\n// the index of the current chunk in relation to `input`.\n// This gives us an almost 4x speed-up.\n//\n// - In many cases, we don't need to match individual tokens;\n// for example, if a value doesn't hold any variables, operations\n// or dynamic references, the parser can effectively 'skip' it,\n// treating it as a literal.\n// An example would be '1px solid #000' - which evaluates to itself,\n// we don't need to know what the individual components are.\n// The drawback, of course is that you don't get the benefits of\n// syntax-checking on the CSS. This gives us a 50% speed-up in the parser,\n// and a smaller speed-up in the code-gen.\n//\n//\n// Token matching is done with the `$` function, which either takes\n// a terminal string or regexp, or a non-terminal function to call.\n// It also takes care of moving all the indices forwards.\n//\n//\nless.Parser = function Parser(env) {\n var input, // LeSS input string\n i, // current index in `input`\n j, // current chunk\n temp, // temporarily holds a chunk's state, for backtracking\n memo, // temporarily holds `i`, when backtracking\n furthest, // furthest index the parser has gone to\n chunks, // chunkified input\n current, // current chunk\n currentPos, // index of current chunk, in `input`\n parser,\n parsers,\n rootFilename = env && env.filename;\n\n // Top parser on an import tree must be sure there is one \"env\"\n // which will then be passed around by reference.\n if (!(env instanceof tree.parseEnv)) {\n env = new tree.parseEnv(env);\n }\n\n var imports = this.imports = {\n paths: env.paths || [], // Search paths, when importing\n queue: [], // Files which haven't been imported yet\n files: env.files, // Holds the imported parse trees\n contents: env.contents, // Holds the imported file contents\n contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less\n mime: env.mime, // MIME type of .less files\n error: null, // Error in parsing/evaluating an import\n push: function (path, currentFileInfo, importOptions, callback) {\n var parserImports = this;\n this.queue.push(path);\n\n var fileParsedFunc = function (e, root, fullPath) {\n parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue\n\n var importedPreviously = fullPath === rootFilename;\n\n parserImports.files[fullPath] = root; // Store the root\n\n if (e && !parserImports.error) { parserImports.error = e; }\n\n callback(e, root, importedPreviously, fullPath);\n };\n\n if (less.Parser.importer) {\n less.Parser.importer(path, currentFileInfo, fileParsedFunc, env);\n } else {\n less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) {\n if (e) {fileParsedFunc(e); return;}\n\n var newEnv = new tree.parseEnv(env);\n\n newEnv.currentFileInfo = newFileInfo;\n newEnv.processImports = false;\n newEnv.contents[fullPath] = contents;\n\n if (currentFileInfo.reference || importOptions.reference) {\n newFileInfo.reference = true;\n }\n\n if (importOptions.inline) {\n fileParsedFunc(null, contents, fullPath);\n } else {\n new(less.Parser)(newEnv).parse(contents, function (e, root) {\n fileParsedFunc(e, root, fullPath);\n });\n }\n }, env);\n }\n }\n };\n\n function save() { temp = current; memo = currentPos = i; }\n function restore() { current = temp; currentPos = i = memo; }\n\n function sync() {\n if (i > currentPos) {\n current = current.slice(i - currentPos);\n currentPos = i;\n }\n }\n function isWhitespace(str, pos) {\n var code = str.charCodeAt(pos | 0);\n return (code <= 32) && (code === 32 || code === 10 || code === 9);\n }\n //\n // Parse from a token, regexp or string, and move forward if match\n //\n function $(tok) {\n var tokType = typeof tok,\n match, length;\n\n // Either match a single character in the input,\n // or match a regexp in the current chunk (`current`).\n //\n if (tokType === \"string\") {\n if (input.charAt(i) !== tok) {\n return null;\n }\n skipWhitespace(1);\n return tok;\n }\n\n // regexp\n sync ();\n if (! (match = tok.exec(current))) {\n return null;\n }\n\n length = match[0].length;\n\n // The match is confirmed, add the match length to `i`,\n // and consume any extra white-space characters (' ' || '\\n')\n // which come after that. The reason for this is that LeSS's\n // grammar is mostly white-space insensitive.\n //\n skipWhitespace(length);\n\n if(typeof(match) === 'string') {\n return match;\n } else {\n return match.length === 1 ? match[0] : match;\n }\n }\n\n // Specialization of $(tok)\n function $re(tok) {\n if (i > currentPos) {\n current = current.slice(i - currentPos);\n currentPos = i;\n }\n var m = tok.exec(current);\n if (!m) {\n return null;\n }\n\n skipWhitespace(m[0].length);\n if(typeof m === \"string\") {\n return m;\n }\n\n return m.length === 1 ? m[0] : m;\n }\n\n var _$re = $re;\n\n // Specialization of $(tok)\n function $char(tok) {\n if (input.charAt(i) !== tok) {\n return null;\n }\n skipWhitespace(1);\n return tok;\n }\n\n function skipWhitespace(length) {\n var oldi = i, oldj = j,\n curr = i - currentPos,\n endIndex = i + current.length - curr,\n mem = (i += length),\n inp = input,\n c;\n\n for (; i < endIndex; i++) {\n c = inp.charCodeAt(i);\n if (c > 32) {\n break;\n }\n\n if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) {\n break;\n }\n }\n\n current = current.slice(length + i - mem + curr);\n currentPos = i;\n\n if (!current.length && (j < chunks.length - 1)) {\n current = chunks[++j];\n skipWhitespace(0); // skip space at the beginning of a chunk\n return true; // things changed\n }\n\n return oldi !== i || oldj !== j;\n }\n\n function expect(arg, msg) {\n // some older browsers return typeof 'function' for RegExp\n var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg);\n if (result) {\n return result;\n }\n error(msg || (typeof(arg) === 'string' ? \"expected '\" + arg + \"' got '\" + input.charAt(i) + \"'\"\n : \"unexpected token\"));\n }\n\n // Specialization of expect()\n function expectChar(arg, msg) {\n if (input.charAt(i) === arg) {\n skipWhitespace(1);\n return arg;\n }\n error(msg || \"expected '\" + arg + \"' got '\" + input.charAt(i) + \"'\");\n }\n\n function error(msg, type) {\n var e = new Error(msg);\n e.index = i;\n e.type = type || 'Syntax';\n throw e;\n }\n\n // Same as $(), but don't change the state of the parser,\n // just return the match.\n function peek(tok) {\n if (typeof(tok) === 'string') {\n return input.charAt(i) === tok;\n } else {\n return tok.test(current);\n }\n }\n\n // Specialization of peek()\n function peekChar(tok) {\n return input.charAt(i) === tok;\n }\n\n function getInput(e, env) {\n if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) {\n return parser.imports.contents[e.filename];\n } else {\n return input;\n }\n }\n\n function getLocation(index, inputStream) {\n var n = index + 1,\n line = null,\n column = -1;\n\n while (--n >= 0 && inputStream.charAt(n) !== '\\n') {\n column++;\n }\n\n if (typeof index === 'number') {\n line = (inputStream.slice(0, index).match(/\\n/g) || \"\").length;\n }\n\n return {\n line: line,\n column: column\n };\n }\n\n function getDebugInfo(index, inputStream, env) {\n var filename = env.currentFileInfo.filename;\n if(less.mode !== 'browser' && less.mode !== 'rhino') {\n filename = require('path').resolve(filename);\n }\n\n return {\n lineNumber: getLocation(index, inputStream).line + 1,\n fileName: filename\n };\n }\n\n function LessError(e, env) {\n var input = getInput(e, env),\n loc = getLocation(e.index, input),\n line = loc.line,\n col = loc.column,\n callLine = e.call && getLocation(e.call, input).line,\n lines = input.split('\\n');\n\n this.type = e.type || 'Syntax';\n this.message = e.message;\n this.filename = e.filename || env.currentFileInfo.filename;\n this.index = e.index;\n this.line = typeof(line) === 'number' ? line + 1 : null;\n this.callLine = callLine + 1;\n this.callExtract = lines[callLine];\n this.stack = e.stack;\n this.column = col;\n this.extract = [\n lines[line - 1],\n lines[line],\n lines[line + 1]\n ];\n }\n\n LessError.prototype = new Error();\n LessError.prototype.constructor = LessError;\n\n this.env = env = env || {};\n\n // The optimization level dictates the thoroughness of the parser,\n // the lower the number, the less nodes it will create in the tree.\n // This could matter for debugging, or if you want to access\n // the individual nodes in the tree.\n this.optimization = ('optimization' in this.env) ? this.env.optimization : 1;\n\n //\n // The Parser\n //\n parser = {\n\n imports: imports,\n //\n // Parse an input string into an abstract syntax tree,\n // @param str A string containing 'less' markup\n // @param callback call `callback` when done.\n // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply\n //\n parse: function (str, callback, additionalData) {\n var root, line, lines, error = null, globalVars, modifyVars, preText = \"\";\n\n i = j = currentPos = furthest = 0;\n\n globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\\n' : '';\n modifyVars = (additionalData && additionalData.modifyVars) ? '\\n' + less.Parser.serializeVars(additionalData.modifyVars) : '';\n\n if (globalVars || (additionalData && additionalData.banner)) {\n preText = ((additionalData && additionalData.banner) ? additionalData.banner : \"\") + globalVars;\n parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length;\n }\n\n str = str.replace(/\\r\\n/g, '\\n');\n // Remove potential UTF Byte Order Mark\n input = str = preText + str.replace(/^\\uFEFF/, '') + modifyVars;\n parser.imports.contents[env.currentFileInfo.filename] = str;\n\n // Split the input into chunks.\n chunks = (function (input) {\n var len = input.length, level = 0, parenLevel = 0,\n lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace,\n chunks = [], emitFrom = 0,\n parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched;\n\n function fail(msg, index) {\n error = new(LessError)({\n index: index || parserCurrentIndex,\n type: 'Parse',\n message: msg,\n filename: env.currentFileInfo.filename\n }, env);\n }\n\n function emitChunk(force) {\n var len = parserCurrentIndex - emitFrom;\n if (((len < 512) && !force) || !len) {\n return;\n }\n chunks.push(input.slice(emitFrom, parserCurrentIndex + 1));\n emitFrom = parserCurrentIndex + 1;\n }\n\n for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) {\n cc = input.charCodeAt(parserCurrentIndex);\n if (((cc >= 97) && (cc <= 122)) || (cc < 34)) {\n // a-z or whitespace\n continue;\n }\n\n switch (cc) {\n case 40: // (\n parenLevel++; \n lastOpeningParen = parserCurrentIndex; \n continue;\n case 41: // )\n if (--parenLevel < 0) {\n return fail(\"missing opening `(`\");\n }\n continue;\n case 59: // ;\n if (!parenLevel) { emitChunk(); }\n continue;\n case 123: // {\n level++; \n lastOpening = parserCurrentIndex; \n continue;\n case 125: // }\n if (--level < 0) {\n return fail(\"missing opening `{`\");\n }\n if (!level) { emitChunk(); }\n continue;\n case 92: // \\\n if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; }\n return fail(\"unescaped `\\\\`\");\n case 34:\n case 39:\n case 96: // \", ' and `\n matched = 0;\n currentChunkStartIndex = parserCurrentIndex;\n for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) {\n cc2 = input.charCodeAt(parserCurrentIndex);\n if (cc2 > 96) { continue; }\n if (cc2 == cc) { matched = 1; break; }\n if (cc2 == 92) { // \\\n if (parserCurrentIndex == len - 1) {\n return fail(\"unescaped `\\\\`\");\n }\n parserCurrentIndex++;\n }\n }\n if (matched) { continue; }\n return fail(\"unmatched `\" + String.fromCharCode(cc) + \"`\", currentChunkStartIndex);\n case 47: // /, check for comment\n if (parenLevel || (parserCurrentIndex == len - 1)) { continue; }\n cc2 = input.charCodeAt(parserCurrentIndex + 1);\n if (cc2 == 47) {\n // //, find lnfeed\n for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) {\n cc2 = input.charCodeAt(parserCurrentIndex);\n if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; }\n }\n } else if (cc2 == 42) {\n // /*, find */\n lastMultiComment = currentChunkStartIndex = parserCurrentIndex;\n for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) {\n cc2 = input.charCodeAt(parserCurrentIndex);\n if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; }\n if (cc2 != 42) { continue; }\n if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; }\n }\n if (parserCurrentIndex == len - 1) {\n return fail(\"missing closing `*/`\", currentChunkStartIndex);\n }\n parserCurrentIndex++;\n }\n continue;\n case 42: // *, check for unmatched */\n if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) {\n return fail(\"unmatched `/*`\");\n }\n continue;\n }\n }\n\n if (level !== 0) {\n if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) {\n return fail(\"missing closing `}` or `*/`\", lastOpening);\n } else {\n return fail(\"missing closing `}`\", lastOpening);\n }\n } else if (parenLevel !== 0) {\n return fail(\"missing closing `)`\", lastOpeningParen);\n }\n\n emitChunk(true);\n return chunks;\n })(str);\n\n if (error) {\n return callback(new(LessError)(error, env));\n }\n\n current = chunks[0];\n\n // Start with the primary rule.\n // The whole syntax tree is held under a Ruleset node,\n // with the `root` property set to true, so no `{}` are\n // output. The callback is called when the input is parsed.\n try {\n root = new(tree.Ruleset)(null, this.parsers.primary());\n root.root = true;\n root.firstRoot = true;\n } catch (e) {\n return callback(new(LessError)(e, env));\n }\n\n root.toCSS = (function (evaluate) {\n return function (options, variables) {\n options = options || {};\n var evaldRoot,\n css,\n evalEnv = new tree.evalEnv(options);\n \n //\n // Allows setting variables with a hash, so:\n //\n // `{ color: new(tree.Color)('#f01') }` will become:\n //\n // new(tree.Rule)('@color',\n // new(tree.Value)([\n // new(tree.Expression)([\n // new(tree.Color)('#f01')\n // ])\n // ])\n // )\n //\n if (typeof(variables) === 'object' && !Array.isArray(variables)) {\n variables = Object.keys(variables).map(function (k) {\n var value = variables[k];\n\n if (! (value instanceof tree.Value)) {\n if (! (value instanceof tree.Expression)) {\n value = new(tree.Expression)([value]);\n }\n value = new(tree.Value)([value]);\n }\n return new(tree.Rule)('@' + k, value, false, null, 0);\n });\n evalEnv.frames = [new(tree.Ruleset)(null, variables)];\n }\n\n try {\n var preEvalVisitors = [],\n visitors = [\n new(tree.joinSelectorVisitor)(),\n new(tree.processExtendsVisitor)(),\n new(tree.toCSSVisitor)({compress: Boolean(options.compress)})\n ], i, root = this;\n\n if (options.plugins) {\n for(i =0; i < options.plugins.length; i++) {\n if (options.plugins[i].isPreEvalVisitor) {\n preEvalVisitors.push(options.plugins[i]);\n } else {\n if (options.plugins[i].isPreVisitor) {\n visitors.splice(0, 0, options.plugins[i]);\n } else {\n visitors.push(options.plugins[i]);\n }\n }\n }\n }\n\n for(i = 0; i < preEvalVisitors.length; i++) {\n preEvalVisitors[i].run(root);\n }\n\n evaldRoot = evaluate.call(root, evalEnv);\n\n for(i = 0; i < visitors.length; i++) {\n visitors[i].run(evaldRoot);\n }\n\n if (options.sourceMap) {\n evaldRoot = new tree.sourceMapOutput(\n {\n contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars,\n writeSourceMap: options.writeSourceMap,\n rootNode: evaldRoot,\n contentsMap: parser.imports.contents,\n sourceMapFilename: options.sourceMapFilename,\n sourceMapURL: options.sourceMapURL,\n outputFilename: options.sourceMapOutputFilename,\n sourceMapBasepath: options.sourceMapBasepath,\n sourceMapRootpath: options.sourceMapRootpath,\n outputSourceFiles: options.outputSourceFiles,\n sourceMapGenerator: options.sourceMapGenerator\n });\n }\n\n css = evaldRoot.toCSS({\n compress: Boolean(options.compress),\n dumpLineNumbers: env.dumpLineNumbers,\n strictUnits: Boolean(options.strictUnits),\n numPrecision: 8});\n } catch (e) {\n throw new(LessError)(e, env);\n }\n\n if (options.cleancss && less.mode === 'node') {\n var CleanCSS = require('clean-css'),\n cleancssOptions = options.cleancssOptions || {};\n\n if (cleancssOptions.keepSpecialComments === undefined) {\n cleancssOptions.keepSpecialComments = \"*\";\n }\n cleancssOptions.processImport = false;\n cleancssOptions.noRebase = true;\n if (cleancssOptions.noAdvanced === undefined) {\n cleancssOptions.noAdvanced = true;\n }\n\n return new CleanCSS(cleancssOptions).minify(css);\n } else if (options.compress) {\n return css.replace(/(^(\\s)+)|((\\s)+$)/g, \"\");\n } else {\n return css;\n }\n };\n })(root.eval);\n\n // If `i` is smaller than the `input.length - 1`,\n // it means the parser wasn't able to parse the whole\n // string, so we've got a parsing error.\n //\n // We try to extract a \\n delimited string,\n // showing the line where the parse error occured.\n // We split it up into two parts (the part which parsed,\n // and the part which didn't), so we can color them differently.\n if (i < input.length - 1) {\n i = furthest;\n var loc = getLocation(i, input);\n lines = input.split('\\n');\n line = loc.line + 1;\n\n error = {\n type: \"Parse\",\n message: \"Unrecognised input\",\n index: i,\n filename: env.currentFileInfo.filename,\n line: line,\n column: loc.column,\n extract: [\n lines[line - 2],\n lines[line - 1],\n lines[line]\n ]\n };\n }\n\n var finish = function (e) {\n e = error || e || parser.imports.error;\n\n if (e) {\n if (!(e instanceof LessError)) {\n e = new(LessError)(e, env);\n }\n\n return callback(e);\n }\n else {\n return callback(null, root);\n }\n };\n\n if (env.processImports !== false) {\n new tree.importVisitor(this.imports, finish)\n .run(root);\n } else {\n return finish();\n }\n },\n\n //\n // Here in, the parsing rules/functions\n //\n // The basic structure of the syntax tree generated is as follows:\n //\n // Ruleset -> Rule -> Value -> Expression -> Entity\n //\n // Here's some LESS code:\n //\n // .class {\n // color: #fff;\n // border: 1px solid #000;\n // width: @w + 4px;\n // > .child {...}\n // }\n //\n // And here's what the parse tree might look like:\n //\n // Ruleset (Selector '.class', [\n // Rule (\"color\", Value ([Expression [Color #fff]]))\n // Rule (\"border\", Value ([Expression [Dimension 1px][Keyword \"solid\"][Color #000]]))\n // Rule (\"width\", Value ([Expression [Operation \"+\" [Variable \"@w\"][Dimension 4px]]]))\n // Ruleset (Selector [Element '>', '.child'], [...])\n // ])\n //\n // In general, most rules will try to parse a token with the `$()` function, and if the return\n // value is truly, will return a new node, of the relevant type. Sometimes, we need to check\n // first, before parsing, that's when we use `peek()`.\n //\n parsers: parsers = {\n //\n // The `primary` rule is the *entry* and *exit* point of the parser.\n // The rules here can appear at any level of the parse tree.\n //\n // The recursive nature of the grammar is an interplay between the `block`\n // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,\n // as represented by this simplified grammar:\n //\n // primary → (ruleset | rule)+\n // ruleset → selector+ block\n // block → '{' primary '}'\n //\n // Only at one point is the primary rule not called from the\n // block rule: at the root level.\n //\n primary: function () {\n var mixin = this.mixin, $re = _$re, root = [], node;\n\n while (current)\n {\n node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() ||\n mixin.call() || this.comment() || this.directive();\n if (node) {\n root.push(node);\n } else {\n if (!($re(/^[\\s\\n]+/) || $re(/^;+/))) {\n break;\n }\n }\n }\n\n return root;\n },\n\n // We create a Comment node for CSS comments `/* */`,\n // but keep the LeSS comments `//` silent, by just skipping\n // over them.\n comment: function () {\n var comment;\n\n if (input.charAt(i) !== '/') { return; }\n\n if (input.charAt(i + 1) === '/') {\n return new(tree.Comment)($re(/^\\/\\/.*/), true, i, env.currentFileInfo);\n }\n comment = $re(/^\\/\\*(?:[^*]|\\*+[^\\/*])*\\*+\\/\\n?/);\n if (comment) {\n return new(tree.Comment)(comment, false, i, env.currentFileInfo);\n }\n },\n\n comments: function () {\n var comment, comments = [];\n\n while(true) {\n comment = this.comment();\n if (!comment) {\n break;\n }\n comments.push(comment);\n }\n\n return comments;\n },\n\n //\n // Entities are tokens which can be found inside an Expression\n //\n entities: {\n //\n // A string, which supports escaping \" and '\n //\n // \"milky way\" 'he\\'s the one!'\n //\n quoted: function () {\n var str, j = i, e, index = i;\n\n if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings\n if (input.charAt(j) !== '\"' && input.charAt(j) !== \"'\") { return; }\n\n if (e) { $char('~'); }\n\n str = $re(/^\"((?:[^\"\\\\\\r\\n]|\\\\.)*)\"|'((?:[^'\\\\\\r\\n]|\\\\.)*)'/);\n if (str) {\n return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo);\n }\n },\n\n //\n // A catch-all word, such as:\n //\n // black border-collapse\n //\n keyword: function () {\n var k;\n\n k = $re(/^[_A-Za-z-][_A-Za-z0-9-]*/);\n if (k) {\n var color = tree.Color.fromKeyword(k);\n if (color) {\n return color;\n }\n return new(tree.Keyword)(k);\n }\n },\n\n //\n // A function call\n //\n // rgb(255, 0, 255)\n //\n // We also try to catch IE's `alpha()`, but let the `alpha` parser\n // deal with the details.\n //\n // The arguments are parsed with the `entities.arguments` parser.\n //\n call: function () {\n var name, nameLC, args, alpha_ret, index = i;\n\n name = /^([\\w-]+|%|progid:[\\w\\.]+)\\(/.exec(current);\n if (!name) { return; }\n\n name = name[1];\n nameLC = name.toLowerCase();\n if (nameLC === 'url') {\n return null;\n }\n\n i += name.length;\n\n if (nameLC === 'alpha') {\n alpha_ret = parsers.alpha();\n if(typeof alpha_ret !== 'undefined') {\n return alpha_ret;\n }\n }\n\n $char('('); // Parse the '(' and consume whitespace.\n\n args = this.arguments();\n\n if (! $char(')')) {\n return;\n }\n\n if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); }\n },\n arguments: function () {\n var args = [], arg;\n\n while (true) {\n arg = this.assignment() || parsers.expression();\n if (!arg) {\n break;\n }\n args.push(arg);\n if (! $char(',')) {\n break;\n }\n }\n return args;\n },\n literal: function () {\n return this.dimension() ||\n this.color() ||\n this.quoted() ||\n this.unicodeDescriptor();\n },\n\n // Assignments are argument entities for calls.\n // They are present in ie filter properties as shown below.\n //\n // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* )\n //\n\n assignment: function () {\n var key, value;\n key = $re(/^\\w+(?=\\s?=)/i);\n if (!key) {\n return;\n }\n if (!$char('=')) {\n return;\n }\n value = parsers.entity();\n if (value) {\n return new(tree.Assignment)(key, value);\n }\n },\n\n //\n // Parse url() tokens\n //\n // We use a specific rule for urls, because they don't really behave like\n // standard function calls. The difference is that the argument doesn't have\n // to be enclosed within a string, so it can't be parsed as an Expression.\n //\n url: function () {\n var value;\n\n if (input.charAt(i) !== 'u' || !$re(/^url\\(/)) {\n return;\n }\n\n value = this.quoted() || this.variable() ||\n $re(/^(?:(?:\\\\[\\(\\)'\"])|[^\\(\\)'\"])+/) || \"\";\n\n expectChar(')');\n\n return new(tree.URL)((value.value != null || value instanceof tree.Variable)\n ? value : new(tree.Anonymous)(value), env.currentFileInfo);\n },\n\n //\n // A Variable entity, such as `@fink`, in\n //\n // width: @fink + 2px\n //\n // We use a different parser for variable definitions,\n // see `parsers.variable`.\n //\n variable: function () {\n var name, index = i;\n\n if (input.charAt(i) === '@' && (name = $re(/^@@?[\\w-]+/))) {\n return new(tree.Variable)(name, index, env.currentFileInfo);\n }\n },\n\n // A variable entity useing the protective {} e.g. @{var}\n variableCurly: function () {\n var curly, index = i;\n\n if (input.charAt(i) === '@' && (curly = $re(/^@\\{([\\w-]+)\\}/))) {\n return new(tree.Variable)(\"@\" + curly[1], index, env.currentFileInfo);\n }\n },\n\n //\n // A Hexadecimal color\n //\n // #4F3C2F\n //\n // `rgb` and `hsl` colors are parsed through the `entities.call` parser.\n //\n color: function () {\n var rgb;\n\n if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) {\n return new(tree.Color)(rgb[1]);\n }\n },\n\n //\n // A Dimension, that is, a number and a unit\n //\n // 0.5em 95%\n //\n dimension: function () {\n var value, c = input.charCodeAt(i);\n //Is the first char of the dimension 0-9, '.', '+' or '-'\n if ((c > 57 || c < 43) || c === 47 || c == 44) {\n return;\n }\n\n value = $re(/^([+-]?\\d*\\.?\\d+)(%|[a-z]+)?/);\n if (value) {\n return new(tree.Dimension)(value[1], value[2]);\n }\n },\n\n //\n // A unicode descriptor, as is used in unicode-range\n //\n // U+0?? or U+00A1-00A9\n //\n unicodeDescriptor: function () {\n var ud;\n\n ud = $re(/^U\\+[0-9a-fA-F?]+(\\-[0-9a-fA-F?]+)?/);\n if (ud) {\n return new(tree.UnicodeDescriptor)(ud[0]);\n }\n },\n\n //\n // JavaScript code to be evaluated\n //\n // `window.location.href`\n //\n javascript: function () {\n var str, j = i, e;\n\n if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings\n if (input.charAt(j) !== '`') { return; }\n if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) {\n error(\"You are using JavaScript, which has been disabled.\");\n }\n\n if (e) { $char('~'); }\n\n str = $re(/^`([^`]*)`/);\n if (str) {\n return new(tree.JavaScript)(str[1], i, e);\n }\n }\n },\n\n //\n // The variable part of a variable definition. Used in the `rule` parser\n //\n // @fink:\n //\n variable: function () {\n var name;\n\n if (input.charAt(i) === '@' && (name = $re(/^(@[\\w-]+)\\s*:/))) { return name[1]; }\n },\n\n //\n // extend syntax - used to extend selectors\n //\n extend: function(isRule) {\n var elements, e, index = i, option, extendList, extend;\n\n if (!(isRule ? $re(/^&:extend\\(/) : $re(/^:extend\\(/))) { return; }\n\n do {\n option = null;\n elements = null;\n while (! (option = $re(/^(all)(?=\\s*(\\)|,))/))) {\n e = this.element();\n if (!e) { break; }\n if (elements) { elements.push(e); } else { elements = [ e ]; }\n }\n\n option = option && option[1];\n\n extend = new(tree.Extend)(new(tree.Selector)(elements), option, index);\n if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; }\n\n } while($char(\",\"));\n \n expect(/^\\)/);\n\n if (isRule) {\n expect(/^;/);\n }\n\n return extendList;\n },\n\n //\n // extendRule - used in a rule to extend all the parent selectors\n //\n extendRule: function() {\n return this.extend(true);\n },\n \n //\n // Mixins\n //\n mixin: {\n //\n // A Mixin call, with an optional argument list\n //\n // #mixins > .square(#fff);\n // .rounded(4px, black);\n // .button;\n //\n // The `while` loop is there because mixins can be\n // namespaced, but we only support the child and descendant\n // selector for now.\n //\n call: function () {\n var s = input.charAt(i), important = false, index = i, elemIndex,\n elements, elem, e, c, args;\n\n if (s !== '.' && s !== '#') { return; }\n\n save(); // stop us absorbing part of an invalid selector\n\n while (true) {\n elemIndex = i;\n e = $re(/^[#.](?:[\\w-]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/);\n if (!e) {\n break;\n }\n elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo);\n if (elements) { elements.push(elem); } else { elements = [ elem ]; }\n c = $char('>');\n }\n\n if (elements) {\n if ($char('(')) {\n args = this.args(true).args;\n expectChar(')');\n }\n\n if (parsers.important()) {\n important = true;\n }\n\n if (parsers.end()) {\n return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important);\n }\n }\n\n restore();\n },\n args: function (isCall) {\n var parsers = parser.parsers, entities = parsers.entities,\n returner = { args:null, variadic: false },\n expressions = [], argsSemiColon = [], argsComma = [],\n isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg;\n\n while (true) {\n if (isCall) {\n arg = parsers.expression();\n } else {\n parsers.comments();\n if (input.charAt(i) === '.' && $re(/^\\.{3}/)) {\n returner.variadic = true;\n if ($char(\";\") && !isSemiColonSeperated) {\n isSemiColonSeperated = true;\n }\n (isSemiColonSeperated ? argsSemiColon : argsComma)\n .push({ variadic: true });\n break;\n }\n arg = entities.variable() || entities.literal() || entities.keyword();\n }\n\n if (!arg) {\n break;\n }\n\n nameLoop = null;\n if (arg.throwAwayComments) {\n arg.throwAwayComments();\n }\n value = arg;\n var val = null;\n\n if (isCall) {\n // Variable\n if (arg.value.length == 1) {\n val = arg.value[0];\n }\n } else {\n val = arg;\n }\n\n if (val && val instanceof tree.Variable) {\n if ($char(':')) {\n if (expressions.length > 0) {\n if (isSemiColonSeperated) {\n error(\"Cannot mix ; and , as delimiter types\");\n }\n expressionContainsNamed = true;\n }\n value = expect(parsers.expression);\n nameLoop = (name = val.name);\n } else if (!isCall && $re(/^\\.{3}/)) {\n returner.variadic = true;\n if ($char(\";\") && !isSemiColonSeperated) {\n isSemiColonSeperated = true;\n }\n (isSemiColonSeperated ? argsSemiColon : argsComma)\n .push({ name: arg.name, variadic: true });\n break;\n } else if (!isCall) {\n name = nameLoop = val.name;\n value = null;\n }\n }\n\n if (value) {\n expressions.push(value);\n }\n\n argsComma.push({ name:nameLoop, value:value });\n\n if ($char(',')) {\n continue;\n }\n\n if ($char(';') || isSemiColonSeperated) {\n\n if (expressionContainsNamed) {\n error(\"Cannot mix ; and , as delimiter types\");\n }\n\n isSemiColonSeperated = true;\n\n if (expressions.length > 1) {\n value = new(tree.Value)(expressions);\n }\n argsSemiColon.push({ name:name, value:value });\n\n name = null;\n expressions = [];\n expressionContainsNamed = false;\n }\n }\n\n returner.args = isSemiColonSeperated ? argsSemiColon : argsComma;\n return returner;\n },\n //\n // A Mixin definition, with a list of parameters\n //\n // .rounded (@radius: 2px, @color) {\n // ...\n // }\n //\n // Until we have a finer grained state-machine, we have to\n // do a look-ahead, to make sure we don't have a mixin call.\n // See the `rule` function for more information.\n //\n // We start by matching `.rounded (`, and then proceed on to\n // the argument list, which has optional default values.\n // We store the parameters in `params`, with a `value` key,\n // if there is a value, such as in the case of `@radius`.\n //\n // Once we've got our params list, and a closing `)`, we parse\n // the `{...}` block.\n //\n definition: function () {\n var name, params = [], match, ruleset, cond, variadic = false;\n if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') ||\n peek(/^[^{]*\\}/)) {\n return;\n }\n\n save();\n\n match = $re(/^([#.](?:[\\w-]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\\s*\\(/);\n if (match) {\n name = match[1];\n\n var argInfo = this.args(false);\n params = argInfo.args;\n variadic = argInfo.variadic;\n\n // .mixincall(\"@{a}\");\n // looks a bit like a mixin definition.. so we have to be nice and restore\n if (!$char(')')) {\n furthest = i;\n restore();\n }\n \n parsers.comments();\n\n if ($re(/^when/)) { // Guard\n cond = expect(parsers.conditions, 'expected condition');\n }\n\n ruleset = parsers.block();\n\n if (ruleset) {\n return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic);\n } else {\n restore();\n }\n }\n }\n },\n\n //\n // Entities are the smallest recognized token,\n // and can be found inside a rule's value.\n //\n entity: function () {\n var entities = this.entities;\n\n return entities.literal() || entities.variable() || entities.url() ||\n entities.call() || entities.keyword() || entities.javascript() ||\n this.comment();\n },\n\n //\n // A Rule terminator. Note that we use `peek()` to check for '}',\n // because the `block` rule will be expecting it, but we still need to make sure\n // it's there, if ';' was ommitted.\n //\n end: function () {\n return $char(';') || peekChar('}');\n },\n\n //\n // IE's alpha function\n //\n // alpha(opacity=88)\n //\n alpha: function () {\n var value;\n\n if (! $re(/^\\(opacity=/i)) { return; }\n value = $re(/^\\d+/) || this.entities.variable();\n if (value) {\n expectChar(')');\n return new(tree.Alpha)(value);\n }\n },\n\n //\n // A Selector Element\n //\n // div\n // + h1\n // #socks\n // input[type=\"text\"]\n //\n // Elements are the building blocks for Selectors,\n // they are made out of a `Combinator` (see combinator rule),\n // and an element name, such as a tag a class, or `*`.\n //\n element: function () {\n var e, c, v, index = i;\n\n c = this.combinator();\n\n e = $re(/^(?:\\d+\\.\\d+|\\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\\w-]|[^\\x00-\\x9f]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) ||\n $char('*') || $char('&') || this.attribute() || $re(/^\\([^()@]+\\)/) || $re(/^[\\.#](?=@)/) ||\n this.entities.variableCurly();\n\n if (! e) {\n if ($char('(')) {\n if ((v = this.selector()) && $char(')')) {\n e = new(tree.Paren)(v);\n }\n }\n }\n\n if (e) { return new(tree.Element)(c, e, index, env.currentFileInfo); }\n },\n\n //\n // Combinators combine elements together, in a Selector.\n //\n // Because our parser isn't white-space sensitive, special care\n // has to be taken, when parsing the descendant combinator, ` `,\n // as it's an empty space. We have to check the previous character\n // in the input, to see if it's a ` ` character. More info on how\n // we deal with this in *combinator.js*.\n //\n combinator: function () {\n var c = input.charAt(i);\n \n if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') {\n i++;\n if (input.charAt(i) === '^') {\n c = '^^';\n i++;\n }\n while (isWhitespace(input, i)) { i++; }\n return new(tree.Combinator)(c);\n } else if (isWhitespace(input, i - 1)) {\n return new(tree.Combinator)(\" \");\n } else {\n return new(tree.Combinator)(null);\n }\n },\n //\n // A CSS selector (see selector below)\n // with less extensions e.g. the ability to extend and guard\n //\n lessSelector: function () {\n return this.selector(true);\n },\n //\n // A CSS Selector\n //\n // .class > div + h1\n // li a:hover\n //\n // Selectors are made out of one or more Elements, see above.\n //\n selector: function (isLess) {\n var index = i, $re = _$re, elements, extendList, c, e, extend, when, condition;\n\n while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) {\n if (when) {\n condition = expect(this.conditions, 'expected condition');\n } else if (condition) {\n error(\"CSS guard can only be used at the end of selector\");\n } else if (extend) {\n if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; }\n } else {\n if (extendList) { error(\"Extend can only be used at the end of selector\"); }\n c = input.charAt(i);\n if (elements) { elements.push(e); } else { elements = [ e ]; }\n e = null;\n }\n if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') {\n break;\n }\n }\n\n if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); }\n if (extendList) { error(\"Extend must be used to extend a selector, it cannot be used on its own\"); }\n },\n attribute: function () {\n if (! $char('[')) { return; }\n\n var entities = this.entities,\n key, val, op;\n\n if (!(key = entities.variableCurly())) {\n key = expect(/^(?:[_A-Za-z0-9-\\*]*\\|)?(?:[_A-Za-z0-9-]|\\\\.)+/);\n }\n\n op = $re(/^[|~*$^]?=/);\n if (op) {\n val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\\w-]+/) || entities.variableCurly();\n }\n\n expectChar(']');\n\n return new(tree.Attribute)(key, op, val);\n },\n\n //\n // The `block` rule is used by `ruleset` and `mixin.definition`.\n // It's a wrapper around the `primary` rule, with added `{}`.\n //\n block: function () {\n var content;\n if ($char('{') && (content = this.primary()) && $char('}')) {\n return content;\n }\n },\n\n //\n // div, .class, body > p {...}\n //\n ruleset: function () {\n var selectors, s, rules, debugInfo;\n \n save();\n\n if (env.dumpLineNumbers) {\n debugInfo = getDebugInfo(i, input, env);\n }\n\n while (true) {\n s = this.lessSelector();\n if (!s) {\n break;\n }\n if (selectors) { selectors.push(s); } else { selectors = [ s ]; }\n this.comments();\n if (s.condition && selectors.length > 1) {\n error(\"Guards are only currently allowed on a single selector.\");\n }\n if (! $char(',')) { break; }\n if (s.condition) {\n error(\"Guards are only currently allowed on a single selector.\");\n }\n this.comments();\n }\n\n if (selectors && (rules = this.block())) {\n var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports);\n if (env.dumpLineNumbers) {\n ruleset.debugInfo = debugInfo;\n }\n return ruleset;\n } else {\n // Backtrack\n furthest = i;\n restore();\n }\n },\n rule: function (tryAnonymous) {\n var name, value, c = input.charAt(i), important, merge = false;\n save();\n\n if (c === '.' || c === '#' || c === '&') { return; }\n\n name = this.variable() || this.ruleProperty();\n if (name) {\n // prefer to try to parse first if its a variable or we are compressing\n // but always fallback on the other one\n value = !tryAnonymous && (env.compress || (name.charAt && (name.charAt(0) === '@'))) ?\n (this.value() || this.anonymousValue()) :\n (this.anonymousValue() || this.value());\n\n important = this.important();\n \n // a name returned by this.ruleProperty() is always an array of the form:\n // [string-1, ..., string-n, \"\"] or [string-1, ..., string-n, \"+\"]\n // where each item is a tree.Keyword or tree.Variable\n merge = name.pop && (name.pop().value === \"+\");\n\n if (value && this.end()) {\n return new (tree.Rule)(name, value, important, merge, memo, env.currentFileInfo);\n } else {\n furthest = i;\n restore();\n if (value && !tryAnonymous) {\n return this.rule(true);\n }\n }\n }\n },\n anonymousValue: function () {\n var match;\n match = /^([^@+\\/'\"*`(;{}-]*);/.exec(current);\n if (match) {\n i += match[0].length - 1;\n return new(tree.Anonymous)(match[1]);\n }\n },\n\n //\n // An @import directive\n //\n // @import \"lib\";\n //\n // Depending on our environemnt, importing is done differently:\n // In the browser, it's an XHR request, in Node, it would be a\n // file-system operation. The function used for importing is\n // stored in `import`, which we pass to the Import constructor.\n //\n \"import\": function () {\n var path, features, index = i;\n\n save();\n\n var dir = $re(/^@import?\\s+/);\n\n var options = (dir ? this.importOptions() : null) || {};\n\n if (dir && (path = this.entities.quoted() || this.entities.url())) {\n features = this.mediaFeatures();\n if ($char(';')) {\n features = features && new(tree.Value)(features);\n return new(tree.Import)(path, features, options, index, env.currentFileInfo);\n }\n }\n\n restore();\n },\n\n importOptions: function() {\n var o, options = {}, optionName, value;\n\n // list of options, surrounded by parens\n if (! $char('(')) { return null; }\n do {\n o = this.importOption();\n if (o) {\n optionName = o;\n value = true;\n switch(optionName) {\n case \"css\":\n optionName = \"less\";\n value = false;\n break;\n case \"once\":\n optionName = \"multiple\";\n value = false;\n break;\n }\n options[optionName] = value;\n if (! $char(',')) { break; }\n }\n } while (o);\n expectChar(')');\n return options;\n },\n\n importOption: function() {\n var opt = $re(/^(less|css|multiple|once|inline|reference)/);\n if (opt) {\n return opt[1];\n }\n },\n\n mediaFeature: function () {\n var entities = this.entities, nodes = [], e, p;\n do {\n e = entities.keyword() || entities.variable();\n if (e) {\n nodes.push(e);\n } else if ($char('(')) {\n p = this.property();\n e = this.value();\n if ($char(')')) {\n if (p && e) {\n nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true)));\n } else if (e) {\n nodes.push(new(tree.Paren)(e));\n } else {\n return null;\n }\n } else { return null; }\n }\n } while (e);\n\n if (nodes.length > 0) {\n return new(tree.Expression)(nodes);\n }\n },\n\n mediaFeatures: function () {\n var entities = this.entities, features = [], e;\n do {\n e = this.mediaFeature();\n if (e) {\n features.push(e);\n if (! $char(',')) { break; }\n } else {\n e = entities.variable();\n if (e) {\n features.push(e);\n if (! $char(',')) { break; }\n }\n }\n } while (e);\n\n return features.length > 0 ? features : null;\n },\n\n media: function () {\n var features, rules, media, debugInfo;\n\n if (env.dumpLineNumbers) {\n debugInfo = getDebugInfo(i, input, env);\n }\n\n if ($re(/^@media/)) {\n features = this.mediaFeatures();\n\n rules = this.block();\n if (rules) {\n media = new(tree.Media)(rules, features, i, env.currentFileInfo);\n if (env.dumpLineNumbers) {\n media.debugInfo = debugInfo;\n }\n return media;\n }\n }\n },\n\n //\n // A CSS Directive\n //\n // @charset \"utf-8\";\n //\n directive: function () {\n var index = i, name, value, rules, nonVendorSpecificName,\n hasBlock, hasIdentifier, hasExpression, identifier;\n\n if (input.charAt(i) !== '@') { return; }\n\n value = this['import']() || this.media();\n if (value) {\n return value;\n }\n\n save();\n\n name = $re(/^@[a-z-]+/);\n \n if (!name) { return; }\n\n nonVendorSpecificName = name;\n if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) {\n nonVendorSpecificName = \"@\" + name.slice(name.indexOf('-', 2) + 1);\n }\n\n switch(nonVendorSpecificName) {\n case \"@font-face\":\n hasBlock = true;\n break;\n case \"@viewport\":\n case \"@top-left\":\n case \"@top-left-corner\":\n case \"@top-center\":\n case \"@top-right\":\n case \"@top-right-corner\":\n case \"@bottom-left\":\n case \"@bottom-left-corner\":\n case \"@bottom-center\":\n case \"@bottom-right\":\n case \"@bottom-right-corner\":\n case \"@left-top\":\n case \"@left-middle\":\n case \"@left-bottom\":\n case \"@right-top\":\n case \"@right-middle\":\n case \"@right-bottom\":\n hasBlock = true;\n break;\n case \"@host\":\n case \"@page\":\n case \"@document\":\n case \"@supports\":\n case \"@keyframes\":\n hasBlock = true;\n hasIdentifier = true;\n break;\n case \"@namespace\":\n hasExpression = true;\n break;\n }\n\n if (hasIdentifier) {\n identifier = ($re(/^[^{]+/) || '').trim();\n if (identifier) {\n name += \" \" + identifier;\n }\n }\n\n if (hasBlock) {\n rules = this.block();\n if (rules) {\n return new(tree.Directive)(name, rules, index, env.currentFileInfo);\n }\n } else {\n value = hasExpression ? this.expression() : this.entity();\n if (value && $char(';')) {\n var directive = new(tree.Directive)(name, value, index, env.currentFileInfo);\n if (env.dumpLineNumbers) {\n directive.debugInfo = getDebugInfo(i, input, env);\n }\n return directive;\n }\n }\n\n restore();\n },\n\n //\n // A Value is a comma-delimited list of Expressions\n //\n // font-family: Baskerville, Georgia, serif;\n //\n // In a Rule, a Value represents everything after the `:`,\n // and before the `;`.\n //\n value: function () {\n var e, expressions = [];\n\n do {\n e = this.expression();\n if (e) {\n expressions.push(e);\n if (! $char(',')) { break; }\n }\n } while(e);\n\n if (expressions.length > 0) {\n return new(tree.Value)(expressions);\n }\n },\n important: function () {\n if (input.charAt(i) === '!') {\n return $re(/^! *important/);\n }\n },\n sub: function () {\n var a, e;\n\n if ($char('(')) {\n a = this.addition();\n if (a) {\n e = new(tree.Expression)([a]);\n expectChar(')');\n e.parens = true;\n return e;\n }\n }\n },\n multiplication: function () {\n var m, a, op, operation, isSpaced;\n m = this.operand();\n if (m) {\n isSpaced = isWhitespace(input, i - 1);\n while (true) {\n if (peek(/^\\/[*\\/]/)) {\n break;\n }\n op = $char('/') || $char('*');\n\n if (!op) { break; }\n\n a = this.operand();\n\n if (!a) { break; }\n\n m.parensInOp = true;\n a.parensInOp = true;\n operation = new(tree.Operation)(op, [operation || m, a], isSpaced);\n isSpaced = isWhitespace(input, i - 1);\n }\n return operation || m;\n }\n },\n addition: function () {\n var m, a, op, operation, isSpaced;\n m = this.multiplication();\n if (m) {\n isSpaced = isWhitespace(input, i - 1);\n while (true) {\n op = $re(/^[-+]\\s+/) || (!isSpaced && ($char('+') || $char('-')));\n if (!op) {\n break;\n }\n a = this.multiplication();\n if (!a) {\n break;\n }\n \n m.parensInOp = true;\n a.parensInOp = true;\n operation = new(tree.Operation)(op, [operation || m, a], isSpaced);\n isSpaced = isWhitespace(input, i - 1);\n }\n return operation || m;\n }\n },\n conditions: function () {\n var a, b, index = i, condition;\n\n a = this.condition();\n if (a) {\n while (true) {\n if (!peek(/^,\\s*(not\\s*)?\\(/) || !$char(',')) {\n break;\n }\n b = this.condition();\n if (!b) {\n break;\n }\n condition = new(tree.Condition)('or', condition || a, b, index);\n }\n return condition || a;\n }\n },\n condition: function () {\n var entities = this.entities, index = i, negate = false,\n a, b, c, op;\n\n if ($re(/^not/)) { negate = true; }\n expectChar('(');\n a = this.addition() || entities.keyword() || entities.quoted();\n if (a) {\n op = $re(/^(?:>=|<=|=<|[<=>])/);\n if (op) {\n b = this.addition() || entities.keyword() || entities.quoted();\n if (b) {\n c = new(tree.Condition)(op, a, b, index, negate);\n } else {\n error('expected expression');\n }\n } else {\n c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate);\n }\n expectChar(')');\n return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c;\n }\n },\n\n //\n // An operand is anything that can be part of an operation,\n // such as a Color, or a Variable\n //\n operand: function () {\n var entities = this.entities,\n p = input.charAt(i + 1), negate;\n\n if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); }\n var o = this.sub() || entities.dimension() ||\n entities.color() || entities.variable() ||\n entities.call();\n\n if (negate) {\n o.parensInOp = true;\n o = new(tree.Negative)(o);\n }\n\n return o;\n },\n\n //\n // Expressions either represent mathematical operations,\n // or white-space delimited Entities.\n //\n // 1px solid black\n // @var * 2\n //\n expression: function () {\n var entities = [], e, delim;\n\n do {\n e = this.addition() || this.entity();\n if (e) {\n entities.push(e);\n // operations do not allow keyword \"/\" dimension (e.g. small/20px) so we support that here\n if (!peek(/^\\/[\\/*]/)) {\n delim = $char('/');\n if (delim) {\n entities.push(new(tree.Anonymous)(delim));\n }\n }\n }\n } while (e);\n if (entities.length > 0) {\n return new(tree.Expression)(entities);\n }\n },\n property: function () {\n var name = $re(/^(\\*?-?[_a-zA-Z0-9-]+)\\s*:/);\n if (name) {\n return name[1];\n }\n },\n ruleProperty: function () {\n var c = current, name = [], index = [], length = 0, s, k;\n \n function match(re) {\n var a = re.exec(c);\n if (a) {\n index.push(i + length);\n length += a[0].length;\n c = c.slice(a[1].length);\n return name.push(a[1]);\n }\n }\n\n match(/^(\\*?)/);\n while (match(/^((?:[\\w-]+)|(?:@\\{[\\w-]+\\}))/)); // !\n if ((name.length > 1) && match(/^\\s*(\\+?)\\s*:/)) {\n // at last, we have the complete match now. move forward, \n // convert name particles to tree objects and return:\n skipWhitespace(length);\n if (name[0] === '') {\n name.shift();\n index.shift();\n }\n for (k = 0; k < name.length; k++) {\n s = name[k];\n name[k] = (s.charAt(0) !== '@')\n ? new(tree.Keyword)(s)\n : new(tree.Variable)('@' + s.slice(2, -1), \n index[k], env.currentFileInfo);\n }\n return name;\n }\n }\n }\n };\n return parser;\n};\nless.Parser.serializeVars = function(vars) {\n var s = '';\n\n for (var name in vars) {\n if (Object.hasOwnProperty.call(vars, name)) {\n var value = vars[name];\n s += ((name[0] === '@') ? '' : '@') + name +': '+ value +\n ((('' + value).slice(-1) === ';') ? '' : ';');\n }\n }\n\n return s;\n};\n\n## 内容8\n### 文件路径:\nlib/thirdparty/less/tree/javascript.js\n### 代码内容:\n(function (tree) {\n\ntree.JavaScript = function (string, index, escaped) {\n this.escaped = escaped;\n this.expression = string;\n this.index = index;\n};\ntree.JavaScript.prototype = {\n type: \"JavaScript\",\n eval: function (env) {\n var result,\n that = this,\n context = {};\n\n var expression = this.expression.replace(/@\\{([\\w-]+)\\}/g, function (_, name) {\n return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env));\n });\n\n try {\n expression = new(Function)('return (' + expression + ')');\n } catch (e) {\n throw { message: \"JavaScript evaluation error: \" + e.message + \" from `\" + expression + \"`\" ,\n index: this.index };\n }\n\n var variables = env.frames[0].variables();\n for (var k in variables) {\n if (variables.hasOwnProperty(k)) {\n /*jshint loopfunc:true */\n context[k.slice(1)] = {\n value: variables[k].value,\n toJS: function () {\n return this.value.eval(env).toCSS();\n }\n };\n }\n }\n\n try {\n result = expression.call(context);\n } catch (e) {\n throw { message: \"JavaScript evaluation error: '\" + e.name + ': ' + e.message.replace(/[\"]/g, \"'\") + \"'\" ,\n index: this.index };\n }\n if (typeof(result) === 'number') {\n return new(tree.Dimension)(result);\n } else if (typeof(result) === 'string') {\n return new(tree.Quoted)('\"' + result + '\"', result, this.escaped, this.index);\n } else if (Array.isArray(result)) {\n return new(tree.Anonymous)(result.join(', '));\n } else {\n return new(tree.Anonymous)(result);\n }\n }\n};\n\n})(require('../tree'));\n\n\n## 内容9\n### 文件路径:\nlib/thirdparty/less/tree/quoted.js\n### 代码内容:\n(function (tree) {\n\ntree.Quoted = function (str, content, escaped, index, currentFileInfo) {\n this.escaped = escaped;\n this.value = content || '';\n this.quote = str.charAt(0);\n this.index = index;\n this.currentFileInfo = currentFileInfo;\n};\ntree.Quoted.prototype = {\n type: \"Quoted\",\n genCSS: function (env, output) {\n if (!this.escaped) {\n output.add(this.quote, this.currentFileInfo, this.index);\n }\n output.add(this.value);\n if (!this.escaped) {\n output.add(this.quote);\n }\n },\n toCSS: tree.toCSS,\n eval: function (env) {\n var that = this;\n var value = this.value.replace(/`([^`]+)`/g, function (_, exp) {\n return new(tree.JavaScript)(exp, that.index, true).eval(env).value;\n }).replace(/@\\{([\\w-]+)\\}/g, function (_, name) {\n var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true);\n return (v instanceof tree.Quoted) ? v.value : v.toCSS();\n });\n return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo);\n },\n compare: function (x) {\n if (!x.toCSS) {\n return -1;\n }\n \n var left = this.toCSS(),\n right = x.toCSS();\n \n if (left === right) {\n return 0;\n }\n \n return left < right ? -1 : 1;\n }\n};\n\n})(require('../tree'));\n\n", "input": "", "output": "【漏洞类型】代码注入漏洞\n【严重性】High\n【漏洞摘要】此代码允许在Less文件中执行JavaScript,这可能导致恶意用户注入恶意代码,从而在服务器端执行任意代码。具体表现为在`lib/thirdparty/less/tree/javascript.js`文件中,`JavaScript.prototype.eval`函数解析并执行Less文件中的JavaScript代码。根本原因是代码中没有禁用JavaScript执行功能,允许用户在Less文件中使用反引号(`` ` ``)包裹的JavaScript代码进行执行。\n【关键代码模式】JavaScript.prototype.eval、`$re(/^`([^`]*)`/)`、new(Function)('return (' + expression + ')')\n【修复方法说明】通过移除`lib/thirdparty/less/tree/javascript.js`文件,并在`lib/thirdparty/less/parser.js`文件中禁用JavaScript执行功能,解决了此安全漏洞。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nxwiki-platform-core/xwiki-platform-rest/xwiki-platform-rest-server/pom.xml\n### 代码内容:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n-->\n\n<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/maven-v4_0_0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n <parent>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-rest</artifactId>\n <version>15.2-SNAPSHOT</version>\n </parent>\n <artifactId>xwiki-platform-rest-server</artifactId>\n <packaging>jar</packaging>\n <name>XWiki Platform - REST - Server</name>\n <description>Service for accessing XWiki through a RESTful API</description>\n <properties>\n <xwiki.jacoco.instructionRatio>0.29</xwiki.jacoco.instructionRatio>\n </properties>\n <dependencies>\n <dependency>\n <groupId>org.apache.commons</groupId>\n <artifactId>commons-lang3</artifactId>\n </dependency>\n <dependency>\n <groupId>commons-io</groupId>\n <artifactId>commons-io</artifactId>\n </dependency>\n <dependency>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-component-api</artifactId>\n <version>${commons.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-context</artifactId>\n <version>${commons.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-job-api</artifactId>\n <version>${commons.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-repository-model</artifactId>\n <version>${commons.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-script</artifactId>\n <version>${commons.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.rendering</groupId>\n <artifactId>xwiki-rendering-api</artifactId>\n <version>${rendering.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-rest-api</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-rest-model</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-localization-api</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-model-api</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-query-manager</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-rendering-configuration-api</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-security-authorization-api</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-wiki-api</artifactId>\n <version>${project.version}</version>\n </dependency>\n <!-- Needed to resolve the user serializer component -->\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-user-default</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>javax.servlet</groupId>\n <artifactId>javax.servlet-api</artifactId>\n </dependency>\n <dependency>\n <groupId>org.restlet.jse</groupId>\n <artifactId>org.restlet</artifactId>\n </dependency>\n <dependency>\n <groupId>org.restlet.jee</groupId>\n <artifactId>org.restlet.ext.servlet</artifactId>\n </dependency>\n <dependency>\n <groupId>org.restlet.jse</groupId>\n <artifactId>org.restlet.ext.jaxrs</artifactId>\n </dependency>\n <!-- Replace the version triggered by restlet by a more accurate and up to date one -->\n <dependency>\n <groupId>com.tdunning</groupId>\n <artifactId>json</artifactId>\n </dependency>\n <dependency>\n <!-- We need Jackson for a proper JSON representation of our REST resources.\n See XWIKI-9136: Rest API JSON does not retrieve inherited fields -->\n <groupId>org.restlet.jse</groupId>\n <artifactId>org.restlet.ext.jackson</artifactId>\n </dependency>\n <!-- We need a JAXB converter for XML representation because we model our REST resources using JAXB. Jackson (see\n above) has a module that understands JAXB annotations but the output of the Restlet Jackson converter is not very\n good (missing XML declaration and namespace, attributes serialized as elements) so we prefer to use a dedicated\n JAXB converter for XML representation. -->\n <dependency>\n <groupId>org.restlet.jse</groupId>\n <artifactId>org.restlet.ext.jaxb</artifactId>\n </dependency>\n <!-- Replace the version triggered by Restlet by a more accurate one -->\n <dependency>\n <groupId>org.glassfish.jaxb</groupId>\n <artifactId>jaxb-runtime</artifactId>\n </dependency>\n\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-oldcore</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-wiki-user-api</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.jvnet.jaxb2_commons</groupId>\n <artifactId>jaxb2-fluent-api</artifactId>\n <version>3.0</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-icon-api</artifactId>\n <version>${project.version}</version>\n </dependency>\n\n <!-- Test dependencies -->\n <dependency>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-tool-test-component</artifactId>\n <version>${commons.version}</version>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-test-oldcore</artifactId>\n <version>${project.version}</version>\n <type>pom</type>\n <scope>test</scope>\n </dependency>\n <!-- Required to test the templates. -->\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-test-page</artifactId>\n <version>${project.version}</version>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-web-templates</artifactId>\n <version>${project.version}</version>\n <type>jar</type>\n <scope>test</scope>\n </dependency>\n </dependencies>\n <build>\n <plugins>\n <plugin>\n <!-- Apply the Checkstyle configurations defined in the top level pom.xml file -->\n <groupId>org.apache.maven.plugins</groupId>\n <artifactId>maven-checkstyle-plugin</artifactId>\n <executions>\n <execution>\n <!-- Specify the \"default\" execution id so that the \"blocker\" one is always executed -->\n <id>default</id>\n <configuration>\n <failsOnError>true</failsOnError>\n <suppressionsLocation>${basedir}/src/checkstyle/checkstyle-suppressions.xml</suppressionsLocation>\n <excludes>\n org/xwiki/rest/Constants.java,\n org/xwiki/rest/XWikiResource.java,\n org/xwiki/rest/internal/DomainObjectFactory.java,\n org/xwiki/rest/internal/ModelFactory.java,\n org/xwiki/rest/internal/RangeIterable.java,\n org/xwiki/rest/internal/Utils.java,\n org/xwiki/rest/internal/XWikiAuthentication.java,\n org/xwiki/rest/internal/resources/BaseAttachmentsResource.java,\n org/xwiki/rest/internal/resources/BaseSearchResult.java,\n org/xwiki/rest/internal/resources/BrowserAuthenticationResource.java,\n org/xwiki/rest/internal/resources/pages/PagesResourceImpl.java,\n org/xwiki/rest/internal/resources/search/AbstractDatabaseSearchSource.java,\n org/xwiki/rest/internal/resources/search/SearchSource.java,\n org/xwiki/rest/internal/resources/wikis/WikiPagesResourceImpl.java\n </excludes>\n </configuration>\n </execution>\n </executions>\n </plugin>\n <plugin>\n <groupId>fr.inria.gforge.spoon</groupId>\n <artifactId>spoon-maven-plugin</artifactId>\n <executions>\n <execution>\n <id>spoon-main</id>\n <configuration>\n <processorProperties combine.children=\"append\">\n <processorProperty>\n <name>org.xwiki.tool.spoon.InjectAnnotationProcessor</name>\n <properties>\n <property>\n <!-- Exclude javax.ws.rs.core.Application & org.restlet.ext.jaxrs.ObjectFactory since we use an\n Inject on them. These are special cases when we don't inject on an interface with a @Role\n and there's no way for the Spoon checker to find out about this, since only the Component\n Manager knows what has been registered in it. -->\n <name>excludedFieldTypes</name>\n <value><![CDATA[\n [\"javax.ws.rs.core.Application\", \"org.restlet.ext.jaxrs.ObjectFactory\"]\n ]]></value>\n </property>\n </properties>\n </processorProperty>\n </processorProperties>\n </configuration>\n </execution>\n </executions>\n </plugin>\n </plugins>\n </build>\n</project>\n\n## 内容2\n### 文件路径:\nxwiki-platform-core/xwiki-platform-rest/xwiki-platform-rest-server/src/main/java/org/xwiki/rest/internal/ModelFactory.java\n### 代码内容:\n/*\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n */\npackage org.xwiki.rest.internal;\n\nimport java.io.IOException;\nimport java.net.URI;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Formatter;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Set;\nimport java.util.Vector;\nimport java.util.stream.Collectors;\n\nimport javax.inject.Inject;\nimport javax.inject.Provider;\nimport javax.inject.Singleton;\nimport javax.xml.parsers.ParserConfigurationException;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.slf4j.Logger;\nimport org.xwiki.component.annotation.Component;\nimport org.xwiki.job.DefaultRequest;\nimport org.xwiki.job.Request;\nimport org.xwiki.logging.LogLevel;\nimport org.xwiki.logging.event.LogEvent;\nimport org.xwiki.logging.tail.LogTail;\nimport org.xwiki.model.EntityType;\nimport org.xwiki.model.reference.DocumentReference;\nimport org.xwiki.model.reference.EntityReference;\nimport org.xwiki.model.reference.EntityReferenceSerializer;\nimport org.xwiki.model.reference.ObjectReference;\nimport org.xwiki.rendering.syntax.Syntax;\nimport org.xwiki.rest.Relations;\nimport org.xwiki.rest.XWikiRestException;\nimport org.xwiki.rest.model.jaxb.Attachment;\nimport org.xwiki.rest.model.jaxb.Attribute;\nimport org.xwiki.rest.model.jaxb.Class;\nimport org.xwiki.rest.model.jaxb.Hierarchy;\nimport org.xwiki.rest.model.jaxb.HierarchyItem;\nimport org.xwiki.rest.model.jaxb.JobId;\nimport org.xwiki.rest.model.jaxb.JobLog;\nimport org.xwiki.rest.model.jaxb.JobProgress;\nimport org.xwiki.rest.model.jaxb.JobRequest;\nimport org.xwiki.rest.model.jaxb.JobStatus;\nimport org.xwiki.rest.model.jaxb.Link;\nimport org.xwiki.rest.model.jaxb.MapEntry;\nimport org.xwiki.rest.model.jaxb.Object;\nimport org.xwiki.rest.model.jaxb.ObjectFactory;\nimport org.xwiki.rest.model.jaxb.ObjectSummary;\nimport org.xwiki.rest.model.jaxb.Page;\nimport org.xwiki.rest.model.jaxb.PageSummary;\nimport org.xwiki.rest.model.jaxb.Property;\nimport org.xwiki.rest.model.jaxb.Space;\nimport org.xwiki.rest.model.jaxb.Translation;\nimport org.xwiki.rest.model.jaxb.Translations;\nimport org.xwiki.rest.model.jaxb.Wiki;\nimport org.xwiki.rest.resources.ModificationsResource;\nimport org.xwiki.rest.resources.SyntaxesResource;\nimport org.xwiki.rest.resources.attachments.AttachmentMetadataResource;\nimport org.xwiki.rest.resources.attachments.AttachmentResource;\nimport org.xwiki.rest.resources.attachments.AttachmentVersionResource;\nimport org.xwiki.rest.resources.attachments.AttachmentsAtPageVersionResource;\nimport org.xwiki.rest.resources.attachments.AttachmentsResource;\nimport org.xwiki.rest.resources.classes.ClassPropertiesResource;\nimport org.xwiki.rest.resources.classes.ClassPropertyResource;\nimport org.xwiki.rest.resources.classes.ClassResource;\nimport org.xwiki.rest.resources.classes.ClassesResource;\nimport org.xwiki.rest.resources.comments.CommentsResource;\nimport org.xwiki.rest.resources.comments.CommentsVersionResource;\nimport org.xwiki.rest.resources.objects.AllObjectsForClassNameResource;\nimport org.xwiki.rest.resources.objects.ObjectAtPageVersionResource;\nimport org.xwiki.rest.resources.objects.ObjectPropertiesAtPageVersionResource;\nimport org.xwiki.rest.resources.objects.ObjectPropertiesResource;\nimport org.xwiki.rest.resources.objects.ObjectPropertyAtPageVersionResource;\nimport org.xwiki.rest.resources.objects.ObjectPropertyResource;\nimport org.xwiki.rest.resources.objects.ObjectResource;\nimport org.xwiki.rest.resources.objects.ObjectsAtPageVersionResource;\nimport org.xwiki.rest.resources.objects.ObjectsResource;\nimport org.xwiki.rest.resources.pages.PageChildrenResource;\nimport org.xwiki.rest.resources.pages.PageHistoryResource;\nimport org.xwiki.rest.resources.pages.PageResource;\nimport org.xwiki.rest.resources.pages.PageTagsResource;\nimport org.xwiki.rest.resources.pages.PageTranslationHistoryResource;\nimport org.xwiki.rest.resources.pages.PageTranslationResource;\nimport org.xwiki.rest.resources.pages.PagesResource;\nimport org.xwiki.rest.resources.spaces.SpaceResource;\nimport org.xwiki.rest.resources.spaces.SpaceSearchResource;\nimport org.xwiki.rest.resources.spaces.SpacesResource;\nimport org.xwiki.rest.resources.wikis.WikiSearchQueryResource;\nimport org.xwiki.rest.resources.wikis.WikiSearchResource;\nimport org.xwiki.security.authorization.ContextualAuthorizationManager;\nimport org.xwiki.security.authorization.Right;\nimport org.xwiki.user.UserReferenceSerializer;\nimport org.xwiki.wiki.descriptor.WikiDescriptor;\nimport org.xwiki.wiki.descriptor.WikiDescriptorManager;\n\nimport com.xpn.xwiki.XWiki;\nimport com.xpn.xwiki.XWikiContext;\nimport com.xpn.xwiki.XWikiException;\nimport com.xpn.xwiki.api.Document;\nimport com.xpn.xwiki.api.PropertyClass;\nimport com.xpn.xwiki.doc.XWikiDocument;\nimport com.xpn.xwiki.objects.BaseObject;\nimport com.xpn.xwiki.objects.BaseProperty;\nimport com.xpn.xwiki.objects.PropertyInterface;\nimport com.xpn.xwiki.objects.classes.BaseClass;\nimport com.xpn.xwiki.objects.classes.ComputedFieldClass;\nimport com.xpn.xwiki.objects.classes.ListClass;\n\n/**\n * Various common tools for resources.\n * \n * @version $Id$\n * @since 7.3M1\n */\n@Component(roles = ModelFactory.class)\n@Singleton\npublic class ModelFactory\n{\n private static final String PASSWORD_TYPE = \"Password\";\n\n private final ObjectFactory objectFactory;\n\n @Inject\n private Provider<XWikiContext> xcontextProvider;\n\n @Inject\n private EntityReferenceSerializer<String> serializer;\n\n @Inject\n private JAXBConverter jaxbConverter;\n\n @Inject\n private Provider<ContextualAuthorizationManager> authorizationManagerProvider;\n\n @Inject\n private Logger logger;\n\n @Inject\n private EntityReferenceSerializer<String> defaultEntityReferenceSerializer;\n\n @Inject\n private WikiDescriptorManager wikiDescriptorManager;\n\n @Inject\n private UserReferenceSerializer<String> userReferenceSerializer;\n\n public ModelFactory()\n {\n this.objectFactory = new ObjectFactory();\n }\n\n public boolean toDocument(Document doc, org.xwiki.rest.model.jaxb.Page restPage) throws XWikiException\n {\n boolean modified = false;\n\n if (restPage.getContent() != null) {\n doc.setContent(restPage.getContent());\n modified = true;\n }\n\n if (restPage.getTitle() != null) {\n doc.setTitle(restPage.getTitle());\n modified = true;\n }\n\n if (restPage.getParent() != null) {\n doc.setParent(restPage.getParent());\n modified = true;\n }\n\n if (restPage.getSyntax() != null) {\n doc.setSyntaxId(restPage.getSyntax());\n modified = true;\n }\n\n doc.setHidden(restPage.isHidden());\n\n // Set objects\n if (restPage.getObjects() != null) {\n Set<ObjectReference> newReferences = new HashSet<>();\n\n // Add/update objects\n for (ObjectSummary restObjectSummary : restPage.getObjects().getObjectSummaries()) {\n if (restObjectSummary != null) {\n org.xwiki.rest.model.jaxb.Object restObject = (org.xwiki.rest.model.jaxb.Object) restObjectSummary;\n com.xpn.xwiki.api.Object xwikiObject =\n doc.getObject(restObject.getClassName(), restObject.getNumber());\n if (xwikiObject == null) {\n xwikiObject = doc.newObject(restObject.getClassName());\n }\n toObject(xwikiObject, restObject);\n modified = true;\n\n newReferences.add(xwikiObject.getReference());\n }\n }\n\n // Remove objects\n List<com.xpn.xwiki.api.Object> toRemove = new ArrayList<>();\n for (Vector<com.xpn.xwiki.api.Object> objects : doc.getxWikiObjects().values()) {\n for (com.xpn.xwiki.api.Object object : objects) {\n if (!newReferences.contains(object.getReference())) {\n toRemove.add(object);\n }\n }\n }\n for (com.xpn.xwiki.api.Object obj : toRemove) {\n doc.removeObject(obj);\n modified = true;\n }\n }\n\n // TODO: add support for class, see https://jira.xwiki.org/browse/XWIKI-12597\n\n // TODO: add attachment content to the REST model ?\n\n return modified;\n }\n\n private void fillObjectSummary(ObjectSummary objectSummary, Document doc, BaseObject xwikiObject,\n Boolean withPrettyNames)\n {\n objectSummary.setId(String.format(\"%s:%s\", doc.getPrefixedFullName(), xwikiObject.getGuid()));\n objectSummary.setGuid(xwikiObject.getGuid());\n objectSummary.setPageId(doc.getPrefixedFullName());\n objectSummary.setPageVersion(doc.getVersion());\n objectSummary.setPageAuthor(doc.getAuthor());\n if (withPrettyNames) {\n XWikiContext xwikiContext = this.xcontextProvider.get();\n objectSummary\n .setPageAuthorName(xwikiContext.getWiki().getUserName(doc.getAuthor(), null, false, xwikiContext));\n }\n objectSummary.setWiki(doc.getWiki());\n objectSummary.setSpace(doc.getSpace());\n objectSummary.setPageName(doc.getDocumentReference().getName());\n objectSummary.setClassName(xwikiObject.getClassName());\n objectSummary.setNumber(xwikiObject.getNumber());\n\n String[] propertyNames = xwikiObject.getPropertyNames();\n if (propertyNames.length > 0) {\n try {\n objectSummary.setHeadline(serializePropertyValue(xwikiObject.get(propertyNames[0])));\n } catch (XWikiException e) {\n // Should never happen\n }\n }\n }\n\n public ObjectSummary toRestObjectSummary(URI baseUri, Document doc, BaseObject xwikiObject, boolean useVersion,\n Boolean withPrettyNames)\n {\n ObjectSummary objectSummary = objectFactory.createObjectSummary();\n fillObjectSummary(objectSummary, doc, xwikiObject, withPrettyNames);\n\n Link objectLink = getObjectLink(objectFactory, baseUri, doc, xwikiObject, useVersion, Relations.OBJECT);\n objectSummary.getLinks().add(objectLink);\n\n String propertiesUri;\n if (useVersion) {\n propertiesUri = Utils.createURI(baseUri, ObjectPropertiesAtPageVersionResource.class, doc.getWiki(),\n Utils.getSpacesFromSpaceId(doc.getSpace()), doc.getDocumentReference().getName(), doc.getVersion(),\n xwikiObject.getClassName(), xwikiObject.getNumber()).toString();\n } else {\n propertiesUri = Utils.createURI(baseUri, ObjectPropertiesResource.class, doc.getWiki(),\n Utils.getSpacesFromSpaceId(doc.getSpace()), doc.getDocumentReference().getName(),\n xwikiObject.getClassName(), xwikiObject.getNumber()).toString();\n }\n\n Link propertyLink = objectFactory.createLink();\n propertyLink.setHref(propertiesUri);\n propertyLink.setRel(Relations.PROPERTIES);\n objectSummary.getLinks().add(propertyLink);\n\n return objectSummary;\n }\n\n public void toObject(com.xpn.xwiki.api.Object xwikiObject, org.xwiki.rest.model.jaxb.Object restObject)\n {\n for (Property restProperty : restObject.getProperties()) {\n xwikiObject.set(restProperty.getName(), restProperty.getValue());\n }\n }\n\n public Object toRestObject(URI baseUri, Document doc, BaseObject xwikiObject, boolean useVersion,\n Boolean withPrettyNames)\n {\n Object object = this.objectFactory.createObject();\n fillObjectSummary(object, doc, xwikiObject, withPrettyNames);\n\n XWikiContext xwikiContext = this.xcontextProvider.get();\n BaseClass xwikiClass = xwikiObject.getXClass(xwikiContext);\n\n for (java.lang.Object propertyClassObject : xwikiClass.getProperties()) {\n com.xpn.xwiki.objects.classes.PropertyClass propertyClass =\n (com.xpn.xwiki.objects.classes.PropertyClass) propertyClassObject;\n\n Property property = this.objectFactory.createProperty();\n\n for (java.lang.Object o : propertyClass.getProperties()) {\n BaseProperty baseProperty = (BaseProperty) o;\n Attribute attribute = this.objectFactory.createAttribute();\n attribute.setName(baseProperty.getName());\n\n /* Check for null values in order to prevent NPEs */\n if (baseProperty.getValue() != null) {\n attribute.setValue(baseProperty.getValue().toString());\n } else {\n attribute.setValue(\"\");\n }\n\n property.getAttributes().add(attribute);\n }\n\n if (propertyClass instanceof ListClass) {\n ListClass listClass = (ListClass) propertyClass;\n\n List allowedValueList = listClass.getList(xwikiContext);\n\n if (!allowedValueList.isEmpty()) {\n Formatter f = new Formatter();\n for (int i = 0; i < allowedValueList.size(); i++) {\n if (i != allowedValueList.size() - 1) {\n f.format(\"%s,\", allowedValueList.get(i).toString());\n } else {\n f.format(\"%s\", allowedValueList.get(i).toString());\n }\n }\n\n Attribute attribute = this.objectFactory.createAttribute();\n attribute.setName(Constants.ALLOWED_VALUES_ATTRIBUTE_NAME);\n attribute.setValue(f.toString());\n property.getAttributes().add(attribute);\n }\n }\n\n property.setName(propertyClass.getName());\n property.setType(propertyClass.getClassType());\n if (hasAccess(property)) {\n try {\n property.setValue(\n serializePropertyValue(xwikiObject.get(propertyClass.getName()), propertyClass, xwikiContext));\n } catch (XWikiException e) {\n // Should never happen\n logger.error(\"Unexpected exception when accessing property [{}]\", propertyClass.getName(), e);\n }\n }\n\n String propertyUri;\n\n if (useVersion) {\n propertyUri = Utils\n .createURI(baseUri, ObjectPropertyAtPageVersionResource.class, doc.getWiki(),\n Utils.getSpacesFromSpaceId(doc.getSpace()), doc.getDocumentReference().getName(),\n doc.getVersion(), xwikiObject.getClassName(), xwikiObject.getNumber(), propertyClass.getName())\n .toString();\n } else {\n propertyUri = Utils.createURI(baseUri, ObjectPropertyResource.class, doc.getWiki(),\n Utils.getSpacesFromSpaceId(doc.getSpace()), doc.getDocumentReference().getName(),\n xwikiObject.getClassName(), xwikiObject.getNumber(), propertyClass.getName()).toString();\n }\n Link propertyLink = this.objectFactory.createLink();\n propertyLink.setHref(propertyUri);\n propertyLink.setRel(Relations.SELF);\n property.getLinks().add(propertyLink);\n\n object.getProperties().add(property);\n }\n\n Link objectLink = getObjectLink(this.objectFactory, baseUri, doc, xwikiObject, useVersion, Relations.SELF);\n object.getLinks().add(objectLink);\n\n return object;\n }\n\n private static Link getObjectLink(ObjectFactory objectFactory, URI baseUri, Document doc, BaseObject xwikiObject,\n boolean useVersion, String relation)\n {\n String objectUri;\n\n if (useVersion) {\n objectUri = Utils.createURI(baseUri, ObjectAtPageVersionResource.class, doc.getWiki(),\n Utils.getSpacesFromSpaceId(doc.getSpace()), doc.getDocumentReference().getName(), doc.getVersion(),\n xwikiObject.getClassName(), xwikiObject.getNumber()).toString();\n } else {\n objectUri = Utils\n .createURI(baseUri, ObjectResource.class, doc.getWiki(), Utils.getSpacesFromSpaceId(doc.getSpace()),\n doc.getDocumentReference().getName(), xwikiObject.getClassName(), xwikiObject.getNumber())\n .toString();\n }\n Link objectLink = objectFactory.createLink();\n objectLink.setHref(objectUri);\n objectLink.setRel(relation);\n\n return objectLink;\n }\n\n // To REST\n\n public Wiki toRestWiki(URI baseUri, String wikiName)\n {\n Wiki wiki = this.objectFactory.createWiki().withId(wikiName).withName(wikiName);\n\n String spacesUri = Utils.createURI(baseUri, SpacesResource.class, wikiName).toString();\n Link spacesLink = this.objectFactory.createLink();\n spacesLink.setHref(spacesUri);\n spacesLink.setRel(Relations.SPACES);\n wiki.getLinks().add(spacesLink);\n\n String classesUri = Utils.createURI(baseUri, ClassesResource.class, wikiName).toString();\n Link classesLink = this.objectFactory.createLink();\n classesLink.setHref(classesUri);\n classesLink.setRel(Relations.CLASSES);\n wiki.getLinks().add(classesLink);\n\n String modificationsUri = Utils.createURI(baseUri, ModificationsResource.class, wikiName).toString();\n Link modificationsLink = this.objectFactory.createLink();\n modificationsLink.setHref(modificationsUri);\n modificationsLink.setRel(Relations.MODIFICATIONS);\n wiki.getLinks().add(modificationsLink);\n\n String searchUri = Utils.createURI(baseUri, WikiSearchResource.class, wikiName).toString();\n Link searchLink = this.objectFactory.createLink();\n searchLink.setHref(searchUri);\n searchLink.setRel(Relations.SEARCH);\n wiki.getLinks().add(searchLink);\n\n String queryUri = Utils.createURI(baseUri, WikiSearchQueryResource.class, wikiName).toString();\n Link queryLink = this.objectFactory.createLink();\n queryLink.setHref(queryUri);\n queryLink.setRel(Relations.QUERY);\n wiki.getLinks().add(queryLink);\n\n return wiki;\n }\n\n public Space toRestSpace(URI baseUri, String wikiName, List<String> spaces, Document home)\n {\n Space space = this.objectFactory.createSpace();\n space.setId(Utils.getSpaceId(wikiName, spaces));\n space.setWiki(wikiName);\n // the name of the space is the last element of the spaces list\n space.setName(spaces.get(spaces.size() - 1));\n if (home != null) {\n space.setHome(home.getPrefixedFullName());\n space.setXwikiRelativeUrl(home.getURL(\"view\"));\n space.setXwikiAbsoluteUrl(home.getExternalURL(\"view\"));\n }\n\n String pagesUri = Utils.createURI(baseUri, PagesResource.class, wikiName, spaces).toString();\n Link pagesLink = this.objectFactory.createLink();\n pagesLink.setHref(pagesUri);\n pagesLink.setRel(Relations.PAGES);\n space.getLinks().add(pagesLink);\n\n if (home != null) {\n String homeUri =\n Utils.createURI(baseUri, PageResource.class, wikiName, spaces, home.getDocumentReference().getName())\n .toString();\n Link homeLink = this.objectFactory.createLink();\n homeLink.setHref(homeUri);\n homeLink.setRel(Relations.HOME);\n space.getLinks().add(homeLink);\n }\n\n String searchUri = Utils.createURI(baseUri, SpaceSearchResource.class, wikiName, spaces).toString();\n Link searchLink = this.objectFactory.createLink();\n searchLink.setHref(searchUri);\n searchLink.setRel(Relations.SEARCH);\n space.getLinks().add(searchLink);\n\n return space;\n }\n\n public Translations toRestTranslations(URI baseUri, Document doc) throws XWikiException\n {\n Translations translations = this.objectFactory.createTranslations();\n Locale defaultLocale = getDefaultLocale(doc);\n translations.setDefault(defaultLocale.toString());\n\n List<Locale> locales = doc.getTranslationLocales();\n\n List<String> spaces = Utils.getSpacesFromSpaceId(doc.getSpace());\n\n // Add the default (original) page translation, if it makes sense.\n if (!locales.isEmpty() && !Locale.ROOT.equals(defaultLocale)) {\n Translation translation = this.objectFactory.createTranslation();\n translation.setLanguage(translations.getDefault());\n\n String pageTranslationUri = Utils\n .createURI(baseUri, PageResource.class, doc.getWiki(), spaces, doc.getDocumentReference().getName())\n .toString();\n Link pageTranslationLink = this.objectFactory.createLink();\n pageTranslationLink.setHref(pageTranslationUri);\n pageTranslationLink.setRel(Relations.PAGE);\n translation.getLinks().add(pageTranslationLink);\n\n String historyUri = Utils.createURI(baseUri, PageHistoryResource.class, doc.getWiki(), spaces,\n doc.getDocumentReference().getName()).toString();\n Link historyLink = this.objectFactory.createLink();\n historyLink.setHref(historyUri);\n historyLink.setRel(Relations.HISTORY);\n translation.getLinks().add(historyLink);\n\n translations.getTranslations().add(translation);\n }\n\n for (Locale locale : locales) {\n Translation translation = this.objectFactory.createTranslation();\n translation.setLanguage(locale.toString());\n\n String pageTranslationUri = Utils.createURI(baseUri, PageTranslationResource.class, doc.getWiki(), spaces,\n doc.getDocumentReference().getName(), locale).toString();\n Link pageTranslationLink = this.objectFactory.createLink();\n pageTranslationLink.setHref(pageTranslationUri);\n pageTranslationLink.setRel(Relations.PAGE);\n translation.getLinks().add(pageTranslationLink);\n\n String historyUri = Utils.createURI(baseUri, PageTranslationHistoryResource.class, doc.getWiki(), spaces,\n doc.getDocumentReference().getName(), locale).toString();\n Link historyLink = this.objectFactory.createLink();\n historyLink.setHref(historyUri);\n historyLink.setRel(Relations.HISTORY);\n translation.getLinks().add(historyLink);\n\n translations.getTranslations().add(translation);\n }\n\n return translations;\n }\n\n private Locale getDefaultLocale(Document document)\n {\n if (document.isTranslation()) {\n // The default locale field is not always set on document translations:\n //\n // * it is empty for translation pages created by the user because the save action doesn't set it and the\n // edit form doesn't include this field;\n // * it may be set for translation pages that are part of an XWiki extension because the XAR Maven plugin\n // used to build the extension has a rule to enforce it;\n //\n // So we should take the default locale from the original document.\n try {\n XWikiContext xcontext = this.xcontextProvider.get();\n return xcontext.getWiki().getDocument(document.getDocumentReference(), xcontext).getRealLocale();\n } catch (XWikiException e) {\n this.logger.warn(\"Failed to get the default locale from [{}]. Root cause is [{}].\",\n document.getDocumentReference(), ExceptionUtils.getRootCauseMessage(e));\n // Fall-back on the default locale specified on the translation page, which may not be accurate.\n return document.getDefaultLocale();\n }\n } else {\n return document.getRealLocale();\n }\n }\n\n /**\n * This method is used to fill the \"common part\" of a Page and a PageSummary.\n */\n private void toRestPageSummary(PageSummary pageSummary, URI baseUri, Document doc, boolean useVersion,\n Boolean withPrettyNames) throws XWikiException\n {\n XWikiContext xwikiContext = this.xcontextProvider.get();\n\n pageSummary.setWiki(doc.getWiki());\n pageSummary.setFullName(doc.getFullName());\n pageSummary.setId(doc.getPrefixedFullName());\n pageSummary.setSpace(doc.getSpace());\n pageSummary.setName(doc.getDocumentReference().getName());\n pageSummary.setTitle(doc.getDisplayTitle());\n pageSummary.setRawTitle(doc.getTitle());\n pageSummary.setXwikiRelativeUrl(doc.getURL(\"view\"));\n pageSummary.setXwikiAbsoluteUrl(doc.getExternalURL(\"view\"));\n pageSummary.setTranslations(toRestTranslations(baseUri, doc));\n pageSummary.setSyntax(doc.getSyntax().toIdString());\n pageSummary.setVersion(doc.getVersion());\n pageSummary.setAuthor(doc.getAuthor());\n if (withPrettyNames) {\n pageSummary.setAuthorName(xwikiContext.getWiki().getUserName(doc.getAuthor(), null, false, xwikiContext));\n }\n\n pageSummary.setParent(doc.getParent());\n DocumentReference parentReference = doc.getParentReference();\n boolean parentExist = parentReference != null && xwikiContext.getWiki().exists(parentReference, xwikiContext);\n // parentId must not be set if the parent document does not exist.\n if (parentExist) {\n pageSummary.setParentId(this.serializer.serialize(parentReference));\n } else {\n pageSummary.setParentId(\"\");\n }\n\n List<String> spaces = Utils.getSpacesFromSpaceId(doc.getSpace());\n String spaceUri = Utils.createURI(baseUri, SpaceResource.class, doc.getWiki(), spaces).toString();\n Link spaceLink = this.objectFactory.createLink();\n spaceLink.setHref(spaceUri);\n spaceLink.setRel(Relations.SPACE);\n pageSummary.getLinks().add(spaceLink);\n\n if (parentExist) {\n String parentUri = Utils.createURI(baseUri, PageResource.class,\n parentReference.getWikiReference().getName(), spaces, parentReference.getName()).toString();\n Link parentLink = this.objectFactory.createLink();\n parentLink.setHref(parentUri);\n parentLink.setRel(Relations.PARENT);\n pageSummary.getLinks().add(parentLink);\n }\n\n String historyUri = Utils.createURI(baseUri, PageHistoryResource.class, doc.getWiki(),\n Utils.getSpacesFromSpaceId(doc.getSpace()), doc.getDocumentReference().getName()).toString();\n Link historyLink = this.objectFactory.createLink();\n historyLink.setHref(historyUri);\n historyLink.setRel(Relations.HISTORY);\n pageSummary.getLinks().add(historyLink);\n\n if (!doc.getChildren().isEmpty()) {\n String pageChildrenUri = Utils.createURI(baseUri, PageChildrenResource.class, doc.getWiki(), spaces,\n doc.getDocumentReference().getName()).toString();\n Link pageChildrenLink = this.objectFactory.createLink();\n pageChildrenLink.setHref(pageChildrenUri);\n pageChildrenLink.setRel(Relations.CHILDREN);\n pageSummary.getLinks().add(pageChildrenLink);\n }\n\n if (!doc.getComments().isEmpty()) {\n String commentsUri;\n if (useVersion) {\n commentsUri = Utils.createURI(baseUri, CommentsVersionResource.class, doc.getWiki(), spaces,\n doc.getDocumentReference().getName(), doc.getVersion()).toString();\n } else {\n commentsUri = Utils.createURI(baseUri, CommentsResource.class, doc.getWiki(), spaces,\n doc.getDocumentReference().getName()).toString();\n }\n\n Link commentsLink = this.objectFactory.createLink();\n commentsLink.setHref(commentsUri);\n commentsLink.setRel(Relations.COMMENTS);\n pageSummary.getLinks().add(commentsLink);\n }\n\n if (!doc.getAttachmentList().isEmpty()) {\n String attachmentsUri;\n if (useVersion) {\n attachmentsUri = Utils.createURI(baseUri, AttachmentsAtPageVersionResource.class, doc.getWiki(), spaces,\n doc.getDocumentReference().getName(), doc.getVersion()).toString();\n } else {\n attachmentsUri = Utils.createURI(baseUri, AttachmentsResource.class, doc.getWiki(), spaces,\n doc.getDocumentReference().getName()).toString();\n }\n\n Link attachmentsLink = this.objectFactory.createLink();\n attachmentsLink.setHref(attachmentsUri);\n attachmentsLink.setRel(Relations.ATTACHMENTS);\n pageSummary.getLinks().add(attachmentsLink);\n }\n\n if (!doc.getxWikiObjects().keySet().isEmpty()) {\n String objectsUri;\n\n if (useVersion) {\n objectsUri = Utils.createURI(baseUri, ObjectsAtPageVersionResource.class, doc.getWiki(), spaces,\n doc.getDocumentReference().getName(), doc.getVersion()).toString();\n } else {\n objectsUri = Utils.createURI(baseUri, ObjectsResource.class, doc.getWiki(), spaces,\n doc.getDocumentReference().getName()).toString();\n }\n Link objectsLink = this.objectFactory.createLink();\n objectsLink.setHref(objectsUri);\n objectsLink.setRel(Relations.OBJECTS);\n pageSummary.getLinks().add(objectsLink);\n }\n\n com.xpn.xwiki.api.Object tagsObject = doc.getObject(\"XWiki.TagClass\", 0);\n if (tagsObject != null) {\n if (tagsObject.getProperty(\"tags\") != null) {\n String tagsUri = Utils.createURI(baseUri, PageTagsResource.class, doc.getWiki(), spaces,\n doc.getDocumentReference().getName()).toString();\n Link tagsLink = this.objectFactory.createLink();\n tagsLink.setHref(tagsUri);\n tagsLink.setRel(Relations.TAGS);\n pageSummary.getLinks().add(tagsLink);\n }\n }\n\n String syntaxesUri = Utils.createURI(baseUri, SyntaxesResource.class).toString();\n Link syntaxesLink = this.objectFactory.createLink();\n syntaxesLink.setHref(syntaxesUri);\n syntaxesLink.setRel(Relations.SYNTAXES);\n pageSummary.getLinks().add(syntaxesLink);\n }\n\n public PageSummary toRestPageSummary(URI baseUri, Document doc, Boolean withPrettyNames) throws XWikiException\n {\n PageSummary pageSummary = this.objectFactory.createPageSummary();\n toRestPageSummary(pageSummary, baseUri, doc, false, withPrettyNames);\n\n String pageUri = Utils.createURI(baseUri, PageResource.class, doc.getWiki(),\n Utils.getSpacesFromSpaceId(doc.getSpace()), doc.getDocumentReference().getName()).toString();\n Link pageLink = this.objectFactory.createLink();\n pageLink.setHref(pageUri);\n pageLink.setRel(Relations.PAGE);\n pageSummary.getLinks().add(pageLink);\n\n return pageSummary;\n }\n\n public Page toRestPage(URI baseUri, URI self, Document doc, boolean useVersion, Boolean withPrettyNames,\n Boolean withObjects, Boolean withXClass, Boolean withAttachments) throws XWikiException\n {\n Page page = this.objectFactory.createPage();\n toRestPageSummary(page, baseUri, doc, useVersion, withPrettyNames);\n\n XWikiContext xwikiContext = this.xcontextProvider.get();\n\n page.setMajorVersion(doc.getRCSVersion().at(0));\n page.setMinorVersion(doc.getRCSVersion().at(1));\n page.setHidden(doc.isHidden());\n page.setLanguage(doc.getLocale().toString());\n page.setCreator(doc.getCreator());\n if (withPrettyNames) {\n page.setCreatorName(xwikiContext.getWiki().getUserName(doc.getCreator(), null, false, xwikiContext));\n }\n\n Calendar calendar = Calendar.getInstance();\n calendar.setTime(doc.getCreationDate());\n page.setCreated(calendar);\n\n page.setModifier(doc.getContentAuthor());\n if (withPrettyNames) {\n page.setModifierName(xwikiContext.getWiki().getUserName(doc.getContentAuthor(), null, false, xwikiContext));\n }\n\n String originalAuthor = this.userReferenceSerializer.serialize(doc.getAuthors().getOriginalMetadataAuthor());\n page.setOriginalMetadataAuthor(originalAuthor);\n if (withPrettyNames) {\n page.setOriginalMetadataAuthorName(\n xwikiContext.getWiki().getUserName(originalAuthor, null, false, xwikiContext));\n }\n\n calendar = Calendar.getInstance();\n calendar.setTime(doc.getContentUpdateDate());\n page.setModified(calendar);\n\n page.setComment(doc.getComment());\n page.setContent(doc.getContent());\n page.setHierarchy(toRestHierarchy(doc.getDocumentReference(), withPrettyNames));\n\n if (self != null) {\n Link pageLink = this.objectFactory.createLink();\n pageLink.setHref(self.toString());\n pageLink.setRel(Relations.SELF);\n page.getLinks().add(pageLink);\n }\n\n com.xpn.xwiki.api.Class xwikiClass = doc.getxWikiClass();\n if (xwikiClass != null) {\n String classUri =\n Utils.createURI(baseUri, ClassResource.class, doc.getWiki(), xwikiClass.getName()).toString();\n Link classLink = this.objectFactory.createLink();\n classLink.setHref(classUri);\n classLink.setRel(Relations.CLASS);\n page.getLinks().add(classLink);\n }\n\n XWikiContext xcontext = xcontextProvider.get();\n\n // Add attachments\n if (withAttachments) {\n page.setAttachments(objectFactory.createAttachments());\n\n for (com.xpn.xwiki.api.Attachment attachment : doc.getAttachmentList()) {\n URL url = xcontext.getURLFactory().createAttachmentURL(attachment.getFilename(), doc.getSpace(),\n doc.getDocumentReference().getName(), \"download\", null, doc.getWiki(), xcontext);\n String attachmentXWikiAbsoluteUrl = url.toString();\n String attachmentXWikiRelativeUrl = xcontext.getURLFactory().getURL(url, xcontext);\n\n page.getAttachments().getAttachments().add(toRestAttachment(baseUri, attachment,\n attachmentXWikiRelativeUrl, attachmentXWikiAbsoluteUrl, withPrettyNames, false));\n }\n }\n\n // Add objects\n if (withObjects) {\n page.setObjects(objectFactory.createObjects());\n\n XWikiDocument xwikiDocument = xcontext.getWiki().getDocument(doc.getDocumentReference(), xcontext);\n\n for (List<BaseObject> objects : xwikiDocument.getXObjects().values()) {\n for (BaseObject object : objects) {\n // Deleting an object leads to a null entry in the list of objects.\n if (object != null) {\n page.getObjects().getObjectSummaries()\n .add(toRestObject(baseUri, doc, object, false, withPrettyNames));\n }\n }\n }\n }\n\n // Add xclass\n if (withXClass) {\n page.setClazz(toRestClass(baseUri, doc.getxWikiClass()));\n }\n\n return page;\n }\n\n public Class toRestClass(URI baseUri, com.xpn.xwiki.api.Class xwikiClass)\n {\n Class clazz = this.objectFactory.createClass();\n clazz.setId(xwikiClass.getName());\n clazz.setName(xwikiClass.getName());\n\n DocumentReference reference = xwikiClass.getReference();\n String wikiName = reference.getWikiReference().getName();\n\n for (java.lang.Object xwikiPropertyClassObject : xwikiClass.getProperties()) {\n PropertyClass xwikiPropertyClass = (PropertyClass) xwikiPropertyClassObject;\n\n Property property = this.objectFactory.createProperty();\n property.setName(xwikiPropertyClass.getName());\n property.setType(xwikiPropertyClass.getxWikiClass().getName());\n\n for (java.lang.Object xwikiPropertyObject : xwikiPropertyClass.getProperties()) {\n com.xpn.xwiki.api.Property xwikiProperty = (com.xpn.xwiki.api.Property) xwikiPropertyObject;\n java.lang.Object value = xwikiProperty.getValue();\n\n Attribute attribute = this.objectFactory.createAttribute();\n attribute.setName(xwikiProperty.getName());\n\n if (value != null) {\n attribute.setValue(value.toString());\n } else {\n attribute.setValue(\"\");\n }\n\n property.getAttributes().add(attribute);\n }\n\n String propertyUri = Utils.createURI(baseUri, ClassPropertyResource.class, wikiName, xwikiClass.getName(),\n xwikiPropertyClass.getName()).toString();\n Link propertyLink = this.objectFactory.createLink();\n propertyLink.setHref(propertyUri);\n propertyLink.setRel(Relations.SELF);\n property.getLinks().add(propertyLink);\n\n clazz.getProperties().add(property);\n }\n\n String classUri = Utils.createURI(baseUri, ClassResource.class, wikiName, xwikiClass.getName()).toString();\n Link classLink = this.objectFactory.createLink();\n classLink.setHref(classUri);\n classLink.setRel(Relations.SELF);\n clazz.getLinks().add(classLink);\n\n String propertiesUri =\n Utils.createURI(baseUri, ClassPropertiesResource.class, wikiName, xwikiClass.getName()).toString();\n Link propertyLink = this.objectFactory.createLink();\n propertyLink.setHref(propertiesUri);\n propertyLink.setRel(Relations.PROPERTIES);\n clazz.getLinks().add(propertyLink);\n\n String objectsUri =\n Utils.createURI(baseUri, AllObjectsForClassNameResource.class, wikiName, xwikiClass.getName()).toString();\n Link objectsLink = this.objectFactory.createLink();\n objectsLink.setHref(objectsUri);\n objectsLink.setRel(Relations.OBJECTS);\n clazz.getLinks().add(objectsLink);\n\n return clazz;\n }\n\n public Attachment toRestAttachment(URI baseUri, com.xpn.xwiki.api.Attachment xwikiAttachment,\n Boolean withPrettyNames, boolean versionURL)\n {\n XWikiContext xcontext = this.xcontextProvider.get();\n String relativeURL = xcontext.getWiki().getURL(xwikiAttachment.getReference(), xcontext);\n String absoluteURL = xcontext.getWiki().getExternalAttachmentURL(xwikiAttachment.getDocument().getFullName(),\n xwikiAttachment.getFilename(), xcontext);\n return toRestAttachment(baseUri, xwikiAttachment, relativeURL, absoluteURL, withPrettyNames, versionURL);\n }\n\n public Attachment toRestAttachment(URI baseUri, com.xpn.xwiki.api.Attachment xwikiAttachment,\n String xwikiRelativeUrl, String xwikiAbsoluteUrl, Boolean withPrettyNames, boolean versionURL)\n {\n Attachment attachment = this.objectFactory.createAttachment();\n\n DocumentReference documentReference = xwikiAttachment.getReference().getDocumentReference();\n attachment.setPageId(this.defaultEntityReferenceSerializer.serialize(documentReference));\n attachment.setPageVersion(xwikiAttachment.getDocument().getVersion());\n\n attachment.setId(this.defaultEntityReferenceSerializer.serialize(xwikiAttachment.getReference()));\n attachment.setName(xwikiAttachment.getFilename());\n attachment.setLongSize(xwikiAttachment.getLongSize());\n attachment.setSize((int) xwikiAttachment.getLongSize());\n attachment.setVersion(xwikiAttachment.getVersion());\n attachment.setMimeType(xwikiAttachment.getMimeType());\n attachment.setAuthor(xwikiAttachment.getAuthor());\n if (withPrettyNames) {\n XWikiContext xcontext = this.xcontextProvider.get();\n attachment\n .setAuthorName(xcontext.getWiki().getUserName(xwikiAttachment.getAuthor(), null, false, xcontext));\n }\n\n Calendar calendar = Calendar.getInstance();\n calendar.setTime(xwikiAttachment.getDate());\n attachment.setDate(calendar);\n\n attachment.setXwikiRelativeUrl(xwikiRelativeUrl);\n attachment.setXwikiAbsoluteUrl(xwikiAbsoluteUrl);\n attachment.setHierarchy(toRestHierarchy(xwikiAttachment.getReference(), withPrettyNames));\n\n String wiki = documentReference.getWikiReference().getName();\n List<String> spaces = Utils.getSpacesHierarchy(documentReference.getLastSpaceReference());\n\n String pageUri =\n Utils.createURI(baseUri, PageResource.class, wiki, spaces, documentReference.getName()).toString();\n Link pageLink = this.objectFactory.createLink();\n pageLink.setHref(pageUri);\n pageLink.setRel(Relations.PAGE);\n attachment.getLinks().add(pageLink);\n\n String attachmentUri;\n if (versionURL) {\n attachmentUri = Utils.createURI(baseUri, AttachmentVersionResource.class, wiki, spaces,\n documentReference.getName(), xwikiAttachment.getFilename(), xwikiAttachment.getVersion()).toString();\n } else {\n attachmentUri = Utils.createURI(baseUri, AttachmentResource.class, wiki, spaces,\n documentReference.getName(), xwikiAttachment.getFilename()).toString();\n }\n\n Link attachmentLink = this.objectFactory.createLink();\n attachmentLink.setHref(attachmentUri);\n attachmentLink.setRel(Relations.ATTACHMENT_DATA);\n attachment.getLinks().add(attachmentLink);\n\n Link attachmentMetadataLink = this.objectFactory.createLink();\n attachmentMetadataLink.setHref(Utils.createURI(baseUri, AttachmentMetadataResource.class, wiki, spaces,\n documentReference.getName(), xwikiAttachment.getFilename()).toString());\n attachmentMetadataLink.setRel(Relations.ATTACHMENT_METADATA);\n attachment.getLinks().add(attachmentMetadataLink);\n\n return attachment;\n }\n\n public Hierarchy toRestHierarchy(EntityReference targetEntityReference, Boolean withPrettyNames)\n {\n XWikiContext xcontext = this.xcontextProvider.get();\n XWiki xwiki = xcontext.getWiki();\n Hierarchy hierarchy = new Hierarchy();\n for (EntityReference entityReference : targetEntityReference.getReversedReferenceChain()) {\n HierarchyItem hierarchyItem = new HierarchyItem();\n hierarchyItem.setName(entityReference.getName());\n hierarchyItem.setLabel(entityReference.getName());\n hierarchyItem.setType(entityReference.getType().getLowerCase());\n hierarchyItem.setUrl(xwiki.getURL(entityReference, xcontext));\n if (withPrettyNames) {\n try {\n if (entityReference.getType() == EntityType.SPACE\n || entityReference.getType() == EntityType.DOCUMENT) {\n XWikiDocument document =\n xwiki.getDocument(entityReference, xcontext).getTranslatedDocument(xcontext);\n hierarchyItem.setLabel(document.getRenderedTitle(Syntax.PLAIN_1_0, xcontext));\n hierarchyItem.setUrl(xwiki.getURL(document.getDocumentReferenceWithLocale(), xcontext));\n } else if (entityReference.getType() == EntityType.WIKI) {\n WikiDescriptor wikiDescriptor = this.wikiDescriptorManager.getById(entityReference.getName());\n if (wikiDescriptor != null) {\n hierarchyItem.setLabel(wikiDescriptor.getPrettyName());\n }\n }\n } catch (Exception e) {\n this.logger.warn(\n \"Failed to get the pretty name of entity [{}]. Continue using the entity name. Root cause is [{}].\",\n entityReference, ExceptionUtils.getRootCauseMessage(e));\n }\n }\n hierarchy.withItems(hierarchyItem);\n }\n return hierarchy;\n }\n\n /**\n * Serializes the value of the given XObject property. {@link ComputedFieldClass} properties are not evaluated.\n *\n * @param property an XObject property\n * @return the String representation of the property value\n */\n private static String serializePropertyValue(PropertyInterface property)\n {\n if (property == null) {\n return \"\";\n }\n\n java.lang.Object value = ((BaseProperty) property).getValue();\n if (value instanceof List) {\n return StringUtils.join((List) value, \"|\");\n } else if (value != null) {\n return value.toString();\n } else {\n return \"\";\n }\n }\n\n /**\n * Serializes the value of the given XObject property. In case the property is an instance of\n * {@link ComputedFieldClass}, the serialized value is the computed property value.\n * \n * @param property an XObject property\n * @param propertyClass the PropertyClass of that XObject proprety\n * @param context the XWikiContext\n * @return the String representation of the property value\n */\n private String serializePropertyValue(PropertyInterface property,\n com.xpn.xwiki.objects.classes.PropertyClass propertyClass, XWikiContext context)\n {\n if (propertyClass instanceof ComputedFieldClass) {\n // TODO: the XWikiDocument needs to be explicitely set in the context, otherwise method\n // PropertyClass.renderInContext fires a null pointer exception: bug?\n XWikiDocument document = context.getDoc();\n try {\n context.setDoc(property.getObject().getOwnerDocument());\n ComputedFieldClass computedFieldClass = (ComputedFieldClass) propertyClass;\n return computedFieldClass.getComputedValue(propertyClass.getName(), \"\", property.getObject(), context);\n } catch (Exception e) {\n logger.error(\"Error while computing property value [{}] of [{}]\", propertyClass.getName(),\n property.getObject(), e);\n return serializePropertyValue(property);\n } finally {\n // Reset the context document to its original value, even if an exception is raised.\n context.setDoc(document);\n }\n } else {\n return serializePropertyValue(property);\n }\n }\n\n public JobRequest toRestJobRequest(Request request) throws XWikiRestException\n {\n JobRequest restJobRequest = this.objectFactory.createJobRequest();\n\n restJobRequest.setId(toRestJobId(request.getId()));\n restJobRequest.setInteractive(request.isInteractive());\n restJobRequest.setRemote(request.isRemote());\n restJobRequest.setVerbose(request.isVerbose());\n restJobRequest.setStatusSerialized(request.isStatusSerialized());\n restJobRequest.setStatusLogIsolated(request.isStatusLogIsolated());\n\n for (String key : request.getPropertyNames()) {\n restJobRequest.getProperties().add(toRestMapEntry(key, request.getProperty(key)));\n }\n\n return restJobRequest;\n }\n\n public JobId toRestJobId(List<String> id)\n {\n if (id == null) {\n return null;\n }\n\n JobId restJobId = this.objectFactory.createJobId();\n\n restJobId.withElements(id);\n\n return restJobId;\n }\n\n public MapEntry toRestMapEntry(String key, java.lang.Object value) throws XWikiRestException\n {\n MapEntry restMapEntry = this.objectFactory.createMapEntry();\n\n restMapEntry.setKey(key);\n try {\n restMapEntry.setValue(this.jaxbConverter.serializeAny(value));\n } catch (ParserConfigurationException e) {\n throw new XWikiRestException(\"Failed to serialize property [\" + key + \"] with value [\" + value + \"]\", e);\n }\n\n return restMapEntry;\n }\n\n public DefaultRequest toJobRequest(JobRequest restJobRequest)\n {\n DefaultRequest request = new DefaultRequest();\n\n if (restJobRequest.getId() != null) {\n request.setId(restJobRequest.getId().getElements());\n }\n\n request.setInteractive(restJobRequest.isInteractive());\n request.setVerbose(restJobRequest.isVerbose());\n request.setStatusSerialized(restJobRequest.isStatusSerialized());\n request.setStatusLogIsolated(restJobRequest.isStatusLogIsolated());\n\n for (MapEntry restEntry : restJobRequest.getProperties()) {\n request.setProperty(restEntry.getKey(), this.jaxbConverter.unserializeAny(restEntry.getValue()));\n }\n\n return request;\n }\n\n public JobStatus toRestJobStatus(org.xwiki.job.event.status.JobStatus jobStatus, URI self, boolean request,\n boolean progress, boolean log, String logFromLevel) throws XWikiRestException\n {\n JobStatus status = this.objectFactory.createJobStatus();\n status.setId(StringUtils.join(jobStatus.getRequest().getId(), \"/\"));\n status.setState(jobStatus.getState().name());\n if (jobStatus.getStartDate() != null) {\n Calendar calendarStartDate = Calendar.getInstance();\n calendarStartDate.setTime(jobStatus.getStartDate());\n status.setStartDate(calendarStartDate);\n }\n if (jobStatus.getEndDate() != null) {\n Calendar calendarEndDate = Calendar.getInstance();\n calendarEndDate.setTime(jobStatus.getEndDate());\n status.setEndDate(calendarEndDate);\n }\n if (jobStatus.getError() != null) {\n status.setErrorMessage(ExceptionUtils.getStackTrace(jobStatus.getError()));\n }\n\n // Request\n if (request) {\n status.setRequest(toRestJobRequest(jobStatus.getRequest()));\n }\n\n // Progress\n if (progress) {\n status.setProgress(toRestJobProgress(jobStatus.getProgress()));\n }\n\n // Log\n if (log) {\n try {\n status.setLog(toRestJobLog(jobStatus.getLogTail(), self, null, logFromLevel));\n } catch (IOException e) {\n this.logger.error(\"Failed to access the log of job {}\", jobStatus.getRequest().getId(), e);\n }\n }\n\n // Link\n if (self != null) {\n Link link = objectFactory.createLink();\n link.setHref(self.toString());\n link.setRel(Relations.SELF);\n status.getLinks().add(link);\n }\n\n // Log isolation\n status.setIsolated(jobStatus.isIsolated());\n // Status serialization\n status.setSerialized(jobStatus.isSerialized());\n\n return status;\n }\n\n public JobProgress toRestJobProgress(org.xwiki.job.event.status.JobProgress progress)\n {\n JobProgress restJobProgress = this.objectFactory.createJobProgress();\n\n restJobProgress.setOffset(progress.getOffset());\n restJobProgress.setCurrentLevelOffset(progress.getCurrentLevelOffset());\n\n // TODO: add support for steps\n\n return restJobProgress;\n }\n\n public JobLog toRestJobLog(LogTail logQueue, URI self, String level, String fromLevel) throws IOException\n {\n // Filter log\n Iterable<LogEvent> logs;\n if (level != null) {\n LogLevel logLevel = LogLevel.valueOf(level.toUpperCase());\n logs = logQueue.getLogEvents(logLevel).stream().filter(log -> log.getLevel() == logLevel)\n .collect(Collectors.toList());\n } else if (fromLevel != null) {\n logs = logQueue.getLogEvents(LogLevel.valueOf(fromLevel.toUpperCase()));\n } else {\n logs = logQueue;\n }\n\n return toRestJobLog(logs, self);\n }\n\n public JobLog toRestJobLog(Iterable<LogEvent> logs, URI self)\n {\n JobLog log = this.objectFactory.createJobLog();\n\n // Serialize log\n for (LogEvent logEvent : logs) {\n // TODO: add support for log arguments\n // TODO: add support for log Marker\n org.xwiki.rest.model.jaxb.LogEvent event = this.objectFactory.createLogEvent();\n event.setLevel(logEvent.getLevel().name());\n Calendar calendarDate = Calendar.getInstance();\n calendarDate.setTimeInMillis(logEvent.getTimeStamp());\n event.setDate(calendarDate);\n event.setFormattedMessage(logEvent.getFormattedMessage());\n log.getLogEvents().add(event);\n }\n\n // Set link\n if (self != null) {\n Link link = this.objectFactory.createLink();\n link.setHref(self.toString());\n link.setRel(Relations.SELF);\n log.getLinks().add(link);\n }\n\n return log;\n }\n\n /**\n * Check if the given property should be exposed via REST.\n * \n * @param restProperty the property to be read/written\n * @return true if the property is considered accessible\n */\n private boolean hasAccess(Property restProperty)\n {\n if (PASSWORD_TYPE.equals(restProperty.getType())) {\n return authorizationManagerProvider.get().hasAccess(Right.ADMIN, xcontextProvider.get().getWikiReference());\n }\n\n return true;\n }\n}\n\n", "input": "", "output": "【漏洞类型】信息泄露漏洞\n【严重性】Medium\n【漏洞摘要】原始代码在处理密码类型属性时,通过hasAccess方法检查是否允许访问,但没有对密码类型属性进行任何清理或遮蔽。这意味着即使管理员权限没有被正确验证,密码信息也可能被泄露。此外,代码在处理电子邮件属性时,没有进行任何特殊处理,电子邮件地址可能会被直接暴露。\n【关键代码模式】\n- hasAccess方法中的PASSWORD_TYPE检查\n- serializePropertyValue方法中的直接属性值返回\n- 未对电子邮件属性进行特殊处理\n【修复方法说明】修复方法通过引入cleanupBeforeMakingPublic方法,对密码类型属性和电子邮件属性进行适当的清理或遮蔽,以防止敏感信息泄露。同时,它检查了是否启用了电子邮件地址的遮蔽功能以及用户是否有权编辑包含该属性的文档。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nrelease-notes/opensearch-security.release-notes-2.2.0.0.md\n### 代码内容:\n## 2022-08-11 Version 2.2.0.0\n\nCompatible with OpenSearch 2.2.0\n\n### Test Fix\n\n* Abstract waitForInit to minimize duplication and improve test reliability ([#1935](https://github.com/opensearch-project/security/pull/1935))\n* Create a manually started workflow for bulk run of integration tests ([#1937](https://github.com/opensearch-project/security/pull/1937))\n\n### Maintenance\n\n* Update to Gradle 7.5 ([#1963](https://github.com/opensearch-project/security/pull/1963))\n* Increment version to 2.2.0.0 ([#1948](https://github.com/opensearch-project/security/pull/1948))\n* Force netty-transport-native-unix-common version ([#1945](https://github.com/opensearch-project/security/pull/1945))\n\n", "input": "", "output": "【漏洞类型】并发问题\n【严重性】Medium\n【漏洞摘要】原始代码中未对用户对象的roles、securityRoles和attributes集合进行同步操作,这可能导致在多线程环境下出现并发修改异常。这个问题的根本原因是使用了非线程安全的集合类型,而没有在多线程访问时进行同步。\n【关键代码模式】User类中使用了非线程安全的Set和Map(例如,HashSet和HashMap)来存储roles、securityRoles和attributes。\n【修复方法说明】修复方法通过使用Collections.synchronizedSet和Collections.synchronizedMap来对这些集合进行同步,从而避免了并发修改异常。此修复方法遵循了线程安全编程的最佳实践,即在多线程环境下使用线程安全的集合类型或对非线程安全的集合进行同步处理。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nhazelcast/src/main/java/com/hazelcast/internal/config/AbstractXmlConfigRootTagRecognizer.java\n### 代码内容:\npackage com.hazelcast.internal.config;\n\nimport com.hazelcast.config.ConfigRecognizer;\nimport com.hazelcast.config.ConfigStream;\nimport com.hazelcast.logging.ILogger;\nimport com.hazelcast.logging.Logger;\nimport org.xml.sax.Attributes;\nimport org.xml.sax.SAXException;\nimport org.xml.sax.SAXParseException;\nimport org.xml.sax.helpers.DefaultHandler;\n\nimport javax.xml.parsers.SAXParser;\nimport javax.xml.parsers.SAXParserFactory;\n\nimport static com.hazelcast.internal.util.StringUtil.equalsIgnoreCase;\nimport static java.util.Objects.requireNonNull;\n\n/**\n * Abstract {@link ConfigRecognizer} implementation that recognizes\n * Hazelcast XML configurations. The recognition is done by looking into\n * the provided configuration to check if the root node is the expected\n * one.\n * <p/>\n * This implementation uses a SAX parser. The parsing is aborted once the\n * root tag is processed.\n * <p/>\n * If the provided configuration is not a valid XML document, no exception\n * is thrown. Instead, the configuration is simply not recognized by this\n * implementation.\n * </p>\n * Note that this {@link ConfigRecognizer} doesn't validate the\n * configuration and doesn't look further into the provided configuration.\n */\npublic class AbstractXmlConfigRootTagRecognizer implements ConfigRecognizer {\n private final SAXParser saxParser;\n private final String expectedRootNode;\n private final ILogger logger = Logger.getLogger(AbstractXmlConfigRootTagRecognizer.class);\n\n public AbstractXmlConfigRootTagRecognizer(String expectedRootNode) throws Exception {\n this.expectedRootNode = expectedRootNode;\n SAXParserFactory factory = SAXParserFactory.newInstance();\n saxParser = factory.newSAXParser();\n }\n\n @Override\n public boolean isRecognized(ConfigStream configStream) throws Exception {\n MemberHandler memberHandler = new MemberHandler(expectedRootNode);\n try {\n saxParser.parse(configStream, memberHandler);\n } catch (TerminateParseException ex) {\n // expected, always\n } catch (SAXParseException ex) {\n // thrown if the provided XML is not a valid XML\n handleParseException(ex);\n return false;\n } catch (Exception ex) {\n // thrown if any unexpected exception is encountered\n handleUnexpectedException(ex);\n throw ex;\n }\n return memberHandler.isMemberXml;\n }\n\n private void handleParseException(SAXParseException ex) {\n if (logger.isFineEnabled()) {\n logger.fine(\"An exception is encountered while processing the provided XML configuration\", ex);\n }\n }\n\n private void handleUnexpectedException(Exception ex) {\n if (logger.isFineEnabled()) {\n logger.fine(\"An unexpected exception is encountered while processing the provided XML configuration\", ex);\n }\n }\n\n private static final class MemberHandler extends DefaultHandler {\n private final String expectedRootNode;\n private boolean isMemberXml;\n\n private MemberHandler(String expectedRootNode) {\n this.expectedRootNode = requireNonNull(expectedRootNode);\n }\n\n @Override\n public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {\n if (equalsIgnoreCase(expectedRootNode, qName)) {\n isMemberXml = true;\n }\n throw new TerminateParseException();\n }\n }\n\n private static final class TerminateParseException extends SAXException {\n }\n}\n\n## 内容2\n### 文件路径:\nhazelcast/src/main/java/com/hazelcast/internal/util/XmlUtil.java\n### 代码内容:\npackage com.hazelcast.internal.util;\n\nimport static com.hazelcast.internal.nio.IOUtil.closeResource;\n\nimport java.io.StringReader;\nimport java.io.StringWriter;\n\nimport javax.annotation.Nullable;\nimport javax.xml.XMLConstants;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.parsers.ParserConfigurationException;\nimport javax.xml.transform.ErrorListener;\nimport javax.xml.transform.OutputKeys;\nimport javax.xml.transform.Source;\nimport javax.xml.transform.Transformer;\nimport javax.xml.transform.TransformerException;\nimport javax.xml.transform.TransformerFactory;\nimport javax.xml.transform.stream.StreamResult;\nimport javax.xml.transform.stream.StreamSource;\nimport javax.xml.validation.SchemaFactory;\n\nimport org.xml.sax.SAXException;\n\nimport com.hazelcast.logging.ILogger;\nimport com.hazelcast.logging.Logger;\n\n/**\n * Utility class for XML processing.\n */\npublic final class XmlUtil {\n\n /**\n * System property name which allows ignoring failures during enabling the XML External Entity protection.\n * This property should only be used as a last\n * resort. Hazelcast uses the XXE protection by setting properties {@link XMLConstants#ACCESS_EXTERNAL_DTD} and\n * {@link XMLConstants#ACCESS_EXTERNAL_SCHEMA}. These properties are supported in modern XML processors (JAXP 1.5+, Java\n * 8+). Old JAXP implementations on the classpath (e.g. Xerces, Xalan) may miss the support and they throw exception\n * during enabling the XXE protection. Setting this system property to true suppresses/ignores such Exceptions.\n */\n public static final String SYSTEM_PROPERTY_IGNORE_XXE_PROTECTION_FAILURES = \"hazelcast.ignoreXxeProtectionFailures\";\n\n private static final ILogger LOGGER = Logger.getLogger(XmlUtil.class);\n\n private XmlUtil() {\n }\n\n /**\n * Returns namespace aware instance of {@link DocumentBuilderFactory} with XXE protection enabled.\n *\n * @throws ParserConfigurationException enabling XXE protection fail\n */\n public static DocumentBuilderFactory getNsAwareDocumentBuilderFactory() throws ParserConfigurationException {\n DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();\n dbf.setNamespaceAware(true);\n setFeature(dbf, \"http://apache.org/xml/features/disallow-doctype-decl\");\n return dbf;\n }\n\n /**\n * Returns {@link TransformerFactory} with XXE protection enabled.\n */\n public static TransformerFactory getTransformerFactory() {\n TransformerFactory transformerFactory = TransformerFactory.newInstance();\n setAttribute(transformerFactory, XMLConstants.ACCESS_EXTERNAL_DTD);\n setAttribute(transformerFactory, XMLConstants.ACCESS_EXTERNAL_STYLESHEET);\n return transformerFactory;\n }\n\n /**\n * Returns {@link SchemaFactory} with XXE protection enabled.\n */\n public static SchemaFactory getSchemaFactory() throws SAXException {\n SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);\n setProperty(schemaFactory, XMLConstants.ACCESS_EXTERNAL_SCHEMA);\n setProperty(schemaFactory, XMLConstants.ACCESS_EXTERNAL_DTD);\n return schemaFactory;\n }\n\n /**\n * Formats given XML String with the given indentation used. If the {@code input} XML string is {@code null}, or\n * {@code indent} parameter is negative, or XML transformation fails, then the original value is returned unchanged. The\n * {@link IllegalArgumentException} is thrown when {@code indent==0}.\n *\n * @param input the XML String\n * @param indent indentation (number of spaces used for one indentation level)\n * @return formatted XML String or the original String if the formatting fails.\n * @throws IllegalArgumentException when indentation is equal to zero\n */\n @SuppressWarnings(\"checkstyle:NPathComplexity\")\n public static String format(@Nullable String input, int indent) throws IllegalArgumentException {\n if (input == null || indent < 0) {\n return input;\n }\n if (indent == 0) {\n throw new IllegalArgumentException(\"Indentation must not be 0.\");\n }\n StreamResult xmlOutput = null;\n try {\n Source xmlInput = new StreamSource(new StringReader(input));\n xmlOutput = new StreamResult(new StringWriter());\n TransformerFactory transformerFactory = getTransformerFactory();\n /*\n * Older versions of Xalan still use this method of setting indent values.\n * Attempt to make this work but don't completely fail if it's a problem.\n */\n try {\n transformerFactory.setAttribute(\"indent-number\", indent);\n } catch (IllegalArgumentException e) {\n if (LOGGER.isFinestEnabled()) {\n LOGGER.finest(\"Failed to set indent-number attribute; cause: \" + e.getMessage());\n }\n }\n Transformer transformer = transformerFactory.newTransformer();\n // workaround IBM Java behavior - the silent ignorance of issues during the transformation.\n transformer.setErrorListener(ThrowingErrorListener.INSTANCE);\n transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, \"yes\");\n transformer.setOutputProperty(OutputKeys.ENCODING, \"UTF-8\");\n transformer.setOutputProperty(OutputKeys.INDENT, \"yes\");\n /*\n * Newer versions of Xalan will look for a fully-qualified output property in order to specify amount of\n * indentation to use. Attempt to make this work as well but again don't completely fail if it's a problem.\n */\n try {\n transformer.setOutputProperty(\"{http://xml.apache.org/xslt}indent-amount\", Integer.toString(indent));\n } catch (IllegalArgumentException e) {\n if (LOGGER.isFinestEnabled()) {\n LOGGER.finest(\"Failed to set indent-amount property; cause: \" + e.getMessage());\n }\n }\n transformer.transform(xmlInput, xmlOutput);\n return xmlOutput.getWriter().toString();\n } catch (Exception e) {\n LOGGER.warning(e);\n return input;\n } finally {\n if (xmlOutput != null) {\n closeResource(xmlOutput.getWriter());\n }\n }\n }\n\n /**\n * Returns ErrorListener implementation which just throws the original error.\n */\n public ErrorListener getErrorListener() {\n return ThrowingErrorListener.INSTANCE;\n }\n\n static void setAttribute(TransformerFactory transformerFactory, String attributeName) {\n try {\n transformerFactory.setAttribute(attributeName, \"\");\n } catch (IllegalArgumentException iae) {\n if (Boolean.getBoolean(SYSTEM_PROPERTY_IGNORE_XXE_PROTECTION_FAILURES)) {\n LOGGER.warning(\"Enabling XXE protection failed. The attribute \" + attributeName\n + \" is not supported by the TransformerFactory. The \" + SYSTEM_PROPERTY_IGNORE_XXE_PROTECTION_FAILURES\n + \" system property is used so the XML processing continues in the UNSECURE mode\"\n + \" with XXE protection disabled!!!\");\n } else {\n LOGGER.severe(\"Enabling XXE protection failed. The attribute \" + attributeName\n + \" is not supported by the TransformerFactory. This usually mean an outdated XML processor\"\n + \" is present on the classpath (e.g. Xerces, Xalan). If you are not able to resolve the issue by\"\n + \" fixing the classpath, the \" + SYSTEM_PROPERTY_IGNORE_XXE_PROTECTION_FAILURES\n + \" system property can be used to disable XML External Entity protections.\"\n + \" We don't recommend disabling the XXE as such the XML processor configuration is unsecure!!!\", iae);\n throw iae;\n }\n }\n }\n\n static void setFeature(DocumentBuilderFactory dbf, String featureName) throws ParserConfigurationException {\n try {\n dbf.setFeature(featureName, true);\n } catch (ParserConfigurationException e) {\n if (Boolean.getBoolean(SYSTEM_PROPERTY_IGNORE_XXE_PROTECTION_FAILURES)) {\n LOGGER.warning(\"Enabling XXE protection failed. The feature \" + featureName\n + \" is not supported by the DocumentBuilderFactory. The \" + SYSTEM_PROPERTY_IGNORE_XXE_PROTECTION_FAILURES\n + \" system property is used so the XML processing continues in the UNSECURE mode\"\n + \" with XXE protection disabled!!!\");\n } else {\n LOGGER.severe(\"Enabling XXE protection failed. The feature \" + featureName\n + \" is not supported by the DocumentBuilderFactory. This usually mean an outdated XML processor\"\n + \" is present on the classpath (e.g. Xerces, Xalan). If you are not able to resolve the issue by\"\n + \" fixing the classpath, the \" + SYSTEM_PROPERTY_IGNORE_XXE_PROTECTION_FAILURES\n + \" system property can be used to disable XML External Entity protections.\"\n + \" We don't recommend disabling the XXE as such the XML processor configuration is unsecure!!!\", e);\n throw e;\n }\n }\n }\n\n static void setProperty(SchemaFactory schemaFactory, String propertyName) throws SAXException {\n try {\n schemaFactory.setProperty(propertyName, \"\");\n } catch (SAXException e) {\n if (Boolean.getBoolean(SYSTEM_PROPERTY_IGNORE_XXE_PROTECTION_FAILURES)) {\n LOGGER.warning(\"Enabling XXE protection failed. The property \" + propertyName\n + \" is not supported by the SchemaFactory. The \" + SYSTEM_PROPERTY_IGNORE_XXE_PROTECTION_FAILURES\n + \" system property is used so the XML processing continues in the UNSECURE mode\"\n + \" with XXE protection disabled!!!\");\n } else {\n LOGGER.severe(\"Enabling XXE protection failed. The property \" + propertyName\n + \" is not supported by the SchemaFactory. This usually mean an outdated XML processor\"\n + \" is present on the classpath (e.g. Xerces, Xalan). If you are not able to resolve the issue by\"\n + \" fixing the classpath, the \" + SYSTEM_PROPERTY_IGNORE_XXE_PROTECTION_FAILURES\n + \" system property can be used to disable XML External Entity protections.\"\n + \" We don't recommend disabling the XXE as such the XML processor configuration is unsecure!!!\", e);\n throw e;\n }\n }\n }\n\n /**\n * ErrorListener implementation which just throws the error. It workarounds IBM Java default behaviour when\n * {@link Transformer#transform(Source, javax.xml.transform.Result)} finishes without problems even if an exception is\n * thrown within the call. If an error happens, we want to know about it and handle it properly.\n */\n static final class ThrowingErrorListener implements ErrorListener {\n public static final ThrowingErrorListener INSTANCE = new ThrowingErrorListener();\n\n private ThrowingErrorListener() {\n }\n\n @Override\n public void warning(TransformerException exception) throws TransformerException {\n throw exception;\n }\n\n @Override\n public void fatalError(TransformerException exception) throws TransformerException {\n throw exception;\n }\n\n @Override\n public void error(TransformerException exception) throws TransformerException {\n throw exception;\n }\n }\n\n}\n\n", "input": "", "output": "【漏洞类型】XML外部实体(XXE)攻击\n【严重性】High\n【漏洞摘要】原始代码在创建SAXParserFactory时没有启用XML外部实体保护的特性,这可能导致XXE攻击。具体表现为SAXParserFactory.newInstance()方法的调用,没有设置disallow-doctype-decl特性。\n【关键代码模式】SAXParserFactory.newInstance()、setFeature()、XMLConstants.ACCESS_EXTERNAL_DTD\n【修复方法说明】修复方法通过在XmlUtil中添加getSAXParserFactory()方法,并在其中启用disallow-doctype-decl特性,解决了XXE攻击的问题。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\njadx-core/src/main/java/jadx/core/export/ExportGradleProject.java\n### 代码内容:\npackage jadx.core.export;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.StringReader;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.NodeList;\nimport org.xml.sax.InputSource;\n\nimport jadx.api.ResourceFile;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.core.xmlgen.ResContainer;\n\npublic class ExportGradleProject {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ExportGradleProject.class);\n\n\tprivate static final Set<String> IGNORE_CLS_NAMES = new HashSet<>(Arrays.asList(\n\t\t\t\"R\",\n\t\t\t\"BuildConfig\"));\n\n\tprivate final RootNode root;\n\tprivate final File projectDir;\n\tprivate final File appDir;\n\tprivate final File srcOutDir;\n\tprivate final File resOutDir;\n\tprivate final ApplicationParams applicationParams;\n\n\tpublic ExportGradleProject(RootNode root, File projectDir, ResourceFile androidManifest, ResContainer appStrings) {\n\t\tthis.root = root;\n\t\tthis.projectDir = projectDir;\n\t\tthis.appDir = new File(projectDir, \"app\");\n\t\tthis.srcOutDir = new File(appDir, \"src/main/java\");\n\t\tthis.resOutDir = new File(appDir, \"src/main\");\n\t\tthis.applicationParams = getApplicationParams(\n\t\t\t\tparseAndroidManifest(androidManifest),\n\t\t\t\tparseAppStrings(appStrings));\n\t}\n\n\tpublic void init() {\n\t\ttry {\n\t\t\tFileUtils.makeDirs(srcOutDir);\n\t\t\tFileUtils.makeDirs(resOutDir);\n\t\t\tsaveProjectBuildGradle();\n\t\t\tsaveApplicationBuildGradle();\n\t\t\tsaveSettingsGradle();\n\t\t\tskipGeneratedClasses();\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Gradle export failed\", e);\n\t\t}\n\t}\n\n\tprivate void saveProjectBuildGradle() throws IOException {\n\t\tTemplateFile tmpl = TemplateFile.fromResources(\"/export/build.gradle.tmpl\");\n\t\ttmpl.save(new File(projectDir, \"build.gradle\"));\n\t}\n\n\tprivate void saveSettingsGradle() throws IOException {\n\t\tTemplateFile tmpl = TemplateFile.fromResources(\"/export/settings.gradle.tmpl\");\n\n\t\ttmpl.add(\"applicationName\", applicationParams.getApplicationName());\n\t\ttmpl.save(new File(projectDir, \"settings.gradle\"));\n\t}\n\n\tprivate void saveApplicationBuildGradle() throws IOException {\n\t\tTemplateFile tmpl = TemplateFile.fromResources(\"/export/app.build.gradle.tmpl\");\n\t\tString appPackage = root.getAppPackage();\n\n\t\tif (appPackage == null) {\n\t\t\tappPackage = \"UNKNOWN\";\n\t\t}\n\n\t\ttmpl.add(\"applicationId\", appPackage);\n\t\ttmpl.add(\"minSdkVersion\", applicationParams.getMinSdkVersion());\n\t\ttmpl.add(\"targetSdkVersion\", applicationParams.getTargetSdkVersion());\n\t\ttmpl.add(\"versionCode\", applicationParams.getVersionCode());\n\t\ttmpl.add(\"versionName\", applicationParams.getVersionName());\n\t\ttmpl.save(new File(appDir, \"build.gradle\"));\n\t}\n\n\tprivate void skipGeneratedClasses() {\n\t\tfor (ClassNode cls : root.getClasses()) {\n\t\t\tString shortName = cls.getClassInfo().getShortName();\n\t\t\tif (IGNORE_CLS_NAMES.contains(shortName)) {\n\t\t\t\tcls.add(AFlag.DONT_GENERATE);\n\t\t\t\tLOG.debug(\"Skip class: {}\", cls);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate ApplicationParams getApplicationParams(Document androidManifest, Document appStrings) {\n\t\tElement manifest = (Element) androidManifest.getElementsByTagName(\"manifest\").item(0);\n\t\tElement usesSdk = (Element) androidManifest.getElementsByTagName(\"uses-sdk\").item(0);\n\t\tElement application = (Element) androidManifest.getElementsByTagName(\"application\").item(0);\n\n\t\tInteger versionCode = Integer.valueOf(manifest.getAttribute(\"android:versionCode\"));\n\t\tString versionName = manifest.getAttribute(\"android:versionName\");\n\t\tInteger minSdk = Integer.valueOf(usesSdk.getAttribute(\"android:minSdkVersion\"));\n\t\tInteger targetSdk = Integer.valueOf(usesSdk.getAttribute(\"android:targetSdkVersion\"));\n\t\tString appName = \"UNKNOWN\";\n\n\t\tif (application.hasAttribute(\"android:label\")) {\n\t\t\tString appLabelName = application.getAttribute(\"android:label\");\n\t\t\tif (appLabelName.startsWith(\"@string\")) {\n\t\t\t\tappLabelName = appLabelName.split(\"/\")[1];\n\t\t\t\tNodeList strings = appStrings.getElementsByTagName(\"string\");\n\n\t\t\t\tfor (int i = 0; i < strings.getLength(); i++) {\n\t\t\t\t\tString stringName = strings.item(i)\n\t\t\t\t\t\t\t.getAttributes()\n\t\t\t\t\t\t\t.getNamedItem(\"name\")\n\t\t\t\t\t\t\t.getNodeValue();\n\n\t\t\t\t\tif (stringName.equals(appLabelName)) {\n\t\t\t\t\t\tappName = strings.item(i).getTextContent();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tappName = appLabelName;\n\t\t\t}\n\t\t}\n\t\treturn new ApplicationParams(appName, minSdk, targetSdk, versionCode, versionName);\n\t}\n\n\tprivate Document parseXml(String xmlContent) {\n\t\ttry {\n\t\t\tDocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();\n\t\t\tDocument document = builder.parse(new InputSource(new StringReader(xmlContent)));\n\n\t\t\tdocument.getDocumentElement().normalize();\n\n\t\t\treturn document;\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Can not parse xml content\", e);\n\t\t}\n\t}\n\n\tprivate Document parseAppStrings(ResContainer appStrings) {\n\t\tString content = appStrings.getText().getCodeStr();\n\n\t\treturn parseXml(content);\n\t}\n\n\tprivate Document parseAndroidManifest(ResourceFile androidManifest) {\n\t\tString content = androidManifest.loadContent().getText().getCodeStr();\n\n\t\treturn parseXml(content);\n\t}\n\n\tpublic File getSrcOutDir() {\n\t\treturn srcOutDir;\n\t}\n\n\tpublic File getResOutDir() {\n\t\treturn resOutDir;\n\t}\n}\n\n", "input": "", "output": "【漏洞类型】XML外部实体(XXE)注入漏洞\n【严重性】High\n【漏洞摘要】这是一个XML外部实体(XXE)注入漏洞。在parseXml方法中,使用了DocumentBuilderFactory.newInstance().newDocumentBuilder()来解析XML内容。如果攻击者可以控制xmlContent的输入,他们可以利用XXE注入攻击来执行任意文件读取、网络请求等操作,从而可能导致数据泄露、系统资源耗尽等问题。\n【关键代码模式】DocumentBuilderFactory.newInstance().newDocumentBuilder(); parseXml方法\n【修复方法说明】修复方法通过使用XmlSecurity.getSecureDbf().newDocumentBuilder()来创建DocumentBuilder实例,从而禁用了XML外部实体的解析,解决了XXE注入漏洞。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/DBManager.java\n### 代码内容:\nimport utils.AccountType;\nimport utils.Pair;\n\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.ResultSet;\nimport java.sql.Statement;\nimport java.util.ArrayList;\n\npublic class DBManager {\n Connection c;\n Statement stmt;\n\n public DBManager() {\n try {\n Class.forName(\"org.postgresql.Driver\");\n c = DriverManager\n .getConnection(\"jdbc:postgresql://localhost:5432/School register\",\n \"postgres\", \"kamil\");\n// System.out.println(\"Opened database successfully\");\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n }\n\n public ArrayList<String> getStudentGrades(String studentID, int subjectID) {\n ArrayList<String> grades = new ArrayList<String>();\n try {\n stmt = c.createStatement();\n ResultSet rs = stmt.executeQuery(\"SELECT wartosc,tematyka,nazwa from oceny_uczniow join rodzaje_aktywnosci ra on id_aktywnosci=ra.id where id_ucznia='\" + studentID + \"' and id_przedmiotu=\" + subjectID + \";\");\n while (rs.next()) {\n int value = rs.getInt(\"wartosc\");\n String topic = rs.getString(\"tematyka\");\n String name = rs.getString(\"nazwa\");\n// System.out.println(topic + \" \" + name + \" \" + value);\n grades.add(topic + \" \" + name + \" \" + value);\n }\n// System.out.println(\"success\");\n rs.close();\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n return grades;\n }\n\n public ArrayList<String> getStudentAbsences(String studentID, String fromData, String toData) {\n ArrayList<String> absences = new ArrayList<String>();\n try {\n stmt = c.createStatement();\n ResultSet rs = stmt.executeQuery(\"SELECT pl.data,p.nr_lekcji from nieobecnosci n join przeprowadzone_lekcje pl on n.id_lekcji=pl.id join plan_lekcji p on pl.id_lekcji=p.id where id_ucznia='\" + studentID + \"' and data>=to_date('\" + fromData + \"', 'DD.MM.YYYY') and data<=to_date('\" + toData + \"', 'DD.MM.YYYY');\");\n while (rs.next()) {\n String date = rs.getDate(\"data\").toString();\n int lesson = rs.getInt(\"nr_lekcji\");\n// System.out.println(date + \" lekcja nr: \" + lesson);\n absences.add(date + \" lekcja nr: \" + lesson);\n }\n// System.out.println(\"success\");\n rs.close();\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n return absences;\n }\n\n public ArrayList<String> getStudentNotes(String studentID, String fromData, String toData) {\n ArrayList<String> notes = new ArrayList<String>();\n try {\n stmt = c.createStatement();\n ResultSet rs = stmt.executeQuery(\"SELECT opis,data_wystawienia,czy_pozytywna from uwagi where id_ucznia='\" + studentID + \"' and data_wystawienia>=to_date('\" + fromData + \"', 'DD.MM.YYYY')\" + \" and data_wystawienia<=to_date('\" + toData + \"', 'DD.MM.YYYY');\");\n while (rs.next()) {\n String description = rs.getString(\"opis\");\n String date = rs.getDate(\"data_wystawienia\").toString();\n boolean positive = rs.getBoolean(\"czy_pozytywna\");\n// System.out.println(date + \" \" + positive + \": \" + description);\n notes.add(date + \" \" + positive + \": \" + description);\n }\n// System.out.println(\"success\");\n rs.close();\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n return notes;\n }\n\n public ArrayList<Pair<Integer,String> > getStudentSubjects(String studentID) {\n ArrayList<Pair<Integer,String> > subjects = new ArrayList<Pair<Integer,String> >();\n try {\n stmt = c.createStatement();\n ResultSet rs = stmt.executeQuery(\"SELECT p.id,nazwa from przedmioty p join klasy k on p.id_klasy=k.id join uczniowie u on u.id_klasy=k.id where pesel='\" + studentID + \"';\");\n while (rs.next()) {\n Pair<Integer,String> pair = new Pair<Integer, String>(rs.getInt(\"id\"),rs.getString(\"nazwa\"));\n\n// System.out.println(pair.getX()+\" \"+pair.getY());\n subjects.add(pair);\n }\n// System.out.println(\"success\");\n rs.close();\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n return subjects;\n }\n\n public ArrayList<Pair<Integer,String> > getTeacherSubjects(int teacherID) {\n ArrayList<Pair<Integer,String> > subjects = new ArrayList<Pair<Integer,String> >();\n try {\n stmt = c.createStatement();\n ResultSet rs = stmt.executeQuery(\"SELECT p.id,nazwa,oddzial,rok_rozpoczecia from przedmioty p join klasy k on p.id_klasy=k.id where aktywny=true and id_prowadzacego='\" + teacherID + \"';\");\n while (rs.next()) {\n Pair<Integer,String> pair = new Pair<Integer, String>(rs.getInt(\"id\"),rs.getString(\"nazwa\") + \" klasa: \" + rs.getString(\"oddzial\") + \" \" + rs.getInt(\"rok_rozpoczecia\"));\n String name = rs.getString(\"nazwa\");\n String section = rs.getString(\"oddzial\");\n int startYear = rs.getInt(\"rok_rozpoczecia\");\n// System.out.println(name + \" klasa \" + startYear + section);\n subjects.add(pair);\n }\n// System.out.println(\"success\");\n rs.close();\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n return subjects;\n }\n\n public ArrayList<Pair<String,String> > getSubjectStudents(int subjectID) {\n ArrayList<Pair<String,String> > students = new ArrayList<Pair<String,String> >();\n try {\n stmt = c.createStatement();\n ResultSet rs = stmt.executeQuery(\"SELECT imie,nazwisko,pesel from przedmioty p join klasy k on p.id_klasy=k.id join uczniowie u on u.id_klasy=k.id where p.id='\" + subjectID + \"';\");\n while (rs.next()) {\n String name = rs.getString(\"imie\");\n String lastname = rs.getString(\"nazwisko\");\n String pesel = rs.getString(\"pesel\");\n Pair<String,String> pair = new Pair<String, String>(rs.getString(\"pesel\"),rs.getString(\"imie\")+ \" \" + rs.getString(\"nazwisko\"));\n// System.out.println(name + \" \" + lastname + \" \" + pesel);\n students.add(pair);\n }\n// System.out.println(\"success\");\n rs.close();\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n return students;\n }\n\n public void addStudentGrade(int subjectID, String studentID, int gradeValue, int activityID, String topic) {\n try {\n stmt = c.createStatement();\n stmt.executeUpdate(\"INSERT INTO oceny_uczniow(id_przedmiotu,id_ucznia,wartosc,id_aktywnosci,tematyka) values(\" + subjectID + \",\" + studentID + \",\" + gradeValue + \",\" + activityID + \",'\" + topic + \"');\");\n// System.out.println(\"success\");\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n }\n\n public void addStudentNote(String studentID, int teacherID, String note, boolean isPositive, String date) {\n try {\n stmt = c.createStatement();\n stmt.executeUpdate(\"INSERT INTO uwagi(id_ucznia,id_nauczyciela,opis,czy_pozytywna,data_wystawienia) values('\" + studentID + \"',\" + teacherID + \",'\" + note + \"',\" + isPositive + \",to_date('\" + date + \"', 'DD.MM.YYYY'));\");\n// System.out.println(\"success\");\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n }\n\n public void addStudentAbsence(String studentID, int lessonID) {\n try {\n stmt = c.createStatement();\n stmt.executeUpdate(\"INSERT INTO nieobecnosci(id_ucznia,id_lekcji) values('\" + studentID + \"',\" + lessonID + \");\");\n// System.out.println(\"success\");\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n }\n\n public int addCompletedLesson(String data, int teacherID, int lessonID, String topic) {\n int i=-1;\n try {\n stmt = c.createStatement();\n stmt.executeUpdate(\"INSERT INTO przeprowadzone_lekcje(data,id_prowadzacego,id_lekcji,temat_zajec) values(to_date('\" + data + \"', 'DD.MM.YYYY'),\" + teacherID + \",\" + lessonID + \",'\" + topic + \"');\");\n// System.out.println(\"success\");\n ResultSet rs = stmt.executeQuery(\"select lastval();\");\n rs.next();\n i=rs.getInt(1);\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n return i;\n }\n\n public void addStudent(String name, String lastname, String pesel, int phoneNumber, int classID) {\n try {\n stmt = c.createStatement();\n stmt.executeUpdate(\"INSERT INTO uczniowie(imie,nazwisko,pesel,telefon_do_rodzica,id_klasy) values('\" + name + \"','\" + lastname + \"','\" + pesel + \"',\" + phoneNumber + \",\" + classID + \");\");\n// System.out.println(\"success\");\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n }\n\n public void addclass(String section, int startYear, int tutorID) {\n try {\n stmt = c.createStatement();\n stmt.executeUpdate(\"INSERT INTO klasy(oddzial,rok_rozpoczecia,id_wychowawcy) values('\" + section + \"',\" + startYear + \",\" + tutorID + \");\");\n// System.out.println(\"success\");\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n }\n\n public void addTeacher(String name, String lastname) {\n try {\n stmt = c.createStatement();\n stmt.executeUpdate(\"INSERT INTO nauczyciele(imie,nazwisko) values('\" + name + \"','\" + lastname + \"');\");\n// System.out.println(\"success\");\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n }\n\n public void addSubject(String name, int classID, int teacherID) {\n try {\n stmt = c.createStatement();\n stmt.executeUpdate(\"INSERT INTO przedmioty(nazwa,id_klasy,id_prowadzacego) values('\" + name + \"',\" + classID + \",\" + teacherID + \");\");\n// System.out.println(\"success\");\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n }\n\n public void addScheduleLesson(int subjectID, int lessonNumber, int weekday) {\n try {\n stmt = c.createStatement();\n stmt.executeUpdate(\"INSERT INTO plan_lekcji(id_przedmiotu,nr_lekcji,dzien_tygodnia) values(\" + subjectID + \",\" + lessonNumber + \",\" + weekday + \");\");\n// System.out.println(\"success\");\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n }\n\n public void yearEnd() {\n try {\n stmt = c.createStatement();\n stmt.executeUpdate(\"UPDATE przedmioty SET aktywny = FALSE;\");\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n }\n\n public void deactivateStudent(String studentID) {\n try {\n stmt = c.createStatement();\n stmt.executeUpdate(\"UPDATE uczniowie SET aktywny = false where pesel='\" + studentID + \"';\");\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n }\n\n public void addStudentUser(String login, String password, String pesel) {\n try {\n stmt = c.createStatement();\n stmt.executeUpdate(\"INSERT INTO uzytkownicy(login,haslo) values('\" + login + \"','\" + password + \"');\");\n stmt.executeUpdate(\"UPDATE uczniowie SET id_uzytkownika = '\" + login + \"' where pesel='\" + pesel + \"';\");\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n }\n\n public void addTeacherUser(String login, String password, int teacherID) {\n try {\n stmt = c.createStatement();\n stmt.executeUpdate(\"INSERT INTO uzytkownicy(login,haslo) values('\" + login + \"','\" + password + \"');\");\n stmt.executeUpdate(\"UPDATE nauczyciele SET id_uzytkownika = '\" + login + \"' where id=\" + teacherID + \";\");\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n }\n\n public User signIn(String login, String password) {\n //w parze pierwszy argument to string drugi boolean\n //i true jesli to uczen, false jesli to nauczyciel\n //null jesli login nie istnieje lub zle haslo\n try {\n stmt = c.createStatement();\n ResultSet rs = stmt.executeQuery(\"SELECT * from uzytkownicy join uczniowie on login=id_uzytkownika where login='\" + login + \"';\");\n while (rs.next()) {\n String pesel = rs.getString(\"pesel\");\n String pass= rs.getString(\"haslo\");\n if (!password.equals(pass)) return null;\n return new User(pesel, AccountType.STUDENT,pass);\n }\n rs = stmt.executeQuery(\"SELECT * from uzytkownicy join nauczyciele on login=id_uzytkownika where login='\" + login + \"';\");\n while (rs.next()) {\n int id = rs.getInt(\"id\");\n String pass= rs.getString(\"haslo\");\n if (!password.equals(pass)) return null;\n return new User(Integer.toString(id), AccountType.TEACHER,pass);\n }\n return null;\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n return null;\n }\n\n public ArrayList<ArrayList<String> > getLessonShedule(String pesel){\n ArrayList<ArrayList<String> > shedule = new ArrayList<ArrayList<String>>();\n for(int i=0;i<5;i++){\n shedule.add(new ArrayList<String>());\n }\n try {\n for(int i=2;i<=6;i++){\n stmt = c.createStatement();\n ResultSet rs = stmt.executeQuery(\"select p.nazwa,pl.nr_lekcji from plan_lekcji pl join przedmioty p on pl.id_przedmiotu = p.id join klasy k on p.id_klasy = k.id join uczniowie u on k.id=u.id_klasy where dzien_tygodnia = \"+i+\" and u.pesel = '\"+pesel+\"' order by pl.nr_lekcji;\");\n int j=0;\n while (rs.next()) {\n int tmp = rs.getInt(\"nr_lekcji\");\n while(j<tmp){ shedule.get(i-2).add(\"\"); j++;}\n shedule.get(i-2).add(rs.getString(\"nazwa\"));\n j++;\n }\n while(shedule.get(i-2).size()<10) shedule.get(i-2).add(\"\");\n rs.close();\n stmt.close();\n }\n// System.out.println(\"success\");\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n return shedule;\n }\n public ArrayList<Pair<String,String> > getStudentsWithoutUser() {\n ArrayList<Pair<String,String> > students = new ArrayList<Pair<String,String> >();\n try {\n stmt = c.createStatement();\n ResultSet rs = stmt.executeQuery(\"SELECT imie,nazwisko,pesel from uczniowie where id_uzytkownika is null;\");\n while (rs.next()) {\n Pair<String,String> pair = new Pair<String, String>(rs.getString(\"pesel\"),rs.getString(\"imie\")+\" \"+rs.getString(\"nazwisko\"));\n// System.out.println(pair.getX()+\" \"+pair.getY());\n students.add(pair);\n }\n// System.out.println(\"success\");\n rs.close();\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n return students;\n }\n public ArrayList<Pair<Integer,String> > getTeachersWithoutUser() {\n ArrayList<Pair<Integer,String> > teachers = new ArrayList<Pair<Integer,String> >();\n try {\n stmt = c.createStatement();\n ResultSet rs = stmt.executeQuery(\"SELECT imie,nazwisko,id from nauczyciele where id_uzytkownika is null;\");\n while (rs.next()) {\n Pair<Integer,String> pair = new Pair<Integer, String>(rs.getInt(\"id\"),rs.getString(\"imie\")+\" \"+rs.getString(\"nazwisko\"));\n// System.out.println(pair.getX()+\" \"+pair.getY());\n teachers.add(pair);\n }\n// System.out.println(\"success\");\n rs.close();\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n return teachers;\n }\n public void changeStudentPassword(String pesel,String password){\n try {\n stmt = c.createStatement();\n ResultSet rs = stmt.executeQuery(\"select id_uzytkownika from uzytkownicy uz join uczniowie on id_uzytkownika=uz.login where pesel='\" + pesel + \"';\");\n rs.next();\n String userID = rs.getString(\"id_uzytkownika\");\n stmt.close();\n stmt = c.createStatement();\n stmt.executeUpdate(\"UPDATE uzytkownicy SET haslo = '\"+password+\"' where login='\"+userID+\"';\");\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n }\n public void changeTeacherPassword(int id,String password){\n try {\n stmt = c.createStatement();\n ResultSet rs = stmt.executeQuery(\"select id_uzytkownika from uzytkownicy uz join nauczyciele on id_uzytkownika=uz.login where id=\" + id + \";\");\n rs.next();\n String userID = rs.getString(\"id_uzytkownika\");\n stmt.close();\n stmt = c.createStatement();\n stmt.executeUpdate(\"UPDATE uzytkownicy SET haslo = '\"+password+\"' where login = '\"+userID+\"';\");\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n }\n\n public ArrayList<Pair<Integer,String> > getActivities(){\n ArrayList<Pair<Integer,String> > activities = new ArrayList<Pair<Integer,String> >();\n try {\n stmt = c.createStatement();\n ResultSet rs = stmt.executeQuery(\"SELECT id,nazwa from rodzaje_aktywnosci;\");\n while (rs.next()) {\n Pair<Integer,String> pair = new Pair<Integer, String>(rs.getInt(\"id\"),rs.getString(\"nazwa\"));\n// System.out.println(pair.getX()+\" \"+pair.getY());\n activities.add(pair);\n }\n// System.out.println(\"success\");\n rs.close();\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n return activities;\n }\n\n public ArrayList<Pair<Integer,String> > getLessonsByDate(String data){\n ArrayList<Pair<Integer,String> > lessons = new ArrayList<Pair<Integer,String> >();\n try {\n stmt = c.createStatement();\n\n ResultSet rs = stmt.executeQuery(\"SELECT pl.id,k.oddzial,k.rok_rozpoczecia,p.nazwa,pl.nr_lekcji from plan_lekcji pl join przedmioty p on pl.id_przedmiotu = p.id join klasy k on p.id_klasy = k.id where pl.dzien_tygodnia=extract(dow from to_date('\" + data + \"', 'DD.MM.YYYY'))+1;\");\n while (rs.next()) {\n Pair<Integer,String> pair = new Pair<Integer, String>(rs.getInt(\"id\"),\"godzina lekcyjna: \"+rs.getInt(\"nr_lekcji\") +\" \"+ rs.getString(\"nazwa\") +\" klasa: \"+rs.getString(\"oddzial\") + \" \" + rs.getInt(\"rok_rozpoczecia\"));\n\n //System.out.println(pair.getX()+\" \"+pair.getY());\n lessons.add(pair);\n }\n// System.out.println(\"success\");\n rs.close();\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n return lessons;\n }\n\n public ArrayList<Pair<String,String> > getStudentsByLesson(int lessonID){\n ArrayList<Pair<String,String> > students = new ArrayList<Pair<String,String> >();\n try {\n stmt = c.createStatement();\n ResultSet rs = stmt.executeQuery(\"SELECT imie,nazwisko,pesel from uczniowie u join klasy k on k.id =u.id_klasy join przedmioty p on p.id_klasy=k.id join plan_lekcji pl on pl.id_przedmiotu =p.id join przeprowadzone_lekcje p_l on pl.id=p_l.id_lekcji where p_l.id=\"+lessonID+\";\");\n while (rs.next()) {\n Pair<String,String> pair = new Pair<String, String>(rs.getString(\"pesel\"),rs.getString(\"imie\")+\" \"+rs.getString(\"nazwisko\"));\n// System.out.println(pair.getX()+\" \"+pair.getY());\n students.add(pair);\n }\n// System.out.println(\"success\");\n rs.close();\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n return students;\n }\n\n public ArrayList<Pair<Integer,String> > getAllClasses(){\n ArrayList<Pair<Integer,String> > classes = new ArrayList<Pair<Integer,String> >();\n try{\n stmt = c.createStatement();\n ResultSet rs = stmt.executeQuery(\"select * from klasy\");\n while(rs.next()){\n classes.add(new Pair<Integer, String>(rs.getInt(\"id\"),rs.getString(\"oddzial\")+\" \"+rs.getString(\"rok_rozpoczecia\")));\n }\n stmt.close();\n }catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n return classes;\n }\n public ArrayList<Pair<String,String> > getAllStudents() {\n ArrayList<Pair<String,String> > students = new ArrayList<Pair<String,String> >();\n try {\n stmt = c.createStatement();\n ResultSet rs = stmt.executeQuery(\"SELECT imie,nazwisko,pesel from uczniowie where aktywny=true;\");\n while (rs.next()) {\n Pair<String,String> pair = new Pair<String, String>(rs.getString(\"pesel\"),rs.getString(\"imie\")+\" \"+rs.getString(\"nazwisko\"));\n// System.out.println(pair.getX()+\" \"+pair.getY());\n students.add(pair);\n }\n// System.out.println(\"success\");\n rs.close();\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n return students;\n }\n public ArrayList<Pair<String,String> > getAllStudentsByAdmin() {\n ArrayList<Pair<String,String> > students = new ArrayList<Pair<String,String> >();\n try {\n stmt = c.createStatement();\n ResultSet rs = stmt.executeQuery(\"SELECT * from uczniowie where aktywny=true;\");\n while (rs.next()) {\n Pair<String,String> pair = new Pair<String, String>(rs.getString(\"pesel\"),rs.getString(\"pesel\")+\" \"+rs.getString(\"imie\")+\" \"+rs.getString(\"nazwisko\")+\" \"+rs.getString(\"telefon_do_rodzica\"));\n// System.out.println(pair.getX()+\" \"+pair.getY());\n students.add(pair);\n }\n// System.out.println(\"success\");\n rs.close();\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n return students;\n }\n\n public ArrayList<Pair<Integer,String> > getTeachersWithoutClass() {\n ArrayList<Pair<Integer,String> > teachers = new ArrayList<Pair<Integer,String> >();\n try {\n stmt = c.createStatement();\n ResultSet rs = stmt.executeQuery(\"SELECT imie,nazwisko,id from (SELECT imie,nazwisko,n.id,id_wychowawcy from nauczyciele n left join klasy k on n.id=k.id_wychowawcy) f where id_wychowawcy is null;\");\n while (rs.next()) {\n Pair<Integer,String> pair = new Pair<Integer, String>(rs.getInt(\"id\"),rs.getString(\"imie\")+\" \"+rs.getString(\"nazwisko\"));\n // System.out.println(pair.getX()+\" \"+pair.getY());\n teachers.add(pair);\n }\n// System.out.println(\"success\");\n rs.close();\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n return teachers;\n }\n\n public ArrayList<Pair<Integer, String>> getAllTeachers() {\n ArrayList<Pair<Integer, String>> teachers = new ArrayList<Pair<Integer, String>>();\n try {\n stmt = c.createStatement();\n ResultSet rs = stmt.executeQuery(\"SELECT imie,nazwisko,id from nauczyciele\");\n while (rs.next()) {\n Pair<Integer, String> pair = new Pair<Integer, String>(rs.getInt(\"id\"), rs.getString(\"imie\") + \" \" + rs.getString(\"nazwisko\"));\n // System.out.println(pair.getX()+\" \"+pair.getY());\n teachers.add(pair);\n }\n// System.out.println(\"success\");\n rs.close();\n stmt.close();\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n return teachers;\n }\n public ArrayList<ArrayList<String> > getTeacherSchedule(int id){\n ArrayList<ArrayList<String> > shedule = new ArrayList<ArrayList<String>>();\n for(int i=0;i<5;i++){\n shedule.add(new ArrayList<String>());\n }\n try {\n for(int i=2;i<=6;i++){\n stmt = c.createStatement();\n ResultSet rs = stmt.executeQuery(\"select p.nazwa,pl.nr_lekcji,k.oddzial,k.rok_rozpoczecia from plan_lekcji pl join przedmioty p on pl.id_przedmiotu = p.id join klasy k on p.id_klasy = k.id join nauczyciele n on n.id=p.id_prowadzacego where dzien_tygodnia = \"+i+\" and n.id = '\"+id+\"' order by pl.nr_lekcji;\");\n int j=0;\n while (rs.next()) {\n int tmp = rs.getInt(\"nr_lekcji\");\n while(j<tmp){ shedule.get(i-2).add(\"\"); j++;}\n shedule.get(i-2).add(rs.getString(\"nazwa\")+\"(\"+rs.getInt(\"rok_rozpoczecia\")+rs.getString(\"oddzial\")+\")\");\n j++;\n }\n while(shedule.get(i-2).size()<10) shedule.get(i-2).add(\"\");\n rs.close();\n stmt.close();\n }\n// System.out.println(\"success\");\n } catch (Exception e) {\n e.printStackTrace();\n System.err.println(e.getClass().getName() + \": \" + e.getMessage());\n }\n return shedule;\n }\n\n public static void main(String args[])\n {\n DBManager dbManager = new DBManager();\n //String a = \"to ja a to nie ja\";\n //String b = \"kurwa mac\";\n //String c = \"a owszem nie\";\n //System.out.printf(\"%-20s %s\",a,b+\"\\n\");\n //System.out.printf(\"%-20s %s\", b, c+\"\\n\");\n //System.out.printf(\"%-20s %s\",c,a+\"\\n\");\n //dbManager.changeStudentPassword(\"95091673574\",\"kamil\");\n // dbManager.getTeachersWithoutClass();\n\n ArrayList<Pair<Integer,String> > sub = dbManager.getStudentSubjects(\"95091673574\");\n for(int i=0;i<sub.size();i++) System.out.println(sub.get(i).getX()+\" \"+sub.get(i).getY());\n }\n}\n\n", "input": "", "output": "【漏洞类型】SQL注入\n【严重性】High\n【漏洞摘要】SQL注入是一种代码注入技术,攻击者可以通过在输入字段中插入恶意的SQL代码,操控数据库查询。在原始代码中,所有查询方法都直接将用户输入拼接到SQL语句中,例如`\"SELECT wartosc,tematyka,nazwa from oceny_uczniow join rodzaje_aktywnosci ra on id_aktywnosci=ra.id where id_ucznia='\" + studentID + \"' and id_przedmiotu=\" + subjectID + \";\"`。这种做法使得攻击者可以通过构造特定的输入来执行任意的SQL命令,导致数据泄露、数据篡改或数据库服务崩溃。\n【关键代码模式】\n- 使用`Statement`对象执行SQL查询\n- 将用户输入直接拼接到SQL语句中\n- 缺乏输入验证和清理\n【修复方法说明】修复方法通过将用户输入绑定到`PreparedStatement`对象的参数中来解决问题。这样可以确保用户输入不会被解释为SQL的一部分,从而防止SQL注入攻击。最佳实践是始终使用`PreparedStatement`来执行SQL查询,特别是在处理用户输入时。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nxwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/internal/DocumentInitializerRightsManager.java\n### 代码内容:\n/*\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n */\npackage org.xwiki.security.internal;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport javax.inject.Inject;\nimport javax.inject.Provider;\nimport javax.inject.Singleton;\n\nimport org.slf4j.Logger;\nimport org.xwiki.component.annotation.Component;\nimport org.xwiki.security.authorization.Right;\n\nimport com.xpn.xwiki.XWikiContext;\nimport com.xpn.xwiki.XWikiException;\nimport com.xpn.xwiki.doc.MandatoryDocumentInitializer;\nimport com.xpn.xwiki.doc.XWikiDocument;\nimport com.xpn.xwiki.objects.BaseObject;\n\nimport static org.xwiki.security.authorization.Right.DELETE;\nimport static org.xwiki.security.authorization.Right.EDIT;\nimport static org.xwiki.security.authorization.Right.VIEW;\nimport static org.xwiki.security.internal.XWikiConstants.ALLOW_FIELD_NAME;\nimport static org.xwiki.security.internal.XWikiConstants.GROUPS_FIELD_NAME;\nimport static org.xwiki.security.internal.XWikiConstants.LEVELS_FIELD_NAME;\nimport static org.xwiki.security.internal.XWikiConstants.LOCAL_CLASS_REFERENCE;\n\n/**\n * Service provided to {@link MandatoryDocumentInitializer}s to initialize documents with the correct rights.\n *\n * @version $Id$\n * @since 14.4.8\n * @since 14.10.5\n * @since 15.1RC1\n */\n@Component(roles = DocumentInitializerRightsManager.class)\n@Singleton\npublic class DocumentInitializerRightsManager\n{\n private static final String XWIKI_ADMIN_GROUP_DOCUMENT_REFERENCE = \"XWiki.XWikiAdminGroup\";\n\n @Inject\n private Provider<XWikiContext> xcontextProvider;\n\n @Inject\n private Logger logger;\n\n /**\n * Restrict the rights of the provided document so that it can only be viewed, edited and deleted by the\n * {@code XWiki.XWikiAdminGroup} group. Note that this restriction is only applied if no rights are already applied\n * on the current document.\n *\n * @param document the document to updated rights on\n * @return {@code true} if the document has been modified, {@code false} otherwise (including if the document hasn't\n * been modified because of an error during the modification)\n */\n public boolean restrictToAdmin(XWikiDocument document)\n {\n boolean updated = false;\n // If some rights have already been set on the document, we consider that it has already been protected \n // manually.\n if (document.getXObjects(LOCAL_CLASS_REFERENCE).isEmpty()) {\n updated = initializeRights(document, XWIKI_ADMIN_GROUP_DOCUMENT_REFERENCE, List.of(VIEW, EDIT, DELETE));\n }\n\n return updated;\n }\n\n private boolean initializeRights(XWikiDocument document, String xwikiAdminGroupDocumentReference,\n List<Right> rights)\n {\n boolean updated = false;\n\n try {\n XWikiContext xwikiContext = this.xcontextProvider.get();\n BaseObject object = document.newXObject(LOCAL_CLASS_REFERENCE, xwikiContext);\n XWikiContext xWikiContext = this.xcontextProvider.get();\n object.set(GROUPS_FIELD_NAME, xwikiAdminGroupDocumentReference, xWikiContext);\n object.set(LEVELS_FIELD_NAME, rights.stream().map(Right::getName).collect(Collectors.toList()),\n xWikiContext);\n object.set(ALLOW_FIELD_NAME, 1, xWikiContext);\n updated = true;\n } catch (XWikiException e) {\n this.logger.error(String.format(\"Error adding a [%s] object to the document [%s]\", LOCAL_CLASS_REFERENCE,\n document.getDocumentReference()));\n }\n\n return updated;\n }\n}\n\n", "input": "", "output": "【漏洞类型】权限管理漏洞\n【严重性】High\n【漏洞摘要】此代码存在权限管理漏洞,具体表现为在初始化文档权限时,未正确处理已设置的权限对象。如果文档中已存在一个权限对象,但其字段值为空或损坏,代码将不会检测到这一情况,并且会错误地认为文档尚未受到保护,从而导致权限设置不正确。根本原因在于代码中缺乏对现有权限对象的验证和修复逻辑。\n【关键代码模式】\n- `document.getXObjects(LOCAL_CLASS_REFERENCE).isEmpty()`:用于检查文档中是否已存在权限对象。\n- `initializeRights(document, XWIKI_ADMIN_GROUP_DOCUMENT_REFERENCE, List.of(VIEW, EDIT, DELETE))`:初始化权限的方法。\n- `object.set(GROUPS_FIELD_NAME, xwikiAdminGroupDocumentReference, xWikiContext)`:设置权限对象的组字段。\n- `object.set(LEVELS_FIELD_NAME, rights.stream().map(Right::getName).collect(Collectors.toList()), xWikiContext)`:设置权限对象的权限级别字段。\n- `object.set(ALLOW_FIELD_NAME, 1, xWikiContext)`:设置允许字段的值。\n- `StringUtils.isEmpty(object.getStringValue(LEVELS_FIELD_NAME))` 和 `StringUtils.isEmpty(object.getLargeStringValue(GROUPS_FIELD_NAME))`:用于检查现有权限对象的字段值是否为空。\n【修复方法说明】修复方法通过添加 `fixBadlyInitializedRights` 函数来检查和修复已存在但字段值损坏的权限对象。这解决了在文档升级过程中可能存在的权限初始化问题,确保了权限设置的正确性。最佳实践是在处理权限对象之前,始终验证其字段值,并在必要时进行修复。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nserver-core/src/main/java/io/onedev/server/entitymanager/impl/DefaultProjectManager.java\n### 代码内容:\npackage io.onedev.server.entitymanager.impl;\n\nimport static java.util.stream.Collectors.toList;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.charset.Charset;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.locks.Lock;\nimport java.util.concurrent.locks.ReadWriteLock;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\n\nimport javax.annotation.Nullable;\nimport javax.inject.Inject;\nimport javax.inject.Singleton;\nimport javax.persistence.criteria.CriteriaBuilder;\nimport javax.persistence.criteria.CriteriaQuery;\nimport javax.persistence.criteria.From;\nimport javax.persistence.criteria.Path;\nimport javax.persistence.criteria.Predicate;\nimport javax.persistence.criteria.Root;\n\nimport org.apache.commons.io.IOUtils;\nimport org.apache.shiro.authz.Permission;\nimport org.apache.shiro.authz.UnauthorizedException;\nimport org.eclipse.jgit.api.Git;\nimport org.eclipse.jgit.api.errors.GitAPIException;\nimport org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm;\nimport org.eclipse.jgit.internal.storage.file.FileRepository;\nimport org.eclipse.jgit.lib.ConfigConstants;\nimport org.eclipse.jgit.lib.ObjectId;\nimport org.eclipse.jgit.lib.Repository;\nimport org.eclipse.jgit.lib.StoredConfig;\nimport org.hibernate.Session;\nimport org.hibernate.query.Query;\nimport org.quartz.ScheduleBuilder;\nimport org.quartz.SimpleScheduleBuilder;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.base.Preconditions;\nimport com.google.common.base.Splitter;\nimport com.google.common.collect.Sets;\n\nimport io.onedev.commons.loader.Listen;\nimport io.onedev.commons.loader.ListenerRegistry;\nimport io.onedev.commons.utils.ExceptionUtils;\nimport io.onedev.commons.utils.FileUtils;\nimport io.onedev.commons.utils.StringUtils;\nimport io.onedev.server.buildspec.job.JobManager;\nimport io.onedev.server.entitymanager.BuildManager;\nimport io.onedev.server.entitymanager.IssueManager;\nimport io.onedev.server.entitymanager.LinkSpecManager;\nimport io.onedev.server.entitymanager.ProjectManager;\nimport io.onedev.server.entitymanager.RoleManager;\nimport io.onedev.server.entitymanager.SettingManager;\nimport io.onedev.server.entitymanager.UserAuthorizationManager;\nimport io.onedev.server.event.ProjectCreated;\nimport io.onedev.server.event.ProjectEvent;\nimport io.onedev.server.event.RefUpdated;\nimport io.onedev.server.event.entity.EntityPersisted;\nimport io.onedev.server.event.entity.EntityRemoved;\nimport io.onedev.server.event.system.SystemStarted;\nimport io.onedev.server.event.system.SystemStopping;\nimport io.onedev.server.git.GitUtils;\nimport io.onedev.server.git.command.CloneCommand;\nimport io.onedev.server.infomanager.CommitInfoManager;\nimport io.onedev.server.model.Build;\nimport io.onedev.server.model.Issue;\nimport io.onedev.server.model.LinkSpec;\nimport io.onedev.server.model.Milestone;\nimport io.onedev.server.model.Project;\nimport io.onedev.server.model.PullRequest;\nimport io.onedev.server.model.User;\nimport io.onedev.server.model.UserAuthorization;\nimport io.onedev.server.model.support.BranchProtection;\nimport io.onedev.server.model.support.TagProtection;\nimport io.onedev.server.persistence.SessionManager;\nimport io.onedev.server.persistence.TransactionManager;\nimport io.onedev.server.persistence.annotation.Sessional;\nimport io.onedev.server.persistence.annotation.Transactional;\nimport io.onedev.server.persistence.dao.BaseEntityManager;\nimport io.onedev.server.persistence.dao.Dao;\nimport io.onedev.server.search.entity.EntityQuery;\nimport io.onedev.server.search.entity.EntitySort;\nimport io.onedev.server.search.entity.EntitySort.Direction;\nimport io.onedev.server.search.entity.issue.IssueQueryUpdater;\nimport io.onedev.server.search.entity.project.ProjectQuery;\nimport io.onedev.server.security.SecurityUtils;\nimport io.onedev.server.security.permission.AccessProject;\nimport io.onedev.server.security.permission.ProjectPermission;\nimport io.onedev.server.util.criteria.Criteria;\nimport io.onedev.server.util.facade.ProjectFacade;\nimport io.onedev.server.util.match.WildcardUtils;\nimport io.onedev.server.util.patternset.PatternSet;\nimport io.onedev.server.util.schedule.SchedulableTask;\nimport io.onedev.server.util.schedule.TaskScheduler;\nimport io.onedev.server.util.usage.Usage;\nimport io.onedev.server.web.avatar.AvatarManager;\n\n@Singleton\npublic class DefaultProjectManager extends BaseEntityManager<Project> \n\t\timplements ProjectManager, SchedulableTask {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(DefaultProjectManager.class);\n\t\n private final CommitInfoManager commitInfoManager;\n \n private final BuildManager buildManager;\n \n private final AvatarManager avatarManager;\n \n private final SettingManager settingManager;\n \n private final SessionManager sessionManager;\n \n private final TransactionManager transactionManager;\n \n private final IssueManager issueManager;\n \n private final LinkSpecManager linkSpecManager;\n \n private final JobManager jobManager;\n \n private final TaskScheduler taskScheduler;\n \n private final ListenerRegistry listenerRegistry;\n \n private final RoleManager roleManager;\n \n private final UserAuthorizationManager userAuthorizationManager;\n \n private final String gitReceiveHook;\n \n\tprivate final Map<Long, Repository> repositoryCache = new ConcurrentHashMap<>();\n\t\n\tprivate final Map<Long, Date> updateDates = new ConcurrentHashMap<>();\n\t\n\tprivate final Map<Long, ProjectFacade> cache = new HashMap<>();\n\t\n\tprivate final ReadWriteLock cacheLock = new ReentrantReadWriteLock();\n\t\n\tprivate String taskId;\n\t\n @Inject\n public DefaultProjectManager(Dao dao, CommitInfoManager commitInfoManager, \n \t\tBuildManager buildManager, AvatarManager avatarManager, \n \t\tSettingManager settingManager, TransactionManager transactionManager, \n \t\tSessionManager sessionManager, ListenerRegistry listenerRegistry, \n \t\tTaskScheduler taskScheduler, UserAuthorizationManager userAuthorizationManager, \n \t\tRoleManager roleManager, JobManager jobManager, IssueManager issueManager, \n \t\tLinkSpecManager linkSpecManager) {\n \tsuper(dao);\n \t\n this.commitInfoManager = commitInfoManager;\n this.buildManager = buildManager;\n this.avatarManager = avatarManager;\n this.settingManager = settingManager;\n this.transactionManager = transactionManager;\n this.sessionManager = sessionManager;\n this.listenerRegistry = listenerRegistry;\n this.taskScheduler = taskScheduler;\n this.userAuthorizationManager = userAuthorizationManager;\n this.roleManager = roleManager;\n this.jobManager = jobManager;\n this.issueManager = issueManager;\n this.linkSpecManager = linkSpecManager;\n \n try (InputStream is = getClass().getClassLoader().getResourceAsStream(\"git-receive-hook\")) {\n \tPreconditions.checkNotNull(is);\n gitReceiveHook = StringUtils.join(IOUtils.readLines(is, Charset.defaultCharset()), \"\\n\");\n } catch (IOException e) {\n throw new RuntimeException(e);\n }\n\n }\n \n @Override\n public Repository getRepository(Project project) {\n \tRepository repository = repositoryCache.get(project.getId());\n \tif (repository == null) {\n \t\tsynchronized (repositoryCache) {\n \t\t\trepository = repositoryCache.get(project.getId());\n \t\t\tif (repository == null) {\n \t\t\t\ttry {\n\t\t\t\t\t\trepository = new FileRepository(project.getGitDir());\n\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\tthrow new RuntimeException(e);\n\t\t\t\t\t}\n \t\t\t\trepositoryCache.put(project.getId(), repository);\n \t\t\t}\n \t\t}\n \t}\n \treturn repository;\n }\n \n @Transactional\n @Override\n public void save(Project project) {\n \tsave(project, null);\n }\n \n @Transactional\n @Override\n public void save(Project project, String oldPath) {\n \tdao.persist(project);\n \tif (oldPath != null && !oldPath.equals(project.getPath())) {\n \t\tCollection<Milestone> milestones = new ArrayList<>();\n \t\tfor (Milestone milestone: issueManager.queryUsedMilestones(project)) {\n \t\t\tif (!project.isSelfOrAncestorOf(milestone.getProject()) \n \t\t\t\t\t&& !milestone.getProject().isSelfOrAncestorOf(project)) {\n \t\t\t\tmilestones.add(milestone);\n \t\t\t}\n \t\t}\n \t\tissueManager.clearSchedules(project, milestones);\n \t\tsettingManager.onMoveProject(oldPath, project.getPath());\n \t\t\n \t\tfor (LinkSpec link: linkSpecManager.query()) {\n \t\t\tfor (IssueQueryUpdater updater: link.getQueryUpdaters())\n \t\t\t\tupdater.onMoveProject(oldPath, project.getPath());\n \t\t}\n \t\t\t\n \t\tscheduleTree(project);\n \t}\n }\n \n private void scheduleTree(Project project) {\n \tjobManager.schedule(project);\n \tfor (Project child: project.getChildren()) \n \t\tscheduleTree(child);\n }\n \n @Transactional\n @Override\n public void create(Project project) {\n \tProject parent = project.getParent();\n \tif (parent != null && parent.isNew())\n \t\tcreate(parent);\n \tdao.persist(project);\n \tcheckSanity(project);\n \tUserAuthorization authorization = new UserAuthorization();\n \tauthorization.setProject(project);\n \tauthorization.setUser(SecurityUtils.getUser());\n \tauthorization.setRole(roleManager.getOwner());\n \tuserAuthorizationManager.save(authorization);\n \tlistenerRegistry.post(new ProjectCreated(project));\n }\n \n @Transactional\n @Listen\n public void on(EntityRemoved event) {\n \tif (event.getEntity() instanceof Project) {\n \t\tProject project = (Project) event.getEntity();\n \t\tLong projectId = project.getId();\n \t\ttransactionManager.runAfterCommit(new Runnable() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tcacheLock.writeLock().lock();\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcache.remove(projectId);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tcacheLock.writeLock().unlock();\n\t\t\t\t\t}\n\t\t\t\t}\n \t\t\t\n \t\t});\n \t}\n }\n \n @Transactional\n @Listen\n public void on(EntityPersisted event) {\n \tif (event.getEntity() instanceof Project) {\n \t\tProjectFacade facade = ((Project) event.getEntity()).getFacade();\n \t\ttransactionManager.runAfterCommit(new Runnable() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tcacheLock.writeLock().lock();\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcache.put(facade.getId(), facade);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tcacheLock.writeLock().unlock();\n\t\t\t\t\t}\n\t\t\t\t}\n \t\t\t\n \t\t});\n \t}\n }\n \n @Transactional\n @Override\n public void delete(Project project) {\n \tfor (Project child: project.getChildren())\n \t\tdelete(child);\n \t\n \tUsage usage = new Usage();\n \tusage.add(settingManager.onDeleteProject(project.getPath()));\n \t\n\t\tfor (LinkSpec link: linkSpecManager.query()) {\n\t\t\tfor (IssueQueryUpdater updater: link.getQueryUpdaters())\n\t\t\t\tusage.add(updater.onDeleteProject(project.getPath()).prefix(\"issue setting\").prefix(\"administration\"));\n\t\t}\n \t\n \tusage.checkInUse(\"Project '\" + project.getPath() + \"'\");\n\n \tfor (Project fork: project.getForks()) {\n \t\tCollection<Project> descendants = fork.getForkChildren();\n \t\tdescendants.add(fork);\n \t\tfor (Project descendant: descendants) {\n \tQuery<?> query = getSession().createQuery(String.format(\"update Issue set %s=:fork where %s=:descendant\", \n \t\t\tIssue.PROP_NUMBER_SCOPE, Issue.PROP_PROJECT));\n \tquery.setParameter(\"fork\", fork);\n \tquery.setParameter(\"descendant\", descendant);\n \tquery.executeUpdate();\n \t\n \tquery = getSession().createQuery(String.format(\"update Build set %s=:fork where %s=:descendant\", \n \t\t\tBuild.PROP_NUMBER_SCOPE, Build.PROP_PROJECT));\n \tquery.setParameter(\"fork\", fork);\n \tquery.setParameter(\"descendant\", descendant);\n \tquery.executeUpdate();\n \t\n \tquery = getSession().createQuery(String.format(\"update PullRequest set %s=:fork where %s=:descendant\", \n \t\t\tPullRequest.PROP_NUMBER_SCOPE, PullRequest.PROP_TARGET_PROJECT));\n \tquery.setParameter(\"fork\", fork);\n \tquery.setParameter(\"descendant\", descendant);\n \tquery.executeUpdate();\n \t\t}\n \t}\n \t\n \tQuery<?> query = getSession().createQuery(String.format(\"update Project set %s=null where %s=:forkedFrom\", \n \t\t\tProject.PROP_FORKED_FROM, Project.PROP_FORKED_FROM));\n \tquery.setParameter(\"forkedFrom\", project);\n \tquery.executeUpdate();\n\n \tquery = getSession().createQuery(String.format(\"update PullRequest set %s=null where %s=:sourceProject\", \n \t\t\tPullRequest.PROP_SOURCE_PROJECT, PullRequest.PROP_SOURCE_PROJECT));\n \tquery.setParameter(\"sourceProject\", project);\n \tquery.executeUpdate();\n\n \tfor (Build build: project.getBuilds()) \n \t\tbuildManager.delete(build);\n \t\n \tdao.remove(project);\n \t\n \tsynchronized (repositoryCache) {\n\t\t\tRepository repository = repositoryCache.remove(project.getId());\n\t\t\tif (repository != null) \n\t\t\t\trepository.close();\n\t\t}\n }\n \n @Override\n public Project findByPath(String path) {\n\t\tcacheLock.readLock().lock();\n\t\ttry {\n\t\t\tLong projectId = findProjectId(path);\n\t\t\tif (projectId != null)\n\t\t\t\treturn load(projectId);\n\t\t\telse\n\t\t\t\treturn null;\n\t\t} finally {\n\t\t\tcacheLock.readLock().unlock();\n\t\t}\n }\n \n @Nullable\n private Long findProjectId(String path) {\n \tLong projectId = null;\n \tfor (String name: Splitter.on(\"/\").omitEmptyStrings().trimResults().split(path)) {\n \t\tprojectId = findProjectId(projectId, name);\n \t\tif (projectId == null)\n \t\t\tbreak;\n \t}\n \treturn projectId;\n }\n \n @Sessional\n @Override\n public Project findByServiceDeskName(String serviceDeskName) {\n\t\tcacheLock.readLock().lock();\n\t\ttry {\n\t\t\tLong projectId = null;\n\t\t\tfor (ProjectFacade facade: cache.values()) {\n\t\t\t\tif (serviceDeskName.equals(facade.getServiceDeskName())) {\n\t\t\t\t\tprojectId = facade.getId();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (projectId != null)\n\t\t\t\treturn load(projectId);\n\t\t\telse\n\t\t\t\treturn null;\n\t\t} finally {\n\t\t\tcacheLock.readLock().unlock();\n\t\t}\n }\n \n @Sessional\n @Override\n public Project initialize(String path) {\n \tList<String> names = Splitter.on(\"/\").omitEmptyStrings().trimResults().splitToList(path);\n \tProject project = null;\n \tfor (String name: names) { \n \t\tProject child;\n \t\tif (project == null || !project.isNew()) {\n \t\t\tchild = find(project, name);\n \t\t\tif (child == null) {\n\t \t\t\tif (project == null && !SecurityUtils.canCreateRootProjects())\n\t \t\t\t\tthrow new UnauthorizedException(\"Not authorized to create root project\");\n\t \t\t\tif (project != null && !SecurityUtils.canCreateChildren(project))\n\t \t\t\t\tthrow new UnauthorizedException(\"Not authorized to create project under '\" + project.getPath() + \"'\");\n\t \t\t\tchild = new Project();\n\t \t\t\tchild.setName(name);\n\t \t\t\tchild.setParent(project); \n \t\t\t}\n \t\t} else {\n \t\t\tchild = new Project();\n \t\t\tchild.setName(name);\n \t\t\tchild.setParent(project);\n \t\t}\n \t\tproject = child;\n \t}\n \t\n \tProject parent = project.getParent();\n \twhile (parent != null && parent.isNew()) {\n \t\tparent.setCodeManagement(false);\n \t\tparent.setIssueManagement(false);\n \t\tparent = parent.getParent();\n \t}\n \t\n \treturn project;\n }\n \n @Sessional\n @Override\n public Project find(Project parent, String name) {\n\t\tcacheLock.readLock().lock();\n\t\ttry {\n\t\t\tLong projectId = null;\n\t\t\tfor (ProjectFacade facade: cache.values()) {\n\t\t\t\tif (facade.getName().equalsIgnoreCase(name) && Objects.equals(Project.idOf(parent), facade.getParentId())) {\n\t\t\t\t\tprojectId = facade.getId();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (projectId != null)\n\t\t\t\treturn load(projectId);\n\t\t\telse\n\t\t\t\treturn null;\n\t\t} finally {\n\t\t\tcacheLock.readLock().unlock();\n\t\t}\n }\n \n @Transactional\n\t@Override\n\tpublic void fork(Project from, Project to) {\n \tProject parent = to.getParent();\n \tif (parent != null && parent.isNew())\n \t\tcreate(parent);\n \t\n \tdao.persist(to);\n \t\n \tUserAuthorization authorization = new UserAuthorization();\n \tauthorization.setProject(to);\n \tauthorization.setUser(SecurityUtils.getUser());\n \tauthorization.setRole(roleManager.getOwner());\n \tuserAuthorizationManager.save(authorization);\n \t\n FileUtils.cleanDir(to.getGitDir());\n new CloneCommand(to.getGitDir()).mirror(true).from(from.getGitDir().getAbsolutePath()).call();\n checkSanity(to);\n commitInfoManager.cloneInfo(from, to);\n avatarManager.copyAvatar(from, to);\n \n if (from.getLfsObjectsDir().exists()) {\n for (File file: FileUtils.listFiles(from.getLfsObjectsDir(), Sets.newHashSet(\"**\"), Sets.newHashSet())) {\n \tString objectId = file.getName();\n \tLock lock = from.getLfsObjectLock(objectId).readLock();\n \tlock.lock();\n \ttry {\n \t\tFileUtils.copyFile(file, to.getLfsObjectFile(objectId));\n \t} catch (IOException e) {\n \t\tthrow new RuntimeException(e);\n \t\t\t} finally {\n \t\tlock.unlock();\n \t}\n }\n }\n \n listenerRegistry.post(new ProjectCreated(to));\n\t}\n \n @Transactional\n @Override\n public void clone(Project project, String repositoryUrl) {\n \tProject parent = project.getParent();\n \tif (parent != null && parent.isNew())\n \t\tcreate(parent);\n \t\n \tdao.persist(project);\n \t\n \tUser user = SecurityUtils.getUser();\n \tUserAuthorization authorization = new UserAuthorization();\n \tauthorization.setProject(project);\n \tauthorization.setUser(user);\n \tauthorization.setRole(roleManager.getOwner());\n \tproject.getUserAuthorizations().add(authorization);\n \tuser.getAuthorizations().add(authorization);\n \tuserAuthorizationManager.save(authorization);\n \t\n FileUtils.cleanDir(project.getGitDir());\n new CloneCommand(project.getGitDir()).mirror(true).from(repositoryUrl).call();\n checkSanity(project);\n \n listenerRegistry.post(new ProjectCreated(project));\n }\n \n\tprivate boolean isGitHookValid(File gitDir, String hookName) {\n File hookFile = new File(gitDir, \"hooks/\" + hookName);\n if (!hookFile.exists()) \n \treturn false;\n \n try {\n\t\t\tString content = FileUtils.readFileToString(hookFile, Charset.defaultCharset());\n\t\t\tif (!content.contains(\"ENV_GIT_ALTERNATE_OBJECT_DIRECTORIES\"))\n\t\t\t\treturn false;\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t\t\n if (!hookFile.canExecute())\n \treturn false;\n \n return true;\n\t}\n\t\n\tprivate void checkSanity(Project project) {\n\t\tFile gitDir = project.getGitDir();\n\t\tif (gitDir.listFiles().length == 0) {\n \tlogger.info(\"Initializing git repository in '\" + gitDir + \"'...\");\n try (Git git = Git.init().setDirectory(gitDir).setBare(true).call()) {\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow ExceptionUtils.unchecked(e);\n\t\t\t}\n\t\t} else if (!GitUtils.isValid(gitDir)) {\n \tlogger.warn(\"Directory '\" + gitDir + \"' is not a valid git repository, reinitializing...\");\n \tFileUtils.cleanDir(gitDir);\n try (Git git = Git.init().setDirectory(gitDir).setBare(true).call()) {\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow ExceptionUtils.unchecked(e);\n\t\t\t}\n } \n\n\t\tif (!isGitHookValid(gitDir, \"pre-receive\") || !isGitHookValid(gitDir, \"post-receive\")) {\n File hooksDir = new File(gitDir, \"hooks\");\n\n File gitPreReceiveHookFile = new File(hooksDir, \"pre-receive\");\n FileUtils.writeFile(gitPreReceiveHookFile, String.format(gitReceiveHook, \"git-prereceive-callback\"));\n gitPreReceiveHookFile.setExecutable(true);\n \n File gitPostReceiveHookFile = new File(hooksDir, \"post-receive\");\n FileUtils.writeFile(gitPostReceiveHookFile, String.format(gitReceiveHook, \"git-postreceive-callback\"));\n gitPostReceiveHookFile.setExecutable(true);\n }\n\n\t\ttry {\n\t\t\tStoredConfig config = project.getRepository().getConfig();\n\t\t\tboolean changed = false;\n\t\t\tif (config.getEnum(ConfigConstants.CONFIG_DIFF_SECTION, null, ConfigConstants.CONFIG_KEY_ALGORITHM, \n\t\t\t\t\tSupportedAlgorithm.MYERS) != SupportedAlgorithm.HISTOGRAM) {\n\t\t\t\tconfig.setEnum(ConfigConstants.CONFIG_DIFF_SECTION, null, ConfigConstants.CONFIG_KEY_ALGORITHM, \n\t\t\t\t\t\tSupportedAlgorithm.HISTOGRAM);\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t\tif (!config.getBoolean(\"uploadpack\", \"allowAnySHA1InWant\", false)) {\n\t\t\t\tconfig.setBoolean(\"uploadpack\", null, \"allowAnySHA1InWant\", true);\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t\tif (changed)\n\t\t\t\tconfig.save();\t\t\t\t\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\t\n\t@Listen\n\tpublic void on(SystemStopping event) {\n\t\ttaskScheduler.unschedule(taskId);\n\t\tsynchronized(repositoryCache) {\n\t\t\tfor (Repository repository: repositoryCache.values()) {\n\t\t\t\trepository.close();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Transactional\n\t@Listen\n\tpublic void on(ProjectEvent event) {\n\t\t/*\n\t\t * Update asynchronously to avoid deadlock \n\t\t */\n\t\tupdateDates.put(event.getProject().getId(), event.getDate());\n\t}\n\t\n\t@Transactional\n\t@Listen(1)\n\tpublic void on(SystemStarted event) {\n\t\tlogger.info(\"Checking projects...\");\n\t\tcacheLock.writeLock().lock();\n\t\ttry {\n\t\t\tfor (Project project: query()) {\n\t\t\t\tcache.put(project.getId(), project.getFacade());\n\t\t\t\tcheckSanity(project);\n\t\t\t}\n\t\t} finally {\n\t\t\tcacheLock.writeLock().unlock();\n\t\t}\n\t\ttaskId = taskScheduler.schedule(this);\n\t}\n\n\t@Transactional\n\t@Override\n\tpublic void onDeleteBranch(Project project, String branchName) {\n\t\tfor (Iterator<BranchProtection> it = project.getBranchProtections().iterator(); it.hasNext();) { \n\t\t\tBranchProtection protection = it.next();\n\t\t\tPatternSet patternSet = PatternSet.parse(protection.getBranches());\n\t\t\tpatternSet.getIncludes().remove(branchName);\n\t\t\tpatternSet.getExcludes().remove(branchName);\n\t\t\tprotection.setBranches(patternSet.toString());\n\t\t\tif (protection.getBranches().length() == 0)\n\t\t\t\tit.remove();\n\t\t}\n\t}\n\t\n\t@Transactional\n\t@Override\n\tpublic void deleteBranch(Project project, String branchName) {\n\t\tonDeleteBranch(project, branchName);\n\n\t\tString refName = GitUtils.branch2ref(branchName);\n \tObjectId commitId = project.getObjectId(refName, true);\n \ttry {\n\t\t\tproject.git().branchDelete().setForce(true).setBranchNames(branchName).call();\n\t\t} catch (Exception e) {\n\t\t\tthrow ExceptionUtils.unchecked(e);\n\t\t}\n \t\n \tLong projectId = project.getId();\n \ttransactionManager.runAfterCommit(new Runnable() {\n\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t \tsessionManager.runAsync(new Runnable() {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tProject project = load(projectId);\n\t\t\t\t\t\tlistenerRegistry.post(new RefUpdated(project, refName, commitId, ObjectId.zeroId()));\n\t\t\t\t\t}\n\t\t \t\t\n\t\t \t});\n\t\t\t}\n \t\t\n \t});\n\t\t\n\t}\n\n\t@Transactional\n\t@Override\n\tpublic void onDeleteTag(Project project, String tagName) {\n\t\tfor (Iterator<TagProtection> it = project.getTagProtections().iterator(); it.hasNext();) { \n\t\t\tTagProtection protection = it.next();\n\t\t\tPatternSet patternSet = PatternSet.parse(protection.getTags());\n\t\t\tpatternSet.getIncludes().remove(tagName);\n\t\t\tpatternSet.getExcludes().remove(tagName);\n\t\t\tprotection.setTags(patternSet.toString());\n\t\t\tif (protection.getTags().length() == 0)\n\t\t\t\tit.remove();\n\t\t}\n\t}\n\t\n\t@Transactional\n\t@Override\n\tpublic void deleteTag(Project project, String tagName) {\n \tonDeleteTag(project, tagName);\n \t\n \tString refName = GitUtils.tag2ref(tagName);\n \tObjectId commitId = project.getRevCommit(refName, true).getId();\n \ttry {\n\t\t\tproject.git().tagDelete().setTags(tagName).call();\n\t\t} catch (GitAPIException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\n \tLong projectId = project.getId();\n \ttransactionManager.runAfterCommit(new Runnable() {\n\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t \tsessionManager.runAsync(new Runnable() {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tProject project = load(projectId);\n\t\t\t\t\t\tlistenerRegistry.post(new RefUpdated(project, refName, commitId, ObjectId.zeroId()));\n\t\t\t\t\t}\n\t\t \t\t\n\t\t \t});\n\t\t\t}\n \t\t\n \t});\n\t}\n\t\n\t@Override\n\tpublic List<Project> query() {\n\t\treturn query(true);\n\t}\n\n\t@Override\n\tpublic int count() {\n\t\treturn count(true);\n\t}\n\t\n\t@Sessional\n\t@Override\n\tpublic Collection<Project> getPermittedProjects(Permission permission) {\n\t\treturn query().stream()\n\t\t\t\t.filter(it->SecurityUtils.getSubject().isPermitted(new ProjectPermission(it, permission)))\n\t\t\t\t.collect(toList());\n\t}\n\n\tprivate CriteriaQuery<Project> buildCriteriaQuery(Session session, EntityQuery<Project> projectQuery) {\n\t\tCriteriaBuilder builder = session.getCriteriaBuilder();\n\t\tCriteriaQuery<Project> query = builder.createQuery(Project.class);\n\t\tRoot<Project> root = query.from(Project.class);\n\t\tquery.select(root);\n\t\t\n\t\tquery.where(getPredicates(projectQuery.getCriteria(), query, root, builder));\n\n\t\tList<javax.persistence.criteria.Order> orders = new ArrayList<>();\n\t\tfor (EntitySort sort: projectQuery.getSorts()) {\n\t\t\tif (sort.getDirection() == Direction.ASCENDING)\n\t\t\t\torders.add(builder.asc(ProjectQuery.getPath(root, Project.ORDER_FIELDS.get(sort.getField()))));\n\t\t\telse\n\t\t\t\torders.add(builder.desc(ProjectQuery.getPath(root, Project.ORDER_FIELDS.get(sort.getField()))));\n\t\t}\n\n\t\tif (orders.isEmpty())\n\t\t\torders.add(builder.desc(ProjectQuery.getPath(root, Project.PROP_UPDATE_DATE)));\n\t\tquery.orderBy(orders);\n\t\t\n\t\treturn query;\n\t}\n\t\n\tprivate Predicate[] getPredicates(@Nullable Criteria<Project> criteria, CriteriaQuery<?> query, \n\t\t\tFrom<Project, Project> from, CriteriaBuilder builder) {\n\t\tList<Predicate> predicates = new ArrayList<>();\n\t\tif (!SecurityUtils.isAdministrator()) {\n\t\t\tCollection<Project> projects = getPermittedProjects(new AccessProject());\n\t\t\tif (!projects.isEmpty())\n\t\t\t\tpredicates.add(getProjectsPredicate(builder, from, projects));\n\t\t\telse\n\t\t\t\tpredicates.add(builder.disjunction());\n\t\t}\n\t\tif (criteria != null) \n\t\t\tpredicates.add(criteria.getPredicate(query, from, builder));\n\t\treturn predicates.toArray(new Predicate[0]);\n\t}\n\t\n\t@Sessional\n\t@Override\n\tpublic List<Project> query(EntityQuery<Project> query, int firstResult, int maxResults) {\n\t\tCriteriaQuery<Project> criteriaQuery = buildCriteriaQuery(getSession(), query);\n\t\tQuery<Project> projectQuery = getSession().createQuery(criteriaQuery);\n\t\tprojectQuery.setFirstResult(firstResult);\n\t\tprojectQuery.setMaxResults(maxResults);\n\t\treturn projectQuery.getResultList();\n\t}\n\n\t@Sessional\n\t@Override\n\tpublic int count(Criteria<Project> projectCriteria) {\n\t\tCriteriaBuilder builder = getSession().getCriteriaBuilder();\n\t\tCriteriaQuery<Long> criteriaQuery = builder.createQuery(Long.class);\n\t\tRoot<Project> root = criteriaQuery.from(Project.class);\n\n\t\tcriteriaQuery.where(getPredicates(projectCriteria, criteriaQuery, root, builder));\n\n\t\tcriteriaQuery.select(builder.count(root));\n\t\treturn getSession().createQuery(criteriaQuery).uniqueResult().intValue();\n\t}\n\n\t@Override\n\tpublic void execute() {\n\t\ttry {\n\t\t\ttransactionManager.run(new Runnable() {\n\t\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tDate now = new Date();\n\t\t\t\t\tfor (Iterator<Map.Entry<Long, Date>> it = updateDates.entrySet().iterator(); it.hasNext();) {\n\t\t\t\t\t\tMap.Entry<Long, Date> entry = it.next();\n\t\t\t\t\t\tif (now.getTime() - entry.getValue().getTime() > 60000) {\n\t\t\t\t\t\t\tProject project = get(entry.getKey());\n\t\t\t\t\t\t\tif (project != null)\n\t\t\t\t\t\t\t\tproject.setUpdateDate(entry.getValue());\n\t\t\t\t\t\t\tit.remove();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t});\n\t\t} catch (Exception e) {\n\t\t\tlogger.error(\"Error flushing project update dates\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic ScheduleBuilder<?> getScheduleBuilder() {\n\t\treturn SimpleScheduleBuilder.repeatMinutelyForever();\n\t}\n\t\n\tprivate String getPath(Long id) {\n\t\tProjectFacade facade = cache.get(id);\n\t\tif (facade != null) {\n\t\t\tif (facade.getParentId() != null) {\n\t\t\t\tString parentPath = getPath(facade.getParentId());\n\t\t\t\tif (parentPath != null)\n\t\t\t\t\treturn parentPath + \"/\" + facade.getName();\n\t\t\t\telse\n\t\t\t\t\treturn null;\n\t\t\t} else {\n\t\t\t\treturn facade.getName();\n\t\t\t}\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate Collection<Long> getMatchingIds(String pathPattern) {\n\t\tCollection<Long> ids = new HashSet<>();\n\t\tfor (Long id: cache.keySet()) {\n\t\t\tString path = getPath(id);\n\t\t\tif (path != null && WildcardUtils.matchPath(pathPattern, path))\n\t\t\t\tids.add(id);\n\t\t}\n\t\treturn ids;\n\t}\n\n\tprivate Collection<Long> getSubtreeIds(Long projectId) {\n\t\tCollection<Long> treeIds = Sets.newHashSet(projectId);\n\t\tfor (ProjectFacade facade: cache.values()) {\n\t\t\tif (projectId.equals(facade.getParentId()))\n\t\t\t\ttreeIds.addAll(getSubtreeIds(facade.getId()));\n\t\t}\n\t\treturn treeIds;\n\t}\n\t\n\t@Override\n\tpublic Predicate getPathMatchPredicate(CriteriaBuilder builder, Path<Project> path, String pathPattern) {\n\t\tcacheLock.readLock().lock();\n\t\ttry {\n\t\t\treturn Criteria.forManyValues(builder, path.get(Project.PROP_ID), \n\t\t\t\t\tgetMatchingIds(pathPattern), cache.keySet());\t\t\n\t\t} finally {\n\t\t\tcacheLock.readLock().unlock();\n\t\t}\n\t}\n\t\n\t@Override\n\tpublic org.apache.lucene.search.Query getPathMatchQuery(String fieldName, String pathPattern) {\n\t\tcacheLock.readLock().lock();\n\t\ttry {\n\t\t\treturn Criteria.forManyValues(fieldName, getMatchingIds(pathPattern), cache.keySet());\t\t\n\t\t} finally {\n\t\t\tcacheLock.readLock().unlock();\n\t\t}\n\t}\n\t\n\t@Override\n\tpublic Predicate getSubtreePredicate(CriteriaBuilder builder, Path<Project> path, Project project) {\n\t\tcacheLock.readLock().lock();\n\t\ttry {\n\t\t\treturn Criteria.forManyValues(builder, path.get(Project.PROP_ID), \n\t\t\t\t\tgetSubtreeIds(project.getId()), cache.keySet());\t\t\n\t\t} finally {\n\t\t\tcacheLock.readLock().unlock();\n\t\t}\n\t}\n\n\t@Override\n\tpublic org.apache.lucene.search.Query getSubtreeQuery(String fieldName, Project project) {\n\t\tcacheLock.readLock().lock();\n\t\ttry {\n\t\t\treturn Criteria.forManyValues(fieldName, getSubtreeIds(project.getId()), cache.keySet());\t\t\n\t\t} finally {\n\t\t\tcacheLock.readLock().unlock();\n\t\t}\n\t}\n\t\n\t@Override\n\tpublic Predicate getProjectsPredicate(CriteriaBuilder builder, Path<Project> path, Collection<Project> projects) {\n\t\tcacheLock.readLock().lock();\n\t\ttry {\n\t\t\treturn Criteria.forManyValues(builder, path.get(Project.PROP_ID), \n\t\t\t\t\tprojects.stream().map(it->it.getId()).collect(toList()), cache.keySet());\t\t\n\t\t} finally {\n\t\t\tcacheLock.readLock().unlock();\n\t\t}\n\t}\n\n\t@Override\n\tpublic org.apache.lucene.search.Query getProjectsQuery(String fieldName, Collection<Project> projects) {\n\t\tcacheLock.readLock().lock();\n\t\ttry {\n\t\t\treturn Criteria.forManyValues(fieldName, projects.stream().map(it->it.getId()).collect(toList()), \n\t\t\t\t\tcache.keySet());\t\t\n\t\t} finally {\n\t\t\tcacheLock.readLock().unlock();\n\t\t}\n\t}\n\t\n\t@Transactional\n\t@Override\n\tpublic void move(Collection<Project> projects, Project parent) {\n\t\tfor (Project project: projects) { \n\t\t\tString oldPath = project.getPath();\n\t\t\tproject.setParent(parent);\n\t\t\tsave(project, oldPath);\n\t\t}\n\t}\n\n\t@Transactional\n\t@Override\n\tpublic void delete(Collection<Project> projects) {\n\t\tCollection<Project> independents = new HashSet<>(projects);\n\t\tfor (Iterator<Project> it = independents.iterator(); it.hasNext();) {\n\t\t\tProject independent = it.next();\n\t\t\tfor (Project each: independents) {\n\t\t\t\tif (!each.equals(independent) && each.isSelfOrAncestorOf(independent)) {\n\t\t\t\t\tit.remove();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (Project independent: independents)\n\t\t\tdelete(independent);\n\t}\n \n\t@Nullable\n private Long findProjectId(@Nullable Long parentId, String name) {\n\t\tfor (ProjectFacade facade: cache.values()) {\n\t\t\tif (facade.getName().equalsIgnoreCase(name) && Objects.equals(parentId, facade.getParentId())) \n\t\t\t\treturn facade.getId();\n\t\t}\n\t\treturn null;\n }\n\n\tprivate int findLongestMatch(@Nullable Long parentId, List<String> pathSegments) {\n\t\tif (!pathSegments.isEmpty()) {\n\t\t\tString name = pathSegments.get(0);\n\t\t\tLong projectId = findProjectId(parentId, name);\n\t\t\tif (projectId != null)\n\t\t\t\treturn findLongestMatch(projectId, pathSegments.subList(1, pathSegments.size())) + 1;\n\t\t\telse\n\t\t\t\treturn 0;\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t}\n\t\n\t@Override\n\tpublic int findLongestMatch(List<String> pathSegments) {\n\t\tcacheLock.readLock().lock();\n\t\ttry {\n\t\t\treturn findLongestMatch(null, pathSegments);\n\t\t} finally {\n\t\t\tcacheLock.readLock().unlock();\n\t\t}\n\t}\n\t\n\t@Override\n\tpublic List<ProjectFacade> getChildren(Long projectId) {\n\t\tcacheLock.readLock().lock();\n\t\ttry {\n\t\t\tList<ProjectFacade> children = new ArrayList<>();\n\t\t\tfor (ProjectFacade facade: cache.values()) {\n\t\t\t\tif (projectId.equals(facade.getParentId()))\n\t\t\t\t\tchildren.add(facade);\n\t\t\t}\n\t\t\tCollections.sort(children, new Comparator<ProjectFacade>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic int compare(ProjectFacade o1, ProjectFacade o2) {\n\t\t\t\t\treturn o1.getName().compareTo(o2.getName());\n\t\t\t\t}\n\t\t\t\t\n\t\t\t});\n\t\t\treturn children;\n\t\t} finally {\n\t\t\tcacheLock.readLock().unlock();\n\t\t}\n\t}\n \n}\n\n## 内容2\n### 文件路径:\nserver-core/src/main/java/io/onedev/server/git/GitFilter.java\n### 代码内容:\npackage io.onedev.server.git;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport javax.inject.Inject;\nimport javax.inject.Singleton;\nimport javax.servlet.Filter;\nimport javax.servlet.FilterChain;\nimport javax.servlet.FilterConfig;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.shiro.authz.UnauthorizedException;\nimport org.eclipse.jgit.http.server.GitSmartHttpTools;\nimport org.eclipse.jgit.http.server.ServletUtils;\nimport org.eclipse.jgit.transport.PacketLineOut;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.onedev.commons.utils.command.ErrorCollector;\nimport io.onedev.commons.utils.command.ExecutionResult;\nimport io.onedev.server.OneDev;\nimport io.onedev.server.entitymanager.ProjectManager;\nimport io.onedev.server.entitymanager.SettingManager;\nimport io.onedev.server.exception.SystemNotReadyException;\nimport io.onedev.server.git.command.AdvertiseReceiveRefsCommand;\nimport io.onedev.server.git.command.AdvertiseUploadRefsCommand;\nimport io.onedev.server.git.command.ReceivePackCommand;\nimport io.onedev.server.git.command.UploadPackCommand;\nimport io.onedev.server.git.exception.GitException;\nimport io.onedev.server.model.Project;\nimport io.onedev.server.persistence.SessionManager;\nimport io.onedev.server.security.CodePullAuthorizationSource;\nimport io.onedev.server.security.SecurityUtils;\nimport io.onedev.server.storage.StorageManager;\nimport io.onedev.server.util.InputStreamWrapper;\nimport io.onedev.server.util.OutputStreamWrapper;\nimport io.onedev.server.util.ServerConfig;\nimport io.onedev.server.util.concurrent.PrioritizedRunnable;\nimport io.onedev.server.util.concurrent.WorkExecutor;\n\n@Singleton\npublic class GitFilter implements Filter {\n\t\n\tprivate static final Logger logger = LoggerFactory.getLogger(GitFilter.class);\n\n\tprivate static final int PRIORITY = 2;\n\t\n\tprivate static final String INFO_REFS = \"info/refs\";\n\t\n\tprivate final OneDev oneDev;\n\t\n\tprivate final StorageManager storageManager;\n\t\n\tprivate final ProjectManager projectManager;\n\t\n\tprivate final WorkExecutor workExecutor;\n\t\n\tprivate final ServerConfig serverConfig;\n\t\n\tprivate final SettingManager settingManager;\n\t\n\tprivate final SessionManager sessionManager;\n\t\n\tprivate final Set<CodePullAuthorizationSource> codePullAuthorizationSources;\n\t\n\t@Inject\n\tpublic GitFilter(OneDev oneDev, StorageManager storageManager, ProjectManager projectManager, \n\t\t\tWorkExecutor workExecutor, ServerConfig serverConfig, SettingManager settingManager,\n\t\t\tSessionManager sessionManager, Set<CodePullAuthorizationSource> codePullAuthorizationSources) {\n\t\tthis.oneDev = oneDev;\n\t\tthis.storageManager = storageManager;\n\t\tthis.projectManager = projectManager;\n\t\tthis.workExecutor = workExecutor;\n\t\tthis.serverConfig = serverConfig;\n\t\tthis.settingManager = settingManager;\n\t\tthis.sessionManager = sessionManager;\n\t\tthis.codePullAuthorizationSources = codePullAuthorizationSources;\n\t}\n\t\n\tprivate String getPathInfo(HttpServletRequest request) {\n\t\tString pathInfo = request.getRequestURI().substring(request.getContextPath().length());\n\t\treturn StringUtils.stripStart(pathInfo, \"/\");\n\t}\n\t\n\tprivate Project getProject(HttpServletRequest request, HttpServletResponse response, String projectInfo) \n\t\t\tthrows IOException {\n\t\tString projectPath = StringUtils.strip(projectInfo, \"/\");\n\n\t\tProject project = projectManager.findByPath(projectPath);\n\t\tif (project == null && projectPath.startsWith(\"projects/\")) {\n\t\t\tprojectPath = projectPath.substring(\"projects/\".length());\n\t\t\tproject = projectManager.findByPath(projectPath);\n\t\t}\n\t\tif (project == null) \n\t\t\tthrow new GitException(String.format(\"Unable to find project '%s'\", projectPath));\n\t\treturn project;\n\t}\n\t\n\tprivate void doNotCache(HttpServletResponse response) {\n\t\tresponse.setHeader(\"Expires\", \"Fri, 01 Jan 1980 00:00:00 GMT\");\n\t\tresponse.setHeader(\"Pragma\", \"no-cache\");\n\t\tresponse.setHeader(\"Cache-Control\", \"no-cache, max-age=0, must-revalidate\");\n\t}\n\t\n\tprotected void processPacks(final HttpServletRequest request, final HttpServletResponse response) \n\t\t\tthrows ServletException, IOException, InterruptedException, ExecutionException {\n\t\tFile gitDir;\n\t\tboolean upload;\n\t\tMap<String, String> environments = new HashMap<>();\n\t\t\n\t\tsessionManager.openSession();\n\t\ttry {\n\t\t\tString pathInfo = getPathInfo(request);\n\t\t\t\n\t\t\tString service = StringUtils.substringAfterLast(pathInfo, \"/\");\n\n\t\t\tString projectInfo = StringUtils.substringBeforeLast(pathInfo, \"/\");\n\t\t\tProject project = getProject(request, response, projectInfo);\n\t\t\t\n\t\t\tdoNotCache(response);\n\t\t\tresponse.setHeader(\"Content-Type\", \"application/x-\" + service + \"-result\");\t\t\t\n\n\t\t\tString serverUrl;\n\t if (serverConfig.getHttpPort() != 0)\n\t serverUrl = \"http://localhost:\" + serverConfig.getHttpPort();\n\t else \n\t serverUrl = \"https://localhost:\" + serverConfig.getHttpsPort();\n\n\t environments.put(\"ONEDEV_CURL\", settingManager.getSystemSetting().getCurlConfig().getExecutable());\n\t\t\tenvironments.put(\"ONEDEV_URL\", serverUrl);\n\t\t\tenvironments.put(\"ONEDEV_USER_ID\", SecurityUtils.getUserId().toString());\n\t\t\tenvironments.put(\"ONEDEV_REPOSITORY_ID\", project.getId().toString());\n\t\t\t\n\t\t\t// to be compatible with old repository\n\t environments.put(\"GITPLEX_CURL\", settingManager.getSystemSetting().getCurlConfig().getExecutable());\n\t\t\tenvironments.put(\"GITPLEX_URL\", serverUrl);\n\t\t\tenvironments.put(\"GITPLEX_USER_ID\", SecurityUtils.getUserId().toString());\n\t\t\tenvironments.put(\"GITPLEX_REPOSITORY_ID\", project.getId().toString());\n\t\t\t\n\t\t\tgitDir = storageManager.getProjectGitDir(project.getId());\n\n\t\t\tif (GitSmartHttpTools.isUploadPack(request)) {\n\t\t\t\tcheckPullPermission(request, project);\n\t\t\t\tupload = true;\n\t\t\t} else {\n\t\t\t\tif (!SecurityUtils.canWriteCode(project))\n\t\t\t\t\tthrow new UnauthorizedException(\"You do not have permission to push to this project.\");\n\t\t\t\tupload = false;\n\t\t\t}\t\t\t\n\t\t} finally {\n\t\t\tsessionManager.closeSession();\n\t\t}\n\t\t\n\t\tInputStream stdin = new InputStreamWrapper(ServletUtils.getInputStream(request)) {\n\n\t\t\t@Override\n\t\t\tpublic void close() throws IOException {\n\t\t\t}\n\t\t\t\n\t\t};\n\t\tOutputStream stdout = new OutputStreamWrapper(response.getOutputStream()) {\n\t\t\t\n\t\t\t@Override\n\t\t\tpublic void close() throws IOException {\n\t\t\t}\n\t\t\t\n\t\t};\n\t\t\n\t\tString protocol = request.getHeader(\"Git-Protocol\");\t\t\n\t\t\n\t\tif (upload) {\n\t\t\tworkExecutor.submit(new PrioritizedRunnable(PRIORITY) {\n\t\t\t\t\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tAtomicBoolean toleratedErrors = new AtomicBoolean(false);\n\t\t\t\t\tErrorCollector stderr = new ErrorCollector(StandardCharsets.UTF_8.name()) {\n\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void consume(String line) {\n\t\t\t\t\t\t\tsuper.consume(line);\n\t\t\t\t\t\t\t// This error may happen during a normal shallow fetch/clone \n\t\t\t\t\t\t\tif (line.contains(\"remote end hung up unexpectedly\")) {\n\t\t\t\t\t\t\t\ttoleratedErrors.set(true);\n\t\t\t\t\t\t\t\tlogger.debug(line);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tlogger.error(line);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t};\n\n\t\t\t\t\tExecutionResult result;\n\t\t\t\t\tUploadPackCommand upload = new UploadPackCommand(gitDir, environments);\n\t\t\t\t\tupload.stdin(stdin).stdout(stdout).stderr(stderr).statelessRpc(true).protocol(protocol);\n\t\t\t\t\tresult = upload.call();\n\t\t\t\t\tresult.setStderr(stderr.getMessage());\n\t\t\t\t\t\n\t\t\t\t\tif (result.getReturnCode() != 0 && !toleratedErrors.get())\n\t\t\t\t\t\tthrow result.buildException();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t}).get();\n\t\t} else {\n\t\t\tworkExecutor.submit(new PrioritizedRunnable(PRIORITY) {\n\t\t\t\t\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tErrorCollector stderr = new ErrorCollector(StandardCharsets.UTF_8.name()) {\n\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void consume(String line) {\n\t\t\t\t\t\t\tsuper.consume(line);\n\t\t\t\t\t\t\tlogger.error(line);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t};\n\t\t\t\t\t\n\t\t\t\t\tReceivePackCommand receive = new ReceivePackCommand(gitDir, environments);\n\t\t\t\t\treceive.stdin(stdin).stdout(stdout).stderr(stderr).statelessRpc(true).protocol(protocol);\n\t\t\t\t\tExecutionResult result = receive.call();\n\t\t\t\t\tresult.setStderr(stderr.getMessage());\n\t\t\t\t\tresult.checkReturnCode();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t}).get();\n\t\t}\n\t}\n\t\n\tprivate void writeInitial(HttpServletResponse response, String service) throws IOException {\n\t\tdoNotCache(response);\n\t\tresponse.setHeader(\"Content-Type\", \"application/x-\" + service + \"-advertisement\");\t\t\t\n\t\t\n\t\tPacketLineOut pack = new PacketLineOut(response.getOutputStream());\n\t\tpack.setFlushOnEnd(false);\n\t\tpack.writeString(\"# service=\" + service + \"\\n\");\n\t\tpack.end();\n\t}\n\t\n\tprivate void checkPullPermission(HttpServletRequest request, Project project) {\n\t\tif (!SecurityUtils.canReadCode(project)) {\n\t\t\tboolean isAuthorized = false;\n\t\t\tfor (CodePullAuthorizationSource source: codePullAuthorizationSources) {\n\t\t\t\tif (source.canPullCode(request, project)) {\n\t\t\t\t\tisAuthorized = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!isAuthorized)\n\t\t\t\tthrow new UnauthorizedException(\"You do not have permission to pull from this project.\");\n\t\t}\n\t}\n\t\n\tprotected void processRefs(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n\t\tFile gitDir;\n\t\tboolean upload;\n\n\t\tsessionManager.openSession();\n\t\ttry {\n\t\t\tString pathInfo = request.getRequestURI().substring(request.getContextPath().length());\n\t\t\tpathInfo = StringUtils.stripStart(pathInfo, \"/\");\n\n\t\t\tString projectInfo = pathInfo.substring(0, pathInfo.length() - INFO_REFS.length());\n\t\t\tProject project = getProject(request, response, projectInfo);\n\t\t\tString service = request.getParameter(\"service\");\n\t\t\t\n\t\t\tgitDir = storageManager.getProjectGitDir(project.getId());\n\n\t\t\tif (service.contains(\"upload\")) {\n\t\t\t\tcheckPullPermission(request, project);\n\t\t\t\twriteInitial(response, service);\n\t\t\t\tupload = true;\n\t\t\t} else {\n\t\t\t\tif (!SecurityUtils.canWriteCode(project))\n\t\t\t\t\tthrow new UnauthorizedException(\"You do not have permission to push to this project.\");\n\t\t\t\twriteInitial(response, service);\n\t\t\t\tupload = false;\n\t\t\t}\n\t\t} finally {\n\t\t\tsessionManager.closeSession();\n\t\t}\n\t\t\n\t\tOutputStream os = new OutputStreamWrapper(response.getOutputStream()) {\n\n\t\t\t@Override\n\t\t\tpublic void close() throws IOException {\n\t\t\t}\n\t\t\t\n\t\t};\n\t\t\n\t\tString protocolVersion = request.getHeader(\"Git-Protocol\");\t\t\n\t\t\n\t\tif (upload) \n\t\t\tnew AdvertiseUploadRefsCommand(gitDir).protocol(protocolVersion).output(os).call();\n\t\telse \n\t\t\tnew AdvertiseReceiveRefsCommand(gitDir).protocol(protocolVersion).output(os).call();\n\t}\n\n\t@Override\n\tpublic void init(FilterConfig filterConfig) throws ServletException {\n\t}\n\n\t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response,\n\t\t\tFilterChain chain) throws IOException, ServletException {\n\t\tHttpServletRequest httpRequest = (HttpServletRequest) request;\n\t\tHttpServletResponse httpResponse = (HttpServletResponse) response;\n\t\t\n\t\ttry {\n\t\t\tif (GitSmartHttpTools.isInfoRefs(httpRequest)) {\n\t\t\t\tif (oneDev.isReady())\n\t\t\t\t\tprocessRefs(httpRequest, httpResponse);\n\t\t\t\telse\n\t\t\t\t\tthrow new SystemNotReadyException();\n\t\t\t} else if (GitSmartHttpTools.isReceivePack(httpRequest) || GitSmartHttpTools.isUploadPack(httpRequest)) {\n\t\t\t\tif (oneDev.isReady())\n\t\t\t\t\tprocessPacks(httpRequest, httpResponse);\n\t\t\t\telse\n\t\t\t\t\tthrow new SystemNotReadyException();\n\t\t\t} else {\n\t\t\t\tchain.doFilter(request, response);\n\t\t\t}\n\t\t} catch (SystemNotReadyException e) {\n\t\t\tlogger.debug(\"Unable to serve git request as system is not ready yet\");\n\t\t\tGitSmartHttpTools.sendError(httpRequest, httpResponse, HttpServletResponse.SC_SERVICE_UNAVAILABLE, e.getMessage());\n\t\t} catch (GitException|InterruptedException|ExecutionException e) {\n\t\t\tlogger.error(\"Error serving git request\", e);\n\t\t\tGitSmartHttpTools.sendError(httpRequest, httpResponse, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());\n\t\t}\n\t}\n\t\n\t@Override\n\tpublic void destroy() {\n\t}\n\t\n}\n \n## 内容3\n### 文件路径:\nserver-core/src/main/java/io/onedev/server/git/GitSshCommand.java\n### 代码内容:\npackage io.onedev.server.git;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.PrintStream;\nimport java.net.InetSocketAddress;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.Future;\n\nimport javax.annotation.Nullable;\n\nimport org.apache.shiro.util.ThreadContext;\nimport org.apache.sshd.common.channel.ChannelOutputStream;\nimport org.apache.sshd.server.Environment;\nimport org.apache.sshd.server.ExitCallback;\nimport org.apache.sshd.server.channel.ChannelSession;\nimport org.apache.sshd.server.command.Command;\nimport org.apache.sshd.server.session.ServerSession;\nimport org.apache.sshd.server.session.ServerSessionAware;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.onedev.commons.utils.StringUtils;\nimport io.onedev.commons.utils.command.ExecutionResult;\nimport io.onedev.server.OneDev;\nimport io.onedev.server.entitymanager.ProjectManager;\nimport io.onedev.server.entitymanager.SettingManager;\nimport io.onedev.server.model.Project;\nimport io.onedev.server.persistence.SessionManager;\nimport io.onedev.server.security.SecurityUtils;\nimport io.onedev.server.ssh.SshAuthenticator;\nimport io.onedev.server.util.InputStreamWrapper;\nimport io.onedev.server.util.OutputStreamWrapper;\nimport io.onedev.server.util.ServerConfig;\nimport io.onedev.server.util.concurrent.WorkExecutor;\nimport io.onedev.server.util.concurrent.PrioritizedRunnable;\n\nabstract class GitSshCommand implements Command, ServerSessionAware {\n\t\n\tprivate static final int PRIORITY = 2;\n\t\n\tprivate static final Logger logger = LoggerFactory.getLogger(GitSshCommand.class);\n\n\tfinal String command;\n\t\n\tInputStream in;\n\t\n\tOutputStream out;\n\t\n\tOutputStream err;\n\t\n\tExitCallback callback;\n\t\n\tServerSession session;\n\t\n\tFuture<?> future;\n\t\n\tGitSshCommand(String command) {\n\t\tthis.command = command;\n\t}\n\t\n private Map<String, String> buildGitEnvs(Project project) {\n\t\tMap<String, String> environments = new HashMap<>();\n\t\t\n\t\tServerConfig serverConfig = OneDev.getInstance(ServerConfig.class);\n\t\tString serverUrl;\n if (serverConfig.getHttpPort() != 0)\n serverUrl = \"http://localhost:\" + serverConfig.getHttpPort();\n else \n serverUrl = \"https://localhost:\" + serverConfig.getHttpsPort();\n\n SettingManager settingManager = OneDev.getInstance(SettingManager.class);\n environments.put(\"ONEDEV_CURL\", settingManager.getSystemSetting().getCurlConfig().getExecutable());\n\t\tenvironments.put(\"ONEDEV_URL\", serverUrl);\n\t\tenvironments.put(\"ONEDEV_USER_ID\", SecurityUtils.getUserId().toString());\n\t\tenvironments.put(\"ONEDEV_REPOSITORY_ID\", project.getId().toString());\n\t\treturn environments;\n }\n \n\t@Override\n\tpublic void start(ChannelSession channel, Environment env) throws IOException {\n\t\tSshAuthenticator authenticator = OneDev.getInstance(SshAuthenticator.class);\n\t\tThreadContext.bind(SecurityUtils.asSubject(authenticator.getPublicKeyOwnerId(session)));\n\t\t\n File gitDir;\n Map<String, String> gitEnvs;\n \n SessionManager sessionManager = OneDev.getInstance(SessionManager.class);\n sessionManager.openSession(); \n try {\n\t\t\tProjectManager projectManager = OneDev.getInstance(ProjectManager.class);\n\t\t\tString projectPath = StringUtils.substringAfter(command, \"'/\"); \n\t\t\tprojectPath = StringUtils.substringBefore(projectPath, \"'\");\n Project project = projectManager.findByPath(projectPath);\n \t\tif (project == null && projectPath.startsWith(\"projects/\")) {\n \t\t\tprojectPath = projectPath.substring(\"projects/\".length());\n \t\t\tproject = projectManager.findByPath(projectPath);\n \t\t}\n if (project == null) {\n onExit(-1, \"Unable to find project '\" + projectPath + \"'\");\n return;\n } \n \n \tString errorMessage = checkPermission(project);\n \tif (errorMessage != null) {\n \t\tonExit(-1, errorMessage);\n \t\treturn;\n \t} \n\n gitDir = project.getGitDir();\n gitEnvs = buildGitEnvs(project);\n } finally { \n sessionManager.closeSession();\n }\n\t\t\n InetSocketAddress address = (InetSocketAddress) session.getRemoteAddress();\n String groupId = \"git-over-ssh-\" + gitDir.getAbsolutePath() \n \t\t+ \"-\" + address.getAddress().getHostAddress();\n \n WorkExecutor workExecutor = OneDev.getInstance(WorkExecutor.class);\n\t\tfuture = workExecutor.submit(groupId, new PrioritizedRunnable(PRIORITY) {\n\t\t\t\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\ttry {\n\t\t\t\t\tExecutionResult result = execute(gitDir, gitEnvs);\n\t\t\t\t\tonExit(result.getReturnCode(), null);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlogger.error(\"Error executing git command\", e);\n\t\t\t\t\tonExit(-1, e.getMessage());\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t});\n\t\t\n\t}\n\t\n\t@Nullable\n\tprotected abstract String checkPermission(Project project);\n\n\tprotected abstract ExecutionResult execute(File gitDir, Map<String, String> gitEnvs);\n\n\t@Override\n\tpublic void destroy(ChannelSession channel) throws Exception {\n\t\tif (future != null)\n\t\t\tfuture.cancel(true);\n\t}\n\n\tprotected void onExit(int exitValue, @Nullable String errorMessage) {\n\t\tif (errorMessage != null)\n\t\t\tnew PrintStream(err).println(\"ERROR: \" + errorMessage);\n\t\tcallback.onExit(exitValue);\n\t}\n\n\t@Override\n\tpublic void setInputStream(InputStream in) {\n\t\tthis.in = new InputStreamWrapper(in) {\n\n\t\t\t@Override\n\t\t\tpublic void close() throws IOException {\n\t\t\t}\n\t\t\t\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setOutputStream(OutputStream out) {\n\t\tthis.out = new OutputStreamWrapper(out) {\n\t\t\t\n\t\t\t@Override\n\t\t\tpublic void close() throws IOException {\n\t\t\t}\n\t\t\t\n\t\t};\n\t\t((ChannelOutputStream) out).setNoDelay(true);\n\t}\n\n\t@Override\n\tpublic void setErrorStream(OutputStream err) {\n\t\tthis.err = new OutputStreamWrapper(err) {\n\t\t\t\n\t\t\t@Override\n\t\t\tpublic void close() throws IOException {\n\t\t\t}\n\t\t\t\n\t\t};\n\t\t((ChannelOutputStream) err).setNoDelay(true);\n\t}\n\n\t@Override\n\tpublic void setExitCallback(ExitCallback callBack) {\n\t\tthis.callback = callBack;\n\t}\n\n\t@Override\n\tpublic void setSession(ServerSession session) {\n\t\tthis.session = session;\n\t}\n\n}\n## 内容4\n### 文件路径:\nserver-core/src/main/java/io/onedev/server/git/GitUtils.java\n### 代码内容:\npackage io.onedev.server.git;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Nullable;\n\nimport org.bouncycastle.bcpg.ArmoredOutputStream;\nimport org.bouncycastle.bcpg.BCPGOutputStream;\nimport org.bouncycastle.bcpg.HashAlgorithmTags;\nimport org.bouncycastle.jce.provider.BouncyCastleProvider;\nimport org.bouncycastle.openpgp.PGPCompressedData;\nimport org.bouncycastle.openpgp.PGPException;\nimport org.bouncycastle.openpgp.PGPPrivateKey;\nimport org.bouncycastle.openpgp.PGPPublicKey;\nimport org.bouncycastle.openpgp.PGPSecretKeyRing;\nimport org.bouncycastle.openpgp.PGPSignature;\nimport org.bouncycastle.openpgp.PGPSignatureGenerator;\nimport org.bouncycastle.openpgp.PGPSignatureList;\nimport org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;\nimport org.bouncycastle.openpgp.PGPUtil;\nimport org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;\nimport org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;\nimport org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;\nimport org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;\nimport org.eclipse.jgit.diff.DiffEntry;\nimport org.eclipse.jgit.diff.DiffEntry.ChangeType;\nimport org.eclipse.jgit.diff.DiffFormatter;\nimport org.eclipse.jgit.diff.RawTextComparator;\nimport org.eclipse.jgit.errors.AmbiguousObjectException;\nimport org.eclipse.jgit.errors.IncorrectObjectTypeException;\nimport org.eclipse.jgit.errors.MissingObjectException;\nimport org.eclipse.jgit.errors.RevisionSyntaxException;\nimport org.eclipse.jgit.lib.AnyObjectId;\nimport org.eclipse.jgit.lib.CommitBuilder;\nimport org.eclipse.jgit.lib.Constants;\nimport org.eclipse.jgit.lib.FileMode;\nimport org.eclipse.jgit.lib.GpgSignature;\nimport org.eclipse.jgit.lib.ObjectBuilder;\nimport org.eclipse.jgit.lib.ObjectId;\nimport org.eclipse.jgit.lib.ObjectInserter;\nimport org.eclipse.jgit.lib.ObjectReader;\nimport org.eclipse.jgit.lib.PersonIdent;\nimport org.eclipse.jgit.lib.RefUpdate;\nimport org.eclipse.jgit.lib.Repository;\nimport org.eclipse.jgit.merge.MergeStrategy;\nimport org.eclipse.jgit.merge.Merger;\nimport org.eclipse.jgit.merge.ResolveMerger;\nimport org.eclipse.jgit.revwalk.RevCommit;\nimport org.eclipse.jgit.revwalk.RevObject;\nimport org.eclipse.jgit.revwalk.RevTag;\nimport org.eclipse.jgit.revwalk.RevWalk;\nimport org.eclipse.jgit.revwalk.RevWalkUtils;\nimport org.eclipse.jgit.revwalk.filter.RevFilter;\nimport org.eclipse.jgit.treewalk.CanonicalTreeParser;\nimport org.eclipse.jgit.treewalk.TreeWalk;\nimport org.eclipse.jgit.treewalk.filter.TreeFilter;\nimport org.eclipse.jgit.util.RawParseUtils;\nimport org.eclipse.jgit.util.SystemReader;\nimport org.eclipse.jgit.util.io.NullOutputStream;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.base.Joiner;\nimport com.google.common.base.Objects;\nimport com.google.common.base.Preconditions;\nimport com.google.common.base.Splitter;\nimport com.google.common.base.Strings;\nimport com.google.common.collect.Iterables;\n\nimport io.onedev.commons.utils.PathUtils;\nimport io.onedev.server.git.command.FetchCommand;\nimport io.onedev.server.git.command.IsAncestorCommand;\nimport io.onedev.server.git.exception.ObsoleteCommitException;\nimport io.onedev.server.git.exception.RefUpdateException;\nimport io.onedev.server.git.signature.SignatureUnverified;\nimport io.onedev.server.git.signature.SignatureVerification;\nimport io.onedev.server.git.signature.SignatureVerificationKey;\nimport io.onedev.server.git.signature.SignatureVerificationKeyLoader;\nimport io.onedev.server.git.signature.SignatureVerified;\nimport io.onedev.server.util.GpgUtils;\n\npublic class GitUtils {\n\n\tpublic static final int SHORT_SHA_LENGTH = 8;\n\t\n\tprivate static final Logger logger = LoggerFactory.getLogger(GitUtils.class);\n\t\n\tpublic static boolean isEmptyPath(String path) {\n\t\treturn Strings.isNullOrEmpty(path) || Objects.equal(path, DiffEntry.DEV_NULL);\n\t}\n\n\tpublic static String abbreviateSHA(String sha, int length) {\n\t\tPreconditions.checkArgument(ObjectId.isId(sha));\n\t\treturn sha.substring(0, length);\n\t}\n\n\tpublic static String abbreviateSHA(String sha) {\n\t\treturn abbreviateSHA(sha, SHORT_SHA_LENGTH);\n\t}\n\n\tpublic static List<DiffEntry> diff(Repository repository, AnyObjectId oldRevId, AnyObjectId newRevId) {\n\t\tList<DiffEntry> diffs = new ArrayList<>();\n\t\ttry (DiffFormatter diffFormatter = new DiffFormatter(NullOutputStream.INSTANCE);\n\t\t\t\tRevWalk revWalk = new RevWalk(repository);\n\t\t\t\tObjectReader reader = repository.newObjectReader();) {\n\t\t\tdiffFormatter.setRepository(repository);\n\t\t\tdiffFormatter.setDetectRenames(true);\n\t\t\tdiffFormatter.setDiffComparator(RawTextComparator.DEFAULT);\n\n\t\t\tCanonicalTreeParser oldTreeParser = new CanonicalTreeParser();\n\t\t\tif (!oldRevId.equals(ObjectId.zeroId()))\n\t\t\t\toldTreeParser.reset(reader, revWalk.parseCommit(oldRevId).getTree());\n\n\t\t\tCanonicalTreeParser newTreeParser = new CanonicalTreeParser();\n\t\t\tif (!newRevId.equals(ObjectId.zeroId()))\n\t\t\t\tnewTreeParser.reset(reader, revWalk.parseCommit(newRevId).getTree());\n\n\t\t\tfor (DiffEntry entry : diffFormatter.scan(oldTreeParser, newTreeParser)) {\n\t\t\t\tif (!Objects.equal(entry.getOldPath(), entry.getNewPath())\n\t\t\t\t\t\t|| !Objects.equal(entry.getOldMode(), entry.getNewMode()) || entry.getOldId() == null\n\t\t\t\t\t\t|| !entry.getOldId().isComplete() || entry.getNewId() == null || !entry.getNewId().isComplete()\n\t\t\t\t\t\t|| !entry.getOldId().equals(entry.getNewId())) {\n\t\t\t\t\tdiffs.add(entry);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t\treturn diffs;\n\t}\n\n\t@Nullable\n\tpublic static String getDetailMessage(RevCommit commit) {\n\t\tint start = 0;\n\t\tString fullMessage = commit.getFullMessage();\n\t\twhile (true) {\n\t\t\tint index = fullMessage.indexOf('\\n', start);\n\t\t\tif (index == -1)\n\t\t\t\treturn null;\n\t\t\tstart = index + 1;\n\t\t\tint nextIndex = fullMessage.indexOf('\\n', start);\n\t\t\tif (nextIndex == -1)\n\t\t\t\treturn null;\n\t\t\tstart = nextIndex + 1;\n\t\t\tif (fullMessage.substring(index, nextIndex).trim().length() == 0) {\n\t\t\t\tString detailMessage = fullMessage.substring(start).trim();\n\t\t\t\treturn detailMessage.length() != 0 ? detailMessage : null;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static String getBlobTypeName(int blobType) {\n\t\tif (blobType == FileMode.TYPE_FILE)\n\t\t\treturn \"File\";\n\t\telse if (blobType == FileMode.TYPE_GITLINK)\n\t\t\treturn \"Sub module\";\n\t\telse if (blobType == FileMode.TYPE_SYMLINK)\n\t\t\treturn \"Symbol link\";\n\t\telse\n\t\t\treturn \"Folder\";\n\t}\n\n\tpublic static PersonIdent newPersonIdent(String name, String email, Date when) {\n\t\treturn new PersonIdent(name, email, when.getTime(), SystemReader.getInstance().getTimezone(when.getTime()));\n\t}\n\n\t/**\n\t * Parse the original git raw date to Java date. The raw git date is in unix\n\t * timestamp with timezone like: 1392312299 -0800\n\t * \n\t * @param input the input raw date string\n\t * @return Java date\n\t */\n\tpublic static Date parseRawDate(String input) {\n\t\tString[] pieces = Iterables.toArray(Splitter.on(\" \").split(input), String.class);\n\t\treturn new Date(Long.valueOf(pieces[0]) * 1000L);\n\t}\n\n\t/**\n\t * Parse the raw user information into PersonIdent object, the raw information\n\t * should be in format <code>[name] [<email>] [epoch timezone]</code>, for\n\t * example:\n\t * \n\t * Jacob Thornton <jacobthornton@gmail.com> 1328060294 -0800\n\t * \n\t * @param raw\n\t * @return\n\t */\n\tpublic static @Nullable PersonIdent parsePersonIdent(String raw) {\n\t\tif (Strings.isNullOrEmpty(raw))\n\t\t\treturn null;\n\n\t\tint pos1 = raw.indexOf('<');\n\t\tif (pos1 <= 0)\n\t\t\tthrow new IllegalArgumentException(\"Raw \" + raw);\n\n\t\tString name = raw.substring(0, pos1 - 1);\n\n\t\tint pos2 = raw.indexOf('>');\n\t\tif (pos2 <= 0)\n\t\t\tthrow new IllegalArgumentException(\"Raw \" + raw);\n\n\t\tString time = raw.substring(pos2 + 1).trim();\n\t\tDate when = parseRawDate(time);\n\n\t\tString email = raw.substring(pos1 + 1, pos2 - 1);\n\n\t\treturn newPersonIdent(name, email, when);\n\t}\n\n\tpublic static int comparePath(@Nullable String path1, @Nullable String path2) {\n\t\tList<String> segments1 = splitPath(path1);\n\t\tList<String> segments2 = splitPath(path2);\n\n\t\tint index = 0;\n\t\tfor (String segment1 : segments1) {\n\t\t\tif (index < segments2.size()) {\n\t\t\t\tint result = segment1.compareTo(segments2.get(index));\n\t\t\t\tif (result != 0)\n\t\t\t\t\treturn result;\n\t\t\t} else {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tindex++;\n\t\t}\n\t\tif (index < segments2.size())\n\t\t\treturn -1;\n\t\telse\n\t\t\treturn 0;\n\t}\n\n\tpublic static List<String> splitPath(@Nullable String path) {\n\t\tList<String> pathSegments;\n\t\tif (path != null)\n\t\t\tpathSegments = Splitter.on(\"/\").omitEmptyStrings().splitToList(path);\n\t\telse\n\t\t\tpathSegments = new ArrayList<>();\n\t\treturn pathSegments;\n\t}\n\n\tpublic static @Nullable String normalizePath(@Nullable String path) {\n\t\tList<String> pathSegments = splitPath(PathUtils.normalizeDots(path));\n\t\tif (!pathSegments.isEmpty())\n\t\t\treturn Joiner.on(\"/\").join(pathSegments);\n\t\telse\n\t\t\treturn null;\n\t}\n\n\t/**\n\t * Convert a git reference name to branch name.\n\t * \n\t * @param refName name of the git reference\n\t * @return name of the branch, or <tt>null</tt> if specified ref does not\n\t * represent a branch\n\t */\n\tpublic static @Nullable String ref2branch(String refName) {\n\t\tif (refName.startsWith(Constants.R_HEADS))\n\t\t\treturn refName.substring(Constants.R_HEADS.length());\n\t\telse\n\t\t\treturn null;\n\t}\n\n\tpublic static String branch2ref(String branch) {\n\t\treturn Constants.R_HEADS + branch;\n\t}\n\n\t/**\n\t * Convert a git reference name to tag name.\n\t * \n\t * @param refName name of the git reference\n\t * @return name of the tag, or <tt>null</tt> if specified ref does not represent\n\t * a tag\n\t */\n\tpublic static @Nullable String ref2tag(String refName) {\n\t\tif (refName.startsWith(Constants.R_TAGS))\n\t\t\treturn refName.substring(Constants.R_TAGS.length());\n\t\telse\n\t\t\treturn null;\n\t}\n\n\tpublic static String tag2ref(String tag) {\n\t\treturn Constants.R_TAGS + tag;\n\t}\n\n\tpublic static BlobIdent getOldBlobIdent(DiffEntry diffEntry, String oldRev) {\n\t\tBlobIdent blobIdent;\n\t\tif (diffEntry.getChangeType() != ChangeType.ADD) {\n\t\t\tblobIdent = new BlobIdent(oldRev, diffEntry.getOldPath(), diffEntry.getOldMode().getBits());\n\t\t} else {\n\t\t\tblobIdent = new BlobIdent(oldRev, null, null);\n\t\t}\n\t\treturn blobIdent;\n\t}\n\n\tpublic static BlobIdent getNewBlobIdent(DiffEntry diffEntry, String newRev) {\n\t\tBlobIdent blobIdent;\n\t\tif (diffEntry.getChangeType() != ChangeType.DELETE) {\n\t\t\tblobIdent = new BlobIdent(newRev, diffEntry.getNewPath(), diffEntry.getNewMode().getBits());\n\t\t} else {\n\t\t\tblobIdent = new BlobIdent(newRev, null, null);\n\t\t}\n\t\treturn blobIdent;\n\t}\n\n\t/**\n\t * @return merge base of specified commits, or <tt>null</tt> if two commits do\n\t * not have related history. In this case, these two commits can not be\n\t * merged\n\t */\n\t@Nullable\n\tpublic static ObjectId getMergeBase(Repository repository, ObjectId commitId1, ObjectId commitId2) {\n\t\ttry (RevWalk revWalk = new RevWalk(repository)) {\n\t\t\trevWalk.setRevFilter(RevFilter.MERGE_BASE);\n\n\t\t\trevWalk.markStart(revWalk.parseCommit(commitId1));\n\t\t\trevWalk.markStart(revWalk.parseCommit(commitId2));\n\t\t\tRevCommit mergeBase = revWalk.next();\n\t\t\treturn mergeBase != null ? mergeBase.copy() : null;\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\t/**\n\t * @return merge base of specified commits, or <tt>null</tt> if two commits do\n\t * not have related history. In this case, these two commits can not be\n\t * merged\n\t */\n\t@Nullable\n\tpublic static ObjectId getMergeBase(Repository repository1, ObjectId commit1, Repository repository2,\n\t\t\tObjectId commit2) {\n\t\tif (repository1.getDirectory() == null || !repository1.getDirectory().equals(repository2.getDirectory())) {\n\t\t\tfetch(repository2, commit2, repository1);\n\t\t}\n\t\treturn GitUtils.getMergeBase(repository1, commit1, commit2);\n\t}\n\n\tpublic static void fetch(Repository fromRepository, ObjectId fromCommit, Repository toRepository) {\n\t\tnew FetchCommand(toRepository.getDirectory(), null)\n\t\t\t\t.from(fromRepository.getDirectory().getAbsolutePath())\n\t\t\t\t.refspec(fromCommit.name())\n\t\t\t\t.force(true)\n\t\t\t\t.quiet(true)\n\t\t\t\t.call();\n\t}\n\n\tpublic static boolean isMergedInto(Repository repository, @Nullable Map<String, String> gitEnvs, ObjectId base,\n\t\t\tObjectId tip) {\n\t\tif (gitEnvs != null && !gitEnvs.isEmpty()) {\n\t\t\tIsAncestorCommand cmd = new IsAncestorCommand(repository.getDirectory(), gitEnvs);\n\t\t\tcmd.ancestor(base.name()).descendant(tip.name());\n\t\t\treturn cmd.call();\n\t\t} else {\n\t\t\ttry (RevWalk revWalk = new RevWalk(repository)) {\n\t\t\t\tRevCommit baseCommit;\n\t\t\t\ttry {\n\t\t\t\t\tbaseCommit = revWalk.parseCommit(base);\n\t\t\t\t} catch (MissingObjectException e) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn revWalk.isMergedInto(baseCommit, revWalk.parseCommit(tip));\n\t\t\t} catch (IOException e) {\n\t\t\t\tthrow new RuntimeException(e);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get commit of specified revision id.\n\t * \n\t * @param revWalk\n\t * @param revId\n\t * @return <tt>null</tt> if specified id does not exist or does not represent a\n\t * commit\n\t */\n\t@Nullable\n\tpublic static RevCommit parseCommit(RevWalk revWalk, ObjectId revId) {\n\t\tRevObject peeled;\n\t\ttry {\n\t\t\tpeeled = revWalk.peel(revWalk.parseAny(revId));\n\t\t\tif (peeled instanceof RevCommit)\n\t\t\t\treturn (RevCommit) peeled;\n\t\t\telse\n\t\t\t\treturn null;\n\t\t} catch (MissingObjectException e) {\n\t\t\treturn null;\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\t@Nullable\n\tpublic static ObjectId resolve(Repository repository, String revision) {\n\t\ttry {\n\t\t\treturn repository.resolve(revision);\n\t\t} catch (RevisionSyntaxException | AmbiguousObjectException | IncorrectObjectTypeException e) {\n\t\t\treturn null;\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\t@Nullable\n\tpublic static ObjectId rebase(Repository repository, ObjectId source, ObjectId target, PersonIdent committer) {\n\t\ttry (RevWalk revWalk = new RevWalk(repository); ObjectInserter inserter = repository.newObjectInserter();) {\n\t\t\tRevCommit sourceCommit = revWalk.parseCommit(source);\n\t\t\tRevCommit targetCommit = revWalk.parseCommit(target);\n\t\t\trevWalk.setRevFilter(RevFilter.NO_MERGES);\n\t\t\tList<RevCommit> commits = RevWalkUtils.find(revWalk, sourceCommit, targetCommit);\n\t\t\tCollections.reverse(commits);\n\t\t\tRevCommit headCommit = targetCommit;\n\t\t\tfor (RevCommit commit : commits) {\n\t\t\t\tResolveMerger merger = (ResolveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);\n\t\t\t\tmerger.setBase(commit.getParent(0));\n\t\t\t\tif (merger.merge(headCommit, commit)) {\n\t\t\t\t\tif (!headCommit.getTree().getId().equals(merger.getResultTreeId())) {\n\t\t\t\t\t\tif (!commit.getTree().getId().equals(merger.getResultTreeId())\n\t\t\t\t\t\t\t\t|| !commit.getParent(0).equals(headCommit)) {\n\t\t\t\t\t\t\tCommitBuilder commitBuilder = new CommitBuilder();\n\t\t\t\t\t\t\tcommitBuilder.setAuthor(commit.getAuthorIdent());\n\t\t\t\t\t\t\tcommitBuilder.setCommitter(committer);\n\t\t\t\t\t\t\tcommitBuilder.setParentId(headCommit);\n\t\t\t\t\t\t\tcommitBuilder.setMessage(commit.getFullMessage());\n\t\t\t\t\t\t\tcommitBuilder.setTreeId(merger.getResultTreeId());\n\t\t\t\t\t\t\theadCommit = revWalk.parseCommit(inserter.insert(commitBuilder));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\theadCommit = commit;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t\tinserter.flush();\n\t\t\treturn headCommit.copy();\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\t@Nullable\n\tpublic static ObjectId merge(Repository repository, ObjectId targetCommitId, ObjectId sourceCommitId,\n\t\t\tboolean squash, PersonIdent committer, PersonIdent author, String commitMessage,\n\t\t\tboolean useOursOnConflict) {\n\t\ttry (RevWalk revWalk = new RevWalk(repository); ObjectInserter inserter = repository.newObjectInserter();) {\n\t\t\tRevCommit sourceCommit = revWalk.parseCommit(sourceCommitId);\n\t\t\tRevCommit targetCommit = revWalk.parseCommit(targetCommitId);\n\t\t\tMerger merger;\n\t\t\tif (useOursOnConflict)\n\t\t\t\tmerger = MergeStrategy.OURS.newMerger(repository, true);\n\t\t\telse\n\t\t\t\tmerger = MergeStrategy.RECURSIVE.newMerger(repository, true);\n\t\t\tif (merger.merge(targetCommit, sourceCommit)) {\n\t\t\t\tCommitBuilder mergedCommit = new CommitBuilder();\n\t\t\t\tmergedCommit.setAuthor(author);\n\t\t\t\tmergedCommit.setCommitter(committer);\n\t\t\t\tif (squash)\n\t\t\t\t\tmergedCommit.setParentId(targetCommit);\n\t\t\t\telse\n\t\t\t\t\tmergedCommit.setParentIds(targetCommit, sourceCommit);\n\t\t\t\tmergedCommit.setMessage(commitMessage);\n\t\t\t\tmergedCommit.setTreeId(merger.getResultTreeId());\n\t\t\t\tObjectId mergedCommitId = inserter.insert(mergedCommit);\n\t\t\t\tinserter.flush();\n\t\t\t\treturn mergedCommitId;\n\t\t\t} else {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tpublic static Collection<String> getChangedFiles(Repository repository, ObjectId oldCommitId,\n\t\t\tObjectId newCommitId) {\n\t\tCollection<String> changedFiles = new HashSet<>();\n\t\ttry (RevWalk revWalk = new RevWalk(repository); TreeWalk treeWalk = new TreeWalk(repository)) {\n\t\t\ttreeWalk.setFilter(TreeFilter.ANY_DIFF);\n\t\t\ttreeWalk.setRecursive(true);\n\t\t\tRevCommit oldCommit = revWalk.parseCommit(oldCommitId);\n\t\t\tRevCommit newCommit = revWalk.parseCommit(newCommitId);\n\t\t\ttreeWalk.addTree(oldCommit.getTree());\n\t\t\ttreeWalk.addTree(newCommit.getTree());\n\t\t\twhile (treeWalk.next()) {\n\t\t\t\tchangedFiles.add(treeWalk.getPathString());\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t\treturn changedFiles;\n\t}\n\n\tpublic static boolean isValid(File gitDir) {\n\t\treturn new File(gitDir, \"objects\").exists();\n\t}\n\n\tpublic static RefUpdate getRefUpdate(Repository repository, String refName) {\n\t\ttry {\n\t\t\treturn repository.updateRef(refName);\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tpublic static void updateRef(RefUpdate refUpdate) {\n\t\ttry {\n\t\t\tRefUpdate.Result result = refUpdate.forceUpdate();\n\t\t\tif (result == RefUpdate.Result.LOCK_FAILURE && refUpdate.getExpectedOldObjectId() != null\n\t\t\t\t\t&& !refUpdate.getExpectedOldObjectId().equals(refUpdate.getOldObjectId())) {\n\t\t\t\tthrow new ObsoleteCommitException(refUpdate.getOldObjectId());\n\t\t\t} else if (result != RefUpdate.Result.FAST_FORWARD && result != RefUpdate.Result.FORCED\n\t\t\t\t\t&& result != RefUpdate.Result.NEW && result != RefUpdate.Result.NO_CHANGE) {\n\t\t\t\tthrow new RefUpdateException(result);\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tpublic static void deleteRef(RefUpdate refUpdate) {\n\t\ttry {\n\t\t\trefUpdate.setForceUpdate(true);\n\t\t\tRefUpdate.Result result = refUpdate.delete();\n\t\t\tif (result == RefUpdate.Result.LOCK_FAILURE && refUpdate.getExpectedOldObjectId() != null\n\t\t\t\t\t&& !refUpdate.getExpectedOldObjectId().equals(refUpdate.getOldObjectId())) {\n\t\t\t\tthrow new ObsoleteCommitException(refUpdate.getOldObjectId());\n\t\t\t} else if (result != RefUpdate.Result.FAST_FORWARD && result != RefUpdate.Result.FORCED\n\t\t\t\t\t&& result != RefUpdate.Result.NEW && result != RefUpdate.Result.NO_CHANGE) {\n\t\t\t\tthrow new RefUpdateException(result);\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tpublic static void linkRef(RefUpdate refUpdate, String target) {\n\t\ttry {\n\t\t\tRefUpdate.Result result = refUpdate.link(target);\n\t\t\tif (result != RefUpdate.Result.FORCED && result != RefUpdate.Result.NEW\n\t\t\t\t\t&& result != RefUpdate.Result.NO_CHANGE)\n\t\t\t\tthrow new RefUpdateException(result);\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\t\n\tpublic static void sign(ObjectBuilder object, PGPSecretKeyRing signingKey) {\n\t\tJcePBESecretKeyDecryptorBuilder decryptorBuilder = new JcePBESecretKeyDecryptorBuilder()\n\t\t\t\t.setProvider(BouncyCastleProvider.PROVIDER_NAME);\n\t\tPGPPrivateKey privateKey;\n\t\ttry {\n\t\t\tprivateKey = signingKey.getSecretKey().extractPrivateKey(\n\t\t\t\t\tdecryptorBuilder.build(new char[0]));\n\t\t} catch (PGPException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t\t\n\t\tPGPPublicKey publicKey = signingKey.getPublicKey();\n\t\t\n\t\tPGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(\n\t\t\t\tnew JcaPGPContentSignerBuilder(publicKey.getAlgorithm(), HashAlgorithmTags.SHA256)\n\t\t\t\t\t\t.setProvider(BouncyCastleProvider.PROVIDER_NAME));\n\t\ttry {\n\t\t\tsignatureGenerator.init(PGPSignature.BINARY_DOCUMENT, privateKey);\n\t\t\tPGPSignatureSubpacketGenerator subpackets = new PGPSignatureSubpacketGenerator();\n\t\t\tsubpackets.setIssuerFingerprint(false, publicKey);\n\t\t\t\n\t\t\tString emailAddress = GpgUtils.getEmailAddress(publicKey.getUserIDs().next());\n\t\t\tsubpackets.addSignerUserID(false, emailAddress);\n\t\t\t\n\t\t\tsignatureGenerator.setHashedSubpackets(subpackets.generate());\n\t\t\tByteArrayOutputStream buffer = new ByteArrayOutputStream();\n\t\t\ttry (BCPGOutputStream out = new BCPGOutputStream(new ArmoredOutputStream(buffer))) {\n\t\t\t\tsignatureGenerator.update(object.build());\n\t\t\t\tsignatureGenerator.generate().encode(out);\n\t\t\t}\n\t\t\tobject.setGpgSignature(new GpgSignature(buffer.toByteArray()));\t\t\n\t\t} catch (IOException | PGPException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\t\n\t@Nullable\n\tpublic static SignatureVerification verifyTagSignature(byte[] raw, \n\t\t\tSignatureVerificationKeyLoader keyLoader) {\n\t\tbyte[] signatureData = TagParser.getRawGpgSignature(raw); \n\t\tif (signatureData == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// The signature is just tacked onto the end of the message, which\n\t\t// is last in the buffer.\n\t\tbyte[] data = Arrays.copyOfRange(raw, 0, raw.length - signatureData.length);\n\t\t\n\t\tPersonIdent taggerIdent = TagParser.getTaggerIdent(raw);\n\t\tif (taggerIdent == null)\n\t\t\treturn null;\n\t\t\n\t\treturn verify(data, signatureData, taggerIdent.getEmailAddress(), keyLoader);\n\t}\n\t\n\t/*\n\t * Most logic here is copied from JGit\n\t */\n\t@Nullable\n\tpublic static SignatureVerification verifyCommitSignature(byte[] raw, \n\t\t\tSignatureVerificationKeyLoader keyLoader) {\n\t\tbyte[] header = {'g', 'p', 'g', 's', 'i', 'g'};\n\t\tint start = RawParseUtils.headerStart(header, raw, 0);\n\t\tif (start < 0)\n\t\t\treturn null;\n\t\tint end = RawParseUtils.headerEnd(raw, start);\n\t\tbyte[] signatureData = Arrays.copyOfRange(raw, start, end);\n\t\t\n\t\t// start is at the beginning of the header's content\n\t\tstart -= header.length + 1;\n\t\t// end is on the terminating LF; we need to skip that, too\n\t\tif (end < raw.length) {\n\t\t\tend++;\n\t\t}\n\t\tbyte[] data = new byte[raw.length - (end - start)];\n\t\tSystem.arraycopy(raw, 0, data, 0, start);\n\t\tSystem.arraycopy(raw, end, data, start, raw.length - end);\n\n\t\tint nameB = RawParseUtils.committer(raw, 0);\n\t\tif (nameB < 0)\n\t\t\treturn null;\n\t\tPersonIdent committerIdent = RawParseUtils.parsePersonIdent(raw, nameB);\n\t\tString emailAddress = committerIdent.getEmailAddress();\n\t\treturn verify(data, signatureData, emailAddress, keyLoader);\n\t}\n\t\n\t@Nullable\n\tpublic static SignatureVerification verifySignature(RevObject object, SignatureVerificationKeyLoader keyLoader) {\n\t\tif (object instanceof RevCommit) {\n\t\t\tRevCommit commit = (RevCommit) object;\n\t\t\treturn verifyCommitSignature(commit.getRawBuffer(), keyLoader);\n\t\t} else if (object instanceof RevTag) {\n\t\t\tRevTag tag = (RevTag) object;\n\t\t\treturn verifyTagSignature(tag.getRawBuffer(), keyLoader);\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate static PGPSignature parseSignature(InputStream in) throws IOException, PGPException {\n\t\ttry (InputStream sigIn = PGPUtil.getDecoderStream(in)) {\n\t\t\tJcaPGPObjectFactory pgpFactory = new JcaPGPObjectFactory(sigIn);\n\t\t\tObject obj = pgpFactory.nextObject();\n\t\t\tif (obj instanceof PGPCompressedData) {\n\t\t\t\tobj = new JcaPGPObjectFactory(((PGPCompressedData) obj).getDataStream()).nextObject();\n\t\t\t}\n\t\t\tif (obj instanceof PGPSignatureList) {\n\t\t\t\treturn ((PGPSignatureList) obj).get(0);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate static SignatureVerification verify(byte[] data, byte[] signatureData, String dataWriter, \n\t\t\tSignatureVerificationKeyLoader keyLoader) {\n\t\ttry (InputStream sigIn = new ByteArrayInputStream(signatureData)) {\n\t\t\tPGPSignature signature = parseSignature(sigIn);\n\t\t\tif (signature != null) {\n\t\t\t\tSignatureVerificationKey key = keyLoader.getSignatureVerificationKey(signature.getKeyID());\n\t\t\t\tif (key != null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tsignature.init(\n\t\t\t\t\t\t\t\tnew JcaPGPContentVerifierBuilderProvider().setProvider(BouncyCastleProvider.PROVIDER_NAME),\n\t\t\t\t\t\t\t\tkey.getPublicKey());\n\t\t\t\t\t\tsignature.update(data);\n\t\t\t\t\t\tif (signature.verify()) {\n\t\t\t\t\t\t\tif (!key.shouldVerifyDataWriter() || key.getEmailAddress().equals(dataWriter)) \n\t\t\t\t\t\t\t\treturn new SignatureVerified(key);\n\t\t\t\t\t\t\telse \n\t\t\t\t\t\t\t\treturn new SignatureUnverified(key, \"Email address of signing key and committer is different\");\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treturn new SignatureUnverified(key, \"Invalid commit signature\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (PGPException e) {\n\t\t\t\t\t\tlogger.error(\"Commit signature verification failed\", e);\n\t\t\t\t\t\treturn new SignatureUnverified(key, \"Signature verification failed\");\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\treturn new SignatureUnverified(null, \"Signature is signed with an unknown key \"\n\t\t\t\t\t\t\t+ \"(key ID: \" + GpgUtils.getKeyIDString(signature.getKeyID()) + \")\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn new SignatureUnverified(null, \"Signature does not decode into a signature object\");\n\t\t\t}\n\t\t} catch (PGPException e) {\n\t\t\tlogger.error(\"Error parsing commit signature\", e);\n\t\t\treturn new SignatureUnverified(null, \"Signature cannot be parsed\");\n\t\t} catch (IOException e2) {\n\t\t\tthrow new RuntimeException(e2);\n\t\t}\n\t}\n\t\n\t/*\n\t * Copied from JGit\n\t */\n\tprivate static class TagParser {\n\t\t\n\t\tprivate static final byte[] hSignature = Constants.encodeASCII(\"-----BEGIN PGP SIGNATURE-----\");\n\n\t\tprivate static int nextStart(byte[] prefix, byte[] buffer, int from) {\n\t\t\tint stop = buffer.length - prefix.length + 1;\n\t\t\tint ptr = from;\n\t\t\tif (ptr > 0) {\n\t\t\t\tptr = RawParseUtils.nextLF(buffer, ptr - 1);\n\t\t\t}\n\t\t\twhile (ptr < stop) {\n\t\t\t\tint lineStart = ptr;\n\t\t\t\tboolean found = true;\n\t\t\t\tfor (byte element : prefix) {\n\t\t\t\t\tif (element != buffer[ptr++]) {\n\t\t\t\t\t\tfound = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (found) {\n\t\t\t\t\treturn lineStart;\n\t\t\t\t}\n\t\t\t\tdo {\n\t\t\t\t\tptr = RawParseUtils.nextLF(buffer, ptr);\n\t\t\t\t} while (ptr < stop && buffer[ptr] == '\\n');\n\t\t\t}\n\t\t\treturn -1;\n\t\t}\n\n\t\tprivate static int getSignatureStart(byte[] raw) {\n\t\t\tint msgB = RawParseUtils.tagMessage(raw, 0);\n\t\t\tif (msgB < 0) {\n\t\t\t\treturn msgB;\n\t\t\t}\n\t\t\t// Find the last signature start and return the rest\n\t\t\tint start = nextStart(hSignature, raw, msgB);\n\t\t\tif (start < 0) {\n\t\t\t\treturn start;\n\t\t\t}\n\t\t\tint next = RawParseUtils.nextLF(raw, start);\n\t\t\twhile (next < raw.length) {\n\t\t\t\tint newStart = nextStart(hSignature, raw, next);\n\t\t\t\tif (newStart < 0) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tstart = newStart;\n\t\t\t\tnext = RawParseUtils.nextLF(raw, start);\n\t\t\t}\n\t\t\treturn start;\n\t\t}\n\t\t\n\t\tprivate static byte[] getRawGpgSignature(byte[] raw) {\n\t\t\tint start = getSignatureStart(raw);\n\t\t\tif (start < 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn Arrays.copyOfRange(raw, start, raw.length);\n\t\t}\n\n\t\tprivate static PersonIdent getTaggerIdent(byte[] raw) {\n\t\t\tint nameB = RawParseUtils.tagger(raw, 0);\n\t\t\tif (nameB < 0)\n\t\t\t\treturn null;\n\t\t\treturn RawParseUtils.parsePersonIdent(raw, nameB);\n\t\t}\n\t\t\n\t}\n\t\n}\n\n## 内容5\n### 文件路径:\nserver-core/src/main/java/io/onedev/server/git/hookcallback/GitPostReceiveCallback.java\n### 代码内容:\npackage io.onedev.server.git.hookcallback;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.List;\n\nimport javax.inject.Inject;\nimport javax.inject.Singleton;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.commons.lang3.tuple.ImmutableTriple;\nimport org.apache.shiro.util.ThreadContext;\nimport org.eclipse.jgit.lib.ObjectId;\nimport org.eclipse.jgit.lib.RefUpdate;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.base.Preconditions;\n\nimport io.onedev.commons.loader.ListenerRegistry;\nimport io.onedev.commons.utils.StringUtils;\nimport io.onedev.server.entitymanager.ProjectManager;\nimport io.onedev.server.event.RefUpdated;\nimport io.onedev.server.git.GitUtils;\nimport io.onedev.server.model.Project;\nimport io.onedev.server.persistence.SessionManager;\nimport io.onedev.server.persistence.annotation.Sessional;\nimport io.onedev.server.security.SecurityUtils;\n\n@SuppressWarnings(\"serial\")\n@Singleton\npublic class GitPostReceiveCallback extends HttpServlet {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(GitPostReceiveCallback.class);\n\t\n public static final String PATH = \"/git-postreceive-callback\";\n \n private final ProjectManager projectManager;\n\n private final ListenerRegistry listenerRegistry;\n \n private final SessionManager sessionManager;\n \n @Inject\n public GitPostReceiveCallback(ProjectManager projectManager, SessionManager sessionManager, ListenerRegistry listenerRegistry) {\n \tthis.projectManager = projectManager;\n \tthis.sessionManager = sessionManager;\n this.listenerRegistry = listenerRegistry;\n }\n\n @Sessional\n @Override\n\tprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n String clientIp = request.getHeader(\"X-Forwarded-For\");\n if (clientIp == null) clientIp = request.getRemoteAddr();\n\n if (!InetAddress.getByName(clientIp).isLoopbackAddress()) {\n response.sendError(HttpServletResponse.SC_FORBIDDEN,\n \"Git hook callbacks can only be accessed from localhost.\");\n return;\n }\n\n List<String> fields = StringUtils.splitAndTrim(request.getPathInfo(), \"/\");\n Preconditions.checkState(fields.size() == 2);\n \n Project project = projectManager.load(Long.valueOf(fields.get(0)));\n Long userId = Long.valueOf(fields.get(1));\n ThreadContext.bind(SecurityUtils.asSubject(userId));\n\n String refUpdateInfo = null;\n Enumeration<String> paramNames = request.getParameterNames();\n while (paramNames.hasMoreElements()) {\n \tString paramName = paramNames.nextElement();\n \tif (paramName.contains(\" \")) {\n \t\trefUpdateInfo = paramName;\n \t} \n }\n Preconditions.checkState(refUpdateInfo != null, \"Git ref update information is not available\");\n\n\t\tOutput output = new Output(response.getOutputStream());\n \n /*\n * If multiple refs are updated, the hook stdin will put each ref update info into\n * a separate line, however the line breaks is omitted when forward the hook stdin\n * to curl via \"@-\", below logic is used to parse these info correctly even \n * without line breaks. \n */\n refUpdateInfo = StringUtils.reverse(StringUtils.remove(refUpdateInfo, '\\n'));\n \n fields.clear();\n fields.addAll(StringUtils.splitAndTrim(refUpdateInfo, \" \"));\n\n List<ImmutableTriple<String, ObjectId, ObjectId>> eventData = new ArrayList<>();\n \n int pos = 0;\n while (true) {\n \tString refName = StringUtils.reverse(fields.get(pos));\n \tpos++;\n \tObjectId newObjectId = ObjectId.fromString(StringUtils.reverse(fields.get(pos)));\n \tpos++;\n \tString field = fields.get(pos);\n \tObjectId oldObjectId = ObjectId.fromString(StringUtils.reverse(field.substring(0, 40)));\n \t\n \tString branch = GitUtils.ref2branch(refName);\n \tif (branch != null && project.getDefaultBranch() == null) {\n \t\tRefUpdate refUpdate = GitUtils.getRefUpdate(project.getRepository(), \"HEAD\");\n \t\tGitUtils.linkRef(refUpdate, refName);\n \t}\n\n \tif (branch != null && project.getDefaultBranch() != null && !branch.equals(project.getDefaultBranch()))\n \t\tshowPullRequestLink(output, project, branch);\n \t\n \teventData.add(new ImmutableTriple<>(refName, oldObjectId, newObjectId));\n \t\t\n \tfield = field.substring(40);\n \tif (field.length() == 0)\n \t\tbreak;\n \telse\n \t\tfields.set(pos, field);\n }\n \n Long projectId = project.getId();\n \n sessionManager.runAsync(new Runnable() {\n\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t try {\n\t\t \t// We can not use a hibernate entity safely in a different thread. Let's reload it \n\t\t Project project = projectManager.load(projectId);\n\n\t\t for (ImmutableTriple<String, ObjectId, ObjectId> each: eventData) {\n\t\t \tString refName = each.getLeft();\n\t\t \tObjectId oldObjectId = each.getMiddle();\n\t\t \tObjectId newObjectId = each.getRight();\n\t\t\t \tif (!newObjectId.equals(ObjectId.zeroId()))\n\t\t\t \t\tproject.cacheObjectId(refName, newObjectId);\n\t\t\t \telse \n\t\t\t \t\tproject.cacheObjectId(refName, null);\n\t\t \t\n\t\t\t \tlistenerRegistry.post(new RefUpdated(project, refName, oldObjectId, newObjectId));\n\t\t }\n\t\t } catch (Exception e) {\n\t\t \tlogger.error(\"Error posting ref updated event\", e);\n\t\t\t\t}\n\t\t\t}\n \t\n });\n\t}\n\n\tprivate void showPullRequestLink(Output output, Project project, String branch) {\n \toutput.writeLine();\n \toutput.writeLine(\"Create a pull request for '\"+ branch +\"' by visiting:\");\n\t\toutput.writeLine(\" \" + project.getUrl() \n\t\t\t\t+\"/pulls/new?target=\" \n\t\t\t\t+ project.getId() \n\t\t\t\t+ \":\" \n\t\t\t\t+ project.getDefaultBranch() \n\t\t\t\t+ \"&source=\" \n\t\t\t\t+ project.getId()\n\t\t\t\t+ \":\"\n\t\t\t\t+ branch);\n\t\toutput.writeLine();\n\t}\n\t\n}\n\n## 内容6\n### 文件路径:\nserver-core/src/main/java/io/onedev/server/git/hookcallback/GitPreReceiveCallback.java\n### 代码内容:\npackage io.onedev.server.git.hookcallback;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Nullable;\nimport javax.inject.Inject;\nimport javax.inject.Singleton;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.eclipse.jgit.lib.Constants;\nimport org.eclipse.jgit.lib.ObjectId;\n\nimport com.google.common.base.Preconditions;\nimport com.google.common.base.Splitter;\nimport com.google.common.collect.Lists;\n\nimport io.onedev.commons.utils.ExplicitException;\nimport io.onedev.commons.utils.StringUtils;\nimport io.onedev.server.entitymanager.ProjectManager;\nimport io.onedev.server.git.GitUtils;\nimport io.onedev.server.model.Project;\nimport io.onedev.server.model.PullRequest;\nimport io.onedev.server.model.PullRequestUpdate;\nimport io.onedev.server.model.User;\nimport io.onedev.server.model.support.BranchProtection;\nimport io.onedev.server.model.support.TagProtection;\nimport io.onedev.server.persistence.annotation.Sessional;\nimport io.onedev.server.security.SecurityUtils;\nimport io.onedev.server.security.permission.ManageProject;\nimport io.onedev.server.security.permission.ProjectPermission;\n\n@SuppressWarnings(\"serial\")\n@Singleton\npublic class GitPreReceiveCallback extends HttpServlet {\n\n\tpublic static final String PATH = \"/git-prereceive-callback\";\n\n\tprivate final ProjectManager projectManager;\n\t\n\t@Inject\n\tpublic GitPreReceiveCallback(ProjectManager projectManager) {\n\t\tthis.projectManager = projectManager;\n\t}\n\t\n\tprivate void error(Output output, @Nullable String refName, List<String> messages) {\n\t\toutput.markError();\n\t\toutput.writeLine();\n\t\toutput.writeLine(\"*******************************************************\");\n\t\toutput.writeLine(\"*\");\n\t\tif (refName != null)\n\t\t\toutput.writeLine(\"* ERROR PUSHING REF: \" + refName);\n\t\telse\n\t\t\toutput.writeLine(\"* ERROR PUSHING\");\n\t\toutput.writeLine(\"-------------------------------------------------------\");\n\t\tfor (String message: messages)\n\t\t\toutput.writeLine(\"* \" + message);\n\t\toutput.writeLine(\"*\");\n\t\toutput.writeLine(\"*******************************************************\");\n\t\toutput.writeLine();\n\t}\n\t\n\t@Sessional\n\t@Override\n\tprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n String clientIp = request.getHeader(\"X-Forwarded-For\");\n if (clientIp == null) clientIp = request.getRemoteAddr();\n\n if (!InetAddress.getByName(clientIp).isLoopbackAddress()) {\n response.sendError(HttpServletResponse.SC_FORBIDDEN,\n \"Git hook callbacks can only be accessed from localhost.\");\n return;\n }\n \n List<String> fields = StringUtils.splitAndTrim(request.getPathInfo(), \"/\");\n Preconditions.checkState(fields.size() == 2);\n \n SecurityUtils.getSubject().runAs(SecurityUtils.asPrincipal(Long.valueOf(fields.get(1))));\n try {\n Project project = projectManager.load(Long.valueOf(fields.get(0)));\n \n String refUpdateInfo = null;\n \n /*\n * Since git 2.11, pushed commits will be placed in to a QUARANTINE directory when pre-receive hook \n * is fired. Current version of jgit does not pick up objects in this directory so we should call \n * native git instead with various environments passed from pre-receive hook \n */\n Map<String, String> gitEnvs = new HashMap<>();\n Enumeration<String> paramNames = request.getParameterNames();\n while (paramNames.hasMoreElements()) {\n \tString paramName = paramNames.nextElement();\n \tif (paramName.contains(\" \")) {\n \t\trefUpdateInfo = paramName;\n \t} else if (paramName.startsWith(\"ENV_\")) {\n \t\tString paramValue = request.getParameter(paramName);\n \t\tif (StringUtils.isNotBlank(paramValue))\n \t\t\tgitEnvs.put(paramName.substring(\"ENV_\".length()), paramValue);\n \t}\n }\n \n Preconditions.checkState(refUpdateInfo != null, \"Git ref update information is not available\");\n \n\t Output output = new Output(response.getOutputStream());\n\t \n\t /*\n\t * If multiple refs are updated, the hook stdin will put each ref update info into\n\t * a separate line, however the line breaks is omitted when forward the hook stdin\n\t * to curl via \"@-\", below logic is used to parse these info correctly even \n\t * without line breaks. \n\t */\n\t refUpdateInfo = StringUtils.reverse(StringUtils.remove(refUpdateInfo, '\\n'));\n\t fields = StringUtils.splitAndTrim(refUpdateInfo, \" \");\n\t \n\t int pos = 0;\n\t while (true) {\n\t \tString refName = StringUtils.reverse(fields.get(pos));\n\t \tpos++;\n\t \tObjectId newObjectId = ObjectId.fromString(StringUtils.reverse(fields.get(pos)));\n\t \tpos++;\n\t \tString field = fields.get(pos);\n\t \tObjectId oldObjectId = ObjectId.fromString(StringUtils.reverse(field.substring(0, 40)));\n\t \t\n\t \t\tUser user = Preconditions.checkNotNull(SecurityUtils.getUser());\n\n\t \t\tif (refName.startsWith(PullRequest.REFS_PREFIX) || refName.startsWith(PullRequestUpdate.REFS_PREFIX)) {\n\t \t\t\tif (!user.asSubject().isPermitted(new ProjectPermission(project, new ManageProject()))) {\n\t \t\t\t\terror(output, refName, Lists.newArrayList(\"Only project administrators can update onedev refs.\"));\n\t \t\t\t\tbreak;\n\t \t\t\t}\n\t \t\t} else if (refName.startsWith(Constants.R_HEADS)) {\n\t \t\t\tString branchName = Preconditions.checkNotNull(GitUtils.ref2branch(refName));\n\t \t\t\tList<String> errorMessages = new ArrayList<>();\n\t \t\t\tBranchProtection protection = project.getHierarchyBranchProtection(branchName, user);\n\t\t\t\t\tif (oldObjectId.equals(ObjectId.zeroId())) {\n\t\t\t\t\t\tif (protection.isPreventCreation()) {\n\t\t\t\t\t\t\terrorMessages.add(\"Can not create this branch according to branch protection setting\");\n\t\t\t\t\t\t} else if (protection.isSignatureRequired() \n\t\t\t\t\t\t\t\t&& !project.hasValidCommitSignature(newObjectId, gitEnvs)) {\n\t\t\t\t\t\t\terrorMessages.add(\"Can not create this branch as branch protection setting \"\n\t\t\t\t\t\t\t\t\t+ \"requires valid signature on head commit\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (newObjectId.equals(ObjectId.zeroId())) {\n\t\t\t\t\t\tif (protection.isPreventDeletion()) \n\t\t\t\t\t\t\terrorMessages.add(\"Can not delete this branch according to branch protection setting\");\n\t\t\t\t\t} else if (protection.isPreventForcedPush() \n\t\t\t\t\t\t\t&& !GitUtils.isMergedInto(project.getRepository(), gitEnvs, oldObjectId, newObjectId)) {\n\t\t\t\t\t\terrorMessages.add(\"Can not force-push to this branch according to branch protection setting\");\n\t\t\t\t\t} else if (protection.isSignatureRequired() \n\t\t\t\t\t\t\t&& !project.hasValidCommitSignature(newObjectId, gitEnvs)) {\n\t\t\t\t\t\terrorMessages.add(\"Can not push to this branch as branch protection rule requires \"\n\t\t\t\t\t\t\t\t+ \"valid signature for head commit\");\n\t\t\t\t\t} else if (protection.isReviewRequiredForPush(user, project, branchName, oldObjectId, newObjectId, gitEnvs)) {\n \t\t\t\t\terrorMessages.add(\"Review required for your change. Please submit pull request instead\");\n\t\t\t\t\t}\n\t \t\t\tif (errorMessages.isEmpty() \n\t \t\t\t\t\t&& !oldObjectId.equals(ObjectId.zeroId()) \n\t \t\t\t\t\t&& !newObjectId.equals(ObjectId.zeroId()) \n\t \t\t\t\t\t&& project.isBuildRequiredForPush(user, branchName, oldObjectId, newObjectId, gitEnvs)) {\n\t \t\t\t\terrorMessages.add(\"Build required for your change. Please submit pull request instead\");\n\t \t\t\t}\n\t \t\t\tif (errorMessages.isEmpty() && newObjectId.equals(ObjectId.zeroId())) {\n\t \t\t\t\ttry {\n\t \t\t\t\t\tprojectManager.onDeleteBranch(project, branchName);\n\t \t\t\t\t} catch (ExplicitException e) {\n\t \t\t\t\t\terrorMessages.addAll(Splitter.on(\"\\n\").splitToList(e.getMessage()));\n\t \t\t\t\t}\n\t \t\t\t}\n\t\t\t\t\tif (!errorMessages.isEmpty())\n\t\t\t\t\t\terror(output, refName, errorMessages);\n\t \t\t} else if (refName.startsWith(Constants.R_TAGS)) {\n\t \t\t\tString tagName = Preconditions.checkNotNull(GitUtils.ref2tag(refName));\n\t \t\t\tList<String> errorMessages = new ArrayList<>();\n\t \t\t\tTagProtection protection = project.getHierarchyTagProtection(tagName, user);\n\t\t\t\t\tif (oldObjectId.equals(ObjectId.zeroId())) {\n\t\t\t\t\t\tif (protection.isPreventCreation()) {\n\t\t\t\t\t\t\terrorMessages.add(\"Can not create this tag according to tag protection setting\");\n\t\t\t\t\t\t} else if (protection.isSignatureRequired() \n\t\t\t\t\t\t\t\t&& !project.hasValidTagSignature(newObjectId, gitEnvs)) {\n\t\t\t\t\t\t\terrorMessages.add(\"Can not create this tag as tag protection setting requires \"\n\t\t\t\t\t\t\t\t\t+ \"valid tag signature\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (newObjectId.equals(ObjectId.zeroId())) {\n\t\t\t\t\t\tif (protection.isPreventDeletion())\n\t\t\t\t\t\t\terrorMessages.add(\"Can not delete this tag according to tag protection setting\");\n\t\t\t\t\t} else if (protection.isPreventUpdate()) {\n\t\t\t\t\t\terrorMessages.add(\"Can not update this tag according to tag protection setting\");\n\t\t\t\t\t} else if (protection.isSignatureRequired() \n\t\t\t\t\t\t\t&& !project.hasValidTagSignature(newObjectId, gitEnvs)) {\n\t\t\t\t\t\terrorMessages.add(\"Can not update this tag as tag protection setting requires \"\n\t\t\t\t\t\t\t\t+ \"valid tag signature\");\n\t\t\t\t\t}\n\t \t\t\tif (errorMessages.isEmpty() && newObjectId.equals(ObjectId.zeroId())) {\n\t \t\t\t\ttry {\n\t \t\t\t\t\tprojectManager.onDeleteTag(project, tagName);\n\t \t\t\t\t} catch (ExplicitException e) {\n\t \t\t\t\t\terrorMessages.addAll(Splitter.on(\"\\n\").splitToList(e.getMessage()));\n\t \t\t\t\t}\n\t \t\t\t}\n\t\t\t\t\tif (!errorMessages.isEmpty())\n\t\t\t\t\t\terror(output, refName, errorMessages);\n\t \t\t}\n\t \t\t\n\t \tfield = field.substring(40);\n\t \tif (field.length() == 0)\n\t \t\tbreak;\n\t \telse\n\t \t\tfields.set(pos, field);\n\t }\n } finally {\n \tSecurityUtils.getSubject().releaseRunAs();\n }\t\t\n\t}\t\n}\n\n## 内容7\n### 文件路径:\nserver-core/src/main/resources/git-receive-hook\n### 代码内容:\n#!/usr/bin/env bash\nunset http_proxy\nunset https_proxy\nIFS=$'\\r\\n'; \nlines=($(${ONEDEV_CURL} -k -s -S -f -X POST --data-urlencode \"ENV_GIT_ALTERNATE_OBJECT_DIRECTORIES=${GIT_ALTERNATE_OBJECT_DIRECTORIES}\" --data-urlencode \"ENV_GIT_OBJECT_DIRECTORY=${GIT_OBJECT_DIRECTORY}\" --data-urlencode \"ENV_GIT_QUARANTINE_PATH=${GIT_QUARANTINE_PATH}\" -d @- ${ONEDEV_URL}/%s/${ONEDEV_REPOSITORY_ID}/${ONEDEV_USER_ID} 2>&1))\n\nreturnCode=0;\n\nfor i in ${lines[@]}\ndo\n if [ \"$i\" = \"ERROR\" ]; then \n returnCode=1\n else\n if [[ $i == curl:* ]]; then\n returnCode=1\n fi;\n echo \"$i\"\n fi;\ndone\n\nexit $returnCode\n", "input": "", "output": "【漏洞类型】远程代码执行(RCE)与路径遍历\n【严重性】High\n【漏洞摘要】原始代码中的 `git-receive-hook` 脚本允许任何本地地址访问,这可能导致恶意用户通过该脚本执行任意命令。此外,`DefaultProjectManager` 类中的 `isGitHookValid` 方法未验证 Git 钩子文件的内容,可能导致路径遍历漏洞。具体问题在于 `git-receive-hook` 脚本中未对传入的 `ENV_GIT_ALTERNATE_OBJECT_DIRECTORIES`、`ENV_GIT_OBJECT_DIRECTORY` 和 `ENV_GIT_QUARANTINE_PATH` 环境变量进行验证,而这些变量可以直接通过 HTTP POST 请求传入,从而导致潜在的路径遍历和远程代码执行问题。\n【关键代码模式】\n- `git-receive-hook` 脚本中的未验证的环境变量使用。\n- `DefaultProjectManager` 类中的 `isGitHookValid` 方法未对 Git 钩子文件内容进行验证。\n- `GitFilter` 和 `GitSshCommand` 类中对环境变量的设置。\n【修复方法说明】修复通过在 `git-receive-hook` 脚本中添加 `ONEDEV_HOOK_TOKEN` 验证,并在 `DefaultProjectManager` 类中的 `isGitHookValid` 方法中检查该 token,防止未经授权的访问。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nxwiki-platform-core/xwiki-platform-mentions/xwiki-platform-mentions-notifications/src/main/resources/templates/notification/rss/mentions.mention.vm\n### 代码内容:\n## ---------------------------------------------------------------------------\n## See the NOTICE file distributed with this work for additional\n## information regarding copyright ownership.\n##\n## This is free software; you can redistribute it and/or modify it\n## under the terms of the GNU Lesser General Public License as\n## published by the Free Software Foundation; either version 2.1 of\n## the License, or (at your option) any later version.\n##\n## This software is distributed in the hope that it will be useful,\n## but WITHOUT ANY WARRANTY; without even the implied warranty of\n## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n## Lesser General Public License for more details.\n##\n## You should have received a copy of the GNU Lesser General Public\n## License along with this software; if not, write to the Free\n## Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n## 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n## ---------------------------------------------------------------------------\n### Default plain text template for RSS feeds\n#template('notification/macros.vm')\n###\n### Get the doc\n###\n#set ($document = $xwiki.getDocument($event.document))\n###\n### Display\n###\n<p>\n <strong>$services.localization.render('mentions.event.mention.type')</strong>\n #if ($document)\n <a href=\"$document.getExternalURL()\">$document.getRenderedTitle('plain/1.0')</a>\n #end\n .<br/>\n $services.localization.render('notifications.events.mentions.mention.description.by.1user',\n [\"#displayNotificationEventUsers($event.users, false)\"])\n</p>\n###\n### Display dates\n###\n<p>\n #set ($dates = $event.dates)\n #if ($dates.size() > 1)\n $services.localization.render('notifications.events.lastChange', [$xwiki.formatDate($event.dates.get(0))])\n #else\n $xwiki.formatDate($event.dates.get(0))\n #end\n <br/>\n ###\n ### Display a link to the diff\n ###\n #if ($document)\n #set ($newdoc = $xwiki.getDocument($event.document, $event.events[0].documentVersion))\n #if ($event.events.size() == 1)\n #set ($origdoc = $newdoc.previousDocument)\n #else\n #set ($lastIndex = $event.events.size() - 1)\n #set ($origdoc =\n $xwiki.getDocument($event.document, $event.events[$lastIndex].documentVersion).previousDocument)\n #end\n #if ($newdoc.version)\n #set ($externalURL = $newdoc.getExternalURL('view',\n \"viewer=changes&rev1=${origdoc.version}&rev2=${newdoc.version}\"))\n #set ($diffLink = \"<a href='$externalURL'>${newdoc.version}</a>\")\n $services.localization.render('notifications.rss.seeChanges', 'xwiki/2.1', [$diffLink])\n #end\n #end\n</p>\n\n## 内容2\n### 文件路径:\nxwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-ui/pom.xml\n### 代码内容:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n-->\n\n<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/maven-v4_0_0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n <parent>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-notifications</artifactId>\n <version>15.2-SNAPSHOT</version>\n </parent>\n <artifactId>xwiki-platform-notifications-ui</artifactId>\n <name>XWiki Platform - Notifications - UI</name>\n <packaging>xar</packaging>\n <description>Display notifications about events happening in the wiki.</description>\n <properties>\n <!-- Name to display by the Extension Manager -->\n <xwiki.extension.name>Notifications Application</xwiki.extension.name>\n </properties>\n <dependencies>\n <!-- We should keep notifications-preferences-default as first dependency since it declares DefaultModelBridge\n which is needed by some other modules -->\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-notifications-preferences-default</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-notifications-macro-ui</artifactId>\n <version>${project.version}</version>\n <type>xar</type>\n <scope>runtime</scope>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-notifications-script</artifactId>\n <version>${project.version}</version>\n </dependency>\n <!-- Add the dependency to the oldcore bridges that we need for sure -->\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-notifications-filters-default</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-notifications-notifiers-default</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-notifications-filters-watch</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-notifications-rest</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-eventstream-store-solr</artifactId>\n <version>${project.version}</version>\n </dependency>\n\n <!-- Needed to have a nice toggle buttons on watchlist notifications area. -->\n <dependency>\n <groupId>org.webjars</groupId>\n <artifactId>bootstrap-switch</artifactId>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-rendering-macro-velocity</artifactId>\n <version>${project.version}</version>\n <scope>runtime</scope>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-icon-script</artifactId>\n <version>${project.version}</version>\n <scope>runtime</scope>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-user-profile-ui</artifactId>\n <version>${project.version}</version>\n <type>xar</type>\n <scope>runtime</scope>\n </dependency>\n <dependency>\n <groupId>org.xwiki.rendering</groupId>\n <artifactId>xwiki-rendering-macro-html</artifactId>\n <version>${rendering.version}</version>\n <scope>runtime</scope>\n </dependency>\n <!-- Needed to have the alerts menu. -->\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-alerts-ui</artifactId>\n <version>${project.version}</version>\n <scope>runtime</scope>\n <type>xar</type>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-date</artifactId>\n <version>${project.version}</version>\n <scope>runtime</scope>\n </dependency>\n </dependencies>\n</project>\n\n## 内容3\n### 文件路径:\nxwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-ui/src/main/resources/XWiki/Notifications/Code/NotificationRSSService.xml\n### 代码内容:\n<?xml version=\"1.1\" encoding=\"UTF-8\"?>\n\n<!--\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n-->\n\n<xwikidoc version=\"1.3\" reference=\"XWiki.Notifications.Code.NotificationRSSService\" locale=\"\">\n <web>XWiki.Notifications.Code</web>\n <name>NotificationRSSService</name>\n <language/>\n <defaultLanguage/>\n <translation>0</translation>\n <creator>xwiki:XWiki.Admin</creator>\n <parent>xwiki:XWiki.Notifications.Code.WebHome</parent>\n <author>xwiki:XWiki.Admin</author>\n <contentAuthor>xwiki:XWiki.Admin</contentAuthor>\n <version>1.1</version>\n <title/>\n <comment/>\n <minorEdit>false</minorEdit>\n <syntaxId>xwiki/2.1</syntaxId>\n <hidden>true</hidden>\n <content>{{velocity}}\n#if ($xcontext.action == 'get' && \"$request.outputSyntax\" == 'plain')\n #set($discard = $response.setContentType('application/xml'))\n#else\n {{code language=\"xml\"}}\n#end\n$services.notification.notifiers.getFeed(20)\n#if ($xcontext.action != 'get' || \"$request.outputSyntax\" != 'plain')\n {{/code}}\n#end\n{{/velocity}}\n</content>\n</xwikidoc>\n\n## 内容4\n### 文件路径:\nxwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/notification/rss/default.vm\n### 代码内容:\n## ---------------------------------------------------------------------------\n## See the NOTICE file distributed with this work for additional\n## information regarding copyright ownership.\n##\n## This is free software; you can redistribute it and/or modify it\n## under the terms of the GNU Lesser General Public License as\n## published by the Free Software Foundation; either version 2.1 of\n## the License, or (at your option) any later version.\n##\n## This software is distributed in the hope that it will be useful,\n## but WITHOUT ANY WARRANTY; without even the implied warranty of\n## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n## Lesser General Public License for more details.\n##\n## You should have received a copy of the GNU Lesser General Public\n## License along with this software; if not, write to the Free\n## Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n## 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n## ---------------------------------------------------------------------------\n### Default plain text template for RSS feeds\n#template('notification/macros.vm')\n###\n### Get the app\n###\n#set ($app = \"$!event.events.get(0).application\")\n#if ($app == 'xwiki')\n #set ($app = $services.localization.render('core.events.appName'))\n#end\n###\n### Get the doc\n###\n#set ($document = $xwiki.getDocument($event.document))\n###\n### Display\n###\n<p>\n <strong>$app: [$event.type]</strong>\n #if ($document)\n <a href=\"$document.getExternalURL()\">$document.getRenderedTitle('plain/1.0')</a>\n #end\n .<br/>\n $services.localization.render('notifications.events.by', [\"#displayNotificationEventUsers($event.users, false)\"])\n</p>\n###\n### Display dates\n###\n<p>\n#set ($dates = $event.dates)\n#if ($dates.size() > 1)\n $services.localization.render('notifications.events.lastChange', [$xwiki.formatDate($event.dates.get(0))])\n#else\n $xwiki.formatDate($event.dates.get(0))\n#end\n<br/>\n###\n### Display a link to the diff\n###\n#if ($document)\n#set ($newdoc = $xwiki.getDocument($event.document, $event.events[0].documentVersion))\n#if ($event.events.size() == 1)\n#set ($origdoc = $newdoc.previousDocument)\n#else\n#set ($lastIndex = $event.events.size() - 1)\n#set ($origdoc = $xwiki.getDocument($event.document, $event.events[$lastIndex].documentVersion).previousDocument)\n#end\n#if ($newdoc.version)\n #set ($externalURL = $newdoc.getExternalURL('view',\n \"viewer=changes&rev1=${origdoc.version}&rev2=${newdoc.version}\"))\n #set ($diffLink = \"<a href='$externalURL'>${newdoc.version}</a>\")\n $services.localization.render('notifications.rss.seeChanges', 'xwiki/2.1', [$diffLink])\n#end\n#end\n</p>\n\n", "input": "", "output": "【漏洞类型】XWiki语法解析漏洞 \n【严重性】High \n【漏洞摘要】原始代码中的RSS通知模板使用了XWiki语法解析,并且未对输出内容进行适当的编码或过滤。具体表现为,在`mentions.mention.vm`和`default.vm`文件中,`$newdoc.version`和`$diffLink`直接插入到HTML中,可能导致XWiki语法注入攻击。特别是在`default.vm`文件的第67行,`$services.localization.render('notifications.rss.seeChanges', 'xwiki/2.1', [$diffLink])`这一行,如果`$diffLink`包含恶意的XWiki语法,攻击者可以利用此漏洞执行任意XWiki代码。 \n【关键代码模式】$newdoc.version, $diffLink, $services.localization.render \n【修复方法说明】通过将RSS feed内容直接写入响应输出流,并避免使用XWiki语法解析宏,修复了XWiki语法注入漏洞。最佳实践是在输出内容到HTML时进行适当的编码,以防止语法注入。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\npom.xml\n### 代码内容:\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n <parent>\n <groupId>org.sonatype.oss</groupId>\n <artifactId>oss-parent</artifactId>\n <version>7</version>\n </parent>\n <groupId>com.sparkjava</groupId>\n <artifactId>spark-core</artifactId>\n <packaging>bundle</packaging>\n <version>3.0.0-SNAPSHOT</version>\n <name>Spark</name>\n <description>A Sinatra inspired java web framework</description>\n <url>http://www.sparkjava.com</url>\n <licenses>\n <license>\n <name>The Apache Software License, Version 2.0</name>\n <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>\n <distribution>repo</distribution>\n </license>\n </licenses>\n <scm>\n <connection>scm:git:git@github.com:perwendel/spark.git</connection>\n <developerConnection>scm:git:git@github.com:perwendel/spark.git</developerConnection>\n <url>scm:git:git@github.com:perwendel/spark.git</url>\n </scm>\n <developers>\n </developers>\n\n <properties>\n <java.version>1.8</java.version>\n <jetty.version>9.4.6.v20170531</jetty.version>\n <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n <powermock.version>1.6.6</powermock.version>\n <mockito.version>1.10.19</mockito.version>\n </properties>\n\n <dependencies>\n <!-- LOGGING DEPENDENCIES -->\n <dependency>\n <groupId>org.slf4j</groupId>\n <artifactId>slf4j-api</artifactId>\n <version>1.7.13</version>\n </dependency>\n\n <dependency>\n <groupId>org.slf4j</groupId>\n <artifactId>slf4j-simple</artifactId>\n <version>1.7.13</version>\n <scope>test</scope>\n </dependency>\n\n <!-- JETTY DEPENDENCIES -->\n <dependency>\n <groupId>org.eclipse.jetty</groupId>\n <artifactId>jetty-server</artifactId>\n <version>${jetty.version}</version>\n </dependency>\n\n <dependency>\n <groupId>org.eclipse.jetty</groupId>\n <artifactId>jetty-webapp</artifactId>\n <version>${jetty.version}</version>\n </dependency>\n\n <dependency>\n <groupId>org.eclipse.jetty.websocket</groupId>\n <artifactId>websocket-server</artifactId>\n <version>${jetty.version}</version>\n </dependency>\n <dependency>\n <groupId>org.eclipse.jetty.websocket</groupId>\n <artifactId>websocket-servlet</artifactId>\n <version>${jetty.version}</version>\n </dependency>\n\n <!-- JUNIT DEPENDENCY FOR TESTING -->\n <dependency>\n <groupId>junit</groupId>\n <artifactId>junit</artifactId>\n <version>4.12</version>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.mockito</groupId>\n <artifactId>mockito-core</artifactId>\n <version>${mockito.version}</version>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.powermock</groupId>\n <artifactId>powermock-module-junit4</artifactId>\n <version>${powermock.version}</version>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.powermock</groupId>\n <artifactId>powermock-api-mockito</artifactId>\n <version>${powermock.version}</version>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.apache.httpcomponents</groupId>\n <artifactId>httpclient</artifactId>\n <version>4.5.1</version>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.freemarker</groupId>\n <artifactId>freemarker</artifactId>\n <version>2.3.23</version>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>com.google.code.gson</groupId>\n <artifactId>gson</artifactId>\n <version>2.4</version>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.eclipse.jetty.websocket</groupId>\n <artifactId>websocket-client</artifactId>\n <version>${jetty.version}</version>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.powermock.tests</groupId>\n <artifactId>powermock-tests-utils</artifactId>\n <version>${powermock.version}</version>\n <scope>test</scope>\n </dependency>\n </dependencies>\n\n <build>\n\n <resources>\n <resource>\n <filtering>false</filtering>\n <directory>src/main/resources</directory>\n </resource>\n <resource>\n <filtering>false</filtering>\n <directory>src/main/java</directory>\n <includes>\n <include>**</include>\n </includes>\n <excludes>\n <exclude>**/*.java</exclude>\n </excludes>\n </resource>\n </resources>\n\n <plugins>\n <plugin>\n <groupId>org.apache.maven.plugins</groupId>\n <artifactId>maven-compiler-plugin</artifactId>\n <version>3.3</version>\n <configuration>\n <source>${java.version}</source>\n <target>${java.version}</target>\n <optimize>true</optimize>\n <debug>true</debug>\n </configuration>\n </plugin>\n <plugin>\n <groupId>org.apache.maven.plugins</groupId>\n <artifactId>maven-enforcer-plugin</artifactId>\n <version>1.4</version>\n <executions>\n <execution>\n <id>enforce-java</id>\n <goals>\n <goal>enforce</goal>\n </goals>\n <configuration>\n <rules>\n <requireJavaVersion>\n <version>[${java.version},)</version>\n </requireJavaVersion>\n </rules>\n </configuration>\n </execution>\n </executions>\n </plugin>\n <plugin>\n <groupId>org.apache.maven.plugins</groupId>\n <artifactId>maven-surefire-plugin</artifactId>\n <version>2.18.1</version>\n <configuration>\n <forkCount>1</forkCount>\n <reuseForks>false</reuseForks>\n </configuration>\n </plugin>\n <plugin>\n <groupId>org.apache.maven.plugins</groupId>\n <artifactId>maven-javadoc-plugin</artifactId>\n <version>2.10.3</version>\n <configuration>\n <failOnError>false</failOnError>\n </configuration>\n </plugin>\n <plugin>\n <groupId>org.apache.felix</groupId>\n <artifactId>maven-bundle-plugin</artifactId>\n <version>2.5.3</version>\n <extensions>true</extensions>\n </plugin>\n </plugins>\n </build>\n</project>\n\n## 内容2\n### 文件路径:\nsrc/main/java/spark/resource/ClassPathResource.java\n### 代码内容:\npackage spark.resource;\n\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URL;\n\nimport spark.utils.Assert;\nimport spark.utils.ClassUtils;\nimport spark.utils.StringUtils;\n\n/**\n * {@link Resource} implementation for class path resources.\n * Uses either a given ClassLoader or a given Class for loading resources.\n * <p>Supports resolution as {@code java.io.File} if the class path\n * resource resides in the file system, but not for resources in a JAR.\n * Always supports resolution as URL.\n *\n * @author Juergen Hoeller\n * @author Sam Brannen\n * @see ClassLoader#getResourceAsStream(String)\n * @see Class#getResourceAsStream(String)\n * Code copied from Spring source. Modifications made (mostly removal of methods) by Per Wendel.\n */\npublic class ClassPathResource extends AbstractFileResolvingResource {\n\n private final String path;\n\n private ClassLoader classLoader;\n\n private Class<?> clazz;\n\n /**\n * Create a new ClassPathResource for ClassLoader usage.\n * A leading slash will be removed, as the ClassLoader\n * resource access methods will not accept it.\n * <p>The thread context class loader will be used for\n * loading the resource.\n *\n * @param path the absolute path within the class path\n * @see java.lang.ClassLoader#getResourceAsStream(String)\n * @see spark.utils.ClassUtils#getDefaultClassLoader()\n */\n public ClassPathResource(String path) {\n this(path, null);\n }\n\n /**\n * Create a new ClassPathResource for ClassLoader usage.\n * A leading slash will be removed, as the ClassLoader\n * resource access methods will not accept it.\n *\n * @param path the absolute path within the classpath\n * @param classLoader the class loader to load the resource with,\n * or {@code null} for the thread context class loader\n * @see ClassLoader#getResourceAsStream(String)\n */\n public ClassPathResource(String path, ClassLoader classLoader) {\n Assert.notNull(path, \"Path must not be null\");\n String pathToUse = StringUtils.cleanPath(path);\n if (pathToUse.startsWith(\"/\")) {\n pathToUse = pathToUse.substring(1);\n }\n this.path = pathToUse;\n this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());\n }\n\n /**\n * Create a new ClassPathResource with optional ClassLoader and Class.\n * Only for internal usage.\n *\n * @param path relative or absolute path within the classpath\n * @param classLoader the class loader to load the resource with, if any\n * @param clazz the class to load resources with, if any\n */\n protected ClassPathResource(String path, ClassLoader classLoader, Class<?> clazz) {\n this.path = StringUtils.cleanPath(path);\n this.classLoader = classLoader;\n this.clazz = clazz;\n }\n\n /**\n * Return the path for this resource (as resource path within the class path).\n *\n * @return the path\n */\n public final String getPath() {\n return this.path;\n }\n\n /**\n * This implementation checks for the resolution of a resource URL.\n *\n * @see java.lang.ClassLoader#getResource(String)\n * @see java.lang.Class#getResource(String)\n *\n * @return if exists.\n */\n @Override\n public boolean exists() {\n URL url;\n if (this.clazz != null) {\n url = this.clazz.getResource(this.path);\n } else {\n url = this.classLoader.getResource(this.path);\n }\n return (url != null);\n }\n\n /**\n * This implementation opens an InputStream for the given class path resource.\n *\n * @see java.lang.ClassLoader#getResourceAsStream(String)\n * @see java.lang.Class#getResourceAsStream(String)\n *\n * @return the input stream.\n */\n @Override\n public InputStream getInputStream() throws IOException {\n InputStream is;\n if (this.clazz != null) {\n is = this.clazz.getResourceAsStream(this.path);\n } else {\n is = this.classLoader.getResourceAsStream(this.path);\n }\n if (is == null) {\n throw new FileNotFoundException(getDescription() + \" cannot be opened because it does not exist\");\n }\n return is;\n }\n\n /**\n * This implementation returns a URL for the underlying class path resource.\n *\n * @see java.lang.ClassLoader#getResource(String)\n * @see java.lang.Class#getResource(String)\n *\n * @return the url.\n */\n @Override\n public URL getURL() throws IOException {\n URL url;\n if (this.clazz != null) {\n url = this.clazz.getResource(this.path);\n } else {\n url = this.classLoader.getResource(this.path);\n }\n if (url == null) {\n throw new FileNotFoundException(getDescription() + \" cannot be resolved to URL because it does not exist\");\n }\n return url;\n }\n\n /**\n * This implementation creates a ClassPathResource, applying the given path\n * relative to the path of the underlying resource of this descriptor.\n *\n * @see spark.utils.StringUtils#applyRelativePath(String, String)\n *\n * @return the resource.\n */\n @Override\n public Resource createRelative(String relativePath) {\n String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);\n return new ClassPathResource(pathToUse, this.classLoader, this.clazz);\n }\n\n /**\n * This implementation returns the name of the file that this class path\n * resource refers to.\n *\n * @see spark.utils.StringUtils#getFilename(String)\n *\n * @return the file name.\n */\n @Override\n public String getFilename() {\n return StringUtils.getFilename(this.path);\n }\n\n /**\n * This implementation returns a description that includes the class path location.\n *\n * @return the description.\n */\n @Override\n public String getDescription() {\n StringBuilder builder = new StringBuilder(\"class path resource [\");\n String pathToUse = path;\n if (this.clazz != null && !pathToUse.startsWith(\"/\")) {\n builder.append(ClassUtils.classPackageAsResourcePath(this.clazz));\n builder.append('/');\n }\n if (pathToUse.startsWith(\"/\")) {\n pathToUse = pathToUse.substring(1);\n }\n builder.append(pathToUse);\n builder.append(']');\n return builder.toString();\n }\n\n /**\n * This implementation compares the underlying class path locations.\n *\n * @return if equals.\n */\n @Override\n public boolean equals(Object obj) {\n if (obj == this) {\n return true;\n }\n if (obj instanceof ClassPathResource) {\n ClassPathResource otherRes = (ClassPathResource) obj;\n\n ClassLoader thisLoader = this.classLoader;\n ClassLoader otherLoader = otherRes.classLoader;\n\n return (this.path.equals(otherRes.path) &&\n thisLoader.equals(otherLoader) &&\n this.clazz.equals(otherRes.clazz));\n }\n return false;\n }\n\n /**\n * This implementation returns the hash code of the underlying\n * class path location.\n *\n * @return the hash code.\n */\n @Override\n public int hashCode() {\n return this.path.hashCode();\n }\n\n}\n\n", "input": "", "output": "【漏洞类型】路径遍历漏洞\n【严重性】High\n【漏洞摘要】此代码存在路径遍历漏洞。`ClassPathResource`类的构造函数中未验证用户输入的路径是否包含`file:`前缀,这可能导致攻击者通过提供特定路径来访问文件系统中的任意文件。具体表现为`ClassPathResource`构造函数中对路径的处理,未对路径进行严格的验证和清理。\n【关键代码模式】`ClassPathResource`构造函数中对路径的处理,特别是路径的格式检查和路径前缀的验证。相关关键词包括`ClassLoader.getResource`、`ClassLoader.getResourceAsStream`、`Class.getResource`、`Class.getResourceAsStream`、`StringUtils.cleanPath`。\n【修复方法说明】修复方法通过添加`doesNotContainFileColon`方法来验证路径是否包含`file:`前缀,确保路径仅限于类路径资源。此修复方法遵循了输入验证的最佳实践,以防止路径遍历攻击。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/main/java/spark/resource/ClassPathResource.java\n### 代码内容:\npackage spark.resource;\n\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URL;\n\nimport spark.utils.Assert;\nimport spark.utils.ClassUtils;\nimport spark.utils.StringUtils;\n\n/**\n * {@link Resource} implementation for class path resources.\n * Uses either a given ClassLoader or a given Class for loading resources.\n * <p>Supports resolution as {@code java.io.File} if the class path\n * resource resides in the file system, but not for resources in a JAR.\n * Always supports resolution as URL.\n *\n * @author Juergen Hoeller\n * @author Sam Brannen\n * @see ClassLoader#getResourceAsStream(String)\n * @see Class#getResourceAsStream(String)\n * Code copied from Spring source. Modifications made (mostly removal of methods) by Per Wendel.\n */\npublic class ClassPathResource extends AbstractFileResolvingResource {\n\n private final String path;\n\n private ClassLoader classLoader;\n\n private Class<?> clazz;\n\n /**\n * Create a new ClassPathResource for ClassLoader usage.\n * A leading slash will be removed, as the ClassLoader\n * resource access methods will not accept it.\n * <p>The thread context class loader will be used for\n * loading the resource.\n *\n * @param path the absolute path within the class path\n * @see java.lang.ClassLoader#getResourceAsStream(String)\n * @see spark.utils.ClassUtils#getDefaultClassLoader()\n */\n public ClassPathResource(String path) {\n this(path, null);\n }\n\n /**\n * Create a new ClassPathResource for ClassLoader usage.\n * A leading slash will be removed, as the ClassLoader\n * resource access methods will not accept it.\n *\n * @param path the absolute path within the classpath\n * @param classLoader the class loader to load the resource with,\n * or {@code null} for the thread context class loader\n * @see ClassLoader#getResourceAsStream(String)\n */\n public ClassPathResource(String path, ClassLoader classLoader) {\n Assert.notNull(path, \"Path must not be null\");\n Assert.state(doesNotContainFileColon(path), \"Path must not contain 'file:'\");\n\n String pathToUse = StringUtils.cleanPath(path);\n\n if (pathToUse.startsWith(\"/\")) {\n pathToUse = pathToUse.substring(1);\n }\n\n this.path = pathToUse;\n this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());\n }\n\n private static boolean doesNotContainFileColon(String path) {\n return !path.contains(\"file:\");\n }\n\n /**\n * Create a new ClassPathResource with optional ClassLoader and Class.\n * Only for internal usage.\n *\n * @param path relative or absolute path within the classpath\n * @param classLoader the class loader to load the resource with, if any\n * @param clazz the class to load resources with, if any\n */\n protected ClassPathResource(String path, ClassLoader classLoader, Class<?> clazz) {\n this.path = StringUtils.cleanPath(path);\n this.classLoader = classLoader;\n this.clazz = clazz;\n }\n\n /**\n * Return the path for this resource (as resource path within the class path).\n *\n * @return the path\n */\n public final String getPath() {\n return this.path;\n }\n\n /**\n * This implementation checks for the resolution of a resource URL.\n *\n * @return if exists.\n * @see java.lang.ClassLoader#getResource(String)\n * @see java.lang.Class#getResource(String)\n */\n @Override\n public boolean exists() {\n URL url;\n if (this.clazz != null) {\n url = this.clazz.getResource(this.path);\n } else {\n url = this.classLoader.getResource(this.path);\n }\n return (url != null);\n }\n\n /**\n * This implementation opens an InputStream for the given class path resource.\n *\n * @return the input stream.\n * @see java.lang.ClassLoader#getResourceAsStream(String)\n * @see java.lang.Class#getResourceAsStream(String)\n */\n @Override\n public InputStream getInputStream() throws IOException {\n InputStream is;\n if (this.clazz != null) {\n is = this.clazz.getResourceAsStream(this.path);\n } else {\n is = this.classLoader.getResourceAsStream(this.path);\n }\n if (is == null) {\n throw new FileNotFoundException(getDescription() + \" cannot be opened because it does not exist\");\n }\n return is;\n }\n\n /**\n * This implementation returns a URL for the underlying class path resource.\n *\n * @return the url.\n * @see java.lang.ClassLoader#getResource(String)\n * @see java.lang.Class#getResource(String)\n */\n @Override\n public URL getURL() throws IOException {\n URL url;\n if (this.clazz != null) {\n url = this.clazz.getResource(this.path);\n } else {\n url = this.classLoader.getResource(this.path);\n }\n if (url == null) {\n throw new FileNotFoundException(getDescription() + \" cannot be resolved to URL because it does not exist\");\n }\n return url;\n }\n\n /**\n * This implementation creates a ClassPathResource, applying the given path\n * relative to the path of the underlying resource of this descriptor.\n *\n * @return the resource.\n * @see spark.utils.StringUtils#applyRelativePath(String, String)\n */\n @Override\n public Resource createRelative(String relativePath) {\n String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);\n return new ClassPathResource(pathToUse, this.classLoader, this.clazz);\n }\n\n /**\n * This implementation returns the name of the file that this class path\n * resource refers to.\n *\n * @return the file name.\n * @see spark.utils.StringUtils#getFilename(String)\n */\n @Override\n public String getFilename() {\n return StringUtils.getFilename(this.path);\n }\n\n /**\n * This implementation returns a description that includes the class path location.\n *\n * @return the description.\n */\n @Override\n public String getDescription() {\n StringBuilder builder = new StringBuilder(\"class path resource [\");\n String pathToUse = path;\n if (this.clazz != null && !pathToUse.startsWith(\"/\")) {\n builder.append(ClassUtils.classPackageAsResourcePath(this.clazz));\n builder.append('/');\n }\n if (pathToUse.startsWith(\"/\")) {\n pathToUse = pathToUse.substring(1);\n }\n builder.append(pathToUse);\n builder.append(']');\n return builder.toString();\n }\n\n /**\n * This implementation compares the underlying class path locations.\n *\n * @return if equals.\n */\n @Override\n public boolean equals(Object obj) {\n if (obj == this) {\n return true;\n }\n if (obj instanceof ClassPathResource) {\n ClassPathResource otherRes = (ClassPathResource) obj;\n\n ClassLoader thisLoader = this.classLoader;\n ClassLoader otherLoader = otherRes.classLoader;\n\n return (this.path.equals(otherRes.path) &&\n thisLoader.equals(otherLoader) &&\n this.clazz.equals(otherRes.clazz));\n }\n return false;\n }\n\n /**\n * This implementation returns the hash code of the underlying\n * class path location.\n *\n * @return the hash code.\n */\n @Override\n public int hashCode() {\n return this.path.hashCode();\n }\n\n}\n\n", "input": "", "output": "【漏洞类型】路径遍历漏洞\n【严重性】High\n【漏洞摘要】这是一个路径遍历漏洞。在原始代码中,`ClassPathResource`类的构造函数`ClassPathResource(String path, ClassLoader classLoader)`和`ClassPathResource(String path)`中,路径参数`path`未经过严格的验证,可能导致攻击者通过构造恶意路径来访问类路径中不应该暴露的文件。具体表现为`doesNotContainFileColon(String path)`方法仅检查路径中是否包含`\"file:\"`,而未检查路径是否包含其他潜在的危险字符序列,如`\"../\"`。\n【关键代码模式】未验证的用户输入路径、路径遍历检查不充分。\n【修复方法说明】修复方法通过引入`isValid(String path)`和`isInvalidPath(String path)`方法,增加了对路径中包含`\"WEB-INF\"`、`\"META-INF\"`、`\":/\"`以及`\"../\"`等字符序列的检查,以防止路径遍历攻击。同时,遵循了安全编码的最佳实践,即对所有用户输入的路径进行严格的验证。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\ncore/src/main/java/jenkins/security/ApiTokenProperty.java\n### 代码内容:\npackage jenkins.security;\n\nimport hudson.Extension;\nimport hudson.Util;\nimport hudson.model.Descriptor.FormException;\nimport hudson.model.User;\nimport hudson.model.UserProperty;\nimport hudson.model.UserPropertyDescriptor;\nimport hudson.util.HttpResponses;\nimport hudson.util.Secret;\nimport net.sf.json.JSONObject;\nimport org.kohsuke.stapler.AncestorInPath;\nimport org.kohsuke.stapler.DataBoundConstructor;\nimport org.kohsuke.stapler.HttpResponse;\nimport org.kohsuke.stapler.StaplerRequest;\nimport org.kohsuke.stapler.StaplerResponse;\n\nimport java.io.IOException;\nimport java.security.SecureRandom;\n\n/**\n * Remembers the API token for this user, that can be used like a password to login.\n *\n *\n * @author Kohsuke Kawaguchi\n * @see ApiTokenFilter\n * @since 1.426\n */\npublic class ApiTokenProperty extends UserProperty {\n private volatile Secret apiToken;\n\n @DataBoundConstructor\n public ApiTokenProperty() {\n _changeApiToken();\n }\n\n /**\n * We don't let the external code set the API token,\n * but for the initial value of the token we need to compute the seed by ourselves.\n */\n private ApiTokenProperty(String seed) {\n apiToken = Secret.fromString(seed);\n }\n\n public String getApiToken() {\n return Util.getDigestOf(apiToken.getPlainText());\n }\n\n public boolean matchesPassword(String password) {\n return getApiToken().equals(password);\n }\n\n public void changeApiToken() throws IOException {\n _changeApiToken();\n if (user!=null)\n user.save();\n }\n\n private void _changeApiToken() {\n byte[] random = new byte[16]; // 16x8=128bit worth of randomness, since we use md5 digest as the API token\n RANDOM.nextBytes(random);\n apiToken = Secret.fromString(Util.toHexString(random));\n }\n\n @Override\n public UserProperty reconfigure(StaplerRequest req, JSONObject form) throws FormException {\n return this;\n }\n\n @Extension\n public static final class DescriptorImpl extends UserPropertyDescriptor {\n public String getDisplayName() {\n return Messages.ApiTokenProperty_DisplayName();\n }\n\n /**\n * When we are creating a default {@link ApiTokenProperty} for User,\n * we need to make sure it yields the same value for the same user,\n * because there's no guarantee that the property is saved.\n *\n * But we also need to make sure that an attacker won't be able to guess\n * the initial API token value. So we take the seed by hashing the secret + user ID.\n */\n public ApiTokenProperty newInstance(User user) {\n return new ApiTokenProperty(API_KEY_SEED.mac(user.getId()));\n }\n\n public HttpResponse doChangeToken(@AncestorInPath User u, StaplerResponse rsp) throws IOException {\n ApiTokenProperty p = u.getProperty(ApiTokenProperty.class);\n if (p==null) {\n p = newInstance(u);\n u.addProperty(p);\n } else {\n p.changeApiToken();\n }\n rsp.setHeader(\"script\",\"document.getElementById('apiToken').value='\"+p.getApiToken()+\"'\");\n return HttpResponses.html(Messages.ApiTokenProperty_ChangeToken_Success());\n }\n }\n\n private static final SecureRandom RANDOM = new SecureRandom();\n\n /**\n * We don't want an API key that's too long, so cut the length to 16 (which produces 32-letter MAC code in hexdump)\n */\n private static final HMACConfidentialKey API_KEY_SEED = new HMACConfidentialKey(ApiTokenProperty.class,\"seed\",16);\n}\n\n", "input": "", "output": "【漏洞类型】API Token Initialization Vulnerability\n【严重性】Medium\n【漏洞摘要】原始代码在初始化API token时,使用了用户ID和一个预定义的密钥(secret.key)来生成初始token。如果用户仍然拥有这个旧的初始token(例如,通过迁移或备份恢复),而系统没有检查并拒绝这个旧token,攻击者可以通过暴力破解这个旧token来访问用户的API权限。根本原因在于未验证用户当前API token是否为初始值,并在检测到初始值时生成新的token。\n【关键代码模式】涉及用户身份验证的API token生成和验证逻辑,特别是使用用户特定信息(如user ID)和预定义密钥生成初始token的地方,以及在验证token时的逻辑。\n【修复方法说明】修复方法在getApiToken函数中增加了对初始token的检查,如果检测到当前token是通过旧的密钥生成的,则强制生成新的token。这通过对比当前token与通过用户ID和Jenkins实例的secret key生成的初始token来实现。最佳实践是在系统中定期更换API token,并确保旧token在系统中失效。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nlibavformat/ape.c\n### 代码内容:\n#include <stdio.h>\n\n#include \"libavutil/intreadwrite.h\"\n#include \"avformat.h\"\n#include \"apetag.h\"\n\n#define ENABLE_DEBUG 0\n\n/* The earliest and latest file formats supported by this library */\n#define APE_MIN_VERSION 3950\n#define APE_MAX_VERSION 3990\n\n#define MAC_FORMAT_FLAG_8_BIT 1 // is 8-bit [OBSOLETE]\n#define MAC_FORMAT_FLAG_CRC 2 // uses the new CRC32 error detection [OBSOLETE]\n#define MAC_FORMAT_FLAG_HAS_PEAK_LEVEL 4 // uint32 nPeakLevel after the header [OBSOLETE]\n#define MAC_FORMAT_FLAG_24_BIT 8 // is 24-bit [OBSOLETE]\n#define MAC_FORMAT_FLAG_HAS_SEEK_ELEMENTS 16 // has the number of seek elements after the peak level\n#define MAC_FORMAT_FLAG_CREATE_WAV_HEADER 32 // create the wave header on decompression (not stored)\n\n#define MAC_SUBFRAME_SIZE 4608\n\n#define APE_EXTRADATA_SIZE 6\n\ntypedef struct {\n int64_t pos;\n int nblocks;\n int size;\n int skip;\n int64_t pts;\n} APEFrame;\n\ntypedef struct {\n /* Derived fields */\n uint32_t junklength;\n uint32_t firstframe;\n uint32_t totalsamples;\n int currentframe;\n APEFrame *frames;\n\n /* Info from Descriptor Block */\n char magic[4];\n int16_t fileversion;\n int16_t padding1;\n uint32_t descriptorlength;\n uint32_t headerlength;\n uint32_t seektablelength;\n uint32_t wavheaderlength;\n uint32_t audiodatalength;\n uint32_t audiodatalength_high;\n uint32_t wavtaillength;\n uint8_t md5[16];\n\n /* Info from Header Block */\n uint16_t compressiontype;\n uint16_t formatflags;\n uint32_t blocksperframe;\n uint32_t finalframeblocks;\n uint32_t totalframes;\n uint16_t bps;\n uint16_t channels;\n uint32_t samplerate;\n\n /* Seektable */\n uint32_t *seektable;\n} APEContext;\n\nstatic int ape_probe(AVProbeData * p)\n{\n if (p->buf[0] == 'M' && p->buf[1] == 'A' && p->buf[2] == 'C' && p->buf[3] == ' ')\n return AVPROBE_SCORE_MAX;\n\n return 0;\n}\n\nstatic void ape_dumpinfo(AVFormatContext * s, APEContext * ape_ctx)\n{\n#if ENABLE_DEBUG\n int i;\n\n av_log(s, AV_LOG_DEBUG, \"Descriptor Block:\\n\\n\");\n av_log(s, AV_LOG_DEBUG, \"magic = \\\"%c%c%c%c\\\"\\n\", ape_ctx->magic[0], ape_ctx->magic[1], ape_ctx->magic[2], ape_ctx->magic[3]);\n av_log(s, AV_LOG_DEBUG, \"fileversion = %d\\n\", ape_ctx->fileversion);\n av_log(s, AV_LOG_DEBUG, \"descriptorlength = %d\\n\", ape_ctx->descriptorlength);\n av_log(s, AV_LOG_DEBUG, \"headerlength = %d\\n\", ape_ctx->headerlength);\n av_log(s, AV_LOG_DEBUG, \"seektablelength = %d\\n\", ape_ctx->seektablelength);\n av_log(s, AV_LOG_DEBUG, \"wavheaderlength = %d\\n\", ape_ctx->wavheaderlength);\n av_log(s, AV_LOG_DEBUG, \"audiodatalength = %d\\n\", ape_ctx->audiodatalength);\n av_log(s, AV_LOG_DEBUG, \"audiodatalength_high = %d\\n\", ape_ctx->audiodatalength_high);\n av_log(s, AV_LOG_DEBUG, \"wavtaillength = %d\\n\", ape_ctx->wavtaillength);\n av_log(s, AV_LOG_DEBUG, \"md5 = \");\n for (i = 0; i < 16; i++)\n av_log(s, AV_LOG_DEBUG, \"%02x\", ape_ctx->md5[i]);\n av_log(s, AV_LOG_DEBUG, \"\\n\");\n\n av_log(s, AV_LOG_DEBUG, \"\\nHeader Block:\\n\\n\");\n\n av_log(s, AV_LOG_DEBUG, \"compressiontype = %d\\n\", ape_ctx->compressiontype);\n av_log(s, AV_LOG_DEBUG, \"formatflags = %d\\n\", ape_ctx->formatflags);\n av_log(s, AV_LOG_DEBUG, \"blocksperframe = %d\\n\", ape_ctx->blocksperframe);\n av_log(s, AV_LOG_DEBUG, \"finalframeblocks = %d\\n\", ape_ctx->finalframeblocks);\n av_log(s, AV_LOG_DEBUG, \"totalframes = %d\\n\", ape_ctx->totalframes);\n av_log(s, AV_LOG_DEBUG, \"bps = %d\\n\", ape_ctx->bps);\n av_log(s, AV_LOG_DEBUG, \"channels = %d\\n\", ape_ctx->channels);\n av_log(s, AV_LOG_DEBUG, \"samplerate = %d\\n\", ape_ctx->samplerate);\n\n av_log(s, AV_LOG_DEBUG, \"\\nSeektable\\n\\n\");\n if ((ape_ctx->seektablelength / sizeof(uint32_t)) != ape_ctx->totalframes) {\n av_log(s, AV_LOG_DEBUG, \"No seektable\\n\");\n } else {\n for (i = 0; i < ape_ctx->seektablelength / sizeof(uint32_t); i++) {\n if (i < ape_ctx->totalframes - 1) {\n av_log(s, AV_LOG_DEBUG, \"%8d %d (%d bytes)\\n\", i, ape_ctx->seektable[i], ape_ctx->seektable[i + 1] - ape_ctx->seektable[i]);\n } else {\n av_log(s, AV_LOG_DEBUG, \"%8d %d\\n\", i, ape_ctx->seektable[i]);\n }\n }\n }\n\n av_log(s, AV_LOG_DEBUG, \"\\nFrames\\n\\n\");\n for (i = 0; i < ape_ctx->totalframes; i++)\n av_log(s, AV_LOG_DEBUG, \"%8d %8lld %8d (%d samples)\\n\", i, ape_ctx->frames[i].pos, ape_ctx->frames[i].size, ape_ctx->frames[i].nblocks);\n\n av_log(s, AV_LOG_DEBUG, \"\\nCalculated information:\\n\\n\");\n av_log(s, AV_LOG_DEBUG, \"junklength = %d\\n\", ape_ctx->junklength);\n av_log(s, AV_LOG_DEBUG, \"firstframe = %d\\n\", ape_ctx->firstframe);\n av_log(s, AV_LOG_DEBUG, \"totalsamples = %d\\n\", ape_ctx->totalsamples);\n#endif\n}\n\nstatic int ape_read_header(AVFormatContext * s, AVFormatParameters * ap)\n{\n AVIOContext *pb = s->pb;\n APEContext *ape = s->priv_data;\n AVStream *st;\n uint32_t tag;\n int i;\n int total_blocks;\n int64_t pts;\n\n /* TODO: Skip any leading junk such as id3v2 tags */\n ape->junklength = 0;\n\n tag = avio_rl32(pb);\n if (tag != MKTAG('M', 'A', 'C', ' '))\n return -1;\n\n ape->fileversion = avio_rl16(pb);\n\n if (ape->fileversion < APE_MIN_VERSION || ape->fileversion > APE_MAX_VERSION) {\n av_log(s, AV_LOG_ERROR, \"Unsupported file version - %d.%02d\\n\", ape->fileversion / 1000, (ape->fileversion % 1000) / 10);\n return -1;\n }\n\n if (ape->fileversion >= 3980) {\n ape->padding1 = avio_rl16(pb);\n ape->descriptorlength = avio_rl32(pb);\n ape->headerlength = avio_rl32(pb);\n ape->seektablelength = avio_rl32(pb);\n ape->wavheaderlength = avio_rl32(pb);\n ape->audiodatalength = avio_rl32(pb);\n ape->audiodatalength_high = avio_rl32(pb);\n ape->wavtaillength = avio_rl32(pb);\n avio_read(pb, ape->md5, 16);\n\n /* Skip any unknown bytes at the end of the descriptor.\n This is for future compatibility */\n if (ape->descriptorlength > 52)\n avio_seek(pb, ape->descriptorlength - 52, SEEK_CUR);\n\n /* Read header data */\n ape->compressiontype = avio_rl16(pb);\n ape->formatflags = avio_rl16(pb);\n ape->blocksperframe = avio_rl32(pb);\n ape->finalframeblocks = avio_rl32(pb);\n ape->totalframes = avio_rl32(pb);\n ape->bps = avio_rl16(pb);\n ape->channels = avio_rl16(pb);\n ape->samplerate = avio_rl32(pb);\n } else {\n ape->descriptorlength = 0;\n ape->headerlength = 32;\n\n ape->compressiontype = avio_rl16(pb);\n ape->formatflags = avio_rl16(pb);\n ape->channels = avio_rl16(pb);\n ape->samplerate = avio_rl32(pb);\n ape->wavheaderlength = avio_rl32(pb);\n ape->wavtaillength = avio_rl32(pb);\n ape->totalframes = avio_rl32(pb);\n ape->finalframeblocks = avio_rl32(pb);\n\n if (ape->formatflags & MAC_FORMAT_FLAG_HAS_PEAK_LEVEL) {\n avio_seek(pb, 4, SEEK_CUR); /* Skip the peak level */\n ape->headerlength += 4;\n }\n\n if (ape->formatflags & MAC_FORMAT_FLAG_HAS_SEEK_ELEMENTS) {\n ape->seektablelength = avio_rl32(pb);\n ape->headerlength += 4;\n ape->seektablelength *= sizeof(int32_t);\n } else\n ape->seektablelength = ape->totalframes * sizeof(int32_t);\n\n if (ape->formatflags & MAC_FORMAT_FLAG_8_BIT)\n ape->bps = 8;\n else if (ape->formatflags & MAC_FORMAT_FLAG_24_BIT)\n ape->bps = 24;\n else\n ape->bps = 16;\n\n if (ape->fileversion >= 3950)\n ape->blocksperframe = 73728 * 4;\n else if (ape->fileversion >= 3900 || (ape->fileversion >= 3800 && ape->compressiontype >= 4000))\n ape->blocksperframe = 73728;\n else\n ape->blocksperframe = 9216;\n\n /* Skip any stored wav header */\n if (!(ape->formatflags & MAC_FORMAT_FLAG_CREATE_WAV_HEADER))\n avio_seek(pb, ape->wavheaderlength, SEEK_CUR);\n }\n\n if(ape->totalframes > UINT_MAX / sizeof(APEFrame)){\n av_log(s, AV_LOG_ERROR, \"Too many frames: %d\\n\", ape->totalframes);\n return -1;\n }\n ape->frames = av_malloc(ape->totalframes * sizeof(APEFrame));\n if(!ape->frames)\n return AVERROR(ENOMEM);\n ape->firstframe = ape->junklength + ape->descriptorlength + ape->headerlength + ape->seektablelength + ape->wavheaderlength;\n ape->currentframe = 0;\n\n ape->totalsamples = ape->finalframeblocks;\n if (ape->totalframes > 1)\n ape->totalsamples += ape->blocksperframe * (ape->totalframes - 1);\n\n if (ape->seektablelength > 0) {\n ape->seektable = av_malloc(ape->seektablelength);\n for (i = 0; i < ape->seektablelength / sizeof(uint32_t); i++)\n ape->seektable[i] = avio_rl32(pb);\n }\n\n ape->frames[0].pos = ape->firstframe;\n ape->frames[0].nblocks = ape->blocksperframe;\n ape->frames[0].skip = 0;\n for (i = 1; i < ape->totalframes; i++) {\n ape->frames[i].pos = ape->seektable[i]; //ape->frames[i-1].pos + ape->blocksperframe;\n ape->frames[i].nblocks = ape->blocksperframe;\n ape->frames[i - 1].size = ape->frames[i].pos - ape->frames[i - 1].pos;\n ape->frames[i].skip = (ape->frames[i].pos - ape->frames[0].pos) & 3;\n }\n ape->frames[ape->totalframes - 1].size = ape->finalframeblocks * 4;\n ape->frames[ape->totalframes - 1].nblocks = ape->finalframeblocks;\n\n for (i = 0; i < ape->totalframes; i++) {\n if(ape->frames[i].skip){\n ape->frames[i].pos -= ape->frames[i].skip;\n ape->frames[i].size += ape->frames[i].skip;\n }\n ape->frames[i].size = (ape->frames[i].size + 3) & ~3;\n }\n\n ape_dumpinfo(s, ape);\n\n /* try to read APE tags */\n if (!url_is_streamed(pb)) {\n ff_ape_parse_tag(s);\n avio_seek(pb, 0, SEEK_SET);\n }\n\n av_log(s, AV_LOG_DEBUG, \"Decoding file - v%d.%02d, compression level %d\\n\", ape->fileversion / 1000, (ape->fileversion % 1000) / 10, ape->compressiontype);\n\n /* now we are ready: build format streams */\n st = av_new_stream(s, 0);\n if (!st)\n return -1;\n\n total_blocks = (ape->totalframes == 0) ? 0 : ((ape->totalframes - 1) * ape->blocksperframe) + ape->finalframeblocks;\n\n st->codec->codec_type = AVMEDIA_TYPE_AUDIO;\n st->codec->codec_id = CODEC_ID_APE;\n st->codec->codec_tag = MKTAG('A', 'P', 'E', ' ');\n st->codec->channels = ape->channels;\n st->codec->sample_rate = ape->samplerate;\n st->codec->bits_per_coded_sample = ape->bps;\n st->codec->frame_size = MAC_SUBFRAME_SIZE;\n\n st->nb_frames = ape->totalframes;\n st->start_time = 0;\n st->duration = total_blocks / MAC_SUBFRAME_SIZE;\n av_set_pts_info(st, 64, MAC_SUBFRAME_SIZE, ape->samplerate);\n\n st->codec->extradata = av_malloc(APE_EXTRADATA_SIZE);\n st->codec->extradata_size = APE_EXTRADATA_SIZE;\n AV_WL16(st->codec->extradata + 0, ape->fileversion);\n AV_WL16(st->codec->extradata + 2, ape->compressiontype);\n AV_WL16(st->codec->extradata + 4, ape->formatflags);\n\n pts = 0;\n for (i = 0; i < ape->totalframes; i++) {\n ape->frames[i].pts = pts;\n av_add_index_entry(st, ape->frames[i].pos, ape->frames[i].pts, 0, 0, AVINDEX_KEYFRAME);\n pts += ape->blocksperframe / MAC_SUBFRAME_SIZE;\n }\n\n return 0;\n}\n\nstatic int ape_read_packet(AVFormatContext * s, AVPacket * pkt)\n{\n int ret;\n int nblocks;\n APEContext *ape = s->priv_data;\n uint32_t extra_size = 8;\n\n if (s->pb->eof_reached)\n return AVERROR(EIO);\n if (ape->currentframe > ape->totalframes)\n return AVERROR(EIO);\n\n avio_seek (s->pb, ape->frames[ape->currentframe].pos, SEEK_SET);\n\n /* Calculate how many blocks there are in this frame */\n if (ape->currentframe == (ape->totalframes - 1))\n nblocks = ape->finalframeblocks;\n else\n nblocks = ape->blocksperframe;\n\n if (av_new_packet(pkt, ape->frames[ape->currentframe].size + extra_size) < 0)\n return AVERROR(ENOMEM);\n\n AV_WL32(pkt->data , nblocks);\n AV_WL32(pkt->data + 4, ape->frames[ape->currentframe].skip);\n ret = avio_read(s->pb, pkt->data + extra_size, ape->frames[ape->currentframe].size);\n\n pkt->pts = ape->frames[ape->currentframe].pts;\n pkt->stream_index = 0;\n\n /* note: we need to modify the packet size here to handle the last\n packet */\n pkt->size = ret + extra_size;\n\n ape->currentframe++;\n\n return 0;\n}\n\nstatic int ape_read_close(AVFormatContext * s)\n{\n APEContext *ape = s->priv_data;\n\n av_freep(&ape->frames);\n av_freep(&ape->seektable);\n return 0;\n}\n\nstatic int ape_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)\n{\n AVStream *st = s->streams[stream_index];\n APEContext *ape = s->priv_data;\n int index = av_index_search_timestamp(st, timestamp, flags);\n\n if (index < 0)\n return -1;\n\n ape->currentframe = index;\n return 0;\n}\n\nAVInputFormat ff_ape_demuxer = {\n \"ape\",\n NULL_IF_CONFIG_SMALL(\"Monkey's Audio\"),\n sizeof(APEContext),\n ape_probe,\n ape_read_header,\n ape_read_packet,\n ape_read_close,\n ape_read_seek,\n .extensions = \"ape,apl,mac\"\n};\n\n", "input": "", "output": "【漏洞类型】业务逻辑漏洞\n【严重性】High\n【漏洞摘要】此代码在处理APE文件时,如果文件中没有帧(totalframes为0),在后续的帧处理过程中可能会导致无效的内存读取和写入。具体表现为在`ape_read_header`函数中,未检查`ape->totalframes`是否为0,直接分配内存并进行帧处理,可能导致空指针解引用或数组越界访问。\n【关键代码模式】\n- `ape_read_header`函数中,`ape->totalframes`的使用。\n- `av_malloc`函数用于分配内存。\n- `ape->frames`的使用。\n【修复方法说明】增加对`ape->totalframes`是否为0的检查,如果为0则记录错误并返回`AVERROR(EINVAL)`,避免后续的无效内存操作。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nunpacked/extensions/TeX/unicode.js\n### 代码内容:\n//\n// The configuration defaults, augmented by the user settings\n// \nMathJax.Extension[\"TeX/unicode\"] = {\n version: \"2.7.3\",\n unicode: {},\n config: MathJax.Hub.CombineConfig(\"TeX.unicode\",{\n fonts: \"STIXGeneral,'Arial Unicode MS'\"\n })\n};\n \nMathJax.Hub.Register.StartupHook(\"TeX Jax Ready\",function () {\n var TEX = MathJax.InputJax.TeX;\n var MML = MathJax.ElementJax.mml;\n var UNICODE = MathJax.Extension[\"TeX/unicode\"].unicode;\n \n //\n // Add \\unicode macro\n //\n TEX.Definitions.Add({macros: {unicode: 'Unicode'}},null,true);\n //\n // Implementation of \\unicode in parser\n //\n TEX.Parse.Augment({\n Unicode: function(name) {\n var HD = this.GetBrackets(name), font;\n if (HD) {\n if (HD.replace(/ /g,\"\").match(/^(\\d+(\\.\\d*)?|\\.\\d+),(\\d+(\\.\\d*)?|\\.\\d+)$/))\n {HD = HD.replace(/ /g,\"\").split(/,/); font = this.GetBrackets(name)}\n else {font = HD; HD = null}\n }\n var n = this.trimSpaces(this.GetArgument(name)),\n N = parseInt(n.match(/^x/) ? \"0\"+n : n);\n if (!UNICODE[N]) {UNICODE[N] = [800,200,font,N]}\n else if (!font) {font = UNICODE[N][2]}\n if (HD) {\n UNICODE[N][0] = Math.floor(HD[0]*1000);\n UNICODE[N][1] = Math.floor(HD[1]*1000);\n }\n var variant = this.stack.env.font, def = {};\n if (font) {\n UNICODE[N][2] = def.fontfamily = font.replace(/\"/g,\"'\");\n if (variant) {\n if (variant.match(/bold/)) {def.fontweight = \"bold\"}\n if (variant.match(/italic|-mathit/)) {def.fontstyle = \"italic\"}\n }\n } else if (variant) {def.mathvariant = variant}\n def.unicode = [].concat(UNICODE[N]); // make a copy\n this.Push(MML.mtext(MML.entity(\"#\"+n)).With(def));\n }\n });\n\n MathJax.Hub.Startup.signal.Post(\"TeX unicode Ready\");\n \n});\n \nMathJax.Hub.Register.StartupHook(\"HTML-CSS Jax Ready\",function () {\n var MML = MathJax.ElementJax.mml;\n var FONTS = MathJax.Extension[\"TeX/unicode\"].config.fonts;\n\n //\n // Override getVariant to make one that includes the font and size\n //\n var GETVARIANT = MML.mbase.prototype.HTMLgetVariant;\n MML.mbase.Augment({\n HTMLgetVariant: function () {\n var variant = GETVARIANT.apply(this,arguments);\n if (variant.unicode) {delete variant.unicode; delete variant.FONTS} // clear font cache in case of restart\n if (!this.unicode) {return variant}\n variant.unicode = true;\n if (!variant.defaultFont) {\n variant = MathJax.Hub.Insert({},variant); // make a copy\n variant.defaultFont = {family:FONTS};\n }\n var family = this.unicode[2]; if (family) {family += \",\"+FONTS} else {family = FONTS}\n variant.defaultFont[this.unicode[3]] = [\n this.unicode[0],this.unicode[1],500,0,500,\n {isUnknown:true, isUnicode:true, font:family}\n ];\n return variant;\n }\n });\n});\n\nMathJax.Hub.Register.StartupHook(\"SVG Jax Ready\",function () {\n var MML = MathJax.ElementJax.mml;\n var FONTS = MathJax.Extension[\"TeX/unicode\"].config.fonts;\n\n //\n // Override getVariant to make one that includes the font and size\n //\n var GETVARIANT = MML.mbase.prototype.SVGgetVariant;\n MML.mbase.Augment({\n SVGgetVariant: function () {\n var variant = GETVARIANT.call(this);\n if (variant.unicode) {delete variant.unicode; delete variant.FONTS} // clear font cache in case of restart\n if (!this.unicode) {return variant}\n variant.unicode = true;\n if (!variant.forceFamily) {variant = MathJax.Hub.Insert({},variant)} // make a copy\n variant.defaultFamily = FONTS; variant.noRemap = true;\n variant.h = this.unicode[0]; variant.d = this.unicode[1];\n return variant;\n }\n });\n});\n\nMathJax.Ajax.loadComplete(\"[MathJax]/extensions/TeX/unicode.js\");\n\n## 内容2\n### 文件路径:\nunpacked/extensions/toMathML.js\n### 代码内容:\nMathJax.Hub.Register.LoadHook(\"[MathJax]/jax/element/mml/jax.js\",function () {\n var VERSION = \"2.7.3\";\n \n var MML = MathJax.ElementJax.mml,\n SETTINGS = MathJax.Hub.config.menuSettings;\n \n MML.mbase.Augment({\n\n toMathML: function (space) {\n var inferred = (this.inferred && this.parent.inferRow);\n if (space == null) {space = \"\"}\n var tag = this.type, attr = this.toMathMLattributes();\n if (tag === \"mspace\") {return space + \"<\"+tag+attr+\" />\"}\n var data = [], SPACE = (this.isToken ? \"\" : space+(inferred ? \"\" : \" \"));\n for (var i = 0, m = this.data.length; i < m; i++) {\n if (this.data[i]) {data.push(this.data[i].toMathML(SPACE))}\n else if (!this.isToken && !this.isChars) {data.push(SPACE+\"<mrow />\")}\n }\n if (this.isToken || this.isChars) {return space + \"<\"+tag+attr+\">\"+data.join(\"\")+\"</\"+tag+\">\"}\n if (inferred) {return data.join(\"\\n\")}\n if (data.length === 0 || (data.length === 1 && data[0] === \"\"))\n {return space + \"<\"+tag+attr+\" />\"}\n return space + \"<\"+tag+attr+\">\\n\"+data.join(\"\\n\")+\"\\n\"+ space +\"</\"+tag+\">\";\n },\n\n toMathMLattributes: function () {\n var defaults = (this.type === \"mstyle\" ? MML.math.prototype.defaults : this.defaults);\n var names = (this.attrNames||MML.copyAttributeNames),\n skip = MML.skipAttributes, copy = MML.copyAttributes;\n var attr = [];\n\n if (this.type === \"math\" && (!this.attr || !('xmlns' in this.attr)))\n {attr.push('xmlns=\"http://www.w3.org/1998/Math/MathML\"')}\n if (!this.attrNames) {\n for (var id in defaults) {if (!skip[id] && !copy[id] && defaults.hasOwnProperty(id)) {\n if (this[id] != null && this[id] !== defaults[id]) {\n if (this.Get(id,null,1) !== this[id])\n attr.push(id+'=\"'+this.toMathMLattribute(this[id])+'\"');\n }\n }}\n }\n for (var i = 0, m = names.length; i < m; i++) {\n if (copy[names[i]] === 1 && !defaults.hasOwnProperty(names[i])) continue;\n value = (this.attr||{})[names[i]]; if (value == null) {value = this[names[i]]}\n if (value != null) {attr.push(names[i]+'=\"'+this.toMathMLquote(value)+'\"')}\n }\n this.toMathMLclass(attr);\n if (attr.length) {return \" \"+attr.join(\" \")} else {return \"\"}\n },\n toMathMLclass: function (attr) {\n var CLASS = []; if (this[\"class\"]) {CLASS.push(this[\"class\"])}\n if (this.isa(MML.TeXAtom) && SETTINGS.texHints) {\n var TEXCLASS = [\"ORD\",\"OP\",\"BIN\",\"REL\",\"OPEN\",\"CLOSE\",\"PUNCT\",\"INNER\",\"VCENTER\"][this.texClass];\n if (TEXCLASS) {\n CLASS.push(\"MJX-TeXAtom-\"+TEXCLASS)\n if (TEXCLASS === \"OP\" && !this.movablelimits) CLASS.push(\"MJX-fixedlimits\");\n }\n }\n if (this.mathvariant && this.toMathMLvariants[this.mathvariant])\n {CLASS.push(\"MJX\"+this.mathvariant)}\n if (this.variantForm) {CLASS.push(\"MJX-variant\")}\n if (CLASS.length) {attr.unshift('class=\"'+CLASS.join(\" \")+'\"')}\n },\n toMathMLattribute: function (value) {\n if (typeof(value) === \"string\" &&\n value.replace(/ /g,\"\").match(/^(([-+])?(\\d+(\\.\\d*)?|\\.\\d+))mu$/)) {\n // FIXME: should take scriptlevel into account\n return (RegExp.$2||\"\")+((1/18)*RegExp.$3).toFixed(3).replace(/\\.?0+$/,\"\")+\"em\";\n }\n else if (this.toMathMLvariants[value]) {return this.toMathMLvariants[value]}\n return this.toMathMLquote(value);\n },\n toMathMLvariants: {\n \"-tex-caligraphic\": MML.VARIANT.SCRIPT,\n \"-tex-caligraphic-bold\": MML.VARIANT.BOLDSCRIPT,\n \"-tex-oldstyle\": MML.VARIANT.NORMAL,\n \"-tex-oldstyle-bold\": MML.VARIANT.BOLD,\n \"-tex-mathit\": MML.VARIANT.ITALIC\n },\n \n toMathMLquote: function (string) {\n string = String(string).split(\"\");\n for (var i = 0, m = string.length; i < m; i++) {\n var n = string[i].charCodeAt(0);\n if (n <= 0xD7FF || 0xE000 <= n) {\n // Code points U+0000 to U+D7FF and U+E000 to U+FFFF.\n // They are directly represented by n.\n if (n > 0x7E || (n < 0x20 && n !== 0x0A && n !== 0x0D && n !== 0x09)) {\n string[i] = \"&#x\"+n.toString(16).toUpperCase()+\";\";\n } else {\n var c =\n {'&':'&', '<':'<', '>':'>', '\"':'"'}[string[i]];\n if (c) {string[i] = c}\n }\n } else if (i+1 < m) {\n // Code points U+10000 to U+10FFFF.\n // n is the lead surrogate, let's read the trail surrogate.\n var trailSurrogate = string[i+1].charCodeAt(0);\n var codePoint = (((n-0xD800)<<10)+(trailSurrogate-0xDC00)+0x10000);\n string[i] = \"&#x\"+codePoint.toString(16).toUpperCase()+\";\";\n string[i+1] = \"\";\n i++;\n } else {\n // n is a lead surrogate without corresponding trail surrogate:\n // remove that character.\n string[i] = \"\";\n }\n }\n return string.join(\"\");\n }\n });\n \n //\n // Override math.toMathML in order to add semantics tag\n // for the input format, if the user requests that in the\n // Show As menu.\n //\n MML.math.Augment({\n toMathML: function (space,jax) {\n var annotation;\n if (space == null) {space = \"\"}\n if (jax && jax.originalText && SETTINGS.semantics)\n {annotation = MathJax.InputJax[jax.inputJax].annotationEncoding}\n var nested = (this.data[0] && this.data[0].data.length > 1);\n var tag = this.type, attr = this.toMathMLattributes();\n var data = [], SPACE = space + (annotation ? \" \" + (nested ? \" \" : \"\") : \"\") + \" \";\n for (var i = 0, m = this.data.length; i < m; i++) {\n if (this.data[i]) {data.push(this.data[i].toMathML(SPACE))}\n else {data.push(SPACE+\"<mrow />\")}\n }\n if (data.length === 0 || (data.length === 1 && data[0] === \"\")) {\n if (!annotation) {return \"<\"+tag+attr+\" />\"}\n data.push(SPACE+\"<mrow />\");\n }\n if (annotation) {\n if (nested) {data.unshift(space+\" <mrow>\"); data.push(space+\" </mrow>\")}\n data.unshift(space+\" <semantics>\");\n var xmlEscapedTex = jax.originalText.replace(/[&<>]/g, function(item) {\n return { '>': '>', '<': '<','&': '&' }[item]\n });\n data.push(space+' <annotation encoding=\"'+annotation+'\">'+xmlEscapedTex+\"</annotation>\");\n data.push(space+\" </semantics>\");\n }\n return space+\"<\"+tag+attr+\">\\n\"+data.join(\"\\n\")+\"\\n\"+space+\"</\"+tag+\">\";\n }\n });\n \n MML.msubsup.Augment({\n toMathML: function (space) {\n var tag = this.type;\n if (this.data[this.sup] == null) {tag = \"msub\"}\n if (this.data[this.sub] == null) {tag = \"msup\"}\n var attr = this.toMathMLattributes();\n delete this.data[0].inferred;\n var data = [];\n for (var i = 0, m = this.data.length; i < m; i++)\n {if (this.data[i]) {data.push(this.data[i].toMathML(space+\" \"))}}\n return space + \"<\"+tag+attr+\">\\n\" + data.join(\"\\n\") + \"\\n\" + space + \"</\"+tag+\">\";\n }\n });\n \n MML.munderover.Augment({\n toMathML: function (space) {\n var tag = this.type;\n var base = this.data[this.base];\n if (base && base.isa(MML.TeXAtom) && base.movablelimits && !base.Get(\"displaystyle\")) {\n type = \"msubsup\";\n if (this.data[this.under] == null) {tag = \"msup\"}\n if (this.data[this.over] == null) {tag = \"msub\"}\n } else {\n if (this.data[this.under] == null) {tag = \"mover\"}\n if (this.data[this.over] == null) {tag = \"munder\"}\n }\n var attr = this.toMathMLattributes();\n delete this.data[0].inferred;\n var data = [];\n for (var i = 0, m = this.data.length; i < m; i++)\n {if (this.data[i]) {data.push(this.data[i].toMathML(space+\" \"))}}\n return space + \"<\"+tag+attr+\">\\n\" + data.join(\"\\n\") + \"\\n\" + space + \"</\"+tag+\">\";\n }\n });\n \n MML.TeXAtom.Augment({\n toMathML: function (space) {\n // FIXME: Handle spacing using mpadded?\n var attr = this.toMathMLattributes();\n if (!attr && this.data[0].data.length === 1) {return space.substr(2) + this.data[0].toMathML(space)}\n return space+\"<mrow\"+attr+\">\\n\" + this.data[0].toMathML(space+\" \")+\"\\n\"+space+\"</mrow>\";\n }\n });\n \n MML.chars.Augment({\n toMathML: function (space) {return (space||\"\") + this.toMathMLquote(this.toString())}\n });\n \n MML.entity.Augment({\n toMathML: function (space) {return (space||\"\") + \"&\"+this.data[0]+\";<!-- \"+this.toString()+\" -->\"}\n });\n \n MML.xml.Augment({\n toMathML: function (space) {return (space||\"\") + this.toString()}\n });\n \n MathJax.Hub.Register.StartupHook(\"TeX mathchoice Ready\",function () {\n MML.TeXmathchoice.Augment({\n toMathML: function (space) {return this.Core().toMathML(space)}\n });\n });\n \n MathJax.Hub.Startup.signal.Post(\"toMathML Ready\");\n \n});\n\nMathJax.Ajax.loadComplete(\"[MathJax]/extensions/toMathML.js\");\n\n", "input": "", "output": "【漏洞类型】未验证的用户输入\n【严重性】High\n【漏洞摘要】在这个代码中,用户输入没有经过充分的验证。具体来说,`Unicode`函数在解析`\\unicode`宏时,直接使用了用户提供的`n`参数(未进行验证),并将其转换为整数。这可能导致潜在的安全问题,例如,用户可以通过输入一个无效的Unicode值来引发错误或执行未定义的行为。\n【关键代码模式】\n- `this.GetArgument(name)`:获取用户输入的参数。\n- `parseInt(n.match(/^x/) ? \"0\"+n : n)`:将用户输入转换为整数,但没有验证其有效性。\n- `this.toMathMLquote(value)`:用于对字符串进行转义,但没有用于处理其他类型的输入。\n【修复方法说明】通过在获取用户输入的参数后立即进行验证,确保`n`参数是一个有效的Unicode值(例如,通过正则表达式检查),从而防止无效输入导致的错误或安全问题。\n最佳实践或安全编码技术:在处理用户输入时,始终进行严格的验证和输入清理,以防止注入攻击和其他安全漏洞。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\ndropwizard-validation/src/main/java/io/dropwizard/validation/selfvalidating/SelfValidating.java\n### 代码内容:\npackage io.dropwizard.validation.selfvalidating;\n\nimport javax.validation.Constraint;\nimport javax.validation.Payload;\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * The annotated element has methods annotated by\n * {@link io.dropwizard.validation.selfvalidating.SelfValidation}.\n * Those methods are executed on validation.\n */\n@Documented\n@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@Constraint(validatedBy = SelfValidatingValidator.class)\npublic @interface SelfValidating {\n\n String message() default \"\";\n\n Class<?>[] groups() default {};\n\n Class<? extends Payload>[] payload() default {};\n}\n\n## 内容2\n### 文件路径:\ndropwizard-validation/src/main/java/io/dropwizard/validation/selfvalidating/SelfValidatingValidator.java\n### 代码内容:\npackage io.dropwizard.validation.selfvalidating;\n\nimport com.fasterxml.classmate.AnnotationConfiguration;\nimport com.fasterxml.classmate.AnnotationInclusion;\nimport com.fasterxml.classmate.MemberResolver;\nimport com.fasterxml.classmate.ResolvedTypeWithMembers;\nimport com.fasterxml.classmate.TypeResolver;\nimport com.fasterxml.classmate.members.ResolvedMethod;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.validation.ConstraintValidator;\nimport javax.validation.ConstraintValidatorContext;\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.stream.Collectors;\n\n/**\n * This class is the base validator for the <code>@SelfValidating</code> annotation. It\n * initiates the self validation process on an object, generating wrapping methods to call\n * the validation methods efficiently and then calls them.\n */\npublic class SelfValidatingValidator implements ConstraintValidator<SelfValidating, Object> {\n private static final Logger log = LoggerFactory.getLogger(SelfValidatingValidator.class);\n\n @SuppressWarnings(\"rawtypes\")\n private final ConcurrentMap<Class<?>, List<ValidationCaller>> methodMap = new ConcurrentHashMap<>();\n private final AnnotationConfiguration annotationConfiguration = new AnnotationConfiguration.StdConfiguration(AnnotationInclusion.INCLUDE_AND_INHERIT_IF_INHERITED);\n private final TypeResolver typeResolver = new TypeResolver();\n private final MemberResolver memberResolver = new MemberResolver(typeResolver);\n\n @Override\n public void initialize(SelfValidating constraintAnnotation) {\n }\n\n @SuppressWarnings({\"unchecked\", \"rawtypes\"})\n @Override\n public boolean isValid(Object value, ConstraintValidatorContext context) {\n final ViolationCollector collector = new ViolationCollector(context);\n context.disableDefaultConstraintViolation();\n for (ValidationCaller caller : methodMap.computeIfAbsent(value.getClass(), this::findMethods)) {\n caller.setValidationObject(value);\n caller.call(collector);\n }\n return !collector.hasViolationOccurred();\n }\n\n /**\n * This method generates <code>ValidationCaller</code>s for each method annotated\n * with <code>@SelfValidation</code> that adheres to required signature.\n */\n @SuppressWarnings({ \"rawtypes\" })\n private <T> List<ValidationCaller> findMethods(Class<T> annotated) {\n ResolvedTypeWithMembers annotatedType = memberResolver.resolve(typeResolver.resolve(annotated), annotationConfiguration, null);\n final List<ValidationCaller> callers = Arrays.stream(annotatedType.getMemberMethods())\n .filter(this::isValidationMethod)\n .filter(this::isMethodCorrect)\n .map(m -> new ProxyValidationCaller<>(annotated, m))\n .collect(Collectors.toList());\n if (callers.isEmpty()) {\n log.warn(\"The class {} is annotated with @SelfValidating but contains no valid methods that are annotated \" +\n \"with @SelfValidation\", annotated);\n }\n return callers;\n }\n\n private boolean isValidationMethod(ResolvedMethod m) {\n return m.get(SelfValidation.class) != null;\n }\n\n boolean isMethodCorrect(ResolvedMethod m) {\n if (m.getReturnType()!=null) {\n log.error(\"The method {} is annotated with @SelfValidation but does not return void. It is ignored\", m.getRawMember());\n return false;\n } else if (m.getArgumentCount() != 1 || !m.getArgumentType(0).getErasedType().equals(ViolationCollector.class)) {\n log.error(\"The method {} is annotated with @SelfValidation but does not have a single parameter of type {}\",\n m.getRawMember(), ViolationCollector.class);\n return false;\n } else if (!m.isPublic()) {\n log.error(\"The method {} is annotated with @SelfValidation but is not public\", m.getRawMember());\n return false;\n }\n return true;\n }\n\n final static class ProxyValidationCaller<T> extends ValidationCaller<T> {\n private final Class<T> cls;\n private final ResolvedMethod resolvedMethod;\n\n ProxyValidationCaller(Class<T> cls, ResolvedMethod resolvedMethod) {\n this.cls = cls;\n this.resolvedMethod = resolvedMethod;\n }\n\n @Override\n public void call(ViolationCollector vc) {\n final Method method = resolvedMethod.getRawMember();\n final T obj = cls.cast(getValidationObject());\n try {\n method.invoke(obj, vc);\n } catch (ReflectiveOperationException e) {\n throw new IllegalStateException(\"Couldn't call \" + resolvedMethod + \" on \" + getValidationObject(), e);\n }\n }\n }\n}\n\n## 内容3\n### 文件路径:\ndropwizard-validation/src/main/java/io/dropwizard/validation/selfvalidating/ViolationCollector.java\n### 代码内容:\npackage io.dropwizard.validation.selfvalidating;\n\nimport javax.annotation.Nullable;\nimport javax.validation.ConstraintValidatorContext;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * This class is a simple wrapper around the ConstraintValidatorContext of hibernate validation.\n * It collects all the violations of the SelfValidation methods of an object.\n */\npublic class ViolationCollector {\n private static final Pattern ESCAPE_PATTERN = Pattern.compile(\"\\\\$\\\\{\");\n\n private boolean violationOccurred = false;\n private ConstraintValidatorContext context;\n\n public ViolationCollector(ConstraintValidatorContext context) {\n this.context = context;\n }\n\n /**\n * Adds a new violation to this collector. This also sets {@code violationOccurred} to {@code true}.\n *\n * @param message the message of the violation (any EL expression will be escaped and not parsed)\n */\n public void addViolation(String message) {\n violationOccurred = true;\n String messageTemplate = escapeEl(message);\n context.buildConstraintViolationWithTemplate(messageTemplate)\n .addConstraintViolation();\n }\n\n /**\n * Adds a new violation to this collector. This also sets {@code violationOccurred} to {@code true}.\n *\n * @param propertyName the name of the property\n * @param message the message of the violation (any EL expression will be escaped and not parsed)\n * @since 2.0.2\n */\n public void addViolation(String propertyName, String message) {\n violationOccurred = true;\n String messageTemplate = escapeEl(message);\n context.buildConstraintViolationWithTemplate(messageTemplate)\n .addPropertyNode(propertyName)\n .addConstraintViolation();\n }\n\n /**\n * Adds a new violation to this collector. This also sets {@code violationOccurred} to {@code true}.\n *\n * @param propertyName the name of the property with the violation\n * @param index the index of the element with the violation\n * @param message the message of the violation (any EL expression will be escaped and not parsed)\n * @since 2.0.2\n */\n public void addViolation(String propertyName, Integer index, String message) {\n violationOccurred = true;\n String messageTemplate = escapeEl(message);\n context.buildConstraintViolationWithTemplate(messageTemplate)\n .addPropertyNode(propertyName)\n .addBeanNode().inIterable().atIndex(index)\n .addConstraintViolation();\n }\n\n /**\n * Adds a new violation to this collector. This also sets {@code violationOccurred} to {@code true}.\n *\n * @param propertyName the name of the property with the violation\n * @param key the key of the element with the violation\n * @param message the message of the violation (any EL expression will be escaped and not parsed)\n * @since 2.0.2\n */\n public void addViolation(String propertyName, String key, String message) {\n violationOccurred = true;\n String messageTemplate = escapeEl(message);\n context.buildConstraintViolationWithTemplate(messageTemplate)\n .addPropertyNode(propertyName)\n .addBeanNode().inIterable().atKey(key)\n .addConstraintViolation();\n }\n\n @Nullable\n private String escapeEl(@Nullable String s) {\n if (s == null || s.isEmpty()) {\n return s;\n }\n\n final Matcher m = ESCAPE_PATTERN.matcher(s);\n final StringBuffer sb = new StringBuffer(s.length() + 16);\n while (m.find()) {\n m.appendReplacement(sb, \"\\\\\\\\\\\\${\");\n }\n m.appendTail(sb);\n\n return sb.toString();\n }\n\n /**\n * This method returns the wrapped context for raw access to the validation framework. If you use\n * the context to add violations make sure to call <code>setViolationOccurred(true)</code>.\n *\n * @return the wrapped Hibernate ConstraintValidatorContext\n */\n public ConstraintValidatorContext getContext() {\n return context;\n }\n\n /**\n * @return if any violation was collected\n */\n public boolean hasViolationOccurred() {\n return violationOccurred;\n }\n\n /**\n * Manually sets if a violation occurred. This is automatically set if <code>addViolation</code> is called.\n *\n * @param violationOccurred if any violation was collected\n */\n public void setViolationOccurred(boolean violationOccurred) {\n this.violationOccurred = violationOccurred;\n }\n\n}\n\n", "input": "", "output": "【漏洞类型】模板注入漏洞\n【严重性】High\n【漏洞摘要】原始代码中的`ViolationCollector`类允许通过EL(Expression Language)表达式在验证消息中进行参数插值。然而,这种插值功能可能会被滥用,导致攻击者通过恶意构造的消息参数执行代码注入攻击。具体表现为在`addViolation`方法中调用`escapeEl`方法来转义EL表达式,但并没有对用户输入进行充分验证和清理,导致潜在的安全风险。\n【关键代码模式】\n- `ViolationCollector`类中的`addViolation`方法,特别是`escapeEl`方法。\n- `SelfValidating`注解及其`escapeExpressions`属性。\n- `ConstraintValidatorContext`类的使用。\n【修复方法说明】修复方法通过在`SelfValidating`注解中添加`escapeExpressions`属性,使得EL表达式默认情况下被转义,从而防止模板注入攻击。同时,提供了新的`addViolation`方法,允许通过传递一个消息参数的Map来进行安全的插值。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/libopensc/pkcs15-oberthur.c\n### 代码内容:\n#ifdef HAVE_CONFIG_H\n#include <config.h>\n#endif\n\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n#include \"../common/compat_strlcpy.h\"\n\n#include \"pkcs15.h\"\n#include \"log.h\"\n#include \"asn1.h\"\n#include \"internal.h\"\n\n#ifdef ENABLE_OPENSSL\n#include <openssl/bio.h>\n#include <openssl/crypto.h>\n#include <openssl/x509.h>\n#include <openssl/x509v3.h>\n#endif\n\n#define OBERTHUR_ATTR_MODIFIABLE\t0x0001\n#define OBERTHUR_ATTR_TRUSTED\t\t0x0002\n#define OBERTHUR_ATTR_LOCAL\t\t0x0004\n#define OBERTHUR_ATTR_ENCRYPT\t\t0x0008\n#define OBERTHUR_ATTR_DECRYPT\t\t0x0010\n#define OBERTHUR_ATTR_SIGN\t\t0x0020\n#define OBERTHUR_ATTR_VERIFY\t\t0x0040\n#define OBERTHUR_ATTR_RSIGN\t\t0x0080\n#define OBERTHUR_ATTR_RVERIFY\t\t0x0100\n#define OBERTHUR_ATTR_WRAP\t\t0x0200\n#define OBERTHUR_ATTR_UNWRAP\t\t0x0400\n#define OBERTHUR_ATTR_DERIVE\t\t0x0800\n\n#define USAGE_PRV_ENC\t(SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_DECRYPT |\\\n\t\t\t SC_PKCS15_PRKEY_USAGE_WRAP | SC_PKCS15_PRKEY_USAGE_UNWRAP)\n#define USAGE_PRV_AUT\t SC_PKCS15_PRKEY_USAGE_SIGN\n#define USAGE_PRV_SIGN\t(SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION)\n#define USAGE_PUB_ENC\t(SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP)\n#define USAGE_PUB_AUT\t SC_PKCS15_PRKEY_USAGE_VERIFY\n#define USAGE_PUB_SIGN\t(SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER)\n\n#define PIN_DOMAIN_LABEL\t\"SCM\"\nconst unsigned char PinDomainID[3] = {0x53, 0x43, 0x4D};\n\n#define AWP_PIN_DF\t\t\"3F005011\"\n#define AWP_TOKEN_INFO\t\t\"3F0050111000\"\n#define AWP_PUK_FILE\t\t\"3F0050112000\"\n#define AWP_CONTAINERS_MS\t\"3F0050113000\"\n#define AWP_OBJECTS_LIST_PUB\t\"3F0050114000\"\n#define AWP_OBJECTS_LIST_PRV\t\"3F0050115000\"\n#define AWP_OBJECTS_DF_PUB\t\"3F0050119001\"\n#define AWP_OBJECTS_DF_PRV\t\"3F0050119002\"\n#define AWP_BASE_RSA_PRV\t\"3F00501190023000\"\n#define AWP_BASE_RSA_PUB\t\"3F00501190011000\"\n#define AWP_BASE_CERTIFICATE\t\"3F00501190012000\"\n\n#define BASE_ID_PUB_RSA 0x10\n#define BASE_ID_CERT\t 0x20\n#define BASE_ID_PRV_RSA 0x30\n#define BASE_ID_PRV_DES 0x40\n#define BASE_ID_PUB_DATA 0x50\n#define BASE_ID_PRV_DATA 0x60\n#define BASE_ID_PUB_DES 0x70\n\nstatic int sc_pkcs15emu_oberthur_add_prvkey(struct sc_pkcs15_card *, unsigned, unsigned);\nstatic int sc_pkcs15emu_oberthur_add_pubkey(struct sc_pkcs15_card *, unsigned, unsigned);\nstatic int sc_pkcs15emu_oberthur_add_cert(struct sc_pkcs15_card *, unsigned);\nstatic int sc_pkcs15emu_oberthur_add_data(struct sc_pkcs15_card *, unsigned, unsigned, int);\n\nstatic int sc_oberthur_parse_tokeninfo (struct sc_pkcs15_card *, unsigned char *, size_t, int);\nstatic int sc_oberthur_parse_containers (struct sc_pkcs15_card *, unsigned char *, size_t, int);\nstatic int sc_oberthur_parse_publicinfo (struct sc_pkcs15_card *, unsigned char *, size_t, int);\nstatic int sc_oberthur_parse_privateinfo (struct sc_pkcs15_card *, unsigned char *, size_t, int);\n\nstatic int sc_awp_parse_df(struct sc_pkcs15_card *, struct sc_pkcs15_df *);\nstatic void sc_awp_clear(struct sc_pkcs15_card *);\n\nstruct crypto_container {\n\tunsigned id_pub;\n\tunsigned id_prv;\n\tunsigned id_cert;\n};\n\nstruct container {\n\tchar uuid[37];\n\tstruct crypto_container exchange;\n\tstruct crypto_container sign;\n\n\tstruct container *next;\n\tstruct container *prev;\n};\n\nstruct container *Containers = NULL;\n\nstatic struct {\n\tconst char *name;\n\tconst char *path;\n\tunsigned char *content;\n\tsize_t len;\n\tint (*parser)(struct sc_pkcs15_card *, unsigned char *, size_t, int);\n\tint postpone_allowed;\n} oberthur_infos[] = {\n\t/* Never change the following order */\n\t{ \"Token info\",\t\t\tAWP_TOKEN_INFO, \tNULL, 0, sc_oberthur_parse_tokeninfo, \t0},\n\t{ \"Containers MS\",\t\tAWP_CONTAINERS_MS, \tNULL, 0, sc_oberthur_parse_containers, \t0},\n\t{ \"Public objects list\",\tAWP_OBJECTS_LIST_PUB, \tNULL, 0, sc_oberthur_parse_publicinfo, \t0},\n\t{ \"Private objects list\",\tAWP_OBJECTS_LIST_PRV,\tNULL, 0, sc_oberthur_parse_privateinfo, 1},\n\t{ NULL, NULL, NULL, 0, NULL, 0}\n};\n\nstatic unsigned\nsc_oberthur_decode_usage(unsigned flags)\n{\n\tunsigned ret = 0;\n\n\tif (flags & OBERTHUR_ATTR_ENCRYPT)\n\t\tret |= SC_PKCS15_PRKEY_USAGE_ENCRYPT;\n\tif (flags & OBERTHUR_ATTR_DECRYPT)\n\t\tret |= SC_PKCS15_PRKEY_USAGE_DECRYPT;\n\tif (flags & OBERTHUR_ATTR_SIGN)\n\t\tret |= SC_PKCS15_PRKEY_USAGE_SIGN;\n\tif (flags & OBERTHUR_ATTR_RSIGN)\n\t\tret |= SC_PKCS15_PRKEY_USAGE_SIGNRECOVER;\n\tif (flags & OBERTHUR_ATTR_WRAP)\n\t\tret |= SC_PKCS15_PRKEY_USAGE_WRAP;\n\tif (flags & OBERTHUR_ATTR_UNWRAP)\n\t\tret |= SC_PKCS15_PRKEY_USAGE_UNWRAP;\n\tif (flags & OBERTHUR_ATTR_VERIFY)\n\t\tret |= SC_PKCS15_PRKEY_USAGE_VERIFY;\n\tif (flags & OBERTHUR_ATTR_RVERIFY)\n\t\tret |= SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER;\n\tif (flags & OBERTHUR_ATTR_DERIVE)\n\t\tret |= SC_PKCS15_PRKEY_USAGE_DERIVE;\n\treturn ret;\n}\n\nstatic int\nsc_oberthur_get_friends (unsigned int id, struct crypto_container *ccont)\n{\n\tstruct container *cont;\n\n\tfor (cont = Containers; cont; cont = cont->next) {\n\t\tif (cont->exchange.id_pub == id || cont->exchange.id_prv == id || cont->exchange.id_cert == id) {\n\t\t\tif (ccont)\n\t\t\t\tmemcpy(ccont, &cont->exchange, sizeof(struct crypto_container));\n\t\t\tbreak;\n\t\t}\n\n\t\tif (cont->sign.id_pub == id || cont->sign.id_prv == id || cont->sign.id_cert == id) {\n\t\t\tif (ccont)\n\t\t\t\tmemcpy(ccont, &cont->sign, sizeof(struct crypto_container));\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn cont ? 0 : SC_ERROR_TEMPLATE_NOT_FOUND;\n}\n\nstatic int\nsc_oberthur_get_certificate_authority(struct sc_pkcs15_der *der, int *out_authority)\n{\n#ifdef ENABLE_OPENSSL\n\tX509\t*x;\n\tBUF_MEM buf_mem;\n\tBIO *bio = NULL;\n\tBASIC_CONSTRAINTS *bs = NULL;\n\n\tif (!der)\n\t\treturn SC_ERROR_INVALID_ARGUMENTS;\n\n\tbuf_mem.data = malloc(der->len);\n\tif (!buf_mem.data)\n\t\treturn SC_ERROR_OUT_OF_MEMORY;\n\n\tmemcpy(buf_mem.data, der->value, der->len);\n\tbuf_mem.max = buf_mem.length = der->len;\n\n\tbio = BIO_new(BIO_s_mem());\n\tif (!bio) {\n\t\tfree(buf_mem.data);\n\t\treturn SC_ERROR_OUT_OF_MEMORY;\n\t}\n\n\tBIO_set_mem_buf(bio, &buf_mem, BIO_NOCLOSE);\n\tx = d2i_X509_bio(bio, 0);\n\tfree(buf_mem.data);\n\tBIO_free(bio);\n\tif (!x)\n\t\treturn SC_ERROR_INVALID_DATA;\n\n\tbs = (BASIC_CONSTRAINTS *)X509_get_ext_d2i(x, NID_basic_constraints, NULL, NULL);\n\tif (out_authority)\n\t\t*out_authority = (bs && bs->ca);\n\n\tX509_free(x);\n\n\treturn SC_SUCCESS;\n#else\n\treturn SC_ERROR_NOT_SUPPORTED;\n#endif\n}\n\nstatic int\nsc_oberthur_read_file(struct sc_pkcs15_card *p15card, const char *in_path,\n\t\tunsigned char **out, size_t *out_len,\n\t\tint verify_pin)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tstruct sc_card *card = p15card->card;\n\tstruct sc_file *file = NULL;\n\tstruct sc_path path;\n\tsize_t sz;\n\tint rv;\n\n\tLOG_FUNC_CALLED(ctx);\n\tif (!in_path || !out || !out_len)\n\t\tLOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, \"Cannot read oberthur file\");\n\n\tsc_log(ctx, \"read file '%s'; verify_pin:%i\", in_path, verify_pin);\n\n\t*out = NULL;\n\t*out_len = 0;\n\n\tsc_format_path(in_path, &path);\n\trv = sc_select_file(card, &path, &file);\n\tif (rv != SC_SUCCESS) {\n\t\tsc_file_free(file);\n\t\tLOG_TEST_RET(ctx, rv, \"Cannot select oberthur file to read\");\n\t}\n\n\tif (file->ef_structure == SC_FILE_EF_TRANSPARENT)\n\t\tsz = file->size;\n\telse\n\t\tsz = (file->record_length + 2) * file->record_count;\n\n\t*out = calloc(sz, 1);\n\tif (*out == NULL) {\n\t\tsc_file_free(file);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, \"Cannot read oberthur file\");\n\t}\n\n\tif (file->ef_structure == SC_FILE_EF_TRANSPARENT) {\n\t\trv = sc_read_binary(card, 0, *out, sz, 0);\n\t}\n\telse\t{\n\t\tsize_t rec;\n\t\tsize_t offs = 0;\n\t\tsize_t rec_len = file->record_length;\n\n\t\tfor (rec = 1; ; rec++) {\n\t\t\tif (rec > file->record_count) {\n\t\t\t\trv = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\trv = sc_read_record(card, rec, *out + offs + 2, rec_len, SC_RECORD_BY_REC_NR);\n\t\t\tif (rv == SC_ERROR_RECORD_NOT_FOUND) {\n\t\t\t\trv = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (rv < 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\trec_len = rv;\n\n\t\t\t*(*out + offs) = 'R';\n\t\t\t*(*out + offs + 1) = rv;\n\n\t\t\toffs += rv + 2;\n\t\t}\n\n\t\tsz = offs;\n\t}\n\n\tsc_log(ctx, \"read oberthur file result %i\", rv);\n\tif (verify_pin && rv == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) {\n\t\tstruct sc_pkcs15_object *objs[0x10], *pin_obj = NULL;\n\t\tconst struct sc_acl_entry *acl = sc_file_get_acl_entry(file, SC_AC_OP_READ);\n\t\tint ii, nobjs;\n\n\t\tif (acl == NULL) {\n\t\t\tsc_file_free(file);\n\t\t\tfree(*out);\n\t\t\t*out = NULL;\n\t\t\tLOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA);\n\t\t}\n\n\t\tnobjs = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, 0x10);\n\t\tif (nobjs < 1) {\n\t\t\tsc_file_free(file);\n\t\t\tfree(*out);\n\t\t\t*out = NULL;\n\t\t\tLOG_TEST_RET(ctx, SC_ERROR_DATA_OBJECT_NOT_FOUND,\n\t\t\t\t\"Cannot read oberthur file: get AUTH objects error\");\n\t\t}\n\n\t\tfor (ii = 0; ii < nobjs; ii++) {\n\t\t\tstruct sc_pkcs15_auth_info *auth_info = (struct sc_pkcs15_auth_info *) objs[ii]->data;\n\t\t\tsc_log(ctx, \"compare PIN/ACL refs:%i/%i, method:%i/%i\",\n\t\t\t\tauth_info->attrs.pin.reference, acl->key_ref, auth_info->auth_method, acl->method);\n\t\t\tif (auth_info->attrs.pin.reference == (int)acl->key_ref && auth_info->auth_method == (unsigned)acl->method) {\n\t\t\t\tpin_obj = objs[ii];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!pin_obj || !pin_obj->content.value) {\n\t\t\trv = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED;\n\t\t}\n\t\telse {\n\t\t\trv = sc_pkcs15_verify_pin(p15card, pin_obj, pin_obj->content.value, pin_obj->content.len);\n\t\t\tif (!rv)\n\t\t\t\trv = sc_oberthur_read_file(p15card, in_path, out, out_len, 0);\n\t\t}\n\t}\n\n\tsc_file_free(file);\n\n\tif (rv < 0) {\n\t\tfree(*out);\n\t\t*out = NULL;\n\t\t*out_len = 0;\n\t}\n\n\t*out_len = sz;\n\n\tLOG_FUNC_RETURN(ctx, rv);\n}\n\nstatic int\nsc_oberthur_parse_tokeninfo (struct sc_pkcs15_card *p15card,\n\t\tunsigned char *buff, size_t len, int postpone_allowed)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tchar label[0x21];\n\tunsigned flags;\n\tint ii;\n\n\tLOG_FUNC_CALLED(ctx);\n\tif (!buff || len < 0x24)\n\t\tLOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, \"Cannot parse token info\");\n\n\tmemset(label, 0, sizeof(label));\n\n\tmemcpy(label, buff, 0x20);\n\tii = 0x20;\n\twhile (*(label + --ii)==' ' && ii)\n\t\t;\n\t*(label + ii + 1) = '\\0';\n\n\tflags = *(buff + 0x22) * 0x100 + *(buff + 0x23);\n\n\tset_string(&p15card->tokeninfo->label, label);\n\tset_string(&p15card->tokeninfo->manufacturer_id, \"Oberthur/OpenSC\");\n\n\tif (flags & 0x01)\n\t\tp15card->tokeninfo->flags |= SC_PKCS15_TOKEN_PRN_GENERATION;\n\n\tsc_log(ctx, \"label %s\", p15card->tokeninfo->label);\n\tsc_log(ctx, \"manufacturer_id %s\", p15card->tokeninfo->manufacturer_id);\n\n\tLOG_FUNC_RETURN(ctx, SC_SUCCESS);\n}\n\nstatic int\nsc_oberthur_parse_containers (struct sc_pkcs15_card *p15card,\n\t\tunsigned char *buff, size_t len, int postpone_allowed)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tsize_t offs;\n\n\tLOG_FUNC_CALLED(ctx);\n\n\twhile (Containers) {\n\t\tstruct container *next = Containers->next;\n\n\t\tfree (Containers);\n\t\tContainers = next;\n\t}\n\n\tfor (offs=0; offs + 2 + 2+2+2 + 2+2+2 + 2+36 <= len;) {\n\t\tstruct container *cont;\n\t\tunsigned char *ptr = buff + offs + 2;\n\n\t\tsc_log(ctx,\n\t\t \"parse contaniers offs:%\"SC_FORMAT_LEN_SIZE_T\"u, len:%\"SC_FORMAT_LEN_SIZE_T\"u\",\n\t\t offs, len);\n\t\tif (*(buff + offs) != 'R')\n\t\t\treturn SC_ERROR_INVALID_DATA;\n\n\t\tcont = (struct container *)calloc(sizeof(struct container), 1);\n\t\tif (!cont)\n\t\t\treturn SC_ERROR_OUT_OF_MEMORY;\n\n\t\tcont->exchange.id_pub = *ptr * 0x100 + *(ptr + 1); ptr += 2;\n\t\tcont->exchange.id_prv = *ptr * 0x100 + *(ptr + 1); ptr += 2;\n\t\tcont->exchange.id_cert = *ptr * 0x100 + *(ptr + 1); ptr += 2;\n\n\t\tcont->sign.id_pub = *ptr * 0x100 + *(ptr + 1); ptr += 2;\n\t\tcont->sign.id_prv = *ptr * 0x100 + *(ptr + 1); ptr += 2;\n\t\tcont->sign.id_cert = *ptr * 0x100 + *(ptr + 1); ptr += 2;\n\n\t\tmemcpy(cont->uuid, ptr + 2, 36);\n\t\tsc_log(ctx, \"UUID: %s; 0x%X, 0x%X, 0x%X\", cont->uuid,\n\t\t\t\tcont->exchange.id_pub, cont->exchange.id_prv, cont->exchange.id_cert);\n\n\t\tif (!Containers) {\n\t\t\tContainers = cont;\n\t\t}\n\t\telse {\n\t\t\tcont->next = Containers;\n\t\t\tContainers->prev = (void *)cont;\n\t\t\tContainers = cont;\n\t\t}\n\n\t\toffs += *(buff + offs + 1) + 2;\n\t}\n\n\tLOG_FUNC_RETURN(ctx, SC_SUCCESS);\n}\n\nstatic int\nsc_oberthur_parse_publicinfo (struct sc_pkcs15_card *p15card,\n\t\tunsigned char *buff, size_t len, int postpone_allowed)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tsize_t ii;\n\tint rv;\n\n\tLOG_FUNC_CALLED(ctx);\n\tfor (ii=0; ii+5<=len; ii+=5) {\n\t\tunsigned int file_id, size;\n\n\t\tif(*(buff+ii) != 0xFF)\n\t\t\tcontinue;\n\n\t\tfile_id = 0x100 * *(buff+ii + 1) + *(buff+ii + 2);\n\t\tsize = 0x100 * *(buff+ii + 3) + *(buff+ii + 4);\n\t\tsc_log(ctx, \"add public object(file-id:%04X,size:%X)\", file_id, size);\n\n\t\tswitch (*(buff+ii + 1)) {\n\t\tcase BASE_ID_PUB_RSA :\n\t\t\trv = sc_pkcs15emu_oberthur_add_pubkey(p15card, file_id, size);\n\t\t\tLOG_TEST_RET(ctx, rv, \"Cannot parse public key info\");\n\t\t\tbreak;\n\t\tcase BASE_ID_CERT :\n\t\t\trv = sc_pkcs15emu_oberthur_add_cert(p15card, file_id);\n\t\t\tLOG_TEST_RET(ctx, rv, \"Cannot parse certificate info\");\n\t\t\tbreak;\n\t\tcase BASE_ID_PUB_DES :\n\t\t\tbreak;\n\t\tcase BASE_ID_PUB_DATA :\n\t\t\trv = sc_pkcs15emu_oberthur_add_data(p15card, file_id, size, 0);\n\t\t\tLOG_TEST_RET(ctx, rv, \"Cannot parse data info\");\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Public object parse error\");\n\t\t}\n\t}\n\n\tLOG_FUNC_RETURN(ctx, SC_SUCCESS);\n}\n\nstatic int\nsc_oberthur_parse_privateinfo (struct sc_pkcs15_card *p15card,\n\t\tunsigned char *buff, size_t len, int postpone_allowed)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tsize_t ii;\n\tint rv;\n\tint no_more_private_keys = 0, no_more_private_data = 0;\n\n\tLOG_FUNC_CALLED(ctx);\n\n\tfor (ii=0; ii+5<=len; ii+=5) {\n\t\tunsigned int file_id, size;\n\n\t\tif(*(buff+ii) != 0xFF)\n\t\t\tcontinue;\n\n\t\tfile_id = 0x100 * *(buff+ii + 1) + *(buff+ii + 2);\n\t\tsize = 0x100 * *(buff+ii + 3) + *(buff+ii + 4);\n\t\tsc_log(ctx, \"add private object (file-id:%04X, size:%X)\", file_id, size);\n\n\t\tswitch (*(buff+ii + 1)) {\n\t\tcase BASE_ID_PRV_RSA :\n\t\t\tif (no_more_private_keys)\n\t\t\t\tbreak;\n\n\t\t\trv = sc_pkcs15emu_oberthur_add_prvkey(p15card, file_id, size);\n\t\t\tif (rv == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED && postpone_allowed) {\n\t\t\t\tstruct sc_path path;\n\n\t\t\t\tsc_log(ctx, \"postpone adding of the private keys\");\n\t\t\t\tsc_format_path(\"5011A5A5\", &path);\n\t\t\t\trv = sc_pkcs15_add_df(p15card, SC_PKCS15_PRKDF, &path);\n\t\t\t\tLOG_TEST_RET(ctx, rv, \"Add PrkDF error\");\n\t\t\t\tno_more_private_keys = 1;\n\t\t\t}\n\t\t\tLOG_TEST_RET(ctx, rv, \"Cannot parse private key info\");\n\t\t\tbreak;\n\t\tcase BASE_ID_PRV_DES :\n\t\t\tbreak;\n\t\tcase BASE_ID_PRV_DATA :\n\t\t\tsc_log(ctx, \"*(buff+ii + 1):%X\", *(buff+ii + 1));\n\t\t\tif (no_more_private_data)\n\t\t\t\tbreak;\n\n\t\t\trv = sc_pkcs15emu_oberthur_add_data(p15card, file_id, size, 1);\n\t\t\tif (rv == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED && postpone_allowed) {\n\t\t\t\tstruct sc_path path;\n\n\t\t\t\tsc_log(ctx, \"postpone adding of the private data\");\n\t\t\t\tsc_format_path(\"5011A6A6\", &path);\n\t\t\t\trv = sc_pkcs15_add_df(p15card, SC_PKCS15_DODF, &path);\n\t\t\t\tLOG_TEST_RET(ctx, rv, \"Add DODF error\");\n\t\t\t\tno_more_private_data = 1;\n\t\t\t}\n\t\t\tLOG_TEST_RET(ctx, rv, \"Cannot parse private data info\");\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Private object parse error\");\n\t\t}\n\t}\n\n\tLOG_FUNC_RETURN(ctx, SC_SUCCESS);\n}\n\n/* Public key info:\n * \tflags:2,\n * \tCN(len:2,value:<variable length>),\n * \tID(len:2,value:(SHA1 value)),\n * \tStartDate(Ascii:8)\n * \tEndDate(Ascii:8)\n * \t??(0x00:2)\n */\nstatic int\nsc_pkcs15emu_oberthur_add_pubkey(struct sc_pkcs15_card *p15card,\n\t\tunsigned int file_id, unsigned int size)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tstruct sc_pkcs15_pubkey_info key_info;\n\tstruct sc_pkcs15_object key_obj;\n\tchar ch_tmp[0x100];\n\tunsigned char *info_blob = NULL;\n\tsize_t len, info_len, offs;\n\tunsigned flags;\n\tint rv;\n\n\tLOG_FUNC_CALLED(ctx);\n\tsc_log(ctx, \"public key(file-id:%04X,size:%X)\", file_id, size);\n\n\tmemset(&key_info, 0, sizeof(key_info));\n\tmemset(&key_obj, 0, sizeof(key_obj));\n\n\tsnprintf(ch_tmp, sizeof(ch_tmp), \"%s%04X\", AWP_OBJECTS_DF_PUB, file_id | 0x100);\n\trv = sc_oberthur_read_file(p15card, ch_tmp, &info_blob, &info_len, 1);\n\tLOG_TEST_RET(ctx, rv, \"Failed to add public key: read oberthur file error\");\n\n\t/* Flags */\n\toffs = 2;\n\tif (offs > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add public key: no 'tag'\");\n\t}\n\tflags = *(info_blob + 0) * 0x100 + *(info_blob + 1);\n\tkey_info.usage = sc_oberthur_decode_usage(flags);\n\tif (flags & OBERTHUR_ATTR_MODIFIABLE)\n\t\tkey_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE;\n\tsc_log(ctx, \"Public key key-usage:%04X\", key_info.usage);\n\n\t/* Label */\n\tif (offs + 2 > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add public key: no 'Label'\");\n\t}\n\tlen = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100;\n\tif (offs + 2 + len > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, \"Failed to add public key: invalid 'Label' length\");\n\t} else if (len) {\n\t\tif (len > sizeof(key_obj.label) - 1)\n\t\t\tlen = sizeof(key_obj.label) - 1;\n\t\tmemcpy(key_obj.label, info_blob + offs + 2, len);\n\t}\n\toffs += 2 + len;\n\n\t/* ID */\n\tif (offs + 2 > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add public key: no 'ID'\");\n\t}\n\tlen = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100;\n\tif (len == 0\n\t\t\t|| len > sizeof(key_info.id.value)\n\t\t\t|| offs + 2 + len > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, \"Failed to add public key: invalid 'ID' length\");\n\t}\n\tmemcpy(key_info.id.value, info_blob + offs + 2, len);\n\tkey_info.id.len = len;\n\n\tfree(info_blob);\n\n\t/* Ignore Start/End dates */\n\n\tsnprintf(ch_tmp, sizeof(ch_tmp), \"%s%04X\", AWP_OBJECTS_DF_PUB, file_id);\n\tsc_format_path(ch_tmp, &key_info.path);\n\n\tkey_info.native = 1;\n\tkey_info.key_reference = file_id & 0xFF;\n\tkey_info.modulus_length = size;\n\n\trv = sc_pkcs15emu_add_rsa_pubkey(p15card, &key_obj, &key_info);\n\n\tLOG_FUNC_RETURN(ctx, rv);\n}\n\n/* Certificate info:\n * \tflags:2,\n * \tLabel(len:2,value:),\n * \tID(len:2,value:(SHA1 value)),\n * \tSubject in ASN.1(len:2,value:)\n * \tIssuer in ASN.1(len:2,value:)\n * \tSerial encoded in LV or ASN.1\tFIXME\n */\nstatic int\nsc_pkcs15emu_oberthur_add_cert(struct sc_pkcs15_card *p15card, unsigned int file_id)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tstruct sc_pkcs15_cert_info cinfo;\n\tstruct sc_pkcs15_object cobj;\n\tunsigned char *info_blob = NULL, *cert_blob = NULL;\n\tsize_t info_len, cert_len, len, offs;\n\tunsigned flags;\n\tint rv;\n\tchar ch_tmp[0x20];\n\n\tLOG_FUNC_CALLED(ctx);\n\tsc_log(ctx, \"add certificate(file-id:%04X)\", file_id);\n\n\tmemset(&cinfo, 0, sizeof(cinfo));\n\tmemset(&cobj, 0, sizeof(cobj));\n\n\tsnprintf(ch_tmp, sizeof(ch_tmp), \"%s%04X\", AWP_OBJECTS_DF_PUB, file_id | 0x100);\n\trv = sc_oberthur_read_file(p15card, ch_tmp, &info_blob, &info_len, 1);\n\tLOG_TEST_RET(ctx, rv, \"Failed to add certificate: read oberthur file error\");\n\n\tif (info_len < 2) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add certificate: no 'tag'\");\n\t}\n\tflags = *(info_blob + 0) * 0x100 + *(info_blob + 1);\n\toffs = 2;\n\n\t/* Label */\n\tif (offs + 2 > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add certificate: no 'CN'\");\n\t}\n\tlen = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100;\n\tif (len + offs + 2 > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Invalid 'CN' length\");\n\t} else if (len) {\n\t\tif (len > sizeof(cobj.label) - 1)\n\t\t\tlen = sizeof(cobj.label) - 1;\n\t\tmemcpy(cobj.label, info_blob + offs + 2, len);\n\t}\n\toffs += 2 + len;\n\n\t/* ID */\n\tif (offs + 2 > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add certificate: no 'ID'\");\n\t}\n\tlen = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100;\n\tif (len + offs + 2 > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Invalid 'ID' length\");\n\t} else if (len > sizeof(cinfo.id.value)) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, \"Failed to add certificate: invalid 'ID' length\");\n\t}\n\tmemcpy(cinfo.id.value, info_blob + offs + 2, len);\n\tcinfo.id.len = len;\n\n\tfree(info_blob);\n\n\t/* Ignore subject, issuer and serial */\n\n\tsnprintf(ch_tmp, sizeof(ch_tmp), \"%s%04X\", AWP_OBJECTS_DF_PUB, file_id);\n\tsc_format_path(ch_tmp, &cinfo.path);\n\trv = sc_oberthur_read_file(p15card, ch_tmp, &cert_blob, &cert_len, 1);\n\tLOG_TEST_RET(ctx, rv, \"Failed to add certificate: read certificate error\");\n\n\tcinfo.value.value = cert_blob;\n\tcinfo.value.len = cert_len;\n\n\trv = sc_oberthur_get_certificate_authority(&cinfo.value, &cinfo.authority);\n\tif (rv != SC_SUCCESS) {\n\t\tfree(cinfo.value.value);\n\t\tLOG_TEST_RET(ctx, rv, \"Failed to add certificate: get certificate attributes error\");\n\t}\n\n\tif (flags & OBERTHUR_ATTR_MODIFIABLE)\n\t\tcobj.flags |= SC_PKCS15_CO_FLAG_MODIFIABLE;\n\n\trv = sc_pkcs15emu_add_x509_cert(p15card, &cobj, &cinfo);\n\n\tLOG_FUNC_RETURN(p15card->card->ctx, rv);\n}\n\n/* Private key info:\n * \tflags:2,\n * \tCN(len:2,value:),\n * \tID(len:2,value:(SHA1 value)),\n * \tStartDate(Ascii:8)\n * \tEndDate(Ascii:8)\n * \tSubject in ASN.1(len:2,value:)\n * \tmodulus(value:)\n *\texponent(length:1, value:3)\n */\nstatic int\nsc_pkcs15emu_oberthur_add_prvkey(struct sc_pkcs15_card *p15card,\n\t\tunsigned int file_id, unsigned int size)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tstruct sc_pkcs15_prkey_info kinfo;\n\tstruct sc_pkcs15_object kobj;\n\tstruct crypto_container ccont;\n\tunsigned char *info_blob = NULL;\n\tsize_t info_len = 0;\n\tunsigned flags;\n\tsize_t offs, len;\n\tchar ch_tmp[0x100];\n\tint rv;\n\n\tLOG_FUNC_CALLED(ctx);\n\tsc_log(ctx, \"add private key(file-id:%04X,size:%04X)\", file_id, size);\n\n\tmemset(&kinfo, 0, sizeof(kinfo));\n\tmemset(&kobj, 0, sizeof(kobj));\n\tmemset(&ccont, 0, sizeof(ccont));\n\n\trv = sc_oberthur_get_friends (file_id, &ccont);\n\tLOG_TEST_RET(ctx, rv, \"Failed to add private key: get friends error\");\n\n\tif (ccont.id_cert) {\n\t\tstruct sc_pkcs15_object *objs[32];\n\t\tint ii;\n\n\t\tsc_log(ctx, \"friend certificate %04X\", ccont.id_cert);\n\t\trv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_CERT_X509, objs, 32);\n\t\tLOG_TEST_RET(ctx, rv, \"Failed to add private key: get certificates error\");\n\n\t\tfor (ii=0; ii<rv; ii++) {\n\t\t\tstruct sc_pkcs15_cert_info *cert = (struct sc_pkcs15_cert_info *)objs[ii]->data;\n\t\t\tstruct sc_path path = cert->path;\n\t\t\tunsigned int id = path.value[path.len - 2] * 0x100 + path.value[path.len - 1];\n\n\t\t\tif (id == ccont.id_cert) {\n\t\t\t\tstrlcpy(kobj.label, objs[ii]->label, sizeof(kobj.label));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (ii == rv)\n\t\t\tLOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_PROFILE, \"Failed to add private key: friend not found\");\n\t}\n\n\tsnprintf(ch_tmp, sizeof(ch_tmp), \"%s%04X\", AWP_OBJECTS_DF_PRV, file_id | 0x100);\n\trv = sc_oberthur_read_file(p15card, ch_tmp, &info_blob, &info_len, 1);\n\tLOG_TEST_RET(ctx, rv, \"Failed to add private key: read oberthur file error\");\n\n\tif (info_len < 2) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add private key: no 'tag'\");\n\t}\n\tflags = *(info_blob + 0) * 0x100 + *(info_blob + 1);\n\toffs = 2;\n\n\t/* CN */\n\tif (offs + 2 > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add private key: no 'CN'\");\n\t}\n\tlen = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100;\n\tif (len + offs + 2 > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Invalid 'CN' length\");\n\t}\n\tif (len && !strlen(kobj.label)) {\n\t\tif (len > sizeof(kobj.label) - 1)\n\t\t\tlen = sizeof(kobj.label) - 1;\n\t\tstrncpy(kobj.label, (char *)(info_blob + offs + 2), len);\n\t}\n\toffs += 2 + len;\n\n\t/* ID */\n\tif (offs + 2 > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add private key: no 'ID'\");\n\t}\n\tlen = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100;\n\tif (!len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add private key: zero length ID\");\n\t} else if (len + offs + 2 > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Invalid 'ID' length\");\n\t} else if (len > sizeof(kinfo.id.value)) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, \"Failed to add private key: invalid ID length\");\n\t}\n\tmemcpy(kinfo.id.value, info_blob + offs + 2, len);\n\tkinfo.id.len = len;\n\toffs += 2 + len;\n\n\t/* Ignore Start/End dates */\n\toffs += 16;\n\n\t/* Subject encoded in ASN1 */\n\tif (offs + 2 > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add private key: no 'subject'\");\n\t}\n\tlen = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100;\n\tif (len + offs + 2 > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Invalid 'subject' length\");\n\t} else if (len) {\n\t\tkinfo.subject.value = malloc(len);\n\t\tif (!kinfo.subject.value) {\n\t\t\tfree(info_blob);\n\t\t\tLOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, \"Failed to add private key: memory allocation error\");\n\t\t}\n\t\tkinfo.subject.len = len;\n\t\tmemcpy(kinfo.subject.value, info_blob + offs + 2, len);\n\t}\n\n\t/* Modulus and exponent are ignored */\n\n\tfree(info_blob);\n\n\tsnprintf(ch_tmp, sizeof(ch_tmp), \"%s%04X\", AWP_OBJECTS_DF_PRV, file_id);\n\tsc_format_path(ch_tmp, &kinfo.path);\n\tsc_log(ctx, \"Private key info path %s\", ch_tmp);\n\n\tkinfo.modulus_length\t= size;\n\tkinfo.native\t\t= 1;\n\tkinfo.key_reference\t = file_id & 0xFF;\n\n\tkinfo.usage = sc_oberthur_decode_usage(flags);\n\tkobj.flags = SC_PKCS15_CO_FLAG_PRIVATE;\n\tif (flags & OBERTHUR_ATTR_MODIFIABLE)\n\t\tkobj.flags |= SC_PKCS15_CO_FLAG_MODIFIABLE;\n\n\tkobj.auth_id.len = sizeof(PinDomainID) > sizeof(kobj.auth_id.value)\n\t\t\t? sizeof(kobj.auth_id.value) : sizeof(PinDomainID);\n\tmemcpy(kobj.auth_id.value, PinDomainID, kobj.auth_id.len);\n\n\tsc_log(ctx, \"Parsed private key(reference:%i,usage:%X,flags:%X)\", kinfo.key_reference, kinfo.usage, kobj.flags);\n\n\trv = sc_pkcs15emu_add_rsa_prkey(p15card, &kobj, &kinfo);\n\tLOG_FUNC_RETURN(ctx, rv);\n}\n\nstatic int\nsc_pkcs15emu_oberthur_add_data(struct sc_pkcs15_card *p15card,\n\t\tunsigned int file_id, unsigned int size, int private)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tstruct sc_pkcs15_data_info dinfo;\n\tstruct sc_pkcs15_object dobj;\n\tunsigned flags;\n\tunsigned char *info_blob = NULL, *label = NULL, *app = NULL, *oid = NULL;\n\tsize_t info_len, label_len, app_len, oid_len, offs;\n\tchar ch_tmp[0x100];\n\tint rv;\n\n\tSC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE);\n\tsc_log(ctx, \"Add data(file-id:%04X,size:%i,is-private:%i)\", file_id, size, private);\n\tmemset(&dinfo, 0, sizeof(dinfo));\n\tmemset(&dobj, 0, sizeof(dobj));\n\n\tsnprintf(ch_tmp, sizeof(ch_tmp), \"%s%04X\", private ? AWP_OBJECTS_DF_PRV : AWP_OBJECTS_DF_PUB, file_id | 0x100);\n\n\trv = sc_oberthur_read_file(p15card, ch_tmp, &info_blob, &info_len, 1);\n\tLOG_TEST_RET(ctx, rv, \"Failed to add data: read oberthur file error\");\n\n\tif (info_len < 2) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add certificate: no 'tag'\");\n\t}\n\tflags = *(info_blob + 0) * 0x100 + *(info_blob + 1);\n\toffs = 2;\n\n\t/* Label */\n\tif (offs + 2 > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add data: no 'label'\");\n\t}\n\tlabel = info_blob + offs + 2;\n\tlabel_len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100;\n\tif (offs + 2 + label_len > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Invalid length of 'label' received\");\n\t}\n\tif (label_len > sizeof(dobj.label) - 1)\n\t\tlabel_len = sizeof(dobj.label) - 1;\n\toffs += 2 + *(info_blob + offs + 1);\n\n\t/* Application */\n\tif (offs + 2 > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add data: no 'application'\");\n\t}\n\tapp = info_blob + offs + 2;\n\tapp_len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100;\n\tif (offs + 2 + app_len > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Invalid length of 'application' received\");\n\t}\n\tif (app_len > sizeof(dinfo.app_label) - 1)\n\t\tapp_len = sizeof(dinfo.app_label) - 1;\n\toffs += 2 + app_len;\n\n\t/* OID encode like DER(ASN.1(oid)) */\n\tif (offs + 2 > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add data: no 'OID'\");\n\t}\n\toid_len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100;\n\tif (offs + 2 + oid_len > info_len) {\n\t\tfree(info_blob);\n\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Invalid length of 'oid' received\");\n\t}\n\tif (oid_len) {\n\t\toid = info_blob + offs + 2;\n\t\tif (*oid != 0x06 || (*(oid + 1) != oid_len - 2)) {\n\t\t\tfree(info_blob);\n\t\t\tLOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, \"Failed to add data: invalid 'OID' format\");\n\t\t}\n\t\toid += 2;\n\t\toid_len -= 2;\n\t}\n\n\tsnprintf(ch_tmp, sizeof(ch_tmp), \"%s%04X\", private ? AWP_OBJECTS_DF_PRV : AWP_OBJECTS_DF_PUB, file_id);\n\n\tsc_format_path(ch_tmp, &dinfo.path);\n\n\tmemcpy(dobj.label, label, label_len);\n\tmemcpy(dinfo.app_label, app, app_len);\n\tif (oid_len)\n\t\tsc_asn1_decode_object_id(oid, oid_len, &dinfo.app_oid);\n\n\tif (flags & OBERTHUR_ATTR_MODIFIABLE)\n\t\tdobj.flags |= SC_PKCS15_CO_FLAG_MODIFIABLE;\n\n\tif (private) {\n\t\tdobj.auth_id.len = sizeof(PinDomainID) > sizeof(dobj.auth_id.value)\n\t\t\t\t? sizeof(dobj.auth_id.value) : sizeof(PinDomainID);\n\t\tmemcpy(dobj.auth_id.value, PinDomainID, dobj.auth_id.len);\n\n\t\tdobj.flags |= SC_PKCS15_CO_FLAG_PRIVATE;\n\t}\n\n\trv = sc_pkcs15emu_add_data_object(p15card, &dobj, &dinfo);\n\n\tfree(info_blob);\n\tLOG_FUNC_RETURN(p15card->card->ctx, rv);\n}\n\nstatic int\nsc_pkcs15emu_oberthur_init(struct sc_pkcs15_card * p15card)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tstruct sc_pkcs15_auth_info auth_info;\n\tstruct sc_pkcs15_object obj;\n\tstruct sc_card *card = p15card->card;\n\tstruct sc_path path;\n\tint rv, ii, tries_left;\n\tchar serial[0x10];\n\tunsigned char sopin_reference = 0x04;\n\n\tSC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);\n\tsc_bin_to_hex(card->serialnr.value, card->serialnr.len, serial, sizeof(serial), 0);\n\tset_string(&p15card->tokeninfo->serial_number, serial);\n\n\tp15card->ops.parse_df = sc_awp_parse_df;\n\tp15card->ops.clear = sc_awp_clear;\n\n\tsc_log(ctx, \"Oberthur init: serial %s\", p15card->tokeninfo->serial_number);\n\n\tsc_format_path(AWP_PIN_DF, &path);\n\trv = sc_select_file(card, &path, NULL);\n\tLOG_TEST_RET(ctx, rv, \"Oberthur init failed: cannot select PIN dir\");\n\n\ttries_left = -1;\n\trv = sc_verify(card, SC_AC_CHV, sopin_reference, (unsigned char *)\"\", 0, &tries_left);\n\tif (rv && rv != SC_ERROR_PIN_CODE_INCORRECT) {\n\t\tsopin_reference = 0x84;\n\t\trv = sc_verify(card, SC_AC_CHV, sopin_reference, (unsigned char *)\"\", 0, &tries_left);\n\t}\n\tif (rv && rv != SC_ERROR_PIN_CODE_INCORRECT)\n\t\tLOG_TEST_RET(ctx, rv, \"Invalid state of SO-PIN\");\n\n\t/* add PIN */\n\tmemset(&auth_info, 0, sizeof(auth_info));\n\tmemset(&obj, 0, sizeof(obj));\n\n\tauth_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN;\n\tauth_info.auth_method\t= SC_AC_CHV;\n\tauth_info.auth_id.len = 1;\n\tauth_info.auth_id.value[0] = 0xFF;\n\tauth_info.attrs.pin.min_length\t\t= 4;\n\tauth_info.attrs.pin.max_length\t\t= 64;\n\tauth_info.attrs.pin.stored_length\t= 64;\n\tauth_info.attrs.pin.type\t\t= SC_PKCS15_PIN_TYPE_ASCII_NUMERIC;\n\tauth_info.attrs.pin.reference\t\t= sopin_reference;\n\tauth_info.attrs.pin.pad_char\t\t= 0xFF;\n\tauth_info.attrs.pin.flags\t\t= SC_PKCS15_PIN_FLAG_CASE_SENSITIVE\n\t\t\t\t| SC_PKCS15_PIN_FLAG_INITIALIZED\n\t\t\t\t| SC_PKCS15_PIN_FLAG_NEEDS_PADDING\n\t\t\t\t| SC_PKCS15_PIN_FLAG_SO_PIN;\n\tauth_info.tries_left\t\t= tries_left;\n\tauth_info.logged_in = SC_PIN_STATE_UNKNOWN;\n\n\tstrncpy(obj.label, \"SO PIN\", SC_PKCS15_MAX_LABEL_SIZE-1);\n\tobj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE;\n\n\tsc_log(ctx, \"Add PIN(%s,auth_id:%s,reference:%i)\", obj.label,\n\t\t\tsc_pkcs15_print_id(&auth_info.auth_id), auth_info.attrs.pin.reference);\n\trv = sc_pkcs15emu_add_pin_obj(p15card, &obj, &auth_info);\n\tLOG_TEST_RET(ctx, rv, \"Oberthur init failed: cannot add PIN object\");\n\n\ttries_left = -1;\n\trv = sc_verify(card, SC_AC_CHV, 0x81, (unsigned char *)\"\", 0, &tries_left);\n\tif (rv == SC_ERROR_PIN_CODE_INCORRECT) {\n\t\t/* add PIN */\n\t\tmemset(&auth_info, 0, sizeof(auth_info));\n\t\tmemset(&obj, 0, sizeof(obj));\n\n\t\tauth_info.auth_id.len = sizeof(PinDomainID) > sizeof(auth_info.auth_id.value)\n\t\t\t\t? sizeof(auth_info.auth_id.value) : sizeof(PinDomainID);\n\t\tmemcpy(auth_info.auth_id.value, PinDomainID, auth_info.auth_id.len);\n\t\tauth_info.auth_method\t= SC_AC_CHV;\n\n\t\tauth_info.attrs.pin.min_length\t\t= 4;\n\t\tauth_info.attrs.pin.max_length\t\t= 64;\n\t\tauth_info.attrs.pin.stored_length\t= 64;\n\t\tauth_info.attrs.pin.type\t\t= SC_PKCS15_PIN_TYPE_ASCII_NUMERIC;\n\t\tauth_info.attrs.pin.reference\t\t= 0x81;\n\t\tauth_info.attrs.pin.pad_char\t\t= 0xFF;\n\t\tauth_info.attrs.pin.flags\t\t= SC_PKCS15_PIN_FLAG_CASE_SENSITIVE\n\t\t\t\t\t| SC_PKCS15_PIN_FLAG_INITIALIZED\n\t\t\t\t\t| SC_PKCS15_PIN_FLAG_NEEDS_PADDING\n\t\t\t\t\t| SC_PKCS15_PIN_FLAG_LOCAL;\n\t\tauth_info.tries_left\t\t= tries_left;\n\n\t\tstrncpy(obj.label, PIN_DOMAIN_LABEL, SC_PKCS15_MAX_LABEL_SIZE-1);\n\t\tobj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE;\n\t\tif (sopin_reference == 0x84) {\n\t\t\t/*\n\t\t\t * auth_pin_reset_oberthur_style() in card-oberthur.c\n\t\t\t * always uses PUK with reference 0x84 for\n\t\t\t * unblocking of User PIN\n\t\t\t */\n\t\t\tobj.auth_id.len = 1;\n\t\t\tobj.auth_id.value[0] = 0xFF;\n\t\t}\n\n\t\tsc_format_path(AWP_PIN_DF, &auth_info.path);\n\t\tauth_info.path.type = SC_PATH_TYPE_PATH;\n\n\t\tsc_log(ctx, \"Add PIN(%s,auth_id:%s,reference:%i)\", obj.label,\n\t\t\t\tsc_pkcs15_print_id(&auth_info.auth_id), auth_info.attrs.pin.reference);\n\t\trv = sc_pkcs15emu_add_pin_obj(p15card, &obj, &auth_info);\n\t\tLOG_TEST_RET(ctx, rv, \"Oberthur init failed: cannot add PIN object\");\n\t}\n\telse if (rv != SC_ERROR_DATA_OBJECT_NOT_FOUND) {\n\t\tLOG_TEST_RET(ctx, rv, \"Oberthur init failed: cannot verify PIN\");\n\t}\n\n\tfor (ii=0; oberthur_infos[ii].name; ii++) {\n\t\tsc_log(ctx, \"Oberthur init: read %s file\", oberthur_infos[ii].name);\n\t\tfree(oberthur_infos[ii].content);\n\t\trv = sc_oberthur_read_file(p15card, oberthur_infos[ii].path,\n\t\t\t\t&oberthur_infos[ii].content, &oberthur_infos[ii].len, 1);\n\t\tLOG_TEST_RET(ctx, rv, \"Oberthur init failed: read oberthur file error\");\n\n\t\tsc_log(ctx,\n\t\t \"Oberthur init: parse %s file, content length %\"SC_FORMAT_LEN_SIZE_T\"u\",\n\t\t oberthur_infos[ii].name, oberthur_infos[ii].len);\n\t\trv = oberthur_infos[ii].parser(p15card, oberthur_infos[ii].content, oberthur_infos[ii].len,\n\t\t\t\toberthur_infos[ii].postpone_allowed);\n\t\tLOG_TEST_RET(ctx, rv, \"Oberthur init failed: parse error\");\n\t}\n\n\tLOG_FUNC_RETURN(ctx, SC_SUCCESS);\n}\n\nstatic int\noberthur_detect_card(struct sc_pkcs15_card * p15card)\n{\n\tstruct sc_card *card = p15card->card;\n\n\tSC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);\n\tif (p15card->card->type != SC_CARD_TYPE_OBERTHUR_64K)\n\t\tLOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_WRONG_CARD);\n\tLOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS);\n}\n\nint\nsc_pkcs15emu_oberthur_init_ex(struct sc_pkcs15_card * p15card, struct sc_aid *aid)\n{\n\tint rv;\n\n\tLOG_FUNC_CALLED(p15card->card->ctx);\n\trv = oberthur_detect_card(p15card);\n\tif (!rv)\n\t\trv = sc_pkcs15emu_oberthur_init(p15card);\n\n\tLOG_FUNC_RETURN(p15card->card->ctx, rv);\n}\n\nstatic int\nsc_awp_parse_df(struct sc_pkcs15_card *p15card, struct sc_pkcs15_df *df)\n{\n\tstruct sc_context *ctx = p15card->card->ctx;\n\tunsigned char *buf = NULL;\n\tsize_t buf_len;\n\tint rv;\n\n\tLOG_FUNC_CALLED(ctx);\n\tif (df->type != SC_PKCS15_PRKDF && df->type != SC_PKCS15_DODF)\n\t\tLOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED);\n\n\tif (df->enumerated)\n\t\tLOG_FUNC_RETURN(ctx, SC_SUCCESS);\n\n\trv = sc_oberthur_read_file(p15card, AWP_OBJECTS_LIST_PRV, &buf, &buf_len, 1);\n\tLOG_TEST_RET(ctx, rv, \"Parse DF: read private objects info failed\");\n\n\trv = sc_oberthur_parse_privateinfo(p15card, buf, buf_len, 0);\n\n\tif (buf)\n\t\tfree(buf);\n\n\tif (rv == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED)\n\t\tLOG_FUNC_RETURN(ctx, SC_SUCCESS);\n\n\tLOG_TEST_RET(ctx, rv, \"Parse DF: private info parse error\");\n\tdf->enumerated = 1;\n\n\tLOG_FUNC_RETURN(ctx, rv);\n}\n\nstatic void\nsc_awp_clear(struct sc_pkcs15_card *p15card)\n{\n\tLOG_FUNC_CALLED(p15card->card->ctx);\n}\n\n", "input": "", "output": "【漏洞类型】漏洞类型名称:路径遍历漏洞\n\n【严重性】严重性等级:High\n\n【漏洞摘要】漏洞类型的通用描述:这是一个路径遍历漏洞。具体表现为在函数 `sc_pkcs15emu_oberthur_add_data` 中,`oid_len` 的验证不足,可能导致攻击者通过构造特殊的 `oid_len` 值来读取和操纵文件系统中的任意文件。问题行出现在 `if (oid_len)` 语句中,该语句没有检查 `oid_len` 的最小值是否为2,这在处理OID时是必要的,因为OID至少需要两个字节(一个标识符0x06和一个长度字节)。\n\n根本原因分析:导致漏洞的根本原因在于代码中对 `oid_len` 的验证不充分,允许了小于2的值,这可能导致内存越界访问和路径遍历攻击。\n\n关键代码模式:与此漏洞相关的关键代码模式包括:\n- `sc_pkcs15emu_oberthur_add_data` 函数中的 `if (oid_len)` 语句。\n- `snprintf` 函数用于构建文件路径的字符串。\n- `sc_oberthur_read_file` 函数用于读取文件。\n\n关键词:路径遍历、文件读取、未验证的用户输入、路径格式化、OID处理。\n\n【修复方法说明】修复方法如何解决问题:通过修改 `if (oid_len)` 语句为 `if (oid_len > 2)`,确保 `oid_len` 至少为2,从而防止路径遍历攻击。最佳实践包括对所有用户输入和文件路径进行严格的验证和限制。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\netc/security/mh_default_org.xml\n### 代码内容:\n<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:sec=\"http://www.springframework.org/schema/security\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:osgi=\"http://www.springframework.org/schema/osgi\"\n xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n http://www.springframework.org/schema/beans/spring-beans.xsd\n http://www.springframework.org/schema/osgi\n http://www.springframework.org/schema/osgi/spring-osgi.xsd\n http://www.springframework.org/schema/security\n http://www.springframework.org/schema/security/spring-security-3.1.xsd\">\n\n <!-- ######################################## -->\n <!-- # Open and unsecured url patterns # -->\n <!-- ######################################## -->\n\n <sec:http pattern=\"/admin/img/**\" security=\"none\" />\n <sec:http pattern=\"/favicon.ico\" security=\"none\" />\n <sec:http pattern=\"/img/**\" security=\"none\" />\n <sec:http pattern=\"/js/**\" security=\"none\" />\n <sec:http pattern=\"/lib/**\" security=\"none\" />\n <sec:http pattern=\"/admin-ng/fonts/**\" security=\"none\" />\n <sec:http pattern=\"/admin-ng/scripts/**\" security=\"none\" />\n <sec:http pattern=\"/admin-ng/styles/**\" security=\"none\" />\n <sec:http pattern=\"/play/*\" security=\"none\" />\n\n <sec:http create-session=\"ifRequired\" servlet-api-provision=\"true\" realm=\"Opencast\"\n entry-point-ref=\"opencastEntryPoint\">\n\n <!-- ################ -->\n <!-- # URL SECURITY # -->\n <!-- ################ -->\n\n <!-- Allow anonymous access to the login form -->\n <sec:intercept-url pattern=\"/admin-ng/login.html\" access=\"ROLE_ANONYMOUS\" />\n <sec:intercept-url pattern=\"/sysinfo/bundles/version\" method=\"GET\" access=\"ROLE_ANONYMOUS\" />\n\n <!-- Protect admin UI facade -->\n <sec:intercept-url pattern=\"/workflow/definitions.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_CREATE\" />\n <sec:intercept-url pattern=\"/admin-ng/acl/acls.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_ACLS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/acl/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_ACLS_VIEW\" />\n <sec:intercept-url pattern=\"/acl-manager/acl/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_ACLS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/capture-agents/agents.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_LOCATIONS_VIEW, ROLE_UI_EVENTS_CREATE\" />\n <sec:intercept-url pattern=\"/admin-ng/capture-agents/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_LOCATIONS_DETAILS_CAPABILITIES_VIEW, ROLE_UI_LOCATIONS_DETAILS_CONFIGURATION_VIEW, ROLE_UI_LOCATIONS_DETAILS_GENERAL_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/asset/assets.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_ASSETS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/asset/attachment/*.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_ASSETS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/asset/catalog/*.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_ASSETS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/asset/media/*.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_ASSETS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/asset/publication/*.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_ASSETS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/event/catalogAdapters\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_METADATA_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/event/events.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/event/workflowProperties\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_CREATE, ROLE_UI_TASKS_CREATE, ROLE_UI_EVENTS_EDITOR_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/event/new/metadata\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_METADATA_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/event/new/processing\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_CREATE, ROLE_UI_TASKS_CREATE\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/attachments.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_ATTACHMENTS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/comments\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_COMMENTS_VIEW\"/>\n <sec:intercept-url pattern=\"/admin-ng/event/*/comment/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_COMMENTS_VIEW\"/>\n <sec:intercept-url pattern=\"/admin-ng/event/*/publications.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_PUBLICATIONS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/hasActiveTransaction\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/hasSnapshots.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DELETE\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/media.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_MEDIA_VIEW\"/>\n <sec:intercept-url pattern=\"/admin-ng/event/*/media/*.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_MEDIA_VIEW\"/>\n <sec:intercept-url pattern=\"/admin-ng/event/*/metadata.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_METADATA_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/scheduling.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_SCHEDULING_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/workflows.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/workflows/*/operations.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_VIEW\"/>\n <sec:intercept-url pattern=\"/admin-ng/event/*/workflows/*/operations/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_VIEW\"/>\n <sec:intercept-url pattern=\"/admin-ng/event/*/workflows/*/errors.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_VIEW\"/>\n <sec:intercept-url pattern=\"/admin-ng/event/*/workflows/*/errors/*.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_VIEW\"/>\n <sec:intercept-url pattern=\"/admin-ng/event/*/workflows/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_VIEW\"/>\n <sec:intercept-url pattern=\"/admin-ng/event/*/access.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_ACL_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/groups/groups.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_GROUPS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/groups/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_GROUPS_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/job/jobs.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_JOBS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/series/series.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_SERIES_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/series/new/metadata\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_SERIES_DETAILS_METADATA_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/series/new/themes\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_SERIES_DETAILS_THEMES_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/series/*/access.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_SERIES_DETAILS_ACL_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/series/*/metadata.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_SERIES_DETAILS_METADATA_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/series/*/theme.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_SERIES_DETAILS_THEMES_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/server/servers.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_SERVERS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/services/services.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_SERVICES_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/tasks/processing.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_TASKS_CREATE\" />\n <sec:intercept-url pattern=\"/admin-ng/themes/themes.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_THEMES_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/themes/*/usage.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_THEMES_DETAILS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/themes/*.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_THEMES_DETAILS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/tools/*/editor.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_EDITOR_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/tools/*/thumbnail.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_EDITOR_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/tools/*.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_EDITOR_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/users/users.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_USERS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/users/*.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_USERS_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/user-settings/signature\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_ADMIN_UI\" />\n <sec:intercept-url pattern=\"/admin-ng/resources/events/filters.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/resources/series/filters.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_SERIES_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/resources/themes/filters.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_THEMES_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/resources/recordings/filters.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_LOCATIONS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/resources/users/filters.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_USERS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/resources/groups/filters.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_GROUPS_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/resources/components.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_ADMIN_UI\" />\n <sec:intercept-url pattern=\"/admin-ng/resources/providers.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_ADMIN_UI\" />\n <sec:intercept-url pattern=\"/admin-ng/resources/THEMES.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_THEMES_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/resources/*.json\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_ADMIN_UI\" />\n <sec:intercept-url pattern=\"/assets/assets/**\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_ASSETS_VIEW\" />\n\n <sec:intercept-url pattern=\"/admin-ng/acl/*\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_UI_ACLS_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/metadata\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_METADATA_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/scheduling\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_SCHEDULING_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/workflows\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/workflows/*/action/stop\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/workflows/*/action/retry\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/workflows/*/action/none\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/comment/*/*\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_COMMENTS_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/comment/*\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_COMMENTS_EDIT\"/>\n <sec:intercept-url pattern=\"/admin-ng/event/bulk/update\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_SCHEDULING_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/groups/*\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_UI_GROUPS_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/series/*/metadata\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_UI_SERIES_DETAILS_METADATA_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/series/*/theme\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_UI_SERIES_DETAILS_THEMES_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/themes/*\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_UI_THEMES_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/users/*\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_UI_USERS_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/user-settings/signature/*\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_ADMIN_UI\" />\n\n <sec:intercept-url pattern=\"/services/maintenance\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_SERVERS_MAINTENANCE_EDIT\" />\n <sec:intercept-url pattern=\"/services/sanitize\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_SERVICES_STATUS_EDIT\" />\n <sec:intercept-url pattern=\"/staticfiles\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_THEMES_CREATE, ROLE_UI_THEMES_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/acl\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_ACLS_CREATE\" />\n <sec:intercept-url pattern=\"/admin-ng/event/bulk/conflicts\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_SCHEDULING_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/event/deleteEvents\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DELETE\" />\n <sec:intercept-url pattern=\"/admin-ng/event/new\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_CREATE\" />\n <sec:intercept-url pattern=\"/admin-ng/event/new/conflicts\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_CREATE\" />\n <sec:intercept-url pattern=\"/admin-ng/event/scheduling.json\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_SCHEDULING_VIEW\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/access\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_ACL_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/assets\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_ASSETS_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/comment\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_COMMENTS_CREATE\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/comment/*/reply\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_COMMENTS_CREATE\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/comment/*\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_COMMENTS_CREATE\" />\n <sec:intercept-url pattern=\"/admin-ng/groups\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_GROUPS_CREATE\" />\n <sec:intercept-url pattern=\"/admin-ng/index/*\" method=\"POST\" access=\"ROLE_ADMIN\" />\n <sec:intercept-url pattern=\"/admin-ng/series/deleteSeries\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_SERIES_DELETE\" />\n <sec:intercept-url pattern=\"/admin-ng/series/new\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_SERIES_CREATE\" />\n <sec:intercept-url pattern=\"/admin-ng/series/*/access\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_SERIES_DETAILS_ACL_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/tasks/new\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_TASKS_CREATE\" />\n <sec:intercept-url pattern=\"/admin-ng/themes\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_THEMES_CREATE\" />\n <sec:intercept-url pattern=\"/admin-ng/tools/*/editor.json\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_EDITOR_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/tools/*/thumbnail.json\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_EDITOR_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/users\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_UI_USERS_CREATE\" />\n <sec:intercept-url pattern=\"/admin-ng/user-settings/signature\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_ADMIN_UI\" />\n\n <sec:intercept-url pattern=\"/admin-ng/acl/*\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_UI_ACLS_DELETE\" />\n <sec:intercept-url pattern=\"/admin-ng/capture-agents/*\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_UI_LOCATIONS_DELETE\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/comment/*/*\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_COMMENTS_DELETE\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/comment/*\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_COMMENTS_DELETE\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*/workflows/*\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_DELETE\" />\n <sec:intercept-url pattern=\"/admin-ng/event/*\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_UI_EVENTS_DELETE\" />\n <sec:intercept-url pattern=\"/admin-ng/groups/*\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_UI_GROUPS_DELETE\" />\n <sec:intercept-url pattern=\"/admin-ng/series/*/theme\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_UI_SERIES_DETAILS_THEMES_EDIT\" />\n <sec:intercept-url pattern=\"/admin-ng/series/*\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_UI_SERIES_DELETE\" />\n <sec:intercept-url pattern=\"/admin-ng/themes/*\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_UI_THEMES_DELETE\" />\n <sec:intercept-url pattern=\"/admin-ng/users/*\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_UI_USERS_DELETE\" />\n\n <!-- Securing the URLs for the external API interface -->\n <!-- External API GET Endpoints -->\n <sec:intercept-url pattern=\"/api\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API\"/>\n <sec:intercept-url pattern=\"/api/agents\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_CAPTURE_AGENTS_VIEW\"/>\n <sec:intercept-url pattern=\"/api/agents/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_CAPTURE_AGENTS_VIEW\"/>\n <sec:intercept-url pattern=\"/api/events\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_VIEW\"/>\n <sec:intercept-url pattern=\"/api/events/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_VIEW\"/>\n <sec:intercept-url pattern=\"/api/events/*/acl\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_ACL_VIEW\"/>\n <sec:intercept-url pattern=\"/api/events/*/media\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_MEDIA_VIEW\"/>\n <sec:intercept-url pattern=\"/api/events/*/media/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_MEDIA_VIEW\"/>\n <sec:intercept-url pattern=\"/api/events/*/metadata\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_METADATA_VIEW\"/>\n <sec:intercept-url pattern=\"/api/events/*/metadata/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_METADATA_VIEW\"/>\n <sec:intercept-url pattern=\"/api/events/*/publications\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_PUBLICATIONS_VIEW\"/>\n <sec:intercept-url pattern=\"/api/events/*/publications/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_PUBLICATIONS_VIEW\"/>\n <sec:intercept-url pattern=\"/api/events/*/scheduling\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_SCHEDULING_VIEW\"/>\n <sec:intercept-url pattern=\"/api/groups\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_GROUPS_VIEW\"/>\n <sec:intercept-url pattern=\"/api/groups/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_GROUPS_VIEW\"/>\n <sec:intercept-url pattern=\"/api/info/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API\" />\n <sec:intercept-url pattern=\"/api/info/me/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API\" />\n <sec:intercept-url pattern=\"/api/series\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_SERIES_VIEW\"/>\n <sec:intercept-url pattern=\"/api/series/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_SERIES_VIEW\"/>\n <sec:intercept-url pattern=\"/api/series/*/acl\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_SERIES_ACL_VIEW\"/>\n <sec:intercept-url pattern=\"/api/series/*/metadata\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_SERIES_METADATA_VIEW\"/>\n <sec:intercept-url pattern=\"/api/series/*/metadata/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_SERIES_METADATA_VIEW\"/>\n <sec:intercept-url pattern=\"/api/series/*/properties\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_SERIES_PROPERTIES_VIEW\"/>\n <sec:intercept-url pattern=\"/api/version\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API\"/>\n <sec:intercept-url pattern=\"/api/version/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API\"/>\n <sec:intercept-url pattern=\"/api/workflows\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_WORKFLOW_INSTANCE_VIEW\"/>\n <sec:intercept-url pattern=\"/api/workflows/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_WORKFLOW_INSTANCE_VIEW\"/>\n <sec:intercept-url pattern=\"/api/workflow-definitions\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_WORKFLOW_DEFINITION_VIEW\"/>\n <sec:intercept-url pattern=\"/api/workflow-definitions/*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_API_WORKFLOW_DEFINITION_VIEW\"/>\n <!-- External API PUT Endpoints -->\n <sec:intercept-url pattern=\"/api/events/*\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_EDIT\"/>\n <sec:intercept-url pattern=\"/api/events/*/acl\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_ACL_EDIT\"/>\n <sec:intercept-url pattern=\"/api/events/*/metadata\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_METADATA_EDIT\"/>\n <sec:intercept-url pattern=\"/api/events/*/metadata/*\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_METADATA_EDIT\"/>\n <sec:intercept-url pattern=\"/api/events/*/scheduling\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_SCHEDULING_EDIT\"/>\n <sec:intercept-url pattern=\"/api/groups/*\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_API_GROUPS_EDIT\"/>\n <sec:intercept-url pattern=\"/api/series/*\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_API_SERIES_EDIT\"/>\n <sec:intercept-url pattern=\"/api/series/*/acl\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_API_SERIES_ACL_EDIT\"/>\n <sec:intercept-url pattern=\"/api/series/*/metadata\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_API_SERIES_METADATA_EDIT\"/>\n <sec:intercept-url pattern=\"/api/series/*/metadata/*\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_API_SERIES_METADATA_EDIT\"/>\n <sec:intercept-url pattern=\"/api/series/*/properties\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_API_SERIES_PROPERTIES_EDIT\"/>\n <sec:intercept-url pattern=\"/api/workflows/*\" method=\"PUT\" access=\"ROLE_ADMIN, ROLE_API_WORKFLOW_INSTANCE_EDIT\"/>\n <!-- External API POST Endpoints -->\n <sec:intercept-url pattern=\"/api/events\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_CREATE\"/>\n <sec:intercept-url pattern=\"/api/events/*\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_EDIT\"/>\n <sec:intercept-url pattern=\"/api/events/*/acl/*\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_ACL_EDIT\"/>\n <sec:intercept-url pattern=\"/api/groups\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_API_GROUPS_CREATE\"/>\n <sec:intercept-url pattern=\"/api/groups/*/members/*\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_API_GROUPS_EDIT\"/>\n <sec:intercept-url pattern=\"/api/clearIndex\" method=\"POST\" access=\"ROLE_ADMIN\"/>\n <sec:intercept-url pattern=\"/api/recreateIndex\" method=\"POST\" access=\"ROLE_ADMIN\"/>\n <sec:intercept-url pattern=\"/api/recreateIndex/*\" method=\"POST\" access=\"ROLE_ADMIN\"/>\n <sec:intercept-url pattern=\"/api/series\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_API_SERIES_CREATE\"/>\n <sec:intercept-url pattern=\"/api/security/sign\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_API_SECURITY_EDIT\"/>\n <sec:intercept-url pattern=\"/api/workflows\" method=\"POST\" access=\"ROLE_ADMIN, ROLE_API_WORKFLOW_INSTANCE_CREATE\"/>\n <!-- External API DELETE Endpoints -->\n <sec:intercept-url pattern=\"/api/events/*\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_DELETE\"/>\n <sec:intercept-url pattern=\"/api/events/*/acl/*/*\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_ACL_DELETE\"/>\n <sec:intercept-url pattern=\"/api/events/*/metadata\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_METADATA_DELETE\"/>\n <sec:intercept-url pattern=\"/api/events/*/metadata/*\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_API_EVENTS_METADATA_DELETE\"/>\n <sec:intercept-url pattern=\"/api/groups/*\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_API_GROUPS_DELETE\"/>\n <sec:intercept-url pattern=\"/api/groups/*/members/*\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_API_GROUPS_EDIT\"/>\n <sec:intercept-url pattern=\"/api/series/*\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_API_SERIES_DELETE\"/>\n <sec:intercept-url pattern=\"/api/series/*/metadata\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_API_SERIES_METADATA_DELETE\"/>\n <sec:intercept-url pattern=\"/api/series/*/metadata/*\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_API_SERIES_METADATA_DELETE\"/>\n <sec:intercept-url pattern=\"/api/workflows/*\" method=\"DELETE\" access=\"ROLE_ADMIN, ROLE_API_WORKFLOW_INSTANCE_DELETE\"/>\n\n <sec:intercept-url pattern=\"/admin-ng/css/**\" access=\"ROLE_ANONYMOUS\" />\n <sec:intercept-url pattern=\"/admin-ng/lib/**\" access=\"ROLE_ANONYMOUS\" />\n <sec:intercept-url pattern=\"/admin-ng/img/**\" access=\"ROLE_ANONYMOUS\" />\n <sec:intercept-url pattern=\"/admin-ng/js/**\" access=\"ROLE_ANONYMOUS\" />\n <sec:intercept-url pattern=\"/admin-ng/modules/**\" access=\"ROLE_ANONYMOUS\" />\n <sec:intercept-url pattern=\"/admin-ng/shared/**\" access=\"ROLE_ANONYMOUS\" />\n <sec:intercept-url pattern=\"/admin-ng/app.js\" access=\"ROLE_ANONYMOUS\" />\n\n <!-- Enable anonymous access to the admin ui -->\n <sec:intercept-url pattern=\"/admin-ng/public/**\" access=\"ROLE_ANONYMOUS\" />\n\n <!-- Enable anonymous access to the /info/me.json resource -->\n <sec:intercept-url pattern=\"/info/me.json\" method=\"GET\" access=\"ROLE_ANONYMOUS\" />\n <sec:intercept-url pattern=\"/i18n/languages.json\" method=\"GET\" access=\"ROLE_ANONYMOUS\" />\n <sec:intercept-url pattern=\"/info/components.json\" method=\"GET\" access=\"ROLE_ANONYMOUS\" />\n\n <!-- anonymous access to user interface configuration -->\n <sec:intercept-url pattern=\"/ui/config/**\" access=\"ROLE_ANONYMOUS\" />\n\n <!-- Paella player -->\n <sec:intercept-url pattern=\"/paella/ui/auth.html\" access=\"ROLE_USER\" />\n <sec:intercept-url pattern=\"/paella/ui/**\" access=\"ROLE_ANONYMOUS\" />\n\n <!-- Enable anonymous access to the engage player and the GET endpoints it requires -->\n <sec:intercept-url pattern=\"/engage/ui/**\" access=\"ROLE_ANONYMOUS\" />\n <sec:intercept-url pattern=\"/engage/**\" access=\"ROLE_ANONYMOUS\" />\n <sec:intercept-url pattern=\"/engage-player/**\" method=\"GET\" access=\"ROLE_ANONYMOUS\" />\n <sec:intercept-url pattern=\"/search/**\" method=\"GET\" access=\"ROLE_ANONYMOUS\" />\n <sec:intercept-url pattern=\"/usertracking/**\" method=\"GET\" access=\"ROLE_ANONYMOUS\" />\n <sec:intercept-url pattern=\"/usertracking/**\" method=\"PUT\" access=\"ROLE_ANONYMOUS\" />\n <sec:intercept-url pattern=\"/static/**\" method=\"GET\" access=\"ROLE_ANONYMOUS\" />\n <sec:intercept-url pattern=\"/export/**\" method=\"GET\" access=\"ROLE_ANONYMOUS\" />\n\n <!-- Enable anonymous access to the annotation and the series endpoints -->\n <sec:intercept-url pattern=\"/series/**\" method=\"GET\" access=\"ROLE_ANONYMOUS, ROLE_CAPTURE_AGENT\" />\n <sec:intercept-url pattern=\"/annotation/**\" method=\"GET\" access=\"ROLE_ANONYMOUS\" />\n <sec:intercept-url pattern=\"/annotation/**\" method=\"PUT\" access=\"ROLE_ANONYMOUS\" />\n\n <!-- Enable anonymous access to the OAI-PMH repository -->\n <!-- The OAI-PMH specification demands boths GET and POST requests -->\n <!-- Please make sure that the path configured here matches -->\n <!-- the path configured for the repository servlet. -->\n <sec:intercept-url pattern=\"/oaipmh/**\" method=\"GET\" access=\"ROLE_ADMIN\"/>\n <sec:intercept-url pattern=\"/oaipmh/**\" method=\"POST\" access=\"ROLE_ADMIN\"/>\n\n <!-- Enable anonymous access to the rss and atom feeds -->\n <sec:intercept-url pattern=\"/feeds/**\" method=\"GET\" access=\"ROLE_ANONYMOUS\" />\n\n <!-- Secure the system management URLs for admins only -->\n <sec:intercept-url pattern=\"/services/available.*\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_CAPTURE_AGENT\" />\n <sec:intercept-url pattern=\"/services/**\" access=\"ROLE_ADMIN\"/>\n <sec:intercept-url pattern=\"/signing/**\" access=\"ROLE_ADMIN\" />\n <sec:intercept-url pattern=\"/system/**\" access=\"ROLE_ADMIN\" />\n <sec:intercept-url pattern=\"/config/**\" access=\"ROLE_ADMIN\" />\n\n <!-- Enable capture agent updates and ingest -->\n <sec:intercept-url pattern=\"/capture-admin/**\" access=\"ROLE_ADMIN, ROLE_CAPTURE_AGENT\" />\n <sec:intercept-url pattern=\"/recordings/**\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_CAPTURE_AGENT\" />\n <sec:intercept-url pattern=\"/ingest/**\" access=\"ROLE_ADMIN, ROLE_CAPTURE_AGENT\" />\n\n <!-- Secure the user management URLs for admins only -->\n <sec:intercept-url pattern=\"/users/**\" access=\"ROLE_ADMIN\" />\n <sec:intercept-url pattern=\"/admin/users.html\" access=\"ROLE_ADMIN\" />\n\n <!-- Enable 2-legged OAuth access (\"signed fetch\") to the LTI launch servlet -->\n <sec:intercept-url pattern=\"/lti\" access=\"ROLE_OAUTH_USER\" />\n\n <!-- Enable access to the LTI tools -->\n <sec:intercept-url pattern=\"/ltitools/**\" access=\"ROLE_OAUTH_USER\" />\n\n <sec:intercept-url pattern=\"/transcripts/watson/results*\" method=\"GET\" access=\"ROLE_ANONYMOUS\" />\n <sec:intercept-url pattern=\"/transcripts/watson/results*\" method=\"POST\" access=\"ROLE_ANONYMOUS\" />\n\n <!-- Everything else is for the admin users -->\n <sec:intercept-url pattern=\"/admin-ng\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_ADMIN_UI, ROLE_COURSE_ADMIN\" />\n <sec:intercept-url pattern=\"/admin-ng/\" method=\"GET\" access=\"ROLE_ADMIN, ROLE_ADMIN_UI, ROLE_COURSE_ADMIN\" />\n <sec:intercept-url pattern=\"/admin-ng/index.html\" access=\"ROLE_ADMIN, ROLE_ADMIN_UI, ROLE_COURSE_ADMIN\" />\n <sec:intercept-url pattern=\"/index.html\" access=\"ROLE_ADMIN, ROLE_ADMIN_UI, ROLE_COURSE_ADMIN\" />\n <sec:intercept-url pattern=\"/**\" access=\"ROLE_ADMIN, ROLE_COURSE_ADMIN\" />\n\n <!-- ############################# -->\n <!-- # LOGIN / LOGOUT MECHANISMS # -->\n <!-- ############################# -->\n\n <!-- Uncomment to enable x509 client certificates for identifying clients -->\n <!-- sec:x509 subject-principal-regex=\"CN=(.*?),\" user-service-ref=\"userDetailsService\" / -->\n\n <!-- Enable and configure the failure URL for form-based logins -->\n <sec:form-login authentication-failure-url=\"/admin-ng/login.html?error\" authentication-success-handler-ref=\"authSuccessHandler\" />\n\n <!-- Authentication filter chain -->\n <sec:custom-filter position=\"BASIC_AUTH_FILTER\" ref=\"authenticationFilters\" />\n\n <sec:custom-filter ref=\"asyncTimeoutRedirectFilter\" after=\"EXCEPTION_TRANSLATION_FILTER\"/>\n\n <!-- Opencast is shipping its own implementation of the anonymous filter -->\n <sec:custom-filter ref=\"anonymousFilter\" position=\"ANONYMOUS_FILTER\" />\n\n <!-- Shibboleth header authentication filter\n <sec:custom-filter ref=\"shibbolethHeaderFilter\" position=\"PRE_AUTH_FILTER\"/>\n -->\n\n <!-- Enables \"remember me\" functionality -->\n <sec:remember-me services-ref=\"rememberMeServices\" />\n\n <!-- Set the request cache -->\n <sec:request-cache ref=\"requestCache\" />\n\n <!-- If any URLs are to be exposed to anonymous users, the \"sec:anonymous\" filter must be present -->\n <sec:anonymous enabled=\"false\" />\n\n <!-- Enables log out -->\n <sec:logout success-handler-ref=\"logoutSuccessHandler\" />\n\n <!-- Shibboleth log out\n Please specifiy the URL to return to after logging out\n <sec:logout logout-success-url=\"/Shibboleth.sso/Logout?return=http://www.opencast.org\" />\n -->\n\n </sec:http>\n\n <bean id=\"rememberMeServices\" class=\"org.opencastproject.kernel.security.SystemTokenBasedRememberMeService\">\n <property name=\"userDetailsService\" ref=\"userDetailsService\"/>\n <!-- All following settings are optional -->\n <property name=\"tokenValiditySeconds\" value=\"1209600\"/>\n <property name=\"cookieName\" value=\"oc-remember-me\"/>\n <!-- The following key will be augmented by system properties. Thus, leaving this untouched is okay -->\n <property name=\"key\" value=\"opencast\"/>\n </bean>\n\n <!-- ############################# -->\n <!-- # Authentication Filters # -->\n <!-- ############################# -->\n\n <bean id=\"authenticationFilters\" class=\"org.springframework.web.filter.CompositeFilter\">\n <property name=\"filters\">\n <list>\n <!-- Digest auth is used by capture agents and is used to enable transparent clustering of services -->\n <!-- ATTENTION! Do not deactivate the digest filter, otherwise the distributed setup would not work -->\n <ref bean=\"digestFilter\" />\n\n <!-- Basic authentication -->\n <ref bean=\"basicAuthenticationFilter\" />\n\n <!-- 2-legged OAuth is used by trusted 3rd party applications, including LTI. -->\n <!-- Uncomment the line below to support LTI or other OAuth clients. -->\n <!-- <ref bean=\"oauthProtectedResourceFilter\" /> -->\n </list>\n </property>\n </bean>\n\n <!-- ########################################### -->\n <!-- # Custom ajax timeout Filter Definition # -->\n <!-- ########################################### -->\n\n <bean id=\"asyncTimeoutRedirectFilter\" class=\"org.opencastproject.kernel.security.AsyncTimeoutRedirectFilter\" />\n\n <!-- ######################################## -->\n <!-- # Custom Anonymous Filter Definition # -->\n <!-- ######################################## -->\n\n <bean id=\"anonymousFilter\" class=\"org.opencastproject.kernel.security.TrustedAnonymousAuthenticationFilter\">\n <property name=\"userAttribute\" ref=\"anonymousUserAttributes\" />\n <property name=\"key\" value=\"anonymousKey\" />\n </bean>\n\n <bean id=\"anonymousUserAttributes\" class=\"org.springframework.security.core.userdetails.memory.UserAttribute\">\n <property name=\"authoritiesAsString\" value=\"ROLE_ANONYMOUS\"/>\n <property name=\"password\" value=\"empty\"/>\n </bean>\n\n <!-- ######################################## -->\n <!-- # Authentication Entry and Exit Points # -->\n <!-- ######################################## -->\n\n <!-- Differentiates between \"normal\" user requests and those requesting digest auth -->\n <bean id=\"opencastEntryPoint\" class=\"org.opencastproject.kernel.security.DelegatingAuthenticationEntryPoint\">\n <property name=\"userEntryPoint\" ref=\"userEntryPoint\" />\n <property name=\"digestAuthenticationEntryPoint\" ref=\"digestEntryPoint\" />\n </bean>\n\n <!-- Redirects unauthenticated requests to the login form -->\n <bean id=\"userEntryPoint\" class=\"org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint\">\n <property name=\"loginFormUrl\" value=\"/admin-ng/login.html\" />\n </bean>\n\n <!-- Returns a 401 request for authentication via digest auth -->\n <bean id=\"digestEntryPoint\" class=\"org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint\">\n <property name=\"realmName\" value=\"Opencast\" />\n <property name=\"key\" value=\"opencast\" />\n <property name=\"nonceValiditySeconds\" value=\"300\" />\n </bean>\n\n <bean id=\"basicEntryPoint\" class=\"org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint\">\n <property name=\"realmName\" value=\"Opencast\"/>\n </bean>\n\n <bean id=\"authSuccessHandler\" class=\"org.opencastproject.kernel.security.AuthenticationSuccessHandler\">\n <property name=\"securityService\" ref=\"securityService\" />\n <property name=\"welcomePages\">\n <map>\n <entry key=\"ROLE_ADMIN\" value=\"/admin-ng/index.html\" />\n <entry key=\"ROLE_ADMIN_UI\" value=\"/admin-ng/index.html\" />\n <entry key=\"*\" value=\"/engage/ui/index.html\" /> <!-- Any role not listed explicitly will redirect here -->\n </map>\n </property>\n </bean>\n\n <bean id=\"logoutSuccessHandler\" class=\"org.opencastproject.kernel.security.LogoutSuccessHandler\">\n <property name=\"userDirectoryService\" ref=\"userDirectoryService\" />\n </bean>\n\n <!-- ################# -->\n <!-- # Digest Filter # -->\n <!-- ################# -->\n\n <!-- Handles the details of the digest authentication dance -->\n <bean id=\"digestFilter\" class=\"org.springframework.security.web.authentication.www.DigestAuthenticationFilter\">\n <!-- Use only the in-memory users, as these have passwords that are not hashed -->\n <property name=\"userDetailsService\" ref=\"userDetailsService\" />\n <property name=\"authenticationEntryPoint\" ref=\"digestEntryPoint\" />\n <property name=\"createAuthenticatedToken\" value=\"true\" />\n <property name=\"userCache\">\n <bean class=\"org.springframework.security.core.userdetails.cache.NullUserCache\" />\n </property>\n </bean>\n\n <!-- ############################### -->\n <!-- # Basic Authentication Filter # -->\n <!-- ############################### -->\n\n <!-- Handles the details of the basic authentication dance -->\n <bean id=\"basicAuthenticationFilter\" class=\"org.springframework.security.web.authentication.www.BasicAuthenticationFilter\">\n <property name=\"authenticationManager\" ref=\"authenticationManager\"/>\n <property name=\"authenticationEntryPoint\" ref=\"basicEntryPoint\"/>\n </bean>\n\n <!-- ####################### -->\n <!-- # OAuth (LTI) Support # -->\n <!-- ####################### -->\n\n <bean name=\"oauthProtectedResourceFilter\" class=\"org.opencastproject.kernel.security.LtiProcessingFilter\">\n <property name=\"consumerDetailsService\" ref=\"oAuthConsumerDetailsService\" />\n <property name=\"tokenServices\">\n <bean class=\"org.springframework.security.oauth.provider.token.InMemoryProviderTokenServices\" />\n </property>\n <property name=\"nonceServices\">\n <bean class=\"org.springframework.security.oauth.provider.nonce.InMemoryNonceServices\" />\n </property>\n <property name=\"authHandler\" ref=\"ltiLaunchAuthenticationHandler\" />\n </bean>\n\n <!-- ###################### -->\n <!-- # Shibboleth Support # -->\n <!-- ###################### -->\n\n <!-- General Shibboleth header extration filter\n <bean id=\"shibbolethHeaderFilter\"\n class=\"org.opencastproject.security.shibboleth.ShibbolethRequestHeaderAuthenticationFilter\">\n <property name=\"principalRequestHeader\" value=\"<this need to be configured>\"/>\n <property name=\"authenticationManager\" ref=\"authenticationManager\" />\n <property name=\"userDetailsService\" ref=\"userDetailsService\" />\n <property name=\"userDirectoryService\" ref=\"userDirectoryService\" />\n <property name=\"shibbolethLoginHandler\" ref=\"aaiLoginHandler\" />\n <property name=\"exceptionIfHeaderMissing\" value=\"false\" />\n </bean>\n -->\n\n <!-- AAI header extractor and user generator\n <bean id=\"aaiLoginHandler\" class=\"org.opencastproject.security.aai.ConfigurableLoginHandler\">\n <property name=\"securityService\" ref=\"securityService\" />\n <property name=\"userReferenceProvider\" ref=\"userReferenceProvider\" />\n </bean>\n -->\n\n <!--\n <bean id=\"preauthAuthProvider\"\n class=\"org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider\">\n <property name=\"preAuthenticatedUserDetailsService\">\n <bean id=\"userDetailsServiceWrapper\" class=\"org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper\">\n <property name=\"userDetailsService\" ref=\"userDetailsService\"/>\n </bean>\n </property>\n </bean>\n -->\n\n <!-- #################### -->\n <!-- # OSGI Integration # -->\n <!-- #################### -->\n\n <!-- Obtain services from the OSGI service registry -->\n <osgi:reference id=\"userDetailsService\" cardinality=\"1..1\"\n interface=\"org.springframework.security.core.userdetails.UserDetailsService\" />\n\n <osgi:reference id=\"securityService\" cardinality=\"1..1\"\n interface=\"org.opencastproject.security.api.SecurityService\" />\n\n <!-- Uncomment to enable external users e.g. used together shibboleth -->\n <!-- <osgi:reference id=\"userReferenceProvider\" cardinality=\"1..1\"\n interface=\"org.opencastproject.userdirectory.api.UserReferenceProvider\" /> -->\n\n <osgi:reference id=\"userDirectoryService\" cardinality=\"1..1\"\n interface=\"org.opencastproject.security.api.UserDirectoryService\" />\n\n <osgi:reference id=\"oAuthConsumerDetailsService\" cardinality=\"1..1\"\n interface=\"org.springframework.security.oauth.provider.ConsumerDetailsService\" />\n\n <osgi:reference id=\"ltiLaunchAuthenticationHandler\" cardinality=\"1..1\"\n interface=\"org.springframework.security.oauth.provider.OAuthAuthenticationHandler\" />\n\n <!-- ############################# -->\n <!-- # Spring Security Internals # -->\n <!-- ############################# -->\n\n <!-- The JPA user directory stores md5 hashed, salted passwords, so we must use a username-salted md5 password encoder. -->\n <sec:authentication-manager alias=\"authenticationManager\">\n <!-- Uncomment this if using Shibboleth authentication -->\n <!--sec:authentication-provider ref=\"preauthAuthProvider\" /-->\n <sec:authentication-provider user-service-ref=\"userDetailsService\">\n <sec:password-encoder hash=\"md5\"><sec:salt-source user-property=\"username\" /></sec:password-encoder>\n </sec:authentication-provider>\n </sec:authentication-manager>\n\n <!-- Do not use a request cache -->\n <bean id=\"requestCache\" class=\"org.springframework.security.web.savedrequest.NullRequestCache\" />\n\n</beans>\n\n", "input": "", "output": "【漏洞类型】权限管理漏洞\n【严重性】Medium\n【漏洞摘要】在原始代码中,`ROLE_COURSE_ADMIN`被错误地配置为允许访问所有`/admin-ng`路径,这意味着拥有此角色的用户可以进行用户创建操作,而不需要`ROLE_ADMIN`权限。这违反了预期的安全策略,因为`ROLE_COURSE_ADMIN`暗示它是一个特定课程的管理员,不应该拥有创建新用户的权限。根本原因在于安全配置文件中错误地包括了`ROLE_COURSE_ADMIN`在所有`/admin-ng`路径的访问控制列表中。\n【关键代码模式】`<sec:intercept-url pattern=\"/**\" access=\"ROLE_ADMIN, ROLE_COURSE_ADMIN\" />` 和 `<sec:intercept-url pattern=\"/admin-ng/**\" access=\"ROLE_ADMIN, ROLE_COURSE_ADMIN, ROLE_ADMIN_UI\" />`\n【修复方法说明】通过移除`ROLE_COURSE_ADMIN`,修复了权限管理漏洞,确保只有具有`ROLE_ADMIN`权限的用户才能进行用户创建操作。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\ngradle.properties\n### 代码内容:\n#################################################\n# Platform metadata for releases, POM generation, etc.\n#################################################\ngroup=org.apereo.cas\nversion=6.0.9-SNAPSHOT\n\nprojectUrl=http://www.apereo.org/cas\nprojectInceptionYear=2004\n\nprojectScmUrl=scm:git@github.com:apereo/cas.git\n\nprojectLicenseName=The Apache Software License, Version 2.0\nprojectLicenseUrl=http://www.apache.org/licenses/LICENSE-2.0.txt\nprojectLicenseDistribution=repo\n\nprojectIssueSystem=GithHub\nprojectIssueUrl=https://github.com/apereo/cas/issues\n\nsonatypeRepositoryUrl=https://oss.sonatype.org/service/local/staging/deploy/maven2/\nsonatypeSnapshotsRepositoryUrl=https://oss.sonatype.org/content/repositories/snapshots/\nsonatypeReleasesRepositoryUrl=https://oss.sonatype.org/content/repositories/releases\n\n###############################################################################################\n# Localized gradle settings to ensure modules can be loaded/configured as quickly as possible.\n################################################################################################\ngradleVersion=5.6.3\norg.gradle.configureondemand=true\norg.gradle.jvmargs=-Xms1024m -Xmx4048m -XX:SoftRefLRUPolicyMSPerMB=0 -noverify -XX:TieredStopAtLevel=1\n\n#################################################\n# Platform SDK versions for both Java, etc.\n#################################################\nsourceCompatibility=11\ntargetCompatibility=11\nlombokVersion=1.18.4\n\n#################################################################################\n# A number of dependency versions are defined in gradle/overrides.gradle file\n# in order to fully override component versions that are provided by Spring Boot.\n##################################################################################\n\naspectjVersion=1.9.2\nerrorProneVersion=2.3.2\nerrorproneJavacVersion=9+181-r4173-1\njunitVersion=5.3.2\njunitPlatformVersion=1.3.2\nmockitoVersion=2.23.4\nobjenesisVersion=2.6\n\njavaxSoapApiVersion=1.4.0\njavaxJwsVersion=3.1.2.2\njavaxJmsVersion=3.1.2.2\njaxbApiVersion=2.3.0\njaxbGlassfishVersion=2.3.0.1\n\n#####################################################\n# Gradle Plugins & Build Utilities\n######################################################\nspotbugsVersion=3.1.10\nfindbugsContribVersion=7.4.2.sb\nfindbugsSecVersion=1.8.0\ncheckstyleVersion=8.15\ngradleLintVersion=10.3.2\ngradleLombokVersion=1.14\ngradleSpotbugsVersion=1.6.8\ngradleSonarqubeVersion=2.6.2\ngradleDependencyVersionsVersion=0.20.0\ngradleDependencyCheckVersion=4.0.1\ngradlePropDepsVersion=0.0.10.RELEASE\ngradleErrorProneVersion=0.5\ngradleNodeVersion=1.2.0\ngradleGitVersion=2.3.0\ngradleJacocoVersion=0.8.2\n\njavaxServletVersion=4.0.1\njavaxValidationVersion=2.0.1.Final\njavassistVersion=3.24.0-GA\njavaxJstlVersion=1.2\n\nsemverVersion=2.2.0\noshiVersion=3.12.0\n\nswaggerVersion=1.5.20\nswaggerSpringFoxVersion=2.9.2\n\nslf4jVersion=1.8.0-beta2\ndisruptorVersion=3.4.2\ninspektrVersion=1.8.4.GA\nsentryRavenVersion=8.0.3\n\n############################### \n# Spring versions\n###############################\n\nspringBootVersion=2.1.2.RELEASE\nspringBootAdminVersion=2.1.2\n\nspringIntegrationVersion=5.1.2.RELEASE\n\nspringWebflowClientVersion=1.0.3\nspringWebflowVersion=2.5.1.RELEASE\n\nspringDataCommonsVersion=2.1.3.RELEASE\nspringDataMongoDbVersion=2.1.3.RELEASE\n\nspringSecurityVersion=5.1.2.RELEASE\nspringSecurityRsaVersion=1.0.8.RELEASE\nspringShellVersion=2.0.1.RELEASE\n\nspringCloudConfigVersion=2.1.0.RELEASE\nspringCloudContextVersion=2.1.0.RELEASE\nspringCloudCommonsVersion=2.1.0.RELEASE\nspringCloudBusVersion=2.1.0.RELEASE\n\nspringCloudVaultVersion=2.1.0.RELEASE\nspringCloudZookeeperVersion=2.1.0.RELEASE\nspringCloudSleuthVersion=2.1.0.RELEASE\nspringCloudEurekaVersion=2.1.0.RELEASE\nspringCloudConsulVersion=2.1.0.RELEASE\n\nspringWsVersion=3.0.6.RELEASE\n########################################\n\nkafkaSpringVersion=2.2.2.RELEASE\nzookeeperVersion=3.4.12\ncuratorVersion=4.0.1\neurekaClientVersion=1.9.8\nribbonVersion=2.3.0\n\nwss4jVersion=2.2.2\nwsdl4jVersion=1.6.3\nsaajImplVersion=1.5.0\nbucket4jVersion=4.2.1\n\nactivemqPoolVersion=5.15.8\n\nretrofitVersion=2.5.0\ninfluxDbVersion=2.14\nokhttp3Version=3.12.0\n\nhazelcastAwsVersion=2.3\nhazelcastAzureVersion=1.2.1\njcloudsGoogleEngineVersion=1.9.3\nhazelcastJCloudsVersion=3.7.2\nhazelcastKubernetesVersion=1.3.1\nhazelcastSwarmVersion=1.0-RC8\njcloudsVersion=2.1.1\n\napacheCxfVersion=3.2.7\napacheFedizVersion=1.4.5\napacheFortressVersion=2.0.3\napacheVelocityVersion=1.7\napacheVelocityEngineVersion=2.0\napacheSyncopeVersion=2.1.2\n\nzuulVersion=2.1.3\n\nhibernateValidatorVersion=6.0.13.Final\ndom4jVersion=2.1.1\njavaParserVersion=3.8.1\n\ncasClientVersion=3.5.1\ncasSecurityFilterVersion=2.0.10.4\nsnakeYamlVersion=1.23\n\nmongoDriverVersion=3.9.1\njtaVersion=1.1\n\nsplunkLoggingVersion=1.6.2\n\n###############################\n# Apache Commons\n###############################\n\ncommonsLangVersion=3.8.1\ncommonsLoggingVersion=1.2\ncommonsValidatorVersion=1.6\ncommonsJexlVersion=1.1\ncommonsCodecVersion=1.11\ncommonsIoVersion=2.6\ncommonsTextVersion=1.6\ncommonsCliVersion=1.4\ncommonsPoolVersion=2.6.0\ncommonsBeansVersion=1.9.3\ncommonsCollections3Version=3.2.2\ncommonsCollectionsVersion=4.2\n\njoolVersion=0.9.14\njasyptVersion=1.9.2\n\njodaTimeVersion=2.10.1\nreflectionsVersion=0.9.11\nguavaVersion=27.0.1-jre\n\npac4jSpringWebmvcVersion=3.2.0\npac4jVersion=3.6.1\n\nstatsdVersion=3.1.0\namazonSdkVersion=1.11.475\nmicrometerVersion=1.1.1\n\nspringDataCosmosDbVersion=2.0.3\ncosmosdbVersion=2.1.0\n\nazureKeyVaultSecretsVersion=2.0.7\n\njsonVersion=20160810\nhjsonVersion=3.0.0\npersonDirectoryVersion=1.8.9\nquartzVersion=2.3.0\n\nokioHttpVersion=2.7.5\nokioVersion=2.1.0\n\nyubicoVersion=3.0.2\nyubicoU2fServerVersion=0.19.1\nduoVersion=1.1\nauthyVersion=1.5.0\ngoogleAuthVersion=1.2.0\ngoogleZxingVersion=3.3.3\nmaxmindVersion=2.12.0\ngoogleMapsGeoCodingVersion=0.9.1\nuserInfoVersion=1.1.0\n\nldaptiveVersion=1.2.4\nunboundidVersion=4.0.9\n\njcifsVersion=1.3.17\njcifsExtVersion=0.9.4\n\nopenidVersion=1.0.0\nunboundidScim2Version=2.2.0\nunboundidScim1Version=1.8.20\njerseyVersion=2.27\n\njradiusVersion=jradius-1.1.5\n\nhttpclientVersion=4.5.6\n\nspymemcachedVersion=2.12.3\nawsmemcachedVersion=1.1.1\nkryoVersion=4.0.2\nkryoSerializersVersion=0.42\n\nehcacheVersion=2.10.6\njcacheVersion=1.0.1\ntwilioVersion=7.33.0\nnexmoVersion=4.0.1\ntextMagicVersion=1.2.0\n\nshiroVersion=1.4.0\njose4jVersion=0.6.4\n\nnimbusJoseVersion=6.5.1\nnimbusOidcVersion=6.2\njsonSmartVersion=2.3\n\n###############################\n# SAML versions\n###############################\nopensamlVersion=3.4.0\nxercesVersion=2.12.0\nidpVersion=3.4.2\nidpLibertyIdwsConsumerVersion=1.0.0\nxmlapisVersion=1.4.01\nbouncyCastleVersion=1.60\nshibbolethUtilJavaSupportVersion=7.4.1\njdomVersion=1.1\n\ncryptacularVersion=1.2.2\n\ngrouperVersion=2.4.0\n\njsonassertVersion=1.5.0\n\ncassandraDriverVersion=3.6.0\n\nigniteVersion=2.7.0\n\nembeddedRedisVersion=0.6\n\nnodeVersion=8.12.0\n\nektorpVersion=1.5.0\n\n###############################\n# Web JARs\n###############################\nheadjsVersion=1.0.3\nfontAwesomeVersion=5.6.1\njqueryUiVersion=1.12.1\njqueryVersion=3.3.1-1\nzxcvbnVersion=4.3.0\nbootstrapVersion=4.1.3\njqueryCookieVersion=1.4.1-1\ndatatablesVersion=1.10.19\nbootstrapSelectVersion=1.12.4\n\n", "input": "", "output": "【漏洞类型】版本管理漏洞\n【严重性】Medium\n【漏洞摘要】此代码中,`cryptacularVersion` 未更新到最新版本,存在安全风险。具体表现为,`cryptacularVersion` 从 1.2.2 更新到 1.2.4,修复了已知的安全漏洞。\n【关键代码模式】版本定义,特别是不常用的库版本,需要定期更新以修复已知的安全漏洞。\n【修复方法说明】将 `cryptacularVersion` 更新到最新版本 1.2.4,解决了已知的安全漏洞。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/main/java/alfio/util/MustacheCustomTag.java\n### 代码内容:\n/**\n * This file is part of alf.io.\n *\n * alf.io is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * alf.io is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with alf.io. If not, see <http://www.gnu.org/licenses/>.\n */\npackage alfio.util;\n\nimport alfio.controller.api.support.TicketHelper;\nimport com.samskivert.mustache.Mustache;\nimport lombok.extern.log4j.Log4j2;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.apache.commons.text.StringEscapeUtils;\nimport org.commonmark.Extension;\nimport org.commonmark.ext.gfm.tables.TablesExtension;\nimport org.commonmark.node.Link;\nimport org.commonmark.node.Node;\nimport org.commonmark.node.Text;\nimport org.commonmark.parser.Parser;\nimport org.commonmark.renderer.html.AttributeProvider;\nimport org.commonmark.renderer.html.HtmlRenderer;\nimport org.commonmark.renderer.text.TextContentRenderer;\nimport org.springframework.security.web.util.UrlUtils;\n\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.*;\nimport java.util.function.Function;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Stream;\n\nimport static org.apache.commons.lang3.StringUtils.substring;\n\n/**\n * For formatting date in a mustache template.\n *\n * <p>\n * Syntax is: {{#format-date}}{{yourDate}} FORMAT locale:YOUR_LOCALE{{/format-date}}.\n * </p>\n * <p>\n * Where\n * </p>\n * <ul>\n * <li>yourDate has been formatted following the java.time.ZonedDateTime\n * <li>FORMAT is a format understood by {@link DateTimeFormatter}</li>\n * <li>optional: locale:YOUR_LOCALE you can define the locale</li>\n * </ul>\n */\n@Log4j2\npublic class MustacheCustomTag {\n\n private MustacheCustomTag() {}\n\n private static final Pattern ARG_PATTERN = Pattern.compile(\"\\\\[(.*?)]\");\n private static final String LOCALE_LABEL = \"locale:\";\n\n static final Mustache.Lambda FORMAT_DATE = (frag, out) -> {\n String execution = frag.execute().trim();\n ZonedDateTime d = ZonedDateTime.parse(substring(execution, 0, execution.indexOf(' ')));\n Pair<String, Optional<Locale>> p = parseParams(execution);\n if (p.getRight().isPresent()) {\n out.write(DateTimeFormatter.ofPattern(p.getLeft(), p.getRight().get()).format(d));\n } else {\n out.write(DateTimeFormatter.ofPattern(p.getLeft()).format(d));\n }\n };\n\n /**\n * {{#render-markdown}}[markdown][.html|.text]{{/render-markdown}}\n * The string must end with either .html or .text, otherwise Markdown won't be parsed\n * e.g.\n * {{#render-markdown}}(link)[description].html{{/render-markdown}} will produce HTML output\n * {{#render-markdown}}(link)[description].text{{/render-markdown}} will produce text/plain output\n */\n static final Mustache.Lambda RENDER_MARKDOWN = (frag, out) -> {\n String execution = frag.execute().strip();\n if(execution.endsWith(\".html\")) {\n out.write(renderToHtmlCommonmarkEscaped(StringUtils.removeEnd(execution, \".html\")));\n } else if(execution.endsWith(\".text\")) {\n out.write(renderToTextCommonmark(StringUtils.removeEnd(execution, \".text\")));\n } else {\n out.write(execution);\n }\n };\n\n static final Mustache.Lambda COUNTRY_NAME = (frag, out) -> {\n String execution = frag.execute().trim();\n String code = substring(execution, 0, 2);\n Pair<String, Optional<Locale>> p = parseParams(execution);\n out.write(translateCountryCode(code, p.getRight().orElse(null)));\n };\n\n static String translateCountryCode(String code, Locale locale) {\n Locale lang = locale != null ? locale : Locale.ENGLISH;\n return Stream.concat(TicketHelper.getLocalizedCountries(lang).stream(), TicketHelper.getLocalizedCountriesForVat(lang).stream())\n .filter(p -> p.getKey().equalsIgnoreCase(code))\n .map(Pair::getValue)\n .findFirst()\n .orElse(code);\n }\n\n /**\n * {{#additional-field-value}}[Prefix][name][suffix]{{/additional-field-value}}\n * prefix is optional, unless a suffix is needed.\n */\n static final Function<Object, Mustache.Lambda> ADDITIONAL_FIELD_VALUE = obj -> (frag, out) -> {\n if( !(obj instanceof Map) || ((Map<?,?>)obj).isEmpty()) {\n log.warn(\"map not found or empty. Skipping additionalFieldValue tag\");\n return;\n }\n Map<?, ?> fieldNamesAndValues = (Map<?, ?>) obj;\n String execution = frag.execute().trim();\n Matcher matcher = ARG_PATTERN.matcher(execution);\n List<String> args = new ArrayList<>();\n while(matcher.find()) {\n args.add(matcher.group(1));\n }\n if(args.isEmpty()) {\n return;\n }\n String name = args.get(args.size() > 1 ? 1 : 0);\n String prefix = args.size() > 1 ? args.get(0) + \" \" : \"\";\n String suffix = args.size() > 2 ? \" \"+args.get(2) : \"\";\n\n if(fieldNamesAndValues.containsKey(name)) {\n out.write(prefix + fieldNamesAndValues.get(name) + suffix);\n }\n };\n\n private static Pair<String, Optional<Locale>> parseParams(String r) {\n\n int indexLocale = r.indexOf(LOCALE_LABEL);\n int end = Math.min(r.length(), indexLocale != -1 ? indexLocale : r.length());\n String format = substring(r, r.indexOf(' '), end);\n\n //\n String[] res = r.split(\"\\\\s+\");\n Optional<Locale> locale = Arrays.stream(res).filter(s -> s.startsWith(LOCALE_LABEL)).findFirst()\n .map(l -> LocaleUtil.forLanguageTag(substring(l, LOCALE_LABEL.length())));\n //\n\n return Pair.of(format, locale);\n }\n\n private static final List<Extension> COMMONMARK_EXTENSIONS = List.of(TablesExtension.create());\n private static final Parser COMMONMARK_PARSER = Parser.builder().extensions(COMMONMARK_EXTENSIONS).build();\n private static final HtmlRenderer COMMONMARK_RENDERER = HtmlRenderer.builder().extensions(COMMONMARK_EXTENSIONS).attributeProviderFactory((ctx) -> new TargetBlankProvider()).build();\n private static final TextContentRenderer COMMONMARK_TEXT_RENDERER = TextContentRenderer.builder().extensions(COMMONMARK_EXTENSIONS).build();\n private static final ThreadLocal<String> A11Y_NEW_TAB_LABEL = new ThreadLocal<>();\n\n //Open in a new window if the link contains an absolute url\n private static class TargetBlankProvider implements AttributeProvider {\n @Override\n public void setAttributes(Node node, String tagName, Map<String, String> attributes) {\n if (node instanceof Link) {\n Link l = (Link) node;\n String destination = StringUtils.trimToEmpty(l.getDestination());\n if (UrlUtils.isAbsoluteUrl(destination)) {\n attributes.put(\"target\", \"_blank\");\n attributes.put(\"rel\", \"nofollow noopener noreferrer\");\n var newTabLabel = A11Y_NEW_TAB_LABEL.get();\n if (newTabLabel != null) {\n attributes.put(\"aria-label\", ((Text)node.getFirstChild()).getLiteral() + \" \" + newTabLabel);\n }\n }\n }\n }\n }\n public static String renderToHtmlCommonmarkEscaped(String input) {\n return renderToHtmlCommonmarkEscaped(input, null);\n }\n\n public static String renderToHtmlCommonmarkEscaped(String input, String localizedNewWindowLabel) {\n try {\n A11Y_NEW_TAB_LABEL.set(localizedNewWindowLabel);\n Node document = COMMONMARK_PARSER.parse(StringEscapeUtils.escapeHtml4(input));\n return COMMONMARK_RENDERER.render(document);\n } finally {\n A11Y_NEW_TAB_LABEL.remove();\n }\n }\n\n public static String renderToTextCommonmark(String input) {\n Node document = COMMONMARK_PARSER.parse(input);\n return COMMONMARK_TEXT_RENDERER.render(document);\n }\n}\n\n", "input": "", "output": "【漏洞类型】协议限制漏洞\n【严重性】High\n【漏洞摘要】此代码中的`RENDER_MARKDOWN` lambda函数允许用户在Markdown渲染时使用任意URI协议,这可能导致安全问题,例如通过`javascript:`协议执行恶意脚本。具体表现为`TargetBlankProvider`类中的`setAttributes`方法,该方法在处理链接时没有限制URI协议,导致潜在的跨站脚本攻击(XSS)。\n【关键代码模式】与漏洞相关的关键代码模式包括`TargetBlankProvider`类中的`setAttributes`方法、`getScheme`函数以及对`UrlUtils.isAbsoluteUrl(destination)`的调用。\n【修复方法说明】修复方法通过在`setAttributes`方法中添加协议限制,仅接受`http`和`https`协议的链接。这解决了安全问题,防止了其他恶意协议的使用。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\ncodec/src/main/java/io/netty/handler/codec/DefaultHeaders.java\n### 代码内容:\npackage io.netty.handler.codec;\n\nimport io.netty.util.HashingStrategy;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.LinkedHashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.NoSuchElementException;\nimport java.util.Set;\n\nimport static io.netty.util.HashingStrategy.JAVA_HASHER;\nimport static io.netty.util.internal.MathUtil.findNextPositivePowerOfTwo;\nimport static io.netty.util.internal.ObjectUtil.checkNotNull;\nimport static java.lang.Math.max;\nimport static java.lang.Math.min;\n\n/**\n * Default implementation of {@link Headers};\n *\n * @param <K> the type of the header name.\n * @param <V> the type of the header value.\n * @param <T> the type to use for return values when the intention is to return {@code this} object.\n */\npublic class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers<K, V, T> {\n /**\n * Constant used to seed the hash code generation. Could be anything but this was borrowed from murmur3.\n */\n static final int HASH_CODE_SEED = 0xc2b2ae35;\n\n private final HeaderEntry<K, V>[] entries;\n protected final HeaderEntry<K, V> head;\n\n private final byte hashMask;\n private final ValueConverter<V> valueConverter;\n private final NameValidator<K> nameValidator;\n private final ValueValidator<V> valueValidator;\n private final HashingStrategy<K> hashingStrategy;\n int size;\n\n public interface NameValidator<K> {\n /**\n * Verify that {@code name} is valid.\n * @param name The name to validate.\n * @throws RuntimeException if {@code name} is not valid.\n */\n void validateName(K name);\n\n @SuppressWarnings(\"rawtypes\")\n NameValidator NOT_NULL = new NameValidator() {\n @Override\n public void validateName(Object name) {\n checkNotNull(name, \"name\");\n }\n };\n }\n\n public interface ValueValidator<V> {\n /**\n * Validate the given value. If the validation fails, then an implementation specific runtime exception may be\n * thrown.\n *\n * @param value The value to validate.\n */\n void validate(V value);\n\n ValueValidator<?> NO_VALIDATION = new ValueValidator<Object>() {\n @Override\n public void validate(Object value) {\n }\n };\n }\n\n @SuppressWarnings(\"unchecked\")\n public DefaultHeaders(ValueConverter<V> valueConverter) {\n this(JAVA_HASHER, valueConverter);\n }\n\n @SuppressWarnings(\"unchecked\")\n public DefaultHeaders(ValueConverter<V> valueConverter, NameValidator<K> nameValidator) {\n this(JAVA_HASHER, valueConverter, nameValidator);\n }\n\n @SuppressWarnings(\"unchecked\")\n public DefaultHeaders(HashingStrategy<K> nameHashingStrategy, ValueConverter<V> valueConverter) {\n this(nameHashingStrategy, valueConverter, NameValidator.NOT_NULL);\n }\n\n public DefaultHeaders(HashingStrategy<K> nameHashingStrategy,\n ValueConverter<V> valueConverter, NameValidator<K> nameValidator) {\n this(nameHashingStrategy, valueConverter, nameValidator, 16);\n }\n\n /**\n * Create a new instance.\n * @param nameHashingStrategy Used to hash and equality compare names.\n * @param valueConverter Used to convert values to/from native types.\n * @param nameValidator Used to validate name elements.\n * @param arraySizeHint A hint as to how large the hash data structure should be.\n * The next positive power of two will be used. An upper bound may be enforced.\n */\n @SuppressWarnings(\"unchecked\")\n public DefaultHeaders(HashingStrategy<K> nameHashingStrategy,\n ValueConverter<V> valueConverter, NameValidator<K> nameValidator, int arraySizeHint) {\n this(nameHashingStrategy, valueConverter, nameValidator, arraySizeHint,\n (ValueValidator<V>) ValueValidator.NO_VALIDATION);\n }\n\n /**\n * Create a new instance.\n * @param nameHashingStrategy Used to hash and equality compare names.\n * @param valueConverter Used to convert values to/from native types.\n * @param nameValidator Used to validate name elements.\n * @param arraySizeHint A hint as to how large the hash data structure should be.\n * The next positive power of two will be used. An upper bound may be enforced.\n * @param valueValidator The validation strategy for entry values.\n */\n @SuppressWarnings(\"unchecked\")\n public DefaultHeaders(HashingStrategy<K> nameHashingStrategy, ValueConverter<V> valueConverter,\n NameValidator<K> nameValidator, int arraySizeHint, ValueValidator<V> valueValidator) {\n this.valueConverter = checkNotNull(valueConverter, \"valueConverter\");\n this.nameValidator = checkNotNull(nameValidator, \"nameValidator\");\n hashingStrategy = checkNotNull(nameHashingStrategy, \"nameHashingStrategy\");\n this.valueValidator = checkNotNull(valueValidator, \"valueValidator\");\n // Enforce a bound of [2, 128] because hashMask is a byte. The max possible value of hashMask is one less\n // than the length of this array, and we want the mask to be > 0.\n entries = new HeaderEntry[findNextPositivePowerOfTwo(max(2, min(arraySizeHint, 128)))];\n hashMask = (byte) (entries.length - 1);\n head = new HeaderEntry<K, V>();\n }\n\n @Override\n public V get(K name) {\n checkNotNull(name, \"name\");\n\n int h = hashingStrategy.hashCode(name);\n int i = index(h);\n HeaderEntry<K, V> e = entries[i];\n V value = null;\n // loop until the first header was found\n while (e != null) {\n if (e.hash == h && hashingStrategy.equals(name, e.key)) {\n value = e.value;\n }\n\n e = e.next;\n }\n return value;\n }\n\n @Override\n public V get(K name, V defaultValue) {\n V value = get(name);\n if (value == null) {\n return defaultValue;\n }\n return value;\n }\n\n @Override\n public V getAndRemove(K name) {\n int h = hashingStrategy.hashCode(name);\n return remove0(h, index(h), checkNotNull(name, \"name\"));\n }\n\n @Override\n public V getAndRemove(K name, V defaultValue) {\n V value = getAndRemove(name);\n if (value == null) {\n return defaultValue;\n }\n return value;\n }\n\n @Override\n public List<V> getAll(K name) {\n checkNotNull(name, \"name\");\n\n LinkedList<V> values = new LinkedList<V>();\n\n int h = hashingStrategy.hashCode(name);\n int i = index(h);\n HeaderEntry<K, V> e = entries[i];\n while (e != null) {\n if (e.hash == h && hashingStrategy.equals(name, e.key)) {\n values.addFirst(e.getValue());\n }\n e = e.next;\n }\n return values;\n }\n\n /**\n * Equivalent to {@link #getAll(Object)} but no intermediate list is generated.\n * @param name the name of the header to retrieve\n * @return an {@link Iterator} of header values corresponding to {@code name}.\n */\n public Iterator<V> valueIterator(K name) {\n return new ValueIterator(name);\n }\n\n @Override\n public List<V> getAllAndRemove(K name) {\n List<V> all = getAll(name);\n remove(name);\n return all;\n }\n\n @Override\n public boolean contains(K name) {\n return get(name) != null;\n }\n\n @Override\n public boolean containsObject(K name, Object value) {\n return contains(name, fromObject(name, value));\n }\n\n @Override\n public boolean containsBoolean(K name, boolean value) {\n return contains(name, fromBoolean(name, value));\n }\n\n @Override\n public boolean containsByte(K name, byte value) {\n return contains(name, fromByte(name, value));\n }\n\n @Override\n public boolean containsChar(K name, char value) {\n return contains(name, fromChar(name, value));\n }\n\n @Override\n public boolean containsShort(K name, short value) {\n return contains(name, fromShort(name, value));\n }\n\n @Override\n public boolean containsInt(K name, int value) {\n return contains(name, fromInt(name, value));\n }\n\n @Override\n public boolean containsLong(K name, long value) {\n return contains(name, fromLong(name, value));\n }\n\n @Override\n public boolean containsFloat(K name, float value) {\n return contains(name, fromFloat(name, value));\n }\n\n @Override\n public boolean containsDouble(K name, double value) {\n return contains(name, fromDouble(name, value));\n }\n\n @Override\n public boolean containsTimeMillis(K name, long value) {\n return contains(name, fromTimeMillis(name, value));\n }\n\n @SuppressWarnings(\"unchecked\")\n @Override\n public boolean contains(K name, V value) {\n return contains(name, value, JAVA_HASHER);\n }\n\n public final boolean contains(K name, V value, HashingStrategy<? super V> valueHashingStrategy) {\n checkNotNull(name, \"name\");\n\n int h = hashingStrategy.hashCode(name);\n int i = index(h);\n HeaderEntry<K, V> e = entries[i];\n while (e != null) {\n if (e.hash == h && hashingStrategy.equals(name, e.key) && valueHashingStrategy.equals(value, e.value)) {\n return true;\n }\n e = e.next;\n }\n return false;\n }\n\n @Override\n public int size() {\n return size;\n }\n\n @Override\n public boolean isEmpty() {\n return head == head.after;\n }\n\n @Override\n public Set<K> names() {\n if (isEmpty()) {\n return Collections.emptySet();\n }\n Set<K> names = new LinkedHashSet<K>(size());\n HeaderEntry<K, V> e = head.after;\n while (e != head) {\n names.add(e.getKey());\n e = e.after;\n }\n return names;\n }\n\n @Override\n public T add(K name, V value) {\n validateName(nameValidator, true, name);\n validateValue(valueValidator, name, value);\n checkNotNull(value, \"value\");\n int h = hashingStrategy.hashCode(name);\n int i = index(h);\n add0(h, i, name, value);\n return thisT();\n }\n\n @Override\n public T add(K name, Iterable<? extends V> values) {\n validateName(nameValidator, true, name);\n int h = hashingStrategy.hashCode(name);\n int i = index(h);\n for (V v: values) {\n validateValue(valueValidator, name, v);\n add0(h, i, name, v);\n }\n return thisT();\n }\n\n @Override\n public T add(K name, V... values) {\n validateName(nameValidator, true, name);\n int h = hashingStrategy.hashCode(name);\n int i = index(h);\n for (V v: values) {\n validateValue(valueValidator, name, v);\n add0(h, i, name, v);\n }\n return thisT();\n }\n\n @Override\n public T addObject(K name, Object value) {\n return add(name, fromObject(name, value));\n }\n\n @Override\n public T addObject(K name, Iterable<?> values) {\n for (Object value : values) {\n addObject(name, value);\n }\n return thisT();\n }\n\n @Override\n public T addObject(K name, Object... values) {\n for (Object value: values) {\n addObject(name, value);\n }\n return thisT();\n }\n\n @Override\n public T addInt(K name, int value) {\n return add(name, fromInt(name, value));\n }\n\n @Override\n public T addLong(K name, long value) {\n return add(name, fromLong(name, value));\n }\n\n @Override\n public T addDouble(K name, double value) {\n return add(name, fromDouble(name, value));\n }\n\n @Override\n public T addTimeMillis(K name, long value) {\n return add(name, fromTimeMillis(name, value));\n }\n\n @Override\n public T addChar(K name, char value) {\n return add(name, fromChar(name, value));\n }\n\n @Override\n public T addBoolean(K name, boolean value) {\n return add(name, fromBoolean(name, value));\n }\n\n @Override\n public T addFloat(K name, float value) {\n return add(name, fromFloat(name, value));\n }\n\n @Override\n public T addByte(K name, byte value) {\n return add(name, fromByte(name, value));\n }\n\n @Override\n public T addShort(K name, short value) {\n return add(name, fromShort(name, value));\n }\n\n @Override\n public T add(Headers<? extends K, ? extends V, ?> headers) {\n if (headers == this) {\n throw new IllegalArgumentException(\"can't add to itself.\");\n }\n addImpl(headers);\n return thisT();\n }\n\n protected void addImpl(Headers<? extends K, ? extends V, ?> headers) {\n if (headers instanceof DefaultHeaders) {\n @SuppressWarnings(\"unchecked\")\n final DefaultHeaders<? extends K, ? extends V, T> defaultHeaders =\n (DefaultHeaders<? extends K, ? extends V, T>) headers;\n HeaderEntry<? extends K, ? extends V> e = defaultHeaders.head.after;\n if (defaultHeaders.hashingStrategy == hashingStrategy &&\n defaultHeaders.nameValidator == nameValidator) {\n // Fastest copy\n while (e != defaultHeaders.head) {\n add0(e.hash, index(e.hash), e.key, e.value);\n e = e.after;\n }\n } else {\n // Fast copy\n while (e != defaultHeaders.head) {\n add(e.key, e.value);\n e = e.after;\n }\n }\n } else {\n // Slow copy\n for (Entry<? extends K, ? extends V> header : headers) {\n add(header.getKey(), header.getValue());\n }\n }\n }\n\n @Override\n public T set(K name, V value) {\n validateName(nameValidator, false, name);\n validateValue(valueValidator, name, value);\n checkNotNull(value, \"value\");\n int h = hashingStrategy.hashCode(name);\n int i = index(h);\n remove0(h, i, name);\n add0(h, i, name, value);\n return thisT();\n }\n\n @Override\n public T set(K name, Iterable<? extends V> values) {\n validateName(nameValidator, false, name);\n checkNotNull(values, \"values\");\n\n int h = hashingStrategy.hashCode(name);\n int i = index(h);\n\n remove0(h, i, name);\n for (V v: values) {\n if (v == null) {\n break;\n }\n validateValue(valueValidator, name, v);\n add0(h, i, name, v);\n }\n\n return thisT();\n }\n\n @Override\n public T set(K name, V... values) {\n validateName(nameValidator, false, name);\n checkNotNull(values, \"values\");\n\n int h = hashingStrategy.hashCode(name);\n int i = index(h);\n\n remove0(h, i, name);\n for (V v: values) {\n if (v == null) {\n break;\n }\n validateValue(valueValidator, name, v);\n add0(h, i, name, v);\n }\n\n return thisT();\n }\n\n @Override\n public T setObject(K name, Object value) {\n V convertedValue = checkNotNull(fromObject(name, value), \"convertedValue\");\n return set(name, convertedValue);\n }\n\n @Override\n public T setObject(K name, Iterable<?> values) {\n validateName(nameValidator, false, name);\n\n int h = hashingStrategy.hashCode(name);\n int i = index(h);\n\n remove0(h, i, name);\n for (Object v: values) {\n if (v == null) {\n break;\n }\n add0(h, i, name, fromObject(name, v));\n }\n\n return thisT();\n }\n\n @Override\n public T setObject(K name, Object... values) {\n validateName(nameValidator, false, name);\n\n int h = hashingStrategy.hashCode(name);\n int i = index(h);\n\n remove0(h, i, name);\n for (Object v: values) {\n if (v == null) {\n break;\n }\n add0(h, i, name, fromObject(name, v));\n }\n\n return thisT();\n }\n\n @Override\n public T setInt(K name, int value) {\n return set(name, fromInt(name, value));\n }\n\n @Override\n public T setLong(K name, long value) {\n return set(name, fromLong(name, value));\n }\n\n @Override\n public T setDouble(K name, double value) {\n return set(name, fromDouble(name, value));\n }\n\n @Override\n public T setTimeMillis(K name, long value) {\n return set(name, fromTimeMillis(name, value));\n }\n\n @Override\n public T setFloat(K name, float value) {\n return set(name, fromFloat(name, value));\n }\n\n @Override\n public T setChar(K name, char value) {\n return set(name, fromChar(name, value));\n }\n\n @Override\n public T setBoolean(K name, boolean value) {\n return set(name, fromBoolean(name, value));\n }\n\n @Override\n public T setByte(K name, byte value) {\n return set(name, fromByte(name, value));\n }\n\n @Override\n public T setShort(K name, short value) {\n return set(name, fromShort(name, value));\n }\n\n @Override\n public T set(Headers<? extends K, ? extends V, ?> headers) {\n if (headers != this) {\n clear();\n addImpl(headers);\n }\n return thisT();\n }\n\n @Override\n public T setAll(Headers<? extends K, ? extends V, ?> headers) {\n if (headers != this) {\n for (K key : headers.names()) {\n remove(key);\n }\n addImpl(headers);\n }\n return thisT();\n }\n\n @Override\n public boolean remove(K name) {\n return getAndRemove(name) != null;\n }\n\n @Override\n public T clear() {\n Arrays.fill(entries, null);\n head.before = head.after = head;\n size = 0;\n return thisT();\n }\n\n @Override\n public Iterator<Entry<K, V>> iterator() {\n return new HeaderIterator();\n }\n\n @Override\n public Boolean getBoolean(K name) {\n V v = get(name);\n try {\n return v != null ? toBoolean(name, v) : null;\n } catch (RuntimeException ignore) {\n return null;\n }\n }\n\n @Override\n public boolean getBoolean(K name, boolean defaultValue) {\n Boolean v = getBoolean(name);\n return v != null ? v : defaultValue;\n }\n\n @Override\n public Byte getByte(K name) {\n V v = get(name);\n try {\n return v != null ? toByte(name, v) : null;\n } catch (RuntimeException ignore) {\n return null;\n }\n }\n\n @Override\n public byte getByte(K name, byte defaultValue) {\n Byte v = getByte(name);\n return v != null ? v : defaultValue;\n }\n\n @Override\n public Character getChar(K name) {\n V v = get(name);\n try {\n return v != null ? toChar(name, v) : null;\n } catch (RuntimeException ignore) {\n return null;\n }\n }\n\n @Override\n public char getChar(K name, char defaultValue) {\n Character v = getChar(name);\n return v != null ? v : defaultValue;\n }\n\n @Override\n public Short getShort(K name) {\n V v = get(name);\n try {\n return v != null ? toShort(name, v) : null;\n } catch (RuntimeException ignore) {\n return null;\n }\n }\n\n @Override\n public short getShort(K name, short defaultValue) {\n Short v = getShort(name);\n return v != null ? v : defaultValue;\n }\n\n @Override\n public Integer getInt(K name) {\n V v = get(name);\n try {\n return v != null ? toInt(name, v) : null;\n } catch (RuntimeException ignore) {\n return null;\n }\n }\n\n @Override\n public int getInt(K name, int defaultValue) {\n Integer v = getInt(name);\n return v != null ? v : defaultValue;\n }\n\n @Override\n public Long getLong(K name) {\n V v = get(name);\n try {\n return v != null ? toLong(name, v) : null;\n } catch (RuntimeException ignore) {\n return null;\n }\n }\n\n @Override\n public long getLong(K name, long defaultValue) {\n Long v = getLong(name);\n return v != null ? v : defaultValue;\n }\n\n @Override\n public Float getFloat(K name) {\n V v = get(name);\n try {\n return v != null ? toFloat(name, v) : null;\n } catch (RuntimeException ignore) {\n return null;\n }\n }\n\n @Override\n public float getFloat(K name, float defaultValue) {\n Float v = getFloat(name);\n return v != null ? v : defaultValue;\n }\n\n @Override\n public Double getDouble(K name) {\n V v = get(name);\n try {\n return v != null ? toDouble(name, v) : null;\n } catch (RuntimeException ignore) {\n return null;\n }\n }\n\n @Override\n public double getDouble(K name, double defaultValue) {\n Double v = getDouble(name);\n return v != null ? v : defaultValue;\n }\n\n @Override\n public Long getTimeMillis(K name) {\n V v = get(name);\n try {\n return v != null ? toTimeMillis(name, v) : null;\n } catch (RuntimeException ignore) {\n return null;\n }\n }\n\n @Override\n public long getTimeMillis(K name, long defaultValue) {\n Long v = getTimeMillis(name);\n return v != null ? v : defaultValue;\n }\n\n @Override\n public Boolean getBooleanAndRemove(K name) {\n V v = getAndRemove(name);\n try {\n return v != null ? toBoolean(name, v) : null;\n } catch (RuntimeException ignore) {\n return null;\n }\n }\n\n @Override\n public boolean getBooleanAndRemove(K name, boolean defaultValue) {\n Boolean v = getBooleanAndRemove(name);\n return v != null ? v : defaultValue;\n }\n\n @Override\n public Byte getByteAndRemove(K name) {\n V v = getAndRemove(name);\n try {\n return v != null ? toByte(name, v) : null;\n } catch (RuntimeException ignore) {\n return null;\n }\n }\n\n @Override\n public byte getByteAndRemove(K name, byte defaultValue) {\n Byte v = getByteAndRemove(name);\n return v != null ? v : defaultValue;\n }\n\n @Override\n public Character getCharAndRemove(K name) {\n V v = getAndRemove(name);\n try {\n return v != null ? toChar(name, v) : null;\n } catch (RuntimeException ignore) {\n return null;\n }\n }\n\n @Override\n public char getCharAndRemove(K name, char defaultValue) {\n Character v = getCharAndRemove(name);\n return v != null ? v : defaultValue;\n }\n\n @Override\n public Short getShortAndRemove(K name) {\n V v = getAndRemove(name);\n try {\n return v != null ? toShort(name, v) : null;\n } catch (RuntimeException ignore) {\n return null;\n }\n }\n\n @Override\n public short getShortAndRemove(K name, short defaultValue) {\n Short v = getShortAndRemove(name);\n return v != null ? v : defaultValue;\n }\n\n @Override\n public Integer getIntAndRemove(K name) {\n V v = getAndRemove(name);\n try {\n return v != null ? toInt(name, v) : null;\n } catch (RuntimeException ignore) {\n return null;\n }\n }\n\n @Override\n public int getIntAndRemove(K name, int defaultValue) {\n Integer v = getIntAndRemove(name);\n return v != null ? v : defaultValue;\n }\n\n @Override\n public Long getLongAndRemove(K name) {\n V v = getAndRemove(name);\n try {\n return v != null ? toLong(name, v) : null;\n } catch (RuntimeException ignore) {\n return null;\n }\n }\n\n @Override\n public long getLongAndRemove(K name, long defaultValue) {\n Long v = getLongAndRemove(name);\n return v != null ? v : defaultValue;\n }\n\n @Override\n public Float getFloatAndRemove(K name) {\n V v = getAndRemove(name);\n try {\n return v != null ? toFloat(name, v) : null;\n } catch (RuntimeException ignore) {\n return null;\n }\n }\n\n @Override\n public float getFloatAndRemove(K name, float defaultValue) {\n Float v = getFloatAndRemove(name);\n return v != null ? v : defaultValue;\n }\n\n @Override\n public Double getDoubleAndRemove(K name) {\n V v = getAndRemove(name);\n try {\n return v != null ? toDouble(name, v) : null;\n } catch (RuntimeException ignore) {\n return null;\n }\n }\n\n @Override\n public double getDoubleAndRemove(K name, double defaultValue) {\n Double v = getDoubleAndRemove(name);\n return v != null ? v : defaultValue;\n }\n\n @Override\n public Long getTimeMillisAndRemove(K name) {\n V v = getAndRemove(name);\n try {\n return v != null ? toTimeMillis(name, v) : null;\n } catch (RuntimeException ignore) {\n return null;\n }\n }\n\n @Override\n public long getTimeMillisAndRemove(K name, long defaultValue) {\n Long v = getTimeMillisAndRemove(name);\n return v != null ? v : defaultValue;\n }\n\n @SuppressWarnings(\"unchecked\")\n @Override\n public boolean equals(Object o) {\n if (!(o instanceof Headers)) {\n return false;\n }\n\n return equals((Headers<K, V, ?>) o, JAVA_HASHER);\n }\n\n @SuppressWarnings(\"unchecked\")\n @Override\n public int hashCode() {\n return hashCode(JAVA_HASHER);\n }\n\n /**\n * Test this object for equality against {@code h2}.\n * @param h2 The object to check equality for.\n * @param valueHashingStrategy Defines how values will be compared for equality.\n * @return {@code true} if this object equals {@code h2} given {@code valueHashingStrategy}.\n * {@code false} otherwise.\n */\n public final boolean equals(Headers<K, V, ?> h2, HashingStrategy<V> valueHashingStrategy) {\n if (h2.size() != size()) {\n return false;\n }\n\n if (this == h2) {\n return true;\n }\n\n for (K name : names()) {\n List<V> otherValues = h2.getAll(name);\n List<V> values = getAll(name);\n if (otherValues.size() != values.size()) {\n return false;\n }\n for (int i = 0; i < otherValues.size(); i++) {\n if (!valueHashingStrategy.equals(otherValues.get(i), values.get(i))) {\n return false;\n }\n }\n }\n return true;\n }\n\n /**\n * Generate a hash code for this object given a {@link HashingStrategy} to generate hash codes for\n * individual values.\n * @param valueHashingStrategy Defines how values will be hashed.\n */\n public final int hashCode(HashingStrategy<V> valueHashingStrategy) {\n int result = HASH_CODE_SEED;\n for (K name : names()) {\n result = 31 * result + hashingStrategy.hashCode(name);\n List<V> values = getAll(name);\n for (int i = 0; i < values.size(); ++i) {\n result = 31 * result + valueHashingStrategy.hashCode(values.get(i));\n }\n }\n return result;\n }\n\n @Override\n public String toString() {\n return HeadersUtils.toString(getClass(), iterator(), size());\n }\n\n /**\n * Call out to the given {@link NameValidator} to validate the given name.\n *\n * @param validator the validator to use\n * @param forAdd {@code true } if this validation is for adding to the headers, or {@code false} if this is for\n * setting (overwriting) the given header.\n * @param name the name to validate.\n */\n protected void validateName(NameValidator<K> validator, boolean forAdd, K name) {\n validator.validateName(name);\n }\n\n protected void validateValue(ValueValidator<V> validator, K name, V value) {\n validator.validate(value);\n }\n\n protected HeaderEntry<K, V> newHeaderEntry(int h, K name, V value, HeaderEntry<K, V> next) {\n return new HeaderEntry<K, V>(h, name, value, next, head);\n }\n\n protected ValueConverter<V> valueConverter() {\n return valueConverter;\n }\n\n protected NameValidator<K> nameValidator() {\n return nameValidator;\n }\n\n protected ValueValidator<V> valueValidator() {\n return valueValidator;\n }\n\n private int index(int hash) {\n return hash & hashMask;\n }\n\n private void add0(int h, int i, K name, V value) {\n // Update the hash table.\n entries[i] = newHeaderEntry(h, name, value, entries[i]);\n ++size;\n }\n\n /**\n * @return the first value inserted whose hash code equals {@code h} and whose name is equal to {@code name}.\n */\n private V remove0(int h, int i, K name) {\n HeaderEntry<K, V> e = entries[i];\n if (e == null) {\n return null;\n }\n\n V value = null;\n HeaderEntry<K, V> next = e.next;\n while (next != null) {\n if (next.hash == h && hashingStrategy.equals(name, next.key)) {\n value = next.value;\n e.next = next.next;\n next.remove();\n --size;\n } else {\n e = next;\n }\n\n next = e.next;\n }\n\n e = entries[i];\n if (e.hash == h && hashingStrategy.equals(name, e.key)) {\n if (value == null) {\n value = e.value;\n }\n entries[i] = e.next;\n e.remove();\n --size;\n }\n\n return value;\n }\n\n HeaderEntry<K, V> remove0(HeaderEntry<K, V> entry, HeaderEntry<K, V> previous) {\n int i = index(entry.hash);\n HeaderEntry<K, V> firstEntry = entries[i];\n if (firstEntry == entry) {\n entries[i] = entry.next;\n previous = entries[i];\n } else if (previous == null) {\n // If we don't have any existing starting point, then start from the beginning.\n previous = firstEntry;\n HeaderEntry<K, V> next = firstEntry.next;\n while (next != null && next != entry) {\n previous = next;\n next = next.next;\n }\n assert next != null: \"Entry not found in its hash bucket: \" + entry;\n previous.next = entry.next;\n } else {\n previous.next = entry.next;\n }\n entry.remove();\n --size;\n return previous;\n }\n\n @SuppressWarnings(\"unchecked\")\n private T thisT() {\n return (T) this;\n }\n\n private V fromObject(K name, Object value) {\n try {\n return valueConverter.convertObject(checkNotNull(value, \"value\"));\n } catch (IllegalArgumentException e) {\n throw new IllegalArgumentException(\"Failed to convert object value for header '\" + name + '\\'', e);\n }\n }\n\n private V fromBoolean(K name, boolean value) {\n try {\n return valueConverter.convertBoolean(value);\n } catch (IllegalArgumentException e) {\n throw new IllegalArgumentException(\"Failed to convert boolean value for header '\" + name + '\\'', e);\n }\n }\n\n private V fromByte(K name, byte value) {\n try {\n return valueConverter.convertByte(value);\n } catch (IllegalArgumentException e) {\n throw new IllegalArgumentException(\"Failed to convert byte value for header '\" + name + '\\'', e);\n }\n }\n\n private V fromChar(K name, char value) {\n try {\n return valueConverter.convertChar(value);\n } catch (IllegalArgumentException e) {\n throw new IllegalArgumentException(\"Failed to convert char value for header '\" + name + '\\'', e);\n }\n }\n\n private V fromShort(K name, short value) {\n try {\n return valueConverter.convertShort(value);\n } catch (IllegalArgumentException e) {\n throw new IllegalArgumentException(\"Failed to convert short value for header '\" + name + '\\'', e);\n }\n }\n\n private V fromInt(K name, int value) {\n try {\n return valueConverter.convertInt(value);\n } catch (IllegalArgumentException e) {\n throw new IllegalArgumentException(\"Failed to convert int value for header '\" + name + '\\'', e);\n }\n }\n\n private V fromLong(K name, long value) {\n try {\n return valueConverter.convertLong(value);\n } catch (IllegalArgumentException e) {\n throw new IllegalArgumentException(\"Failed to convert long value for header '\" + name + '\\'', e);\n }\n }\n\n private V fromFloat(K name, float value) {\n try {\n return valueConverter.convertFloat(value);\n } catch (IllegalArgumentException e) {\n throw new IllegalArgumentException(\"Failed to convert float value for header '\" + name + '\\'', e);\n }\n }\n\n private V fromDouble(K name, double value) {\n try {\n return valueConverter.convertDouble(value);\n } catch (IllegalArgumentException e) {\n throw new IllegalArgumentException(\"Failed to convert double value for header '\" + name + '\\'', e);\n }\n }\n\n private V fromTimeMillis(K name, long value) {\n try {\n return valueConverter.convertTimeMillis(value);\n } catch (IllegalArgumentException e) {\n throw new IllegalArgumentException(\"Failed to convert millsecond value for header '\" + name + '\\'', e);\n }\n }\n\n private boolean toBoolean(K name, V value) {\n try {\n return valueConverter.convertToBoolean(value);\n } catch (IllegalArgumentException e) {\n throw new IllegalArgumentException(\"Failed to convert header value to boolean for header '\" + name + '\\'');\n }\n }\n\n private byte toByte(K name, V value) {\n try {\n return valueConverter.convertToByte(value);\n } catch (IllegalArgumentException e) {\n throw new IllegalArgumentException(\"Failed to convert header value to byte for header '\" + name + '\\'');\n }\n }\n\n private char toChar(K name, V value) {\n try {\n return valueConverter.convertToChar(value);\n } catch (IllegalArgumentException e) {\n throw new IllegalArgumentException(\"Failed to convert header value to char for header '\" + name + '\\'');\n }\n }\n\n private short toShort(K name, V value) {\n try {\n return valueConverter.convertToShort(value);\n } catch (IllegalArgumentException e) {\n throw new IllegalArgumentException(\"Failed to convert header value to short for header '\" + name + '\\'');\n }\n }\n\n private int toInt(K name, V value) {\n try {\n return valueConverter.convertToInt(value);\n } catch (IllegalArgumentException e) {\n throw new IllegalArgumentException(\"Failed to convert header value to int for header '\" + name + '\\'');\n }\n }\n\n private long toLong(K name, V value) {\n try {\n return valueConverter.convertToLong(value);\n } catch (IllegalArgumentException e) {\n throw new IllegalArgumentException(\"Failed to convert header value to long for header '\" + name + '\\'');\n }\n }\n\n private float toFloat(K name, V value) {\n try {\n return valueConverter.convertToFloat(value);\n } catch (IllegalArgumentException e) {\n throw new IllegalArgumentException(\"Failed to convert header value to float for header '\" + name + '\\'');\n }\n }\n\n private double toDouble(K name, V value) {\n try {\n return valueConverter.convertToDouble(value);\n } catch (IllegalArgumentException e) {\n throw new IllegalArgumentException(\"Failed to convert header value to double for header '\" + name + '\\'');\n }\n }\n\n private long toTimeMillis(K name, V value) {\n try {\n return valueConverter.convertToTimeMillis(value);\n } catch (IllegalArgumentException e) {\n throw new IllegalArgumentException(\n \"Failed to convert header value to millsecond for header '\" + name + '\\'');\n }\n }\n\n /**\n * Returns a deep copy of this instance.\n */\n public DefaultHeaders<K, V, T> copy() {\n DefaultHeaders<K, V, T> copy = new DefaultHeaders<K, V, T>(\n hashingStrategy, valueConverter, nameValidator, entries.length);\n copy.addImpl(this);\n return copy;\n }\n\n private final class HeaderIterator implements Iterator<Entry<K, V>> {\n private HeaderEntry<K, V> current = head;\n\n @Override\n public boolean hasNext() {\n return current.after != head;\n }\n\n @Override\n public Entry<K, V> next() {\n current = current.after;\n\n if (current == head) {\n throw new NoSuchElementException();\n }\n\n return current;\n }\n\n @Override\n public void remove() {\n throw new UnsupportedOperationException(\"read only\");\n }\n }\n\n private final class ValueIterator implements Iterator<V> {\n private final K name;\n private final int hash;\n private HeaderEntry<K, V> removalPrevious;\n private HeaderEntry<K, V> previous;\n private HeaderEntry<K, V> next;\n\n ValueIterator(K name) {\n this.name = checkNotNull(name, \"name\");\n hash = hashingStrategy.hashCode(name);\n calculateNext(entries[index(hash)]);\n }\n\n @Override\n public boolean hasNext() {\n return next != null;\n }\n\n @Override\n public V next() {\n if (!hasNext()) {\n throw new NoSuchElementException();\n }\n if (previous != null) {\n removalPrevious = previous;\n }\n previous = next;\n calculateNext(next.next);\n return previous.value;\n }\n\n @Override\n public void remove() {\n if (previous == null) {\n throw new IllegalStateException();\n }\n removalPrevious = remove0(previous, removalPrevious);\n previous = null;\n }\n\n private void calculateNext(HeaderEntry<K, V> entry) {\n while (entry != null) {\n if (entry.hash == hash && hashingStrategy.equals(name, entry.key)) {\n next = entry;\n return;\n }\n entry = entry.next;\n }\n next = null;\n }\n }\n\n protected static class HeaderEntry<K, V> implements Entry<K, V> {\n protected final int hash;\n protected final K key;\n protected V value;\n /**\n * In bucket linked list\n */\n protected HeaderEntry<K, V> next;\n /**\n * Overall insertion order linked list\n */\n protected HeaderEntry<K, V> before, after;\n\n protected HeaderEntry(int hash, K key) {\n this.hash = hash;\n this.key = key;\n }\n\n HeaderEntry(int hash, K key, V value, HeaderEntry<K, V> next, HeaderEntry<K, V> head) {\n this.hash = hash;\n this.key = key;\n this.value = value;\n this.next = next;\n\n after = head;\n before = head.before;\n pointNeighborsToThis();\n }\n\n HeaderEntry() {\n hash = -1;\n key = null;\n before = after = this;\n }\n\n protected final void pointNeighborsToThis() {\n before.after = this;\n after.before = this;\n }\n\n public final HeaderEntry<K, V> before() {\n return before;\n }\n\n public final HeaderEntry<K, V> after() {\n return after;\n }\n\n protected void remove() {\n before.after = after;\n after.before = before;\n }\n\n @Override\n public final K getKey() {\n return key;\n }\n\n @Override\n public final V getValue() {\n return value;\n }\n\n @Override\n public final V setValue(V value) {\n checkNotNull(value, \"value\");\n V oldValue = this.value;\n this.value = value;\n return oldValue;\n }\n\n @Override\n public final String toString() {\n return key.toString() + '=' + value.toString();\n }\n\n @Override\n public boolean equals(Object o) {\n if (!(o instanceof Map.Entry)) {\n return false;\n }\n Entry<?, ?> other = (Entry<?, ?>) o;\n return (getKey() == null ? other.getKey() == null : getKey().equals(other.getKey())) &&\n (getValue() == null ? other.getValue() == null : getValue().equals(other.getValue()));\n }\n\n @Override\n public int hashCode() {\n return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode());\n }\n }\n}\n\n", "input": "", "output": "【漏洞类型】安全逻辑漏洞\n【严重性】Medium\n【漏洞摘要】原始代码中的`setObject`方法,当传入数组或迭代器时,没有对转换后的值进行验证,导致可能绕过值验证。例如,在`setObject(K name, Iterable<?> values)`和`setObject(K name, Object... values)`方法中,`valueValidator.validate(value)`在调用`fromObject(name, v)`后被遗漏。\n【关键代码模式】`setObject`方法,`validateValue`函数调用缺失,`fromObject`函数调用。\n【修复方法说明】修复方法通过在调用`fromObject`后添加`validateValue`函数调用来确保值验证逻辑不会被绕过。这遵循了输入验证的最佳实践,即确保所有输入数据在使用前都经过适当的验证。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nxwiki-platform-core/xwiki-platform-mail/xwiki-platform-mail-send/xwiki-platform-mail-send-default/pom.xml\n### 代码内容:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n-->\n\n<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/maven-v4_0_0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n <parent>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-mail-send</artifactId>\n <version>15.1-SNAPSHOT</version>\n </parent>\n <packaging>jar</packaging>\n <artifactId>xwiki-platform-mail-send-default</artifactId>\n <name>XWiki Platform - Mail - Send - Default Implementation</name>\n <description>Default implementation of the API to send emails</description>\n <properties>\n <xwiki.jacoco.instructionRatio>0.77</xwiki.jacoco.instructionRatio>\n <!-- Name to display by the Extension Manager -->\n <xwiki.extension.name>Mail Sender Implementation</xwiki.extension.name>\n <xwiki.extension.namespaces>{root}</xwiki.extension.namespaces>\n </properties>\n <dependencies>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-mail-general</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-mail-send-api</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-script</artifactId>\n <version>${commons.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-configuration-default</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-oldcore</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-text</artifactId>\n <version>${commons.version}</version>\n </dependency>\n <!-- Needed for working with XWikiStubContextProvider and XWikiRequest and by the Extension Manager to properly\n load some components (e.g. DefaultXWikiStubContextProvider) -->\n <dependency>\n <groupId>javax.servlet</groupId>\n <artifactId>javax.servlet-api</artifactId>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-model-api</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-bridge</artifactId>\n <version>${project.version}</version>\n </dependency>\n\n <!-- Test Dependencies -->\n <!-- Override the Servlet environment that is drawn by the oldcore dependency. This works because the\n StandardEnvironment component has a higher priority than the ServletEnvironment one (they both implement\n Environment). -->\n <dependency>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-environment-standard</artifactId>\n <version>${commons.version}</version>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-oldcore</artifactId>\n <version>${project.version}</version>\n <type>test-jar</type>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>com.icegreen</groupId>\n <artifactId>greenmail-junit5</artifactId>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-tool-test-component</artifactId>\n <version>${commons.version}</version>\n <scope>test</scope>\n </dependency>\n </dependencies>\n</project>\n\n## 内容2\n### 文件路径:\nxwiki-platform-core/xwiki-platform-mail/xwiki-platform-mail-send/xwiki-platform-mail-send-default/src/main/java/org/xwiki/mail/internal/MailConfigMandatoryDocumentInitializer.java\n### 代码内容:\n/*\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n */\npackage org.xwiki.mail.internal;\n\nimport com.xpn.xwiki.XWiki;\nimport com.xpn.xwiki.XWikiContext;\nimport com.xpn.xwiki.XWikiException;\nimport com.xpn.xwiki.doc.MandatoryDocumentInitializer;\nimport com.xpn.xwiki.doc.XWikiDocument;\nimport com.xpn.xwiki.objects.BaseObject;\nimport org.apache.commons.lang3.StringUtils;\nimport org.slf4j.Logger;\nimport org.xwiki.component.annotation.Component;\nimport org.xwiki.mail.internal.configuration.SendMailConfigClassDocumentConfigurationSource;\nimport org.xwiki.model.reference.DocumentReference;\nimport org.xwiki.model.reference.EntityReference;\nimport org.xwiki.model.reference.LocalDocumentReference;\nimport org.xwiki.wiki.descriptor.WikiDescriptorManager;\n\nimport javax.inject.Inject;\nimport javax.inject.Named;\nimport javax.inject.Provider;\nimport javax.inject.Singleton;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Create the mandatory {@code Mail.MailConfig} document in the current wiki.\n *\n * @version $Id$\n * @since 11.8RC1\n */\n@Component\n@Singleton\n@Named(\"Mail.MailConfig\")\npublic class MailConfigMandatoryDocumentInitializer implements MandatoryDocumentInitializer\n{\n @Inject\n private Logger logger;\n\n @Inject\n private WikiDescriptorManager wikiDescriptorManager;\n\n @Inject\n private Provider<XWikiContext> xcontextProvider;\n\n @Override\n public EntityReference getDocumentReference()\n {\n return SendMailConfigClassDocumentConfigurationSource.MAILCONFIG_REFERENCE;\n }\n\n @Override\n public boolean updateDocument(XWikiDocument document)\n {\n boolean needsUpdate = false;\n\n if (updateReferences(document)) {\n needsUpdate = true;\n }\n\n // Ensure the document is hidden, like every technical document\n if (!document.isHidden()) {\n document.setHidden(true);\n needsUpdate = true;\n }\n\n // Set the title\n if (document.getTitle().isEmpty()) {\n document.setTitle(\"Mail Configuration\");\n needsUpdate = true;\n }\n\n // Ensure the document has a Mail.GeneralMailConfigClass xobject\n if (addGeneralMailConfigClassXObject(document)) {\n needsUpdate = true;\n }\n\n // Ensure the document has a Mail.SendMailConfigClass xobject\n if (addSendMailConfigClassXObject(document)) {\n needsUpdate = true;\n }\n\n // Migration: Since the XWiki.Configurable class used to be located in Mail.MailConfig and has been moved to\n // Mail.MailConfigurable, we need to remove it from this page in case it's there as otherwise there would be\n // duplicate Admin UI entries...\n // TODO: Ideally this code should be executed only once.\n if (removeConfigurableClass(document)) {\n needsUpdate = true;\n }\n\n return needsUpdate;\n }\n\n private boolean updateReferences(XWikiDocument document)\n {\n boolean needsUpdate = false;\n\n // Ensure the document has a creator\n if (document.getCreatorReference() == null) {\n document.setCreatorReference(new DocumentReference(this.wikiDescriptorManager.getMainWikiId(),\n XWiki.SYSTEM_SPACE, \"superadmin\"));\n needsUpdate = true;\n }\n\n // Ensure the document has an author\n if (document.getAuthorReference() == null) {\n document.setAuthorReference(document.getCreatorReference());\n needsUpdate = true;\n }\n\n // Ensure the document has a parent\n if (document.getParentReference() == null) {\n LocalDocumentReference parentReference = new LocalDocumentReference(\n SendMailConfigClassDocumentConfigurationSource.MAIL_SPACE, \"WebHome\");\n document.setParentReference(parentReference);\n needsUpdate = true;\n }\n\n return needsUpdate;\n }\n\n private boolean removeConfigurableClass(XWikiDocument document)\n {\n boolean needsUpdate = false;\n\n LocalDocumentReference configurableClassReference =\n new LocalDocumentReference(XWiki.SYSTEM_SPACE, \"ConfigurableClass\");\n if (document.getXObject(configurableClassReference) != null) {\n document.removeXObjects(configurableClassReference);\n needsUpdate = true;\n }\n\n return needsUpdate;\n }\n\n private boolean addGeneralMailConfigClassXObject(XWikiDocument document)\n {\n LocalDocumentReference generalMailClassReference = new LocalDocumentReference(\n SendMailConfigClassDocumentConfigurationSource.MAIL_SPACE, \"GeneralMailConfigClass\");\n return addXObject(document, generalMailClassReference,\n Collections.singletonMap(\"obfuscateEmailAddresses\", \"obfuscate\"));\n }\n\n private boolean addSendMailConfigClassXObject(XWikiDocument document)\n {\n LocalDocumentReference sendMailClassReference =\n SendMailConfigClassDocumentConfigurationSource.SENDMAILCONFIGCLASS_REFERENCE;\n Map<String, String> fieldMappings = new HashMap<>();\n fieldMappings.put(\"admin_email\", \"from\");\n fieldMappings.put(\"smtp_server\", \"host\");\n fieldMappings.put(\"smtp_port\", \"port\");\n fieldMappings.put(\"smtp_server_username\", \"username\");\n fieldMappings.put(\"smtp_server_password\", \"password\");\n fieldMappings.put(\"javamail_extra_props\", \"properties\");\n return addXObject(document, sendMailClassReference, fieldMappings);\n }\n\n private boolean addXObject(XWikiDocument document, LocalDocumentReference classReference,\n Map<String, String> fieldMappings)\n {\n boolean needsUpdate = false;\n\n if (document.getXObject(classReference) == null) {\n try {\n XWikiContext xwikiContext = this.xcontextProvider.get();\n XWiki xwiki = xwikiContext.getWiki();\n BaseObject object = document.newXObject(classReference, xwikiContext);\n // Backward compatibility: migrate data from XWiki.XWikiPreferences.\n // Only set values if they're different from the default so that values are inherited by default\n for (Map.Entry<String, String> entry : fieldMappings.entrySet()) {\n setField(entry.getKey(), entry.getValue(), object, xwiki, xwikiContext);\n }\n needsUpdate = true;\n } catch (XWikiException e) {\n this.logger.error(\n String.format(\"Error adding a [%s] object to the document [%s]\",\n classReference.toString(), document.getDocumentReference().toString()));\n }\n }\n\n return needsUpdate;\n }\n\n private void setField(String oldPropertyName, String newPropertyName, BaseObject object, XWiki xwiki,\n XWikiContext xwikiContext)\n {\n String oldValue = xwiki.getSpacePreference(oldPropertyName, xwikiContext);\n if (!StringUtils.isEmpty(oldValue)) {\n object.set(newPropertyName, oldValue, xwikiContext);\n }\n }\n}\n\n## 内容3\n### 文件路径:\nxwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/pom.xml\n### 代码内容:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n-->\n\n<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/maven-v4_0_0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n <parent>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-security-authorization</artifactId>\n <version>15.1-SNAPSHOT</version>\n </parent>\n <artifactId>xwiki-platform-security-authorization-bridge</artifactId>\n <name>XWiki Platform - Security - Authorization - Bridge</name>\n <description>Authorization API implementation using oldcore</description>\n <properties>\n <xwiki.jacoco.instructionRatio>0.70</xwiki.jacoco.instructionRatio>\n <!-- TODO: Remove once the tests have been fixed to not output anything to the console! -->\n <xwiki.surefire.captureconsole.skip>true</xwiki.surefire.captureconsole.skip>\n <!-- Name to display by the Extension Manager -->\n <xwiki.extension.name>Authorization Bridge</xwiki.extension.name>\n <xwiki.extension.features>\n <!-- Old names of this module used for retro compatibility when resolving dependencies of old extensions -->\n org.xwiki.platform:xwiki-platform-security-bridge\n </xwiki.extension.features>\n </properties>\n <dependencies>\n <dependency>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-context</artifactId>\n <version>${commons.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-observation-api</artifactId>\n <version>${commons.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-text</artifactId>\n <version>${commons.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-bridge</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-model-api</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-oldcore</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-security-authorization-api</artifactId>\n <version>${project.version}</version>\n </dependency>\n <dependency>\n <groupId>org.apache.commons</groupId>\n <artifactId>commons-lang3</artifactId>\n </dependency>\n <!-- Test dependencies -->\n <dependency>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-tool-test-component</artifactId>\n <version>${commons.version}</version>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.xwiki.commons</groupId>\n <artifactId>xwiki-commons-environment-standard</artifactId>\n <version>${commons.version}</version>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-test-oldcore</artifactId>\n <version>${project.version}</version>\n <type>pom</type>\n <scope>test</scope>\n </dependency>\n <!-- XWikiDocument needs UserReferenceSerializer and UserReferenceResolver components -->\n <dependency>\n <groupId>org.xwiki.platform</groupId>\n <artifactId>xwiki-platform-user-default</artifactId>\n <version>${project.version}</version>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.jmock</groupId>\n <artifactId>jmock-legacy</artifactId>\n <scope>test</scope>\n </dependency>\n </dependencies>\n</project>\n\n## 内容4\n### 文件路径:\nxwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/resources/META-INF/components.txt\n### 代码内容:\n500:org.xwiki.security.authorization.internal.BridgeAuthorizationManager\norg.xwiki.security.authorization.internal.DefaultContextualAuthorizationManager\norg.xwiki.security.authorization.internal.DefaultSecurityCacheRulesInvalidator\norg.xwiki.security.authorization.internal.DefaultSecurityCacheRulesInvalidatorListener\norg.xwiki.security.authorization.internal.DefaultSecurityEntryReader\norg.xwiki.security.authorization.internal.RightsFilterListener\norg.xwiki.security.internal.DefaultUserBridge\norg.xwiki.security.internal.DefaultXWikiBridge\n\n", "input": "", "output": "【漏洞类型】权限配置漏洞\n\n【严重性】High\n\n【漏洞摘要】原始代码中,`MailConfigMandatoryDocumentInitializer` 类在初始化邮件配置文档时,没有设置明确的权限控制,这可能导致未授权用户能够查看、编辑或删除邮件配置信息,从而引发安全风险。具体表现为,邮件配置文档的创建和更新逻辑中,没有对文档的访问权限进行验证和设置,这使得文档在默认情况下可能对所有用户开放。\n\n【关键代码模式】`updateDocument` 方法中没有权限控制逻辑。\n\n【修复方法说明】修复方法通过引入 `DocumentInitializerRightsManager` 类,并在 `updateDocument` 方法中调用其 `restrictToAdmin` 方法,来确保邮件配置文档的访问权限仅限于管理员组。这解决了权限配置缺失的问题。最佳实践是确保所有关键文档在初始化时都有明确的权限设置,以防止未授权访问。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nairsonic-main/src/main/java/org/airsonic/player/security/GlobalSecurityConfig.java\n### 代码内容:\npackage org.airsonic.player.security;\n\nimport org.airsonic.player.service.JWTSecurityService;\nimport org.airsonic.player.service.SecurityService;\nimport org.airsonic.player.service.SettingsService;\nimport org.apache.commons.lang3.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.security.SecurityProperties;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;\nimport org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.config.http.SessionCreationPolicy;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;\nimport org.springframework.security.web.util.matcher.AntPathRequestMatcher;\n\n@Configuration\n@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)\n@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)\npublic class GlobalSecurityConfig extends GlobalAuthenticationConfigurerAdapter {\n\n private static Logger logger = LoggerFactory.getLogger(GlobalSecurityConfig.class);\n\n static final String FAILURE_URL = \"/login?error=1\";\n\n @Autowired\n private SecurityService securityService;\n\n @Autowired\n private CsrfSecurityRequestMatcher csrfSecurityRequestMatcher;\n\n @Autowired\n SettingsService settingsService;\n\n @Autowired\n CustomUserDetailsContextMapper customUserDetailsContextMapper;\n\n @Autowired\n ApplicationEventPublisher eventPublisher;\n\n @Autowired\n public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n if (settingsService.isLdapEnabled()) {\n auth.ldapAuthentication()\n .contextSource()\n .managerDn(settingsService.getLdapManagerDn())\n .managerPassword(settingsService.getLdapManagerPassword())\n .url(settingsService.getLdapUrl())\n .and()\n .userSearchFilter(settingsService.getLdapSearchFilter())\n .userDetailsContextMapper(customUserDetailsContextMapper);\n }\n auth.userDetailsService(securityService);\n String jwtKey = settingsService.getJWTKey();\n if(StringUtils.isBlank(jwtKey)) {\n logger.warn(\"Generating new jwt key\");\n jwtKey = JWTSecurityService.generateKey();\n settingsService.setJWTKey(jwtKey);\n settingsService.save();\n }\n auth.authenticationProvider(new JWTAuthenticationProvider(jwtKey));\n }\n\n @Configuration\n @Order(1)\n public class ExtSecurityConfiguration extends WebSecurityConfigurerAdapter {\n\n public ExtSecurityConfiguration() {\n super(true);\n }\n\n @Bean(name = \"jwtAuthenticationFilter\")\n public JWTRequestParameterProcessingFilter jwtAuthFilter() throws Exception {\n return new JWTRequestParameterProcessingFilter(authenticationManager(), FAILURE_URL);\n }\n\n @Override\n protected void configure(HttpSecurity http) throws Exception {\n\n http = http.addFilter(new WebAsyncManagerIntegrationFilter());\n http = http.addFilterBefore(jwtAuthFilter(), UsernamePasswordAuthenticationFilter.class);\n\n http\n .antMatcher(\"/ext/**\")\n .csrf().requireCsrfProtectionMatcher(csrfSecurityRequestMatcher).and()\n .headers().frameOptions().sameOrigin().and()\n .authorizeRequests()\n .antMatchers(\"/ext/stream/**\", \"/ext/coverArt*\", \"/ext/share/**\", \"/ext/hls/**\")\n .hasAnyRole(\"TEMP\", \"USER\").and()\n .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()\n .exceptionHandling().and()\n .securityContext().and()\n .requestCache().and()\n .anonymous().and()\n .servletApi();\n }\n }\n\n @Configuration\n @Order(2)\n public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {\n\n @Override\n protected void configure(HttpSecurity http) throws Exception {\n\n RESTRequestParameterProcessingFilter restAuthenticationFilter = new RESTRequestParameterProcessingFilter();\n restAuthenticationFilter.setAuthenticationManager(authenticationManagerBean());\n restAuthenticationFilter.setSecurityService(securityService);\n restAuthenticationFilter.setEventPublisher(eventPublisher);\n http = http.addFilterBefore(restAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);\n\n http\n .csrf()\n .requireCsrfProtectionMatcher(csrfSecurityRequestMatcher)\n .and().headers()\n .frameOptions()\n .sameOrigin()\n .and().authorizeRequests()\n .antMatchers(\"/recover*\", \"/accessDenied*\",\n \"/style/**\", \"/icons/**\", \"/flash/**\", \"/script/**\",\n \"/sonos/**\", \"/crossdomain.xml\", \"/login\", \"/error\")\n .permitAll()\n .antMatchers(\"/personalSettings*\", \"/passwordSettings*\",\n \"/playerSettings*\", \"/shareSettings*\", \"/passwordSettings*\")\n .hasRole(\"SETTINGS\")\n .antMatchers(\"/generalSettings*\", \"/advancedSettings*\", \"/userSettings*\",\n \"/musicFolderSettings*\", \"/databaseSettings*\", \"/transcodeSettings*\", \"/rest/startScan*\")\n .hasRole(\"ADMIN\")\n .antMatchers(\"/deletePlaylist*\", \"/savePlaylist*\", \"/db*\")\n .hasRole(\"PLAYLIST\")\n .antMatchers(\"/download*\")\n .hasRole(\"DOWNLOAD\")\n .antMatchers(\"/upload*\")\n .hasRole(\"UPLOAD\")\n .antMatchers(\"/createShare*\")\n .hasRole(\"SHARE\")\n .antMatchers(\"/changeCoverArt*\", \"/editTags*\")\n .hasRole(\"COVERART\")\n .antMatchers(\"/setMusicFileInfo*\")\n .hasRole(\"COMMENT\")\n .antMatchers(\"/podcastReceiverAdmin*\")\n .hasRole(\"PODCAST\")\n .antMatchers(\"/**\")\n .hasRole(\"USER\")\n .anyRequest().authenticated()\n .and().formLogin()\n .loginPage(\"/login\")\n .permitAll()\n .defaultSuccessUrl(\"/index\", true)\n .failureUrl(FAILURE_URL)\n .usernameParameter(\"j_username\")\n .passwordParameter(\"j_password\")\n // see http://docs.spring.io/spring-security/site/docs/3.2.4.RELEASE/reference/htmlsingle/#csrf-logout\n .and().logout().logoutRequestMatcher(new AntPathRequestMatcher(\"/logout\", \"GET\")).logoutSuccessUrl(\n \"/login?logout\")\n .and().rememberMe().key(\"airsonic\");\n }\n\n }\n}\n", "input": "", "output": "【漏洞类型】固定密钥的Remember-Me功能\n【严重性】Medium\n【漏洞摘要】在原始代码中,Remember-Me功能使用了一个固定密钥(\"airsonic\")来加密用户的cookie值。这种做法在安全性上存在缺陷,因为攻击者可以通过捕获cookie并使用这个固定密钥来尝试离线破解用户的密码。Spring Security的默认Remember-Me机制使用的是MD5哈希,这在现代密码学中被认为是不安全的,因为MD5容易受到碰撞攻击。因此,使用固定密钥进一步降低了安全性。\n【关键代码模式】\n- `rememberMe().key(\"airsonic\")`:这是导致漏洞的根本行。使用固定密钥来加密Remember-Me cookie。\n- `SecureRandom`:在修复代码中使用的类,用于生成伪随机数,以提高密钥的安全性。\n- `StringUtils.isBlank(jwtKey)`:在原始代码中用于检查JWT密钥是否为空的函数。\n- `JWTSecurityService.generateKey()`:在原始代码中用于生成JWT密钥的函数。\n【修复方法说明】修复方法通过使用`SecureRandom`生成一个随机密钥来替代固定密钥,从而提高Remember-Me cookie的安全性。最佳实践是使用强随机数生成器来生成密钥,以防止攻击者通过已知密钥进行破解。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nthemes/src/main/resources/theme/keycloak.v2/account/resources/welcome-page-scripts.js\n### 代码内容:\nvar isWelcomePage = function () {\n var winHash = window.location.hash;\n return winHash === '#/';\n};\n\nvar toggleReact = function () {\n var welcomeScreen = document.getElementById(\"welcomeScreen\");\n var spinnerScreen = document.getElementById(\"spinner_screen\");\n var reactScreen = document.getElementById(\"main_react_container\");\n\n if (!isWelcomePage() && !isReactLoading) {\n if (welcomeScreen) welcomeScreen.style.display = 'none';\n if (spinnerScreen) spinnerScreen.style.display = 'none';\n if (reactScreen) reactScreen.style.display = 'block';\n if (reactScreen) reactScreen.style.height = '100%';\n } else if (!isWelcomePage() && isReactLoading) {\n if (welcomeScreen) welcomeScreen.style.display = 'none';\n if (reactScreen) reactScreen.style.display = 'none';\n if (spinnerScreen) spinnerScreen.style.display = 'block';\n if (spinnerScreen) spinnerScreen.style.height = '100%';\n } else {\n if (reactScreen) reactScreen.style.display = 'none';\n if (spinnerScreen) spinnerScreen.style.display = 'none';\n if (welcomeScreen) welcomeScreen.style.display = 'block';\n if (welcomeScreen) welcomeScreen.style.height = '100%';\n }\n};\n\nfunction loggedInUserName() {\n let userName = l18nMsg['unknownUser'];\n if (keycloak.tokenParsed) {\n const givenName = keycloak.tokenParsed.given_name;\n const familyName = keycloak.tokenParsed.family_name;\n const preferredUsername = keycloak.tokenParsed.preferred_username;\n if (givenName && familyName) {\n userName = [givenName, familyName].reduce((acc, value, index) =>\n acc.replace('{{param_'+ index + '}}', value), l18nMsg['fullName']\n );\n } else {\n userName = (givenName || familyName) || preferredUsername || userName;\n }\n }\n return userName;\n}\n\nvar toggleMobileDropdown = function () {\t\n var mobileDropdown = document.getElementById(\"landingMobileDropdown\");\n var mobileKebab = document.getElementById(\"landingMobileKebab\");\n var mobileKebabButton = document.getElementById(\"landingMobileKebabButton\");\n if (mobileDropdown.style.display === 'none') {\n mobileDropdown.style.display = 'block';\n mobileKebab.classList.add(\"pf-m-expanded\");\n mobileKebabButton.setAttribute(\"aria-expanded\", \"true\");\n } else {\n mobileDropdown.style.display = 'none';\n mobileKebab.classList.remove(\"pf-m-expanded\");\n mobileKebabButton.setAttribute(\"aria-expanded\", \"false\");\n }\n}\n\nvar loadjs = function (url, loadListener) {\n const script = document.createElement(\"script\");\n script.src = resourceUrl + url;\n script.type = \"module\";\n if (loadListener)\n script.addEventListener(\"load\", loadListener);\n document.head.appendChild(script);\n};\n\n", "input": "", "output": "【漏洞类型】Stored Cross-Site Scripting (XSS) Attack\n【严重性】High\n【漏洞摘要】这是由于用户输入(如 `keycloak.tokenParsed.given_name`, `keycloak.tokenParsed.family_name`, `keycloak.tokenParsed.preferred_username`)未进行适当的验证和清理,直接插入到页面中,可能导致攻击者通过操纵这些输入在用户浏览器中执行恶意脚本。具体表现为在 `loggedInUserName` 函数中,用户输入未经处理直接替换到页面元素中。\n【关键代码模式】`l18nMsg['fullName']` 中的占位符替换,以及直接将用户输入赋值给 `userName`。\n【修复方法说明】通过创建一个虚拟的 `span` 元素并将用户输入设置为文本内容,然后返回其 `innerHTML`,实现了对用户输入的HTML转义,防止XSS攻击。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n", "input": "", "output": "【漏洞类型】SQL注入漏洞\n【严重性】High\n【漏洞摘要】SQL注入是一种常见的安全漏洞,攻击者通过在输入字段中插入恶意SQL代码,可以操纵数据库查询。在这个代码中,原始代码通过拼接字符串来构建SQL查询,使得`c.getPackagesLike()`的值直接插入到SQL语句中,这可能导致SQL注入攻击。具体表现为`String query = \"select object_name from user_objects where object_type = 'PACKAGE' and object_name like '\" + c.getPackagesLike() + \"'\";`这一行。\n【关键代码模式】字符串拼接SQL查询,直接将用户输入或配置值插入到SQL语句中。\n【修复方法说明】修复方法通过使用预编译的SQL语句和参数绑定来防止SQL注入,具体实现为`String query = \"select object_name from user_objects where object_type = 'PACKAGE' and object_name like ?\";`,然后在`jdbcTemplate.query`方法中传递参数`c.getPackagesLike()`。这种方法遵循了使用参数化查询的最佳实践,有效防止了SQL注入攻击。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\ncodec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpHeaders.java\n### 代码内容:\npackage io.netty.handler.codec.http;\n\nimport io.netty.handler.codec.CharSequenceValueConverter;\nimport io.netty.handler.codec.DateFormatter;\nimport io.netty.handler.codec.DefaultHeaders;\nimport io.netty.handler.codec.DefaultHeaders.NameValidator;\nimport io.netty.handler.codec.DefaultHeadersImpl;\nimport io.netty.handler.codec.HeadersUtils;\nimport io.netty.handler.codec.ValueConverter;\nimport io.netty.util.AsciiString;\nimport io.netty.util.ByteProcessor;\nimport io.netty.util.internal.PlatformDependent;\n\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\n\nimport static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER;\nimport static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER;\n\n/**\n * Default implementation of {@link HttpHeaders}.\n */\npublic class DefaultHttpHeaders extends HttpHeaders {\n private static final int HIGHEST_INVALID_VALUE_CHAR_MASK = ~15;\n private static final ByteProcessor HEADER_NAME_VALIDATOR = new ByteProcessor() {\n @Override\n public boolean process(byte value) throws Exception {\n validateHeaderNameElement(value);\n return true;\n }\n };\n static final NameValidator<CharSequence> HttpNameValidator = new NameValidator<CharSequence>() {\n @Override\n public void validateName(CharSequence name) {\n if (name == null || name.length() == 0) {\n throw new IllegalArgumentException(\"empty headers are not allowed [\" + name + \"]\");\n }\n if (name instanceof AsciiString) {\n try {\n ((AsciiString) name).forEachByte(HEADER_NAME_VALIDATOR);\n } catch (Exception e) {\n PlatformDependent.throwException(e);\n }\n } else {\n // Go through each character in the name\n for (int index = 0; index < name.length(); ++index) {\n validateHeaderNameElement(name.charAt(index));\n }\n }\n }\n };\n\n private final DefaultHeaders<CharSequence, CharSequence, ?> headers;\n\n public DefaultHttpHeaders() {\n this(true);\n }\n\n /**\n * <b>Warning!</b> Setting <code>validate</code> to <code>false</code> will mean that Netty won't\n * validate & protect against user-supplied header values that are malicious.\n * This can leave your server implementation vulnerable to\n * <a href=\"https://cwe.mitre.org/data/definitions/113.html\">\n * CWE-113: Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP Response Splitting')\n * </a>.\n * When disabling this validation, it is the responsibility of the caller to ensure that the values supplied\n * do not contain a non-url-escaped carriage return (CR) and/or line feed (LF) characters.\n *\n * @param validate Should Netty validate Header values to ensure they aren't malicious.\n */\n public DefaultHttpHeaders(boolean validate) {\n this(validate, nameValidator(validate));\n }\n\n protected DefaultHttpHeaders(boolean validate, NameValidator<CharSequence> nameValidator) {\n this(new DefaultHeadersImpl<CharSequence, CharSequence>(CASE_INSENSITIVE_HASHER,\n valueConverter(validate),\n nameValidator));\n }\n\n protected DefaultHttpHeaders(DefaultHeaders<CharSequence, CharSequence, ?> headers) {\n this.headers = headers;\n }\n\n @Override\n public HttpHeaders add(HttpHeaders headers) {\n if (headers instanceof DefaultHttpHeaders) {\n this.headers.add(((DefaultHttpHeaders) headers).headers);\n return this;\n } else {\n return super.add(headers);\n }\n }\n\n @Override\n public HttpHeaders set(HttpHeaders headers) {\n if (headers instanceof DefaultHttpHeaders) {\n this.headers.set(((DefaultHttpHeaders) headers).headers);\n return this;\n } else {\n return super.set(headers);\n }\n }\n\n @Override\n public HttpHeaders add(String name, Object value) {\n headers.addObject(name, value);\n return this;\n }\n\n @Override\n public HttpHeaders add(CharSequence name, Object value) {\n headers.addObject(name, value);\n return this;\n }\n\n @Override\n public HttpHeaders add(String name, Iterable<?> values) {\n headers.addObject(name, values);\n return this;\n }\n\n @Override\n public HttpHeaders add(CharSequence name, Iterable<?> values) {\n headers.addObject(name, values);\n return this;\n }\n\n @Override\n public HttpHeaders addInt(CharSequence name, int value) {\n headers.addInt(name, value);\n return this;\n }\n\n @Override\n public HttpHeaders addShort(CharSequence name, short value) {\n headers.addShort(name, value);\n return this;\n }\n\n @Override\n public HttpHeaders remove(String name) {\n headers.remove(name);\n return this;\n }\n\n @Override\n public HttpHeaders remove(CharSequence name) {\n headers.remove(name);\n return this;\n }\n\n @Override\n public HttpHeaders set(String name, Object value) {\n headers.setObject(name, value);\n return this;\n }\n\n @Override\n public HttpHeaders set(CharSequence name, Object value) {\n headers.setObject(name, value);\n return this;\n }\n\n @Override\n public HttpHeaders set(String name, Iterable<?> values) {\n headers.setObject(name, values);\n return this;\n }\n\n @Override\n public HttpHeaders set(CharSequence name, Iterable<?> values) {\n headers.setObject(name, values);\n return this;\n }\n\n @Override\n public HttpHeaders setInt(CharSequence name, int value) {\n headers.setInt(name, value);\n return this;\n }\n\n @Override\n public HttpHeaders setShort(CharSequence name, short value) {\n headers.setShort(name, value);\n return this;\n }\n\n @Override\n public HttpHeaders clear() {\n headers.clear();\n return this;\n }\n\n @Override\n public String get(String name) {\n return get((CharSequence) name);\n }\n\n @Override\n public String get(CharSequence name) {\n return HeadersUtils.getAsString(headers, name);\n }\n\n @Override\n public Integer getInt(CharSequence name) {\n return headers.getInt(name);\n }\n\n @Override\n public int getInt(CharSequence name, int defaultValue) {\n return headers.getInt(name, defaultValue);\n }\n\n @Override\n public Short getShort(CharSequence name) {\n return headers.getShort(name);\n }\n\n @Override\n public short getShort(CharSequence name, short defaultValue) {\n return headers.getShort(name, defaultValue);\n }\n\n @Override\n public Long getTimeMillis(CharSequence name) {\n return headers.getTimeMillis(name);\n }\n\n @Override\n public long getTimeMillis(CharSequence name, long defaultValue) {\n return headers.getTimeMillis(name, defaultValue);\n }\n\n @Override\n public List<String> getAll(String name) {\n return getAll((CharSequence) name);\n }\n\n @Override\n public List<String> getAll(CharSequence name) {\n return HeadersUtils.getAllAsString(headers, name);\n }\n\n @Override\n public List<Entry<String, String>> entries() {\n if (isEmpty()) {\n return Collections.emptyList();\n }\n List<Entry<String, String>> entriesConverted = new ArrayList<Entry<String, String>>(\n headers.size());\n for (Entry<String, String> entry : this) {\n entriesConverted.add(entry);\n }\n return entriesConverted;\n }\n\n @Deprecated\n @Override\n public Iterator<Map.Entry<String, String>> iterator() {\n return HeadersUtils.iteratorAsString(headers);\n }\n\n @Override\n public Iterator<Entry<CharSequence, CharSequence>> iteratorCharSequence() {\n return headers.iterator();\n }\n\n @Override\n public Iterator<String> valueStringIterator(CharSequence name) {\n final Iterator<CharSequence> itr = valueCharSequenceIterator(name);\n return new Iterator<String>() {\n @Override\n public boolean hasNext() {\n return itr.hasNext();\n }\n\n @Override\n public String next() {\n return itr.next().toString();\n }\n\n @Override\n public void remove() {\n itr.remove();\n }\n };\n }\n\n @Override\n public Iterator<CharSequence> valueCharSequenceIterator(CharSequence name) {\n return headers.valueIterator(name);\n }\n\n @Override\n public boolean contains(String name) {\n return contains((CharSequence) name);\n }\n\n @Override\n public boolean contains(CharSequence name) {\n return headers.contains(name);\n }\n\n @Override\n public boolean isEmpty() {\n return headers.isEmpty();\n }\n\n @Override\n public int size() {\n return headers.size();\n }\n\n @Override\n public boolean contains(String name, String value, boolean ignoreCase) {\n return contains((CharSequence) name, (CharSequence) value, ignoreCase);\n }\n\n @Override\n public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) {\n return headers.contains(name, value, ignoreCase ? CASE_INSENSITIVE_HASHER : CASE_SENSITIVE_HASHER);\n }\n\n @Override\n public Set<String> names() {\n return HeadersUtils.namesAsString(headers);\n }\n\n @Override\n public boolean equals(Object o) {\n return o instanceof DefaultHttpHeaders\n && headers.equals(((DefaultHttpHeaders) o).headers, CASE_SENSITIVE_HASHER);\n }\n\n @Override\n public int hashCode() {\n return headers.hashCode(CASE_SENSITIVE_HASHER);\n }\n\n @Override\n public HttpHeaders copy() {\n return new DefaultHttpHeaders(headers.copy());\n }\n\n private static void validateHeaderNameElement(byte value) {\n switch (value) {\n case 0x00:\n case '\\t':\n case '\\n':\n case 0x0b:\n case '\\f':\n case '\\r':\n case ' ':\n case ',':\n case ':':\n case ';':\n case '=':\n throw new IllegalArgumentException(\n \"a header name cannot contain the following prohibited characters: =,;: \\\\t\\\\r\\\\n\\\\v\\\\f: \" +\n value);\n default:\n // Check to see if the character is not an ASCII character, or invalid\n if (value < 0) {\n throw new IllegalArgumentException(\"a header name cannot contain non-ASCII character: \" + value);\n }\n }\n }\n\n private static void validateHeaderNameElement(char value) {\n switch (value) {\n case 0x00:\n case '\\t':\n case '\\n':\n case 0x0b:\n case '\\f':\n case '\\r':\n case ' ':\n case ',':\n case ':':\n case ';':\n case '=':\n throw new IllegalArgumentException(\n \"a header name cannot contain the following prohibited characters: =,;: \\\\t\\\\r\\\\n\\\\v\\\\f: \" +\n value);\n default:\n // Check to see if the character is not an ASCII character, or invalid\n if (value > 127) {\n throw new IllegalArgumentException(\"a header name cannot contain non-ASCII character: \" +\n value);\n }\n }\n }\n\n static ValueConverter<CharSequence> valueConverter(boolean validate) {\n return validate ? HeaderValueConverterAndValidator.INSTANCE : HeaderValueConverter.INSTANCE;\n }\n\n @SuppressWarnings(\"unchecked\")\n static NameValidator<CharSequence> nameValidator(boolean validate) {\n return validate ? HttpNameValidator : NameValidator.NOT_NULL;\n }\n\n private static class HeaderValueConverter extends CharSequenceValueConverter {\n static final HeaderValueConverter INSTANCE = new HeaderValueConverter();\n\n @Override\n public CharSequence convertObject(Object value) {\n if (value instanceof CharSequence) {\n return (CharSequence) value;\n }\n if (value instanceof Date) {\n return DateFormatter.format((Date) value);\n }\n if (value instanceof Calendar) {\n return DateFormatter.format(((Calendar) value).getTime());\n }\n return value.toString();\n }\n }\n\n private static final class HeaderValueConverterAndValidator extends HeaderValueConverter {\n static final HeaderValueConverterAndValidator INSTANCE = new HeaderValueConverterAndValidator();\n\n @Override\n public CharSequence convertObject(Object value) {\n CharSequence seq = super.convertObject(value);\n int state = 0;\n // Start looping through each of the character\n for (int index = 0; index < seq.length(); index++) {\n state = validateValueChar(seq, state, seq.charAt(index));\n }\n\n if (state != 0) {\n throw new IllegalArgumentException(\"a header value must not end with '\\\\r' or '\\\\n':\" + seq);\n }\n return seq;\n }\n\n private static int validateValueChar(CharSequence seq, int state, char character) {\n /*\n * State:\n * 0: Previous character was neither CR nor LF\n * 1: The previous character was CR\n * 2: The previous character was LF\n */\n if ((character & HIGHEST_INVALID_VALUE_CHAR_MASK) == 0) {\n // Check the absolutely prohibited characters.\n switch (character) {\n case 0x0: // NULL\n throw new IllegalArgumentException(\"a header value contains a prohibited character '\\0': \" + seq);\n case 0x0b: // Vertical tab\n throw new IllegalArgumentException(\"a header value contains a prohibited character '\\\\v': \" + seq);\n case '\\f':\n throw new IllegalArgumentException(\"a header value contains a prohibited character '\\\\f': \" + seq);\n default:\n break;\n }\n }\n\n // Check the CRLF (HT | SP) pattern\n switch (state) {\n case 0:\n switch (character) {\n case '\\r':\n return 1;\n case '\\n':\n return 2;\n default:\n break;\n }\n break;\n case 1:\n if (character == '\\n') {\n return 2;\n }\n throw new IllegalArgumentException(\"only '\\\\n' is allowed after '\\\\r': \" + seq);\n case 2:\n switch (character) {\n case '\\t':\n case ' ':\n return 0;\n default:\n throw new IllegalArgumentException(\"only ' ' and '\\\\t' are allowed after '\\\\n': \" + seq);\n }\n default:\n break;\n }\n return state;\n }\n }\n}\n\n## 内容2\n### 文件路径:\ncodec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java\n### 代码内容:\npackage io.netty.handler.codec.http;\n\nimport static io.netty.util.internal.ObjectUtil.checkPositive;\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.buffer.Unpooled;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.channel.ChannelPipeline;\nimport io.netty.handler.codec.ByteToMessageDecoder;\nimport io.netty.handler.codec.DecoderResult;\nimport io.netty.handler.codec.PrematureChannelClosureException;\nimport io.netty.handler.codec.TooLongFrameException;\nimport io.netty.util.ByteProcessor;\nimport io.netty.util.internal.AppendableCharSequence;\n\nimport java.util.List;\n\n/**\n * Decodes {@link ByteBuf}s into {@link HttpMessage}s and\n * {@link HttpContent}s.\n *\n * <h3>Parameters that prevents excessive memory consumption</h3>\n * <table border=\"1\">\n * <tr>\n * <th>Name</th><th>Default value</th><th>Meaning</th>\n * </tr>\n * <tr>\n * <td>{@code maxInitialLineLength}</td>\n * <td>{@value #DEFAULT_MAX_INITIAL_LINE_LENGTH}</td>\n * <td>The maximum length of the initial line\n * (e.g. {@code \"GET / HTTP/1.0\"} or {@code \"HTTP/1.0 200 OK\"})\n * If the length of the initial line exceeds this value, a\n * {@link TooLongFrameException} will be raised.</td>\n * </tr>\n * <tr>\n * <td>{@code maxHeaderSize}</td>\n * <td>{@value #DEFAULT_MAX_HEADER_SIZE}</td>\n * <td>The maximum length of all headers. If the sum of the length of each\n * header exceeds this value, a {@link TooLongFrameException} will be raised.</td>\n * </tr>\n * <tr>\n * <td>{@code maxChunkSize}</td>\n * <td>{@value #DEFAULT_MAX_CHUNK_SIZE}</td>\n * <td>The maximum length of the content or each chunk. If the content length\n * (or the length of each chunk) exceeds this value, the content or chunk\n * will be split into multiple {@link HttpContent}s whose length is\n * {@code maxChunkSize} at maximum.</td>\n * </tr>\n * </table>\n *\n * <h3>Parameters that control parsing behavior</h3>\n * <table border=\"1\">\n * <tr>\n * <th>Name</th><th>Default value</th><th>Meaning</th>\n * </tr>\n * <tr>\n * <td>{@code allowDuplicateContentLengths}</td>\n * <td>{@value #DEFAULT_ALLOW_DUPLICATE_CONTENT_LENGTHS}</td>\n * <td>When set to {@code false}, will reject any messages that contain multiple Content-Length header fields.\n * When set to {@code true}, will allow multiple Content-Length headers only if they are all the same decimal value.\n * The duplicated field-values will be replaced with a single valid Content-Length field.\n * See <a href=\"https://tools.ietf.org/html/rfc7230#section-3.3.2\">RFC 7230, Section 3.3.2</a>.</td>\n * </tr>\n * <tr>\n * <td>{@code allowPartialChunks}</td>\n * <td>{@value #DEFAULT_ALLOW_PARTIAL_CHUNKS}</td>\n * <td>If the length of a chunk exceeds the {@link ByteBuf}s readable bytes and {@code allowPartialChunks}\n * is set to {@code true}, the chunk will be split into multiple {@link HttpContent}s.\n * Otherwise, if the chunk size does not exceed {@code maxChunkSize} and {@code allowPartialChunks}\n * is set to {@code false}, the {@link ByteBuf} is not decoded into an {@link HttpContent} until\n * the readable bytes are greater or equal to the chunk size.</td>\n * </tr>\n * </table>\n *\n * <h3>Chunked Content</h3>\n *\n * If the content of an HTTP message is greater than {@code maxChunkSize} or\n * the transfer encoding of the HTTP message is 'chunked', this decoder\n * generates one {@link HttpMessage} instance and its following\n * {@link HttpContent}s per single HTTP message to avoid excessive memory\n * consumption. For example, the following HTTP message:\n * <pre>\n * GET / HTTP/1.1\n * Transfer-Encoding: chunked\n *\n * 1a\n * abcdefghijklmnopqrstuvwxyz\n * 10\n * 1234567890abcdef\n * 0\n * Content-MD5: ...\n * <i>[blank line]</i>\n * </pre>\n * triggers {@link HttpRequestDecoder} to generate 3 objects:\n * <ol>\n * <li>An {@link HttpRequest},</li>\n * <li>The first {@link HttpContent} whose content is {@code 'abcdefghijklmnopqrstuvwxyz'},</li>\n * <li>The second {@link LastHttpContent} whose content is {@code '1234567890abcdef'}, which marks\n * the end of the content.</li>\n * </ol>\n *\n * If you prefer not to handle {@link HttpContent}s by yourself for your\n * convenience, insert {@link HttpObjectAggregator} after this decoder in the\n * {@link ChannelPipeline}. However, please note that your server might not\n * be as memory efficient as without the aggregator.\n *\n * <h3>Extensibility</h3>\n *\n * Please note that this decoder is designed to be extended to implement\n * a protocol derived from HTTP, such as\n * <a href=\"https://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol\">RTSP</a> and\n * <a href=\"https://en.wikipedia.org/wiki/Internet_Content_Adaptation_Protocol\">ICAP</a>.\n * To implement the decoder of such a derived protocol, extend this class and\n * implement all abstract methods properly.\n */\npublic abstract class HttpObjectDecoder extends ByteToMessageDecoder {\n public static final int DEFAULT_MAX_INITIAL_LINE_LENGTH = 4096;\n public static final int DEFAULT_MAX_HEADER_SIZE = 8192;\n public static final boolean DEFAULT_CHUNKED_SUPPORTED = true;\n public static final boolean DEFAULT_ALLOW_PARTIAL_CHUNKS = true;\n public static final int DEFAULT_MAX_CHUNK_SIZE = 8192;\n public static final boolean DEFAULT_VALIDATE_HEADERS = true;\n public static final int DEFAULT_INITIAL_BUFFER_SIZE = 128;\n public static final boolean DEFAULT_ALLOW_DUPLICATE_CONTENT_LENGTHS = false;\n\n private static final String EMPTY_VALUE = \"\";\n\n private final int maxChunkSize;\n private final boolean chunkedSupported;\n private final boolean allowPartialChunks;\n protected final boolean validateHeaders;\n private final boolean allowDuplicateContentLengths;\n private final HeaderParser headerParser;\n private final LineParser lineParser;\n\n private HttpMessage message;\n private long chunkSize;\n private long contentLength = Long.MIN_VALUE;\n private volatile boolean resetRequested;\n\n // These will be updated by splitHeader(...)\n private CharSequence name;\n private CharSequence value;\n\n private LastHttpContent trailer;\n\n /**\n * The internal state of {@link HttpObjectDecoder}.\n * <em>Internal use only</em>.\n */\n private enum State {\n SKIP_CONTROL_CHARS,\n READ_INITIAL,\n READ_HEADER,\n READ_VARIABLE_LENGTH_CONTENT,\n READ_FIXED_LENGTH_CONTENT,\n READ_CHUNK_SIZE,\n READ_CHUNKED_CONTENT,\n READ_CHUNK_DELIMITER,\n READ_CHUNK_FOOTER,\n BAD_MESSAGE,\n UPGRADED\n }\n\n private State currentState = State.SKIP_CONTROL_CHARS;\n\n /**\n * Creates a new instance with the default\n * {@code maxInitialLineLength (4096}}, {@code maxHeaderSize (8192)}, and\n * {@code maxChunkSize (8192)}.\n */\n protected HttpObjectDecoder() {\n this(DEFAULT_MAX_INITIAL_LINE_LENGTH, DEFAULT_MAX_HEADER_SIZE, DEFAULT_MAX_CHUNK_SIZE,\n DEFAULT_CHUNKED_SUPPORTED);\n }\n\n /**\n * Creates a new instance with the specified parameters.\n */\n protected HttpObjectDecoder(\n int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean chunkedSupported) {\n this(maxInitialLineLength, maxHeaderSize, maxChunkSize, chunkedSupported, DEFAULT_VALIDATE_HEADERS);\n }\n\n /**\n * Creates a new instance with the specified parameters.\n */\n protected HttpObjectDecoder(\n int maxInitialLineLength, int maxHeaderSize, int maxChunkSize,\n boolean chunkedSupported, boolean validateHeaders) {\n this(maxInitialLineLength, maxHeaderSize, maxChunkSize, chunkedSupported, validateHeaders,\n DEFAULT_INITIAL_BUFFER_SIZE);\n }\n\n /**\n * Creates a new instance with the specified parameters.\n */\n protected HttpObjectDecoder(\n int maxInitialLineLength, int maxHeaderSize, int maxChunkSize,\n boolean chunkedSupported, boolean validateHeaders, int initialBufferSize) {\n this(maxInitialLineLength, maxHeaderSize, maxChunkSize, chunkedSupported, validateHeaders, initialBufferSize,\n DEFAULT_ALLOW_DUPLICATE_CONTENT_LENGTHS);\n }\n\n /**\n * Creates a new instance with the specified parameters.\n */\n protected HttpObjectDecoder(\n int maxInitialLineLength, int maxHeaderSize, int maxChunkSize,\n boolean chunkedSupported, boolean validateHeaders, int initialBufferSize,\n boolean allowDuplicateContentLengths) {\n this(maxInitialLineLength, maxHeaderSize, maxChunkSize, chunkedSupported, validateHeaders, initialBufferSize,\n allowDuplicateContentLengths, DEFAULT_ALLOW_PARTIAL_CHUNKS);\n }\n\n /**\n * Creates a new instance with the specified parameters.\n */\n protected HttpObjectDecoder(\n int maxInitialLineLength, int maxHeaderSize, int maxChunkSize,\n boolean chunkedSupported, boolean validateHeaders, int initialBufferSize,\n boolean allowDuplicateContentLengths, boolean allowPartialChunks) {\n checkPositive(maxInitialLineLength, \"maxInitialLineLength\");\n checkPositive(maxHeaderSize, \"maxHeaderSize\");\n checkPositive(maxChunkSize, \"maxChunkSize\");\n\n AppendableCharSequence seq = new AppendableCharSequence(initialBufferSize);\n lineParser = new LineParser(seq, maxInitialLineLength);\n headerParser = new HeaderParser(seq, maxHeaderSize);\n this.maxChunkSize = maxChunkSize;\n this.chunkedSupported = chunkedSupported;\n this.validateHeaders = validateHeaders;\n this.allowDuplicateContentLengths = allowDuplicateContentLengths;\n this.allowPartialChunks = allowPartialChunks;\n }\n\n @Override\n protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {\n if (resetRequested) {\n resetNow();\n }\n\n switch (currentState) {\n case SKIP_CONTROL_CHARS:\n // Fall-through\n case READ_INITIAL: try {\n AppendableCharSequence line = lineParser.parse(buffer);\n if (line == null) {\n return;\n }\n String[] initialLine = splitInitialLine(line);\n if (initialLine.length < 3) {\n // Invalid initial line - ignore.\n currentState = State.SKIP_CONTROL_CHARS;\n return;\n }\n\n message = createMessage(initialLine);\n currentState = State.READ_HEADER;\n // fall-through\n } catch (Exception e) {\n out.add(invalidMessage(buffer, e));\n return;\n }\n case READ_HEADER: try {\n State nextState = readHeaders(buffer);\n if (nextState == null) {\n return;\n }\n currentState = nextState;\n switch (nextState) {\n case SKIP_CONTROL_CHARS:\n // fast-path\n // No content is expected.\n out.add(message);\n out.add(LastHttpContent.EMPTY_LAST_CONTENT);\n resetNow();\n return;\n case READ_CHUNK_SIZE:\n if (!chunkedSupported) {\n throw new IllegalArgumentException(\"Chunked messages not supported\");\n }\n // Chunked encoding - generate HttpMessage first. HttpChunks will follow.\n out.add(message);\n return;\n default:\n /**\n * <a href=\"https://tools.ietf.org/html/rfc7230#section-3.3.3\">RFC 7230, 3.3.3</a> states that if a\n * request does not have either a transfer-encoding or a content-length header then the message body\n * length is 0. However for a response the body length is the number of octets received prior to the\n * server closing the connection. So we treat this as variable length chunked encoding.\n */\n long contentLength = contentLength();\n if (contentLength == 0 || contentLength == -1 && isDecodingRequest()) {\n out.add(message);\n out.add(LastHttpContent.EMPTY_LAST_CONTENT);\n resetNow();\n return;\n }\n\n assert nextState == State.READ_FIXED_LENGTH_CONTENT ||\n nextState == State.READ_VARIABLE_LENGTH_CONTENT;\n\n out.add(message);\n\n if (nextState == State.READ_FIXED_LENGTH_CONTENT) {\n // chunkSize will be decreased as the READ_FIXED_LENGTH_CONTENT state reads data chunk by chunk.\n chunkSize = contentLength;\n }\n\n // We return here, this forces decode to be called again where we will decode the content\n return;\n }\n } catch (Exception e) {\n out.add(invalidMessage(buffer, e));\n return;\n }\n case READ_VARIABLE_LENGTH_CONTENT: {\n // Keep reading data as a chunk until the end of connection is reached.\n int toRead = Math.min(buffer.readableBytes(), maxChunkSize);\n if (toRead > 0) {\n ByteBuf content = buffer.readRetainedSlice(toRead);\n out.add(new DefaultHttpContent(content));\n }\n return;\n }\n case READ_FIXED_LENGTH_CONTENT: {\n int readLimit = buffer.readableBytes();\n\n // Check if the buffer is readable first as we use the readable byte count\n // to create the HttpChunk. This is needed as otherwise we may end up with\n // create an HttpChunk instance that contains an empty buffer and so is\n // handled like it is the last HttpChunk.\n //\n // See https://github.com/netty/netty/issues/433\n if (readLimit == 0) {\n return;\n }\n\n int toRead = Math.min(readLimit, maxChunkSize);\n if (toRead > chunkSize) {\n toRead = (int) chunkSize;\n }\n ByteBuf content = buffer.readRetainedSlice(toRead);\n chunkSize -= toRead;\n\n if (chunkSize == 0) {\n // Read all content.\n out.add(new DefaultLastHttpContent(content, validateHeaders));\n resetNow();\n } else {\n out.add(new DefaultHttpContent(content));\n }\n return;\n }\n /**\n * everything else after this point takes care of reading chunked content. basically, read chunk size,\n * read chunk, read and ignore the CRLF and repeat until 0\n */\n case READ_CHUNK_SIZE: try {\n AppendableCharSequence line = lineParser.parse(buffer);\n if (line == null) {\n return;\n }\n int chunkSize = getChunkSize(line.toString());\n this.chunkSize = chunkSize;\n if (chunkSize == 0) {\n currentState = State.READ_CHUNK_FOOTER;\n return;\n }\n currentState = State.READ_CHUNKED_CONTENT;\n // fall-through\n } catch (Exception e) {\n out.add(invalidChunk(buffer, e));\n return;\n }\n case READ_CHUNKED_CONTENT: {\n assert chunkSize <= Integer.MAX_VALUE;\n int toRead = Math.min((int) chunkSize, maxChunkSize);\n if (!allowPartialChunks && buffer.readableBytes() < toRead) {\n return;\n }\n toRead = Math.min(toRead, buffer.readableBytes());\n if (toRead == 0) {\n return;\n }\n HttpContent chunk = new DefaultHttpContent(buffer.readRetainedSlice(toRead));\n chunkSize -= toRead;\n\n out.add(chunk);\n\n if (chunkSize != 0) {\n return;\n }\n currentState = State.READ_CHUNK_DELIMITER;\n // fall-through\n }\n case READ_CHUNK_DELIMITER: {\n final int wIdx = buffer.writerIndex();\n int rIdx = buffer.readerIndex();\n while (wIdx > rIdx) {\n byte next = buffer.getByte(rIdx++);\n if (next == HttpConstants.LF) {\n currentState = State.READ_CHUNK_SIZE;\n break;\n }\n }\n buffer.readerIndex(rIdx);\n return;\n }\n case READ_CHUNK_FOOTER: try {\n LastHttpContent trailer = readTrailingHeaders(buffer);\n if (trailer == null) {\n return;\n }\n out.add(trailer);\n resetNow();\n return;\n } catch (Exception e) {\n out.add(invalidChunk(buffer, e));\n return;\n }\n case BAD_MESSAGE: {\n // Keep discarding until disconnection.\n buffer.skipBytes(buffer.readableBytes());\n break;\n }\n case UPGRADED: {\n int readableBytes = buffer.readableBytes();\n if (readableBytes > 0) {\n // Keep on consuming as otherwise we may trigger an DecoderException,\n // other handler will replace this codec with the upgraded protocol codec to\n // take the traffic over at some point then.\n // See https://github.com/netty/netty/issues/2173\n out.add(buffer.readBytes(readableBytes));\n }\n break;\n }\n default:\n break;\n }\n }\n\n @Override\n protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {\n super.decodeLast(ctx, in, out);\n\n if (resetRequested) {\n // If a reset was requested by decodeLast() we need to do it now otherwise we may produce a\n // LastHttpContent while there was already one.\n resetNow();\n }\n // Handle the last unfinished message.\n if (message != null) {\n boolean chunked = HttpUtil.isTransferEncodingChunked(message);\n if (currentState == State.READ_VARIABLE_LENGTH_CONTENT && !in.isReadable() && !chunked) {\n // End of connection.\n out.add(LastHttpContent.EMPTY_LAST_CONTENT);\n resetNow();\n return;\n }\n\n if (currentState == State.READ_HEADER) {\n // If we are still in the state of reading headers we need to create a new invalid message that\n // signals that the connection was closed before we received the headers.\n out.add(invalidMessage(Unpooled.EMPTY_BUFFER,\n new PrematureChannelClosureException(\"Connection closed before received headers\")));\n resetNow();\n return;\n }\n\n // Check if the closure of the connection signifies the end of the content.\n boolean prematureClosure;\n if (isDecodingRequest() || chunked) {\n // The last request did not wait for a response.\n prematureClosure = true;\n } else {\n // Compare the length of the received content and the 'Content-Length' header.\n // If the 'Content-Length' header is absent, the length of the content is determined by the end of the\n // connection, so it is perfectly fine.\n prematureClosure = contentLength() > 0;\n }\n\n if (!prematureClosure) {\n out.add(LastHttpContent.EMPTY_LAST_CONTENT);\n }\n resetNow();\n }\n }\n\n @Override\n public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {\n if (evt instanceof HttpExpectationFailedEvent) {\n switch (currentState) {\n case READ_FIXED_LENGTH_CONTENT:\n case READ_VARIABLE_LENGTH_CONTENT:\n case READ_CHUNK_SIZE:\n reset();\n break;\n default:\n break;\n }\n }\n super.userEventTriggered(ctx, evt);\n }\n\n protected boolean isContentAlwaysEmpty(HttpMessage msg) {\n if (msg instanceof HttpResponse) {\n HttpResponse res = (HttpResponse) msg;\n int code = res.status().code();\n\n // Correctly handle return codes of 1xx.\n //\n // See:\n // - https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html Section 4.4\n // - https://github.com/netty/netty/issues/222\n if (code >= 100 && code < 200) {\n // One exception: Hixie 76 websocket handshake response\n return !(code == 101 && !res.headers().contains(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT)\n && res.headers().contains(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET, true));\n }\n\n switch (code) {\n case 204: case 304:\n return true;\n default:\n return false;\n }\n }\n return false;\n }\n\n /**\n * Returns true if the server switched to a different protocol than HTTP/1.0 or HTTP/1.1, e.g. HTTP/2 or Websocket.\n * Returns false if the upgrade happened in a different layer, e.g. upgrade from HTTP/1.1 to HTTP/1.1 over TLS.\n */\n protected boolean isSwitchingToNonHttp1Protocol(HttpResponse msg) {\n if (msg.status().code() != HttpResponseStatus.SWITCHING_PROTOCOLS.code()) {\n return false;\n }\n String newProtocol = msg.headers().get(HttpHeaderNames.UPGRADE);\n return newProtocol == null ||\n !newProtocol.contains(HttpVersion.HTTP_1_0.text()) &&\n !newProtocol.contains(HttpVersion.HTTP_1_1.text());\n }\n\n /**\n * Resets the state of the decoder so that it is ready to decode a new message.\n * This method is useful for handling a rejected request with {@code Expect: 100-continue} header.\n */\n public void reset() {\n resetRequested = true;\n }\n\n private void resetNow() {\n HttpMessage message = this.message;\n this.message = null;\n name = null;\n value = null;\n contentLength = Long.MIN_VALUE;\n lineParser.reset();\n headerParser.reset();\n trailer = null;\n if (!isDecodingRequest()) {\n HttpResponse res = (HttpResponse) message;\n if (res != null && isSwitchingToNonHttp1Protocol(res)) {\n currentState = State.UPGRADED;\n return;\n }\n }\n\n resetRequested = false;\n currentState = State.SKIP_CONTROL_CHARS;\n }\n\n private HttpMessage invalidMessage(ByteBuf in, Exception cause) {\n currentState = State.BAD_MESSAGE;\n\n // Advance the readerIndex so that ByteToMessageDecoder does not complain\n // when we produced an invalid message without consuming anything.\n in.skipBytes(in.readableBytes());\n\n if (message == null) {\n message = createInvalidMessage();\n }\n message.setDecoderResult(DecoderResult.failure(cause));\n\n HttpMessage ret = message;\n message = null;\n return ret;\n }\n\n private HttpContent invalidChunk(ByteBuf in, Exception cause) {\n currentState = State.BAD_MESSAGE;\n\n // Advance the readerIndex so that ByteToMessageDecoder does not complain\n // when we produced an invalid message without consuming anything.\n in.skipBytes(in.readableBytes());\n\n HttpContent chunk = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER);\n chunk.setDecoderResult(DecoderResult.failure(cause));\n message = null;\n trailer = null;\n return chunk;\n }\n\n private State readHeaders(ByteBuf buffer) {\n final HttpMessage message = this.message;\n final HttpHeaders headers = message.headers();\n\n AppendableCharSequence line = headerParser.parse(buffer);\n if (line == null) {\n return null;\n }\n if (line.length() > 0) {\n do {\n char firstChar = line.charAtUnsafe(0);\n if (name != null && (firstChar == ' ' || firstChar == '\\t')) {\n //please do not make one line from below code\n //as it breaks +XX:OptimizeStringConcat optimization\n String trimmedLine = line.toString().trim();\n String valueStr = String.valueOf(value);\n value = valueStr + ' ' + trimmedLine;\n } else {\n if (name != null) {\n headers.add(name, value);\n }\n splitHeader(line);\n }\n\n line = headerParser.parse(buffer);\n if (line == null) {\n return null;\n }\n } while (line.length() > 0);\n }\n\n // Add the last header.\n if (name != null) {\n headers.add(name, value);\n }\n\n // reset name and value fields\n name = null;\n value = null;\n\n // Done parsing initial line and headers. Set decoder result.\n HttpMessageDecoderResult decoderResult = new HttpMessageDecoderResult(lineParser.size, headerParser.size);\n message.setDecoderResult(decoderResult);\n\n List<String> contentLengthFields = headers.getAll(HttpHeaderNames.CONTENT_LENGTH);\n if (!contentLengthFields.isEmpty()) {\n HttpVersion version = message.protocolVersion();\n boolean isHttp10OrEarlier = version.majorVersion() < 1 || (version.majorVersion() == 1\n && version.minorVersion() == 0);\n // Guard against multiple Content-Length headers as stated in\n // https://tools.ietf.org/html/rfc7230#section-3.3.2:\n contentLength = HttpUtil.normalizeAndGetContentLength(contentLengthFields,\n isHttp10OrEarlier, allowDuplicateContentLengths);\n if (contentLength != -1) {\n headers.set(HttpHeaderNames.CONTENT_LENGTH, contentLength);\n }\n }\n\n if (isContentAlwaysEmpty(message)) {\n HttpUtil.setTransferEncodingChunked(message, false);\n return State.SKIP_CONTROL_CHARS;\n } else if (HttpUtil.isTransferEncodingChunked(message)) {\n if (!contentLengthFields.isEmpty() && message.protocolVersion() == HttpVersion.HTTP_1_1) {\n handleTransferEncodingChunkedWithContentLength(message);\n }\n return State.READ_CHUNK_SIZE;\n } else if (contentLength() >= 0) {\n return State.READ_FIXED_LENGTH_CONTENT;\n } else {\n return State.READ_VARIABLE_LENGTH_CONTENT;\n }\n }\n\n /**\n * Invoked when a message with both a \"Transfer-Encoding: chunked\" and a \"Content-Length\" header field is detected.\n * The default behavior is to <i>remove</i> the Content-Length field, but this method could be overridden\n * to change the behavior (to, e.g., throw an exception and produce an invalid message).\n * <p>\n * See: https://tools.ietf.org/html/rfc7230#section-3.3.3\n * <pre>\n * If a message is received with both a Transfer-Encoding and a\n * Content-Length header field, the Transfer-Encoding overrides the\n * Content-Length. Such a message might indicate an attempt to\n * perform request smuggling (Section 9.5) or response splitting\n * (Section 9.4) and ought to be handled as an error. A sender MUST\n * remove the received Content-Length field prior to forwarding such\n * a message downstream.\n * </pre>\n * Also see:\n * https://github.com/apache/tomcat/blob/b693d7c1981fa7f51e58bc8c8e72e3fe80b7b773/\n * java/org/apache/coyote/http11/Http11Processor.java#L747-L755\n * https://github.com/nginx/nginx/blob/0ad4393e30c119d250415cb769e3d8bc8dce5186/\n * src/http/ngx_http_request.c#L1946-L1953\n */\n protected void handleTransferEncodingChunkedWithContentLength(HttpMessage message) {\n message.headers().remove(HttpHeaderNames.CONTENT_LENGTH);\n contentLength = Long.MIN_VALUE;\n }\n\n private long contentLength() {\n if (contentLength == Long.MIN_VALUE) {\n contentLength = HttpUtil.getContentLength(message, -1L);\n }\n return contentLength;\n }\n\n private LastHttpContent readTrailingHeaders(ByteBuf buffer) {\n AppendableCharSequence line = headerParser.parse(buffer);\n if (line == null) {\n return null;\n }\n LastHttpContent trailer = this.trailer;\n if (line.length() == 0 && trailer == null) {\n // We have received the empty line which signals the trailer is complete and did not parse any trailers\n // before. Just return an empty last content to reduce allocations.\n return LastHttpContent.EMPTY_LAST_CONTENT;\n }\n\n CharSequence lastHeader = null;\n if (trailer == null) {\n trailer = this.trailer = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, validateHeaders);\n }\n while (line.length() > 0) {\n char firstChar = line.charAtUnsafe(0);\n if (lastHeader != null && (firstChar == ' ' || firstChar == '\\t')) {\n List<String> current = trailer.trailingHeaders().getAll(lastHeader);\n if (!current.isEmpty()) {\n int lastPos = current.size() - 1;\n //please do not make one line from below code\n //as it breaks +XX:OptimizeStringConcat optimization\n String lineTrimmed = line.toString().trim();\n String currentLastPos = current.get(lastPos);\n current.set(lastPos, currentLastPos + lineTrimmed);\n }\n } else {\n splitHeader(line);\n CharSequence headerName = name;\n if (!HttpHeaderNames.CONTENT_LENGTH.contentEqualsIgnoreCase(headerName) &&\n !HttpHeaderNames.TRANSFER_ENCODING.contentEqualsIgnoreCase(headerName) &&\n !HttpHeaderNames.TRAILER.contentEqualsIgnoreCase(headerName)) {\n trailer.trailingHeaders().add(headerName, value);\n }\n lastHeader = name;\n // reset name and value fields\n name = null;\n value = null;\n }\n line = headerParser.parse(buffer);\n if (line == null) {\n return null;\n }\n }\n\n this.trailer = null;\n return trailer;\n }\n\n protected abstract boolean isDecodingRequest();\n protected abstract HttpMessage createMessage(String[] initialLine) throws Exception;\n protected abstract HttpMessage createInvalidMessage();\n\n private static int getChunkSize(String hex) {\n hex = hex.trim();\n for (int i = 0; i < hex.length(); i ++) {\n char c = hex.charAt(i);\n if (c == ';' || Character.isWhitespace(c) || Character.isISOControl(c)) {\n hex = hex.substring(0, i);\n break;\n }\n }\n\n return Integer.parseInt(hex, 16);\n }\n\n private static String[] splitInitialLine(AppendableCharSequence sb) {\n int aStart;\n int aEnd;\n int bStart;\n int bEnd;\n int cStart;\n int cEnd;\n\n aStart = findNonSPLenient(sb, 0);\n aEnd = findSPLenient(sb, aStart);\n\n bStart = findNonSPLenient(sb, aEnd);\n bEnd = findSPLenient(sb, bStart);\n\n cStart = findNonSPLenient(sb, bEnd);\n cEnd = findEndOfString(sb);\n\n return new String[] {\n sb.subStringUnsafe(aStart, aEnd),\n sb.subStringUnsafe(bStart, bEnd),\n cStart < cEnd? sb.subStringUnsafe(cStart, cEnd) : \"\" };\n }\n\n private void splitHeader(AppendableCharSequence sb) {\n final int length = sb.length();\n int nameStart;\n int nameEnd;\n int colonEnd;\n int valueStart;\n int valueEnd;\n\n nameStart = findNonWhitespace(sb, 0, false);\n for (nameEnd = nameStart; nameEnd < length; nameEnd ++) {\n char ch = sb.charAtUnsafe(nameEnd);\n // https://tools.ietf.org/html/rfc7230#section-3.2.4\n //\n // No whitespace is allowed between the header field-name and colon. In\n // the past, differences in the handling of such whitespace have led to\n // security vulnerabilities in request routing and response handling. A\n // server MUST reject any received request message that contains\n // whitespace between a header field-name and colon with a response code\n // of 400 (Bad Request). A proxy MUST remove any such whitespace from a\n // response message before forwarding the message downstream.\n if (ch == ':' ||\n // In case of decoding a request we will just continue processing and header validation\n // is done in the DefaultHttpHeaders implementation.\n //\n // In the case of decoding a response we will \"skip\" the whitespace.\n (!isDecodingRequest() && isOWS(ch))) {\n break;\n }\n }\n\n if (nameEnd == length) {\n // There was no colon present at all.\n throw new IllegalArgumentException(\"No colon found\");\n }\n\n for (colonEnd = nameEnd; colonEnd < length; colonEnd ++) {\n if (sb.charAtUnsafe(colonEnd) == ':') {\n colonEnd ++;\n break;\n }\n }\n\n name = sb.subStringUnsafe(nameStart, nameEnd);\n valueStart = findNonWhitespace(sb, colonEnd, true);\n if (valueStart == length) {\n value = EMPTY_VALUE;\n } else {\n valueEnd = findEndOfString(sb);\n value = sb.subStringUnsafe(valueStart, valueEnd);\n }\n }\n\n private static int findNonSPLenient(AppendableCharSequence sb, int offset) {\n for (int result = offset; result < sb.length(); ++result) {\n char c = sb.charAtUnsafe(result);\n // See https://tools.ietf.org/html/rfc7230#section-3.5\n if (isSPLenient(c)) {\n continue;\n }\n if (Character.isWhitespace(c)) {\n // Any other whitespace delimiter is invalid\n throw new IllegalArgumentException(\"Invalid separator\");\n }\n return result;\n }\n return sb.length();\n }\n\n private static int findSPLenient(AppendableCharSequence sb, int offset) {\n for (int result = offset; result < sb.length(); ++result) {\n if (isSPLenient(sb.charAtUnsafe(result))) {\n return result;\n }\n }\n return sb.length();\n }\n\n private static boolean isSPLenient(char c) {\n // See https://tools.ietf.org/html/rfc7230#section-3.5\n return c == ' ' || c == (char) 0x09 || c == (char) 0x0B || c == (char) 0x0C || c == (char) 0x0D;\n }\n\n private static int findNonWhitespace(AppendableCharSequence sb, int offset, boolean validateOWS) {\n for (int result = offset; result < sb.length(); ++result) {\n char c = sb.charAtUnsafe(result);\n if (!Character.isWhitespace(c)) {\n return result;\n } else if (validateOWS && !isOWS(c)) {\n // Only OWS is supported for whitespace\n throw new IllegalArgumentException(\"Invalid separator, only a single space or horizontal tab allowed,\" +\n \" but received a '\" + c + \"' (0x\" + Integer.toHexString(c) + \")\");\n }\n }\n return sb.length();\n }\n\n private static int findEndOfString(AppendableCharSequence sb) {\n for (int result = sb.length() - 1; result > 0; --result) {\n if (!Character.isWhitespace(sb.charAtUnsafe(result))) {\n return result + 1;\n }\n }\n return 0;\n }\n\n private static boolean isOWS(char ch) {\n return ch == ' ' || ch == (char) 0x09;\n }\n\n private static class HeaderParser implements ByteProcessor {\n private final AppendableCharSequence seq;\n private final int maxLength;\n int size;\n\n HeaderParser(AppendableCharSequence seq, int maxLength) {\n this.seq = seq;\n this.maxLength = maxLength;\n }\n\n public AppendableCharSequence parse(ByteBuf buffer) {\n final int oldSize = size;\n seq.reset();\n int i = buffer.forEachByte(this);\n if (i == -1) {\n size = oldSize;\n return null;\n }\n buffer.readerIndex(i + 1);\n return seq;\n }\n\n public void reset() {\n size = 0;\n }\n\n @Override\n public boolean process(byte value) throws Exception {\n char nextByte = (char) (value & 0xFF);\n if (nextByte == HttpConstants.LF) {\n int len = seq.length();\n // Drop CR if we had a CRLF pair\n if (len >= 1 && seq.charAtUnsafe(len - 1) == HttpConstants.CR) {\n -- size;\n seq.setLength(len - 1);\n }\n return false;\n }\n\n increaseCount();\n\n seq.append(nextByte);\n return true;\n }\n\n protected final void increaseCount() {\n if (++ size > maxLength) {\n // TODO: Respond with Bad Request and discard the traffic\n // or close the connection.\n // No need to notify the upstream handlers - just log.\n // If decoding a response, just throw an exception.\n throw newException(maxLength);\n }\n }\n\n protected TooLongFrameException newException(int maxLength) {\n return new TooLongFrameException(\"HTTP header is larger than \" + maxLength + \" bytes.\");\n }\n }\n\n private final class LineParser extends HeaderParser {\n\n LineParser(AppendableCharSequence seq, int maxLength) {\n super(seq, maxLength);\n }\n\n @Override\n public AppendableCharSequence parse(ByteBuf buffer) {\n // Suppress a warning because HeaderParser.reset() is supposed to be called\n reset(); // lgtm[java/subtle-inherited-call]\n return super.parse(buffer);\n }\n\n @Override\n public boolean process(byte value) throws Exception {\n if (currentState == State.SKIP_CONTROL_CHARS) {\n char c = (char) (value & 0xFF);\n if (Character.isISOControl(c) || Character.isWhitespace(c)) {\n increaseCount();\n return true;\n }\n currentState = State.READ_INITIAL;\n }\n return super.process(value);\n }\n\n @Override\n protected TooLongFrameException newException(int maxLength) {\n return new TooLongFrameException(\"An HTTP line is larger than \" + maxLength + \" bytes.\");\n }\n }\n}\n\n", "input": "", "output": "【漏洞类型】HTTP Response Splitting\n【严重性】High\n【漏洞摘要】在`DefaultHttpHeaders`类中,HTTP头名称和值的验证不够严格。具体来说,`validateHeaderNameElement`方法中没有禁止0x1c、0x1d、0x1e和0x1f这些控制字符,以及在`HttpObjectDecoder`类中,虽然禁止了请求头名称和值之间的其他空白字符,但在响应头解析中,这些空白字符会被忽略,而不是抛出异常。这可能导致攻击者通过注入CRLF序列来操纵HTTP响应,引发HTTP响应拆分攻击。\n【关键代码模式】`validateHeaderNameElement`、`splitHeader`、`findNonWhitespace`\n【修复方法说明】修复方法是在`validateHeaderNameElement`方法中添加了对0x1c、0x1d、0x1e和0x1f的禁止,以及在`findNonWhitespace`方法中移除了对响应头的宽松处理,确保所有空白字符都严格符合OWS(仅允许空格和水平制表符)的要求。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nomod/src/main/webapp/pages/userApp.gsp\n### 代码内容:\n<%\n ui.decorateWith(\"appui\", \"standardEmrPage\", [ title: ui.message(\"referenceapplication.app.userApp.\"+param.action[0]) ])\n\n ui.includeJavascript(\"referenceapplication\", \"userApp.js\");\n%>\n\n<script type=\"text/javascript\">\n var breadcrumbs = [\n { icon: \"icon-home\", link: '/' + OPENMRS_CONTEXT_PATH + '/index.htm' },\n { label: \"${ ui.message(\"coreapps.app.systemAdministration.label\")}\",\n link: \"${ui.pageLink(\"coreapps\", \"systemadministration/systemAdministration\")}\"\n },\n { label: \"${ ui.message(\"referenceapplication.app.manageApps.title\")}\",\n link: \"${ui.pageLink(\"referenceapplication\", \"manageApps\")}\"\n },\n { label: \"${ ui.message(\"referenceapplication.app.userApp.\"+param.action[0])}\"}\n ];\n\n jq(function(){\n setAction('${param.action[0]}');\n });\n</script>\n\n<h2>${ ui.message(\"referenceapplication.app.userApp.\"+param.action[0])}</h2>\n\n<form class=\"simple-form-ui\" method=\"POST\" action=\"userApp.page\">\n <span id=\"errorMsg\" class=\"field-error\" style=\"display: none\">\n ${ui.message(\"referenceapplication.app.errors.invalidJson\")}\n </span>\n <span id=\"server-error-msg\" class=\"field-error\" style=\"display: none\">\n ${ui.message(\"referenceapplication.app.errors.serverError\")}\n </span>\n <input type=\"hidden\" name=\"action\" value=\"${param.action[0]}\" />\n <p>\n <%if(param.action[0] == 'edit'){%>\n <span class=\"title\">\n ${ui.message(\"referenceapplication.app.appId.label\")}:\n </span> ${ui.escapeHtml(userApp.appId)}\n <input class=\"form-control form-control-sm form-control-lg form-control-md\" id=\"appId-field-hidden\" type=\"hidden\" name=\"appId\" value=\"${userApp.appId ? userApp.appId : \"\"}\" />\n <%} else{%>\n <label for=\"appId-field\">\n <span class=\"title\">\n ${ui.message(\"referenceapplication.app.appId.label\")} (${ ui.message(\"coreapps.formValidation.messages.requiredField.label\") })\n </span>\n </label>\n <input class=\"form-control form-control-sm form-control-lg form-control-md required\" id=\"appId-field\" type=\"text\" name=\"appId\" value=\"${userApp.appId ? ui.escapeJs(ui.escapeHtml(userApp.appId)) : \"\"}\" size=\"80\" placeholder=\"${ ui.message(\"referenceapplication.app.definition.placeholder\") }\" />\n <%}%>\n </p>\n <p>\n <label for=\"json-field\">\n <span class=\"title\">\n ${ui.message(\"referenceapplication.app.definition.label\")} (${ ui.message(\"coreapps.formValidation.messages.requiredField.label\") })\n </span>\n </label>\n <textarea class=\"form-control form-control-sm form-control-lg form-control-md required\" id=\"json-field\" name=\"json\" rows=\"15\" cols=\"80\">${userApp.json ? userApp.json : \"\"}</textarea>\n </p>\n\n <input type=\"button\" class=\"cancel\" value=\"${ ui.message(\"general.cancel\") }\" onclick=\"javascript:window.location='/${ contextPath }/referenceapplication/manageApps.page'\" />\n <input type=\"submit\" class=\"confirm right\" id=\"save-button\" value=\"${ ui.message(\"general.save\") }\" disabled=\"disabled\" />\n</form>\n", "input": "", "output": "【漏洞类型】跨站脚本攻击(XSS)\n【严重性】High\n【漏洞摘要】原始代码中使用了 `ui.escapeJs(ui.escapeHtml(userApp.appId))` 来处理用户输入的 `appId`,但实际上是多余的,因为 `ui.escapeHtml` 已经对 HTML 特殊字符进行了转义。问题在于 `ui.escapeJs` 的使用可能导致对 JavaScript 的不充分转义,从而允许潜在的 XSS 攻击。具体问题行是 `<input class=\"form-control form-control-sm form-control-lg form-control-md required\" id=\"appId-field\" type=\"text\" name=\"appId\" value=\"${userApp.appId ? ui.escapeJs(ui.escapeHtml(userApp.appId)) : \"\"}\" size=\"80\" placeholder=\"${ ui.message(\"referenceapplication.app.definition.placeholder\") }\" />`。\n【关键代码模式】`ui.escapeJs`、`ui.escapeHtml`、`param.action[0]`、`userApp.appId`\n【修复方法说明】修复方法通过将 `ui.escapeJs` 替换为 `ui.encodeJavaScript` 来确保对 JavaScript 的正确转义,从而防止 XSS 攻击。最佳实践是使用专门的函数来处理不同类型的输出转义,以确保所有的用户输入都被正确处理。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nservices/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java\n### 代码内容:\npackage org.keycloak.broker.saml;\n\nimport org.jboss.logging.Logger;\nimport org.jboss.resteasy.annotations.cache.NoCache;\n\nimport org.jboss.resteasy.spi.ResteasyProviderFactory;\nimport org.keycloak.broker.provider.BrokeredIdentityContext;\nimport org.keycloak.broker.provider.IdentityBrokerException;\nimport org.keycloak.broker.provider.IdentityProvider;\nimport org.keycloak.common.ClientConnection;\nimport org.keycloak.common.VerificationException;\nimport org.keycloak.dom.saml.v2.assertion.AssertionType;\nimport org.keycloak.dom.saml.v2.assertion.AttributeStatementType;\nimport org.keycloak.dom.saml.v2.assertion.AttributeType;\nimport org.keycloak.dom.saml.v2.assertion.AuthnStatementType;\nimport org.keycloak.dom.saml.v2.assertion.NameIDType;\nimport org.keycloak.dom.saml.v2.assertion.SubjectType;\nimport org.keycloak.dom.saml.v2.protocol.LogoutRequestType;\nimport org.keycloak.dom.saml.v2.protocol.RequestAbstractType;\nimport org.keycloak.dom.saml.v2.protocol.ResponseType;\nimport org.keycloak.dom.saml.v2.protocol.StatusResponseType;\nimport org.keycloak.events.Details;\nimport org.keycloak.events.Errors;\nimport org.keycloak.events.EventBuilder;\nimport org.keycloak.events.EventType;\nimport org.keycloak.models.ClientModel;\nimport org.keycloak.models.KeyManager;\nimport org.keycloak.models.KeycloakSession;\nimport org.keycloak.models.RealmModel;\nimport org.keycloak.models.UserSessionModel;\nimport org.keycloak.protocol.LoginProtocol;\nimport org.keycloak.protocol.LoginProtocolFactory;\nimport org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder;\nimport org.keycloak.protocol.saml.SamlProtocol;\nimport org.keycloak.protocol.saml.SamlProtocolUtils;\nimport org.keycloak.protocol.saml.SamlService;\nimport org.keycloak.protocol.saml.SamlSessionUtils;\nimport org.keycloak.protocol.saml.preprocessor.SamlAuthenticationPreprocessor;\nimport org.keycloak.saml.SAML2LogoutResponseBuilder;\nimport org.keycloak.saml.SAMLRequestParser;\nimport org.keycloak.saml.common.constants.GeneralConstants;\nimport org.keycloak.saml.common.constants.JBossSAMLConstants;\nimport org.keycloak.saml.common.constants.JBossSAMLURIConstants;\nimport org.keycloak.saml.common.exceptions.ConfigurationException;\nimport org.keycloak.saml.common.exceptions.ProcessingException;\nimport org.keycloak.saml.common.util.DocumentUtil;\nimport org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;\nimport org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;\nimport org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil;\nimport org.keycloak.saml.processing.core.util.XMLSignatureUtil;\nimport org.keycloak.saml.processing.web.util.PostBindingUtil;\nimport org.keycloak.services.ErrorPage;\nimport org.keycloak.services.Urls;\nimport org.keycloak.services.managers.AuthenticationManager;\nimport org.keycloak.services.messages.Messages;\n\nimport javax.ws.rs.Consumes;\nimport javax.ws.rs.FormParam;\nimport javax.ws.rs.GET;\nimport javax.ws.rs.POST;\nimport javax.ws.rs.Path;\nimport javax.ws.rs.QueryParam;\nimport javax.ws.rs.PathParam;\nimport javax.ws.rs.WebApplicationException;\nimport javax.ws.rs.core.Context;\nimport javax.ws.rs.core.HttpHeaders;\nimport javax.ws.rs.core.MediaType;\nimport javax.ws.rs.core.Response;\nimport javax.ws.rs.core.UriBuilder;\nimport javax.ws.rs.core.UriInfo;\nimport javax.xml.namespace.QName;\nimport java.io.IOException;\nimport java.security.Key;\nimport java.security.cert.X509Certificate;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.keycloak.protocol.saml.SamlPrincipalType;\nimport org.keycloak.rotation.HardcodedKeyLocator;\nimport org.keycloak.rotation.KeyLocator;\nimport org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;\nimport org.keycloak.saml.validators.ConditionsValidator;\nimport org.keycloak.saml.validators.DestinationValidator;\nimport org.keycloak.services.util.CacheControlUtil;\nimport org.keycloak.sessions.AuthenticationSessionModel;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.NodeList;\n\nimport java.net.URI;\nimport java.security.cert.CertificateException;\n\nimport javax.ws.rs.core.MultivaluedMap;\nimport javax.xml.crypto.dsig.XMLSignature;\n\n/**\n * @author <a href=\"mailto:bill@burkecentral.com\">Bill Burke</a>\n * @version $Revision: 1 $\n */\npublic class SAMLEndpoint {\n protected static final Logger logger = Logger.getLogger(SAMLEndpoint.class);\n public static final String SAML_FEDERATED_SESSION_INDEX = \"SAML_FEDERATED_SESSION_INDEX\";\n @Deprecated // in favor of SAML_FEDERATED_SUBJECT_NAMEID\n public static final String SAML_FEDERATED_SUBJECT = \"SAML_FEDERATED_SUBJECT\";\n @Deprecated // in favor of SAML_FEDERATED_SUBJECT_NAMEID\n public static final String SAML_FEDERATED_SUBJECT_NAMEFORMAT = \"SAML_FEDERATED_SUBJECT_NAMEFORMAT\";\n public static final String SAML_FEDERATED_SUBJECT_NAMEID = \"SAML_FEDERATED_SUBJECT_NAME_ID\";\n public static final String SAML_LOGIN_RESPONSE = \"SAML_LOGIN_RESPONSE\";\n public static final String SAML_ASSERTION = \"SAML_ASSERTION\";\n public static final String SAML_AUTHN_STATEMENT = \"SAML_AUTHN_STATEMENT\";\n protected RealmModel realm;\n protected EventBuilder event;\n protected SAMLIdentityProviderConfig config;\n protected IdentityProvider.AuthenticationCallback callback;\n protected SAMLIdentityProvider provider;\n private final DestinationValidator destinationValidator;\n\n @Context\n private KeycloakSession session;\n\n @Context\n private ClientConnection clientConnection;\n\n @Context\n private HttpHeaders headers;\n\n public SAMLEndpoint(RealmModel realm, SAMLIdentityProvider provider, SAMLIdentityProviderConfig config, IdentityProvider.AuthenticationCallback callback, DestinationValidator destinationValidator) {\n this.realm = realm;\n this.config = config;\n this.callback = callback;\n this.provider = provider;\n this.destinationValidator = destinationValidator;\n }\n\n @GET\n @NoCache\n @Path(\"descriptor\")\n public Response getSPDescriptor() {\n return provider.export(session.getContext().getUri(), realm, null);\n }\n\n @GET\n public Response redirectBinding(@QueryParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest,\n @QueryParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse,\n @QueryParam(GeneralConstants.RELAY_STATE) String relayState) {\n return new RedirectBinding().execute(samlRequest, samlResponse, relayState, null);\n }\n\n /**\n */\n @POST\n @Consumes(MediaType.APPLICATION_FORM_URLENCODED)\n public Response postBinding(@FormParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest,\n @FormParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse,\n @FormParam(GeneralConstants.RELAY_STATE) String relayState) {\n return new PostBinding().execute(samlRequest, samlResponse, relayState, null);\n }\n\n @Path(\"clients/{client_id}\")\n @GET\n public Response redirectBinding(@QueryParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest,\n @QueryParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse,\n @QueryParam(GeneralConstants.RELAY_STATE) String relayState,\n @PathParam(\"client_id\") String clientId) {\n return new RedirectBinding().execute(samlRequest, samlResponse, relayState, clientId);\n }\n\n /**\n */\n @Path(\"clients/{client_id}\")\n @POST\n @Consumes(MediaType.APPLICATION_FORM_URLENCODED)\n public Response postBinding(@FormParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest,\n @FormParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse,\n @FormParam(GeneralConstants.RELAY_STATE) String relayState,\n @PathParam(\"client_id\") String clientId) {\n return new PostBinding().execute(samlRequest, samlResponse, relayState, clientId);\n }\n\n protected abstract class Binding {\n private boolean checkSsl() {\n if (session.getContext().getUri().getBaseUri().getScheme().equals(\"https\")) {\n return true;\n } else {\n return !realm.getSslRequired().isRequired(clientConnection);\n }\n }\n\n protected Response basicChecks(String samlRequest, String samlResponse) {\n if (!checkSsl()) {\n event.event(EventType.LOGIN);\n event.error(Errors.SSL_REQUIRED);\n return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.HTTPS_REQUIRED);\n }\n if (!realm.isEnabled()) {\n event.event(EventType.LOGIN_ERROR);\n event.error(Errors.REALM_DISABLED);\n return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.REALM_NOT_ENABLED);\n }\n\n if (samlRequest == null && samlResponse == null) {\n event.event(EventType.LOGIN);\n event.error(Errors.INVALID_REQUEST);\n return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.INVALID_REQUEST);\n\n }\n return null;\n }\n\n protected abstract String getBindingType();\n protected abstract boolean containsUnencryptedSignature(SAMLDocumentHolder documentHolder);\n protected abstract void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException;\n protected abstract SAMLDocumentHolder extractRequestDocument(String samlRequest);\n protected abstract SAMLDocumentHolder extractResponseDocument(String response);\n\n protected boolean isDestinationRequired() {\n return true;\n }\n\n protected KeyLocator getIDPKeyLocator() {\n List<Key> keys = new LinkedList<>();\n\n for (String signingCertificate : config.getSigningCertificates()) {\n X509Certificate cert = null;\n try {\n cert = XMLSignatureUtil.getX509CertificateFromKeyInfoString(signingCertificate.replaceAll(\"\\\\s\", \"\"));\n cert.checkValidity();\n keys.add(cert.getPublicKey());\n } catch (CertificateException e) {\n logger.warnf(\"Ignoring invalid certificate: %s\", cert);\n } catch (ProcessingException e) {\n throw new RuntimeException(e);\n }\n }\n\n return new HardcodedKeyLocator(keys);\n }\n\n public Response execute(String samlRequest, String samlResponse, String relayState, String clientId) {\n event = new EventBuilder(realm, session, clientConnection);\n Response response = basicChecks(samlRequest, samlResponse);\n if (response != null) return response;\n if (samlRequest != null) return handleSamlRequest(samlRequest, relayState);\n else return handleSamlResponse(samlResponse, relayState, clientId);\n }\n\n protected Response handleSamlRequest(String samlRequest, String relayState) {\n SAMLDocumentHolder holder = extractRequestDocument(samlRequest);\n RequestAbstractType requestAbstractType = (RequestAbstractType) holder.getSamlObject();\n // validate destination\n if (isDestinationRequired() &&\n requestAbstractType.getDestination() == null && containsUnencryptedSignature(holder)) {\n event.event(EventType.IDENTITY_PROVIDER_RESPONSE);\n event.detail(Details.REASON, Errors.MISSING_REQUIRED_DESTINATION);\n event.error(Errors.INVALID_REQUEST);\n return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.INVALID_REQUEST);\n }\n if (! destinationValidator.validate(getExpectedDestination(config.getAlias(), null), requestAbstractType.getDestination())) {\n event.event(EventType.IDENTITY_PROVIDER_RESPONSE);\n event.detail(Details.REASON, Errors.INVALID_DESTINATION);\n event.error(Errors.INVALID_SAML_RESPONSE);\n return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.INVALID_REQUEST);\n }\n if (config.isValidateSignature()) {\n try {\n verifySignature(GeneralConstants.SAML_REQUEST_KEY, holder);\n } catch (VerificationException e) {\n logger.error(\"validation failed\", e);\n event.event(EventType.IDENTITY_PROVIDER_RESPONSE);\n event.error(Errors.INVALID_SIGNATURE);\n return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.INVALID_REQUESTER);\n }\n }\n\n if (requestAbstractType instanceof LogoutRequestType) {\n logger.debug(\"** logout request\");\n event.event(EventType.LOGOUT);\n LogoutRequestType logout = (LogoutRequestType) requestAbstractType;\n return logoutRequest(logout, relayState);\n\n } else {\n event.event(EventType.LOGIN);\n event.error(Errors.INVALID_TOKEN);\n return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.INVALID_REQUEST);\n }\n }\n\n protected Response logoutRequest(LogoutRequestType request, String relayState) {\n String brokerUserId = config.getAlias() + \".\" + request.getNameID().getValue();\n if (request.getSessionIndex() == null || request.getSessionIndex().isEmpty()) {\n AtomicReference<LogoutRequestType> ref = new AtomicReference<>(request);\n session.sessions().getUserSessionByBrokerUserIdStream(realm, brokerUserId)\n .filter(userSession -> userSession.getState() != UserSessionModel.State.LOGGING_OUT &&\n userSession.getState() != UserSessionModel.State.LOGGED_OUT)\n .collect(Collectors.toList()) // collect to avoid concurrent modification as backchannelLogout removes the user sessions.\n .forEach(processLogout(ref));\n request = ref.get();\n\n } else {\n for (String sessionIndex : request.getSessionIndex()) {\n String brokerSessionId = brokerUserId + \".\" + sessionIndex;\n UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm, brokerSessionId);\n if (userSession != null) {\n if (userSession.getState() == UserSessionModel.State.LOGGING_OUT || userSession.getState() == UserSessionModel.State.LOGGED_OUT) {\n continue;\n }\n\n for(Iterator<SamlAuthenticationPreprocessor> it = SamlSessionUtils.getSamlAuthenticationPreprocessorIterator(session); it.hasNext();) {\n request = it.next().beforeProcessingLogoutRequest(request, userSession, null);\n }\n\n try {\n AuthenticationManager.backchannelLogout(session, realm, userSession, session.getContext().getUri(), clientConnection, headers, false);\n } catch (Exception e) {\n logger.warn(\"failed to do backchannel logout for userSession\", e);\n }\n }\n }\n }\n\n String issuerURL = getEntityId(session.getContext().getUri(), realm);\n SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder();\n builder.logoutRequestID(request.getID());\n builder.destination(config.getSingleLogoutServiceUrl());\n builder.issuer(issuerURL);\n JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder(session)\n .relayState(relayState);\n boolean postBinding = config.isPostBindingLogout();\n if (config.isWantAuthnRequestsSigned()) {\n KeyManager.ActiveRsaKey keys = session.keys().getActiveRsaKey(realm);\n String keyName = config.getXmlSigKeyInfoKeyNameTransformer().getKeyName(keys.getKid(), keys.getCertificate());\n binding.signWith(keyName, keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate())\n .signatureAlgorithm(provider.getSignatureAlgorithm())\n .signDocument();\n if (! postBinding && config.isAddExtensionsElementWithKeyInfo()) { // Only include extension if REDIRECT binding and signing whole SAML protocol message\n builder.addExtension(new KeycloakKeySamlExtensionGenerator(keyName));\n }\n }\n try {\n if (postBinding) {\n return binding.postBinding(builder.buildDocument()).response(config.getSingleLogoutServiceUrl());\n } else {\n return binding.redirectBinding(builder.buildDocument()).response(config.getSingleLogoutServiceUrl());\n }\n } catch (ConfigurationException e) {\n throw new RuntimeException(e);\n } catch (ProcessingException e) {\n throw new RuntimeException(e);\n } catch (IOException e) {\n throw new RuntimeException(e);\n }\n\n }\n\n private Consumer<UserSessionModel> processLogout(AtomicReference<LogoutRequestType> ref) {\n return userSession -> {\n for(Iterator<SamlAuthenticationPreprocessor> it = SamlSessionUtils.getSamlAuthenticationPreprocessorIterator(session); it.hasNext();) {\n ref.set(it.next().beforeProcessingLogoutRequest(ref.get(), userSession, null));\n }\n try {\n AuthenticationManager.backchannelLogout(session, realm, userSession, session.getContext().getUri(), clientConnection, headers, false);\n } catch (Exception e) {\n logger.warn(\"failed to do backchannel logout for userSession\", e);\n }\n };\n }\n\n private String getEntityId(UriInfo uriInfo, RealmModel realm) {\n String configEntityId = config.getEntityId();\n\n if (configEntityId == null || configEntityId.isEmpty())\n return UriBuilder.fromUri(uriInfo.getBaseUri()).path(\"realms\").path(realm.getName()).build().toString();\n else\n return configEntityId;\n }\n\n protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder holder, ResponseType responseType, String relayState, String clientId) {\n\n try {\n AuthenticationSessionModel authSession;\n if (clientId != null && ! clientId.trim().isEmpty()) {\n authSession = samlIdpInitiatedSSO(clientId);\n } else {\n authSession = callback.getAndVerifyAuthenticationSession(relayState);\n }\n session.getContext().setAuthenticationSession(authSession);\n\n KeyManager.ActiveRsaKey keys = session.keys().getActiveRsaKey(realm);\n if (! isSuccessfulSamlResponse(responseType)) {\n String statusMessage = responseType.getStatus() == null ? Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR : responseType.getStatus().getStatusMessage();\n return callback.error(statusMessage);\n }\n if (responseType.getAssertions() == null || responseType.getAssertions().isEmpty()) {\n return callback.error(Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);\n }\n\n boolean assertionIsEncrypted = AssertionUtil.isAssertionEncrypted(responseType);\n\n if (config.isWantAssertionsEncrypted() && !assertionIsEncrypted) {\n logger.error(\"The assertion is not encrypted, which is required.\");\n event.event(EventType.IDENTITY_PROVIDER_RESPONSE);\n event.error(Errors.INVALID_SAML_RESPONSE);\n return ErrorPage.error(session, authSession, Response.Status.BAD_REQUEST, Messages.INVALID_REQUESTER);\n }\n\n Element assertionElement;\n\n if (assertionIsEncrypted) {\n // This methods writes the parsed and decrypted assertion back on the responseType parameter:\n assertionElement = AssertionUtil.decryptAssertion(holder, responseType, keys.getPrivateKey());\n } else {\n /* We verify the assertion using original document to handle cases where the IdP\n includes whitespace and/or newlines inside tags. */\n assertionElement = DocumentUtil.getElement(holder.getSamlDocument(), new QName(JBossSAMLConstants.ASSERTION.get()));\n }\n\n boolean signed = AssertionUtil.isSignedElement(assertionElement);\n final boolean assertionSignatureNotExistsWhenRequired = config.isWantAssertionsSigned() && !signed;\n final boolean signatureNotValid = signed && config.isValidateSignature() && !AssertionUtil.isSignatureValid(assertionElement, getIDPKeyLocator());\n final boolean hasNoSignatureWhenRequired = ! signed && config.isValidateSignature() && ! containsUnencryptedSignature(holder);\n\n if (assertionSignatureNotExistsWhenRequired || signatureNotValid || hasNoSignatureWhenRequired) {\n logger.error(\"validation failed\");\n event.event(EventType.IDENTITY_PROVIDER_RESPONSE);\n event.error(Errors.INVALID_SIGNATURE);\n return ErrorPage.error(session, authSession, Response.Status.BAD_REQUEST, Messages.INVALID_REQUESTER);\n }\n\n AssertionType assertion = responseType.getAssertions().get(0).getAssertion();\n NameIDType subjectNameID = getSubjectNameID(assertion);\n String principal = getPrincipal(assertion);\n\n if (principal == null) {\n logger.errorf(\"no principal in assertion; expected: %s\", expectedPrincipalType());\n event.event(EventType.IDENTITY_PROVIDER_RESPONSE);\n event.error(Errors.INVALID_SAML_RESPONSE);\n return ErrorPage.error(session, authSession, Response.Status.BAD_REQUEST, Messages.INVALID_REQUESTER);\n }\n\n //Map<String, String> notes = new HashMap<>();\n BrokeredIdentityContext identity = new BrokeredIdentityContext(principal);\n identity.getContextData().put(SAML_LOGIN_RESPONSE, responseType);\n identity.getContextData().put(SAML_ASSERTION, assertion);\n identity.setAuthenticationSession(authSession);\n\n identity.setUsername(principal);\n\n //SAML Spec 2.2.2 Format is optional\n if (subjectNameID != null && subjectNameID.getFormat() != null && subjectNameID.getFormat().toString().equals(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get())) {\n identity.setEmail(subjectNameID.getValue());\n }\n\n if (config.isStoreToken()) {\n identity.setToken(samlResponse);\n }\n\n ConditionsValidator.Builder cvb = new ConditionsValidator.Builder(assertion.getID(), assertion.getConditions(), destinationValidator)\n .clockSkewInMillis(1000 * config.getAllowedClockSkew());\n try {\n String issuerURL = getEntityId(session.getContext().getUri(), realm);\n cvb.addAllowedAudience(URI.create(issuerURL));\n // getDestination has been validated to match request URL already so it matches SAML endpoint\n if (responseType.getDestination() != null) {\n cvb.addAllowedAudience(URI.create(responseType.getDestination()));\n }\n } catch (IllegalArgumentException ex) {\n // warning has been already emitted in DeploymentBuilder\n }\n if (! cvb.build().isValid()) {\n logger.error(\"Assertion expired.\");\n event.event(EventType.IDENTITY_PROVIDER_RESPONSE);\n event.error(Errors.INVALID_SAML_RESPONSE);\n return ErrorPage.error(session, authSession, Response.Status.BAD_REQUEST, Messages.EXPIRED_CODE);\n }\n\n AuthnStatementType authn = null;\n for (Object statement : assertion.getStatements()) {\n if (statement instanceof AuthnStatementType) {\n authn = (AuthnStatementType)statement;\n identity.getContextData().put(SAML_AUTHN_STATEMENT, authn);\n break;\n }\n }\n if (assertion.getAttributeStatements() != null ) {\n String email = getX500Attribute(assertion, X500SAMLProfileConstants.EMAIL);\n if (email != null)\n identity.setEmail(email);\n }\n\n String brokerUserId = config.getAlias() + \".\" + principal;\n identity.setBrokerUserId(brokerUserId);\n identity.setIdpConfig(config);\n identity.setIdp(provider);\n if (authn != null && authn.getSessionIndex() != null) {\n identity.setBrokerSessionId(identity.getBrokerUserId() + \".\" + authn.getSessionIndex());\n }\n\n return callback.authenticated(identity);\n } catch (WebApplicationException e) {\n return e.getResponse();\n } catch (Exception e) {\n throw new IdentityBrokerException(\"Could not process response from SAML identity provider.\", e);\n }\n }\n\n /**\n * If there is a client whose SAML IDP-initiated SSO URL name is set to the\n * given {@code clientUrlName}, creates a fresh authentication session for that\n * client and returns a {@link AuthenticationSessionModel} object with that session.\n * Otherwise returns \"client not found\" response.\n *\n * @param clientUrlName\n * @return see description\n */\n private AuthenticationSessionModel samlIdpInitiatedSSO(final String clientUrlName) {\n event.event(EventType.LOGIN);\n CacheControlUtil.noBackButtonCacheControlHeader();\n Optional<ClientModel> oClient = SAMLEndpoint.this.realm.getClientsStream()\n .filter(c -> Objects.equals(c.getAttribute(SamlProtocol.SAML_IDP_INITIATED_SSO_URL_NAME), clientUrlName))\n .findFirst();\n\n if (! oClient.isPresent()) {\n event.error(Errors.CLIENT_NOT_FOUND);\n Response response = ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.CLIENT_NOT_FOUND);\n throw new WebApplicationException(response);\n }\n\n LoginProtocolFactory factory = (LoginProtocolFactory) session.getKeycloakSessionFactory().getProviderFactory(LoginProtocol.class, SamlProtocol.LOGIN_PROTOCOL);\n SamlService samlService = (SamlService) factory.createProtocolEndpoint(SAMLEndpoint.this.realm, event);\n ResteasyProviderFactory.getInstance().injectProperties(samlService);\n AuthenticationSessionModel authSession = samlService.getOrCreateLoginSessionForIdpInitiatedSso(session, SAMLEndpoint.this.realm, oClient.get(), null);\n if (authSession == null) {\n event.error(Errors.INVALID_REDIRECT_URI);\n Response response = ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.INVALID_REDIRECT_URI);\n throw new WebApplicationException(response);\n }\n\n return authSession;\n }\n\n private boolean isSuccessfulSamlResponse(ResponseType responseType) {\n return responseType != null\n && responseType.getStatus() != null\n && responseType.getStatus().getStatusCode() != null\n && responseType.getStatus().getStatusCode().getValue() != null\n && Objects.equals(responseType.getStatus().getStatusCode().getValue().toString(), JBossSAMLURIConstants.STATUS_SUCCESS.get());\n }\n\n public Response handleSamlResponse(String samlResponse, String relayState, String clientId) {\n SAMLDocumentHolder holder = extractResponseDocument(samlResponse);\n if (holder == null) {\n event.event(EventType.IDENTITY_PROVIDER_RESPONSE);\n event.detail(Details.REASON, Errors.INVALID_SAML_DOCUMENT);\n event.error(Errors.INVALID_SAML_RESPONSE);\n return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.INVALID_FEDERATED_IDENTITY_ACTION);\n }\n StatusResponseType statusResponse = (StatusResponseType)holder.getSamlObject();\n // validate destination\n if (isDestinationRequired()\n && statusResponse.getDestination() == null && containsUnencryptedSignature(holder)) {\n event.event(EventType.IDENTITY_PROVIDER_RESPONSE);\n event.detail(Details.REASON, Errors.MISSING_REQUIRED_DESTINATION);\n event.error(Errors.INVALID_SAML_LOGOUT_RESPONSE);\n return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.INVALID_REQUEST);\n }\n if (! destinationValidator.validate(getExpectedDestination(config.getAlias(), clientId), statusResponse.getDestination())) {\n event.event(EventType.IDENTITY_PROVIDER_RESPONSE);\n event.detail(Details.REASON, Errors.INVALID_DESTINATION);\n event.error(Errors.INVALID_SAML_RESPONSE);\n return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.INVALID_REQUEST);\n }\n if (config.isValidateSignature()) {\n try {\n verifySignature(GeneralConstants.SAML_RESPONSE_KEY, holder);\n } catch (VerificationException e) {\n logger.error(\"validation failed\", e);\n event.event(EventType.IDENTITY_PROVIDER_RESPONSE);\n event.error(Errors.INVALID_SIGNATURE);\n return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.INVALID_FEDERATED_IDENTITY_ACTION);\n }\n }\n if (statusResponse instanceof ResponseType) {\n return handleLoginResponse(samlResponse, holder, (ResponseType)statusResponse, relayState, clientId);\n\n } else {\n // todo need to check that it is actually a LogoutResponse\n return handleLogoutResponse(holder, statusResponse, relayState);\n }\n //throw new RuntimeException(\"Unknown response type\");\n\n }\n\n protected Response handleLogoutResponse(SAMLDocumentHolder holder, StatusResponseType responseType, String relayState) {\n if (relayState == null) {\n logger.error(\"no valid user session\");\n event.event(EventType.LOGOUT);\n event.error(Errors.USER_SESSION_NOT_FOUND);\n return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);\n }\n UserSessionModel userSession = session.sessions().getUserSession(realm, relayState);\n if (userSession == null) {\n logger.error(\"no valid user session\");\n event.event(EventType.LOGOUT);\n event.error(Errors.USER_SESSION_NOT_FOUND);\n return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);\n }\n if (userSession.getState() != UserSessionModel.State.LOGGING_OUT) {\n logger.error(\"usersession in different state\");\n event.event(EventType.LOGOUT);\n event.error(Errors.USER_SESSION_NOT_FOUND);\n return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.SESSION_NOT_ACTIVE);\n }\n return AuthenticationManager.finishBrowserLogout(session, realm, userSession, session.getContext().getUri(), clientConnection, headers);\n }\n\n private String getExpectedDestination(String providerAlias, String clientId) {\n if(clientId != null) {\n return session.getContext().getUri().getAbsolutePath().toString();\n }\n return Urls.identityProviderAuthnResponse(session.getContext().getUri().getBaseUri(), providerAlias, realm.getName()).toString();\n }\n }\n\n protected class PostBinding extends Binding {\n @Override\n protected boolean containsUnencryptedSignature(SAMLDocumentHolder documentHolder) {\n NodeList nl = documentHolder.getSamlDocument().getElementsByTagNameNS(XMLSignature.XMLNS, \"Signature\");\n return (nl != null && nl.getLength() > 0);\n }\n\n @Override\n protected void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException {\n if ((! containsUnencryptedSignature(documentHolder)) && (documentHolder.getSamlObject() instanceof ResponseType)) {\n ResponseType responseType = (ResponseType) documentHolder.getSamlObject();\n List<ResponseType.RTChoiceType> assertions = responseType.getAssertions();\n if (! assertions.isEmpty() ) {\n // Only relax verification if the response is an authnresponse and contains (encrypted/plaintext) assertion.\n // In that case, signature is validated on assertion element\n return;\n }\n }\n SamlProtocolUtils.verifyDocumentSignature(documentHolder.getSamlDocument(), getIDPKeyLocator());\n }\n\n @Override\n protected SAMLDocumentHolder extractRequestDocument(String samlRequest) {\n return SAMLRequestParser.parseRequestPostBinding(samlRequest);\n }\n @Override\n protected SAMLDocumentHolder extractResponseDocument(String response) {\n byte[] samlBytes = PostBindingUtil.base64Decode(response);\n return SAMLRequestParser.parseResponseDocument(samlBytes);\n }\n\n @Override\n protected String getBindingType() {\n return SamlProtocol.SAML_POST_BINDING;\n }\n }\n\n protected class RedirectBinding extends Binding {\n @Override\n protected boolean containsUnencryptedSignature(SAMLDocumentHolder documentHolder) {\n MultivaluedMap<String, String> encodedParams = session.getContext().getUri().getQueryParameters(false);\n String algorithm = encodedParams.getFirst(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);\n String signature = encodedParams.getFirst(GeneralConstants.SAML_SIGNATURE_REQUEST_KEY);\n return algorithm != null && signature != null;\n }\n\n @Override\n protected void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException {\n KeyLocator locator = getIDPKeyLocator();\n SamlProtocolUtils.verifyRedirectSignature(documentHolder, locator, session.getContext().getUri(), key);\n }\n\n @Override\n protected SAMLDocumentHolder extractRequestDocument(String samlRequest) {\n return SAMLRequestParser.parseRequestRedirectBinding(samlRequest);\n }\n\n @Override\n protected SAMLDocumentHolder extractResponseDocument(String response) {\n return SAMLRequestParser.parseResponseRedirectBinding(response);\n }\n\n @Override\n protected String getBindingType() {\n return SamlProtocol.SAML_REDIRECT_BINDING;\n }\n\n }\n\n private String getX500Attribute(AssertionType assertion, X500SAMLProfileConstants attribute) {\n return getFirstMatchingAttribute(assertion, attribute::correspondsTo);\n }\n\n private String getAttributeByName(AssertionType assertion, String name) {\n return getFirstMatchingAttribute(assertion, attribute -> Objects.equals(attribute.getName(), name));\n }\n\n private String getAttributeByFriendlyName(AssertionType assertion, String friendlyName) {\n return getFirstMatchingAttribute(assertion, attribute -> Objects.equals(attribute.getFriendlyName(), friendlyName));\n }\n\n private String getPrincipal(AssertionType assertion) {\n\n SamlPrincipalType principalType = config.getPrincipalType();\n\n if (principalType == null || principalType.equals(SamlPrincipalType.SUBJECT)) {\n NameIDType subjectNameID = getSubjectNameID(assertion);\n return subjectNameID != null ? subjectNameID.getValue() : null;\n } else if (principalType.equals(SamlPrincipalType.ATTRIBUTE)) {\n return getAttributeByName(assertion, config.getPrincipalAttribute());\n } else {\n return getAttributeByFriendlyName(assertion, config.getPrincipalAttribute());\n }\n\n }\n\n private String getFirstMatchingAttribute(AssertionType assertion, Predicate<AttributeType> predicate) {\n return assertion.getAttributeStatements().stream()\n .map(AttributeStatementType::getAttributes)\n .flatMap(Collection::stream)\n .map(AttributeStatementType.ASTChoiceType::getAttribute)\n .filter(predicate)\n .map(AttributeType::getAttributeValue)\n .flatMap(Collection::stream)\n .findFirst()\n .map(Object::toString)\n .orElse(null);\n }\n\n private String expectedPrincipalType() {\n SamlPrincipalType principalType = config.getPrincipalType();\n switch (principalType) {\n case SUBJECT:\n return principalType.name();\n case ATTRIBUTE:\n case FRIENDLY_ATTRIBUTE:\n return String.format(\"%s(%s)\", principalType.name(), config.getPrincipalAttribute());\n default:\n return null;\n }\n }\n\n private NameIDType getSubjectNameID(final AssertionType assertion) {\n SubjectType subject = assertion.getSubject();\n SubjectType.STSubType subType = subject.getSubType();\n return subType != null ? (NameIDType) subType.getBaseID() : null;\n }\n}\n\n", "input": "", "output": "【漏洞类型】信息泄露\n【严重性】Medium\n【漏洞摘要】原始代码在处理SAML注销请求时,将用户的principal(标识符)包含在brokerSessionId中。由于brokerSessionId被用于识别和管理用户会话,攻击者可能通过分析brokerSessionId来获取用户的principal信息,从而泄露用户的标识符。\n【关键代码模式】\n- `String brokerUserId = config.getAlias() + \".\" + request.getNameID().getValue();`\n- `String brokerSessionId = brokerUserId + \".\" + sessionIndex;`\n- `String brokerUserId = config.getAlias() + \".\" + principal;`\n- `String brokerSessionId = identity.getBrokerUserId() + \".\" + authn.getSessionIndex();`\n【修复方法说明】修复方法通过移除principal从brokerSessionId中,避免了用户标识符的泄露。这有助于保护用户的隐私和安全,因为principal信息不再直接暴露在会话标识符中。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nbrowserup-proxy-rest/src/main/java/com/browserup/bup/rest/validation/LongPositiveConstraint.java\n### 代码内容:\npackage com.browserup.bup.rest.validation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport javax.validation.Constraint;\nimport javax.validation.ConstraintValidator;\nimport javax.validation.ConstraintValidatorContext;\nimport javax.validation.Payload;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n@Retention(RetentionPolicy.RUNTIME)\n@Constraint(validatedBy = { LongPositiveConstraint.LongPositiveValidator.class })\npublic @interface LongPositiveConstraint {\n\n String message() default \"\";\n\n String paramName() default \"\";\n\n Class<?>[] groups() default {};\n\n int value();\n\n Class<? extends Payload>[] payload() default {};\n\n class LongPositiveValidator implements ConstraintValidator<LongPositiveConstraint, String> {\n private static final Logger LOG = LoggerFactory.getLogger(LongPositiveValidator.class);\n\n @Override\n public void initialize(LongPositiveConstraint constraintAnnotation) {\n }\n\n @Override\n public boolean isValid(String value, ConstraintValidatorContext context) {\n long longValue = 0;\n boolean failed = false;\n String errorMessage = \"\";\n try {\n longValue = Long.parseLong(value);\n } catch (NumberFormatException ex) {\n failed = true;\n errorMessage = String.format(\"Invalid integer value: '%s'\", value);\n }\n\n if (!failed && longValue < 0) {\n failed = true;\n errorMessage = String.format(\"Expected positive integer value, got: '%s'\", value);\n }\n\n if (!failed) {\n return true;\n }\n\n LOG.warn(errorMessage);\n context.buildConstraintViolationWithTemplate(errorMessage).addConstraintViolation();\n\n return false;\n }\n }\n}\n## 内容2\n### 文件路径:\nbrowserup-proxy-rest/src/main/java/com/browserup/bup/rest/validation/NotBlankConstraint.java\n### 代码内容:\npackage com.browserup.bup.rest.validation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport javax.validation.Constraint;\nimport javax.validation.ConstraintValidator;\nimport javax.validation.ConstraintValidatorContext;\nimport javax.validation.Payload;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n@Retention(RetentionPolicy.RUNTIME)\n@Constraint(validatedBy = { NotBlankConstraint.NotBlankValidator.class })\npublic @interface NotBlankConstraint {\n\n String message() default \"\";\n\n String paramName() default \"\";\n\n Class<?>[] groups() default {};\n\n Class<? extends Payload>[] payload() default {};\n\n class NotBlankValidator implements ConstraintValidator<NotBlankConstraint, Object> {\n private static final Logger LOG = LoggerFactory.getLogger(NotBlankValidator.class);\n\n @Override\n public void initialize(NotBlankConstraint constraintAnnotation) {\n }\n\n @Override\n public boolean isValid(Object value, ConstraintValidatorContext context) {\n if (value != null && StringUtils.isNotEmpty(String.valueOf(value))) {\n return true;\n }\n String errorMessage = String.format(\"Expected not empty value, got '%s'\", value);\n LOG.warn(errorMessage);\n\n context.buildConstraintViolationWithTemplate(errorMessage).addConstraintViolation();\n return false;\n }\n }\n}\n## 内容3\n### 文件路径:\nbrowserup-proxy-rest/src/main/java/com/browserup/bup/rest/validation/PatternConstraint.java\n### 代码内容:\npackage com.browserup.bup.rest.validation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.util.regex.Pattern;\n\nimport javax.validation.Constraint;\nimport javax.validation.ConstraintValidator;\nimport javax.validation.ConstraintValidatorContext;\nimport javax.validation.Payload;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n@Retention(RetentionPolicy.RUNTIME)\n@Constraint(validatedBy = { PatternConstraint.PatternValidator.class })\npublic @interface PatternConstraint {\n\n String message() default \"\";\n\n String paramName() default \"\";\n\n Class<?>[] groups() default {};\n\n Class<? extends Payload>[] payload() default {};\n\n class PatternValidator implements ConstraintValidator<PatternConstraint, String> {\n private static final Logger LOG = LoggerFactory.getLogger(PatternValidator.class);\n\n @Override\n public void initialize(PatternConstraint constraintAnnotation) {\n }\n\n @Override\n public boolean isValid(String value, ConstraintValidatorContext context) {\n if (StringUtils.isEmpty(value)) {\n return true;\n }\n\n try {\n Pattern.compile(value);\n return true;\n } catch (Exception ex) {\n String errorMessage = String.format(\"URL parameter '%s' is not a valid regexp\", value);\n LOG.warn(errorMessage);\n\n context.buildConstraintViolationWithTemplate(errorMessage).addConstraintViolation();\n }\n return false;\n }\n }\n}\n## 内容4\n### 文件路径:\nbrowserup-proxy-rest/src/main/java/com/browserup/bup/rest/validation/PortWithExistingProxyConstraint.java\n### 代码内容:\npackage com.browserup.bup.rest.validation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport javax.validation.Constraint;\nimport javax.validation.ConstraintValidator;\nimport javax.validation.ConstraintValidatorContext;\nimport javax.validation.Payload;\nimport javax.ws.rs.core.Context;\n\nimport com.browserup.bup.proxy.ProxyManager;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n@Retention(RetentionPolicy.RUNTIME)\n@Constraint(validatedBy = { PortWithExistingProxyConstraint.PortWithExistingProxyConstraintValidator.class })\npublic @interface PortWithExistingProxyConstraint {\n\n String message() default \"\";\n\n String paramName() default \"port\";\n\n Class<?>[] groups() default {};\n\n Class<? extends Payload>[] payload() default {};\n\n class PortWithExistingProxyConstraintValidator\n implements ConstraintValidator<PortWithExistingProxyConstraint, Integer> {\n private static final Logger LOG = LoggerFactory.getLogger(PortWithExistingProxyConstraintValidator.class);\n private static final String PARAM_NAME = \"proxy port\";\n\n private final ProxyManager proxyManager;\n\n public PortWithExistingProxyConstraintValidator(@Context ProxyManager proxyManager) {\n this.proxyManager = proxyManager;\n }\n\n @Override\n public void initialize(PortWithExistingProxyConstraint constraintAnnotation) {\n }\n\n @Override\n public boolean isValid(Integer value, ConstraintValidatorContext context) {\n if (proxyManager.get(value) != null) {\n return true;\n }\n\n String errorMessage = String.format(\"No proxy server found for specified port %d\", value);\n LOG.warn(errorMessage);\n\n context.buildConstraintViolationWithTemplate(errorMessage).addPropertyNode(PARAM_NAME).addConstraintViolation();\n return false;\n }\n }\n}\n", "input": "", "output": "【漏洞类型】Java EL Injection RCE(远程代码执行)漏洞\n【严重性】High(高)\n【漏洞摘要】此代码通过未验证的用户输入构建日志消息和约束验证错误信息,可能导致Java EL(表达式语言)注入攻击,从而执行恶意代码。具体表现为在 `LongPositiveConstraint`、`NotBlankConstraint`、`PatternConstraint` 和 `PortWithExistingProxyConstraint` 类中,`String.format` 方法直接使用了未验证的用户输入作为日志消息和错误信息的一部分,而这些输入可能包含恶意的EL表达式。\n【关键代码模式】\n- `String.format` 方法使用未验证的用户输入。\n- `Long.parseLong` 和 `Pattern.compile` 方法处理可能的恶意输入。\n- `context.buildConstraintViolationWithTemplate(errorMessage).addConstraintViolation();` 方法使用日志消息作为模板。\n【修复方法说明】修复方法通过引入 `MessageSanitizer.escape` 函数来转义可能的EL注入字符,从而防止用户输入中的恶意代码被执行。最佳实践是始终对用户输入进行验证和转义,特别是在构建日志消息或约束验证错误信息时。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/main/java/com/synopsys/integration/jenkins/coverity/SynopsysCoverityCredentialsHelper.java\n### 代码内容:\npackage com.synopsys.integration.jenkins.coverity;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Collections;\nimport java.util.Optional;\n\nimport org.jenkinsci.plugins.plaincredentials.FileCredentials;\n\nimport com.cloudbees.plugins.credentials.CredentialsMatcher;\nimport com.cloudbees.plugins.credentials.CredentialsMatchers;\nimport com.cloudbees.plugins.credentials.common.StandardCredentials;\nimport com.cloudbees.plugins.credentials.common.StandardListBoxModel;\nimport com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl;\nimport com.synopsys.integration.coverity.authentication.AuthenticationKeyFile;\nimport com.synopsys.integration.coverity.authentication.AuthenticationKeyFileUtility;\nimport com.synopsys.integration.jenkins.wrapper.JenkinsWrapper;\nimport com.synopsys.integration.jenkins.wrapper.SynopsysCredentialsHelper;\nimport com.synopsys.integration.log.IntLogger;\nimport com.synopsys.integration.log.SilentIntLogger;\nimport com.synopsys.integration.rest.credentials.CredentialsBuilder;\n\nimport hudson.security.ACL;\nimport hudson.util.ListBoxModel;\nimport hudson.util.Secret;\nimport jenkins.model.Jenkins;\n\npublic class SynopsysCoverityCredentialsHelper extends SynopsysCredentialsHelper {\n public static final Class<FileCredentials> AUTH_KEY_FILE_CREDENTIALS_CLASS = FileCredentials.class;\n public static final CredentialsMatcher AUTH_KEY_FILE_CREDENTIALS = CredentialsMatchers.instanceOf(AUTH_KEY_FILE_CREDENTIALS_CLASS);\n public static final CredentialsMatcher SUPPORTED_CREDENTIALS_TYPES = CredentialsMatchers.anyOf(AUTH_KEY_FILE_CREDENTIALS, CredentialsMatchers.instanceOf(USERNAME_PASSWORD_CREDENTIALS_CLASS));\n private final IntLogger logger;\n private final JenkinsWrapper jenkinsWrapper;\n\n public SynopsysCoverityCredentialsHelper(IntLogger logger, JenkinsWrapper jenkinsWrapper) {\n super(jenkinsWrapper);\n this.jenkinsWrapper = jenkinsWrapper;\n this.logger = logger;\n }\n\n public static SynopsysCoverityCredentialsHelper silentHelper(JenkinsWrapper jenkinsWrapper) {\n return new SynopsysCoverityCredentialsHelper(new SilentIntLogger(), jenkinsWrapper);\n }\n\n public ListBoxModel listSupportedCredentials() {\n Optional<Jenkins> optionalJenkins = jenkinsWrapper.getJenkins();\n if (optionalJenkins.isPresent()) {\n Jenkins jenkins = optionalJenkins.get();\n return new StandardListBoxModel()\n .includeEmptyValue()\n .includeMatchingAs(ACL.SYSTEM, jenkins, StandardCredentials.class, Collections.emptyList(), SUPPORTED_CREDENTIALS_TYPES);\n } else {\n return new StandardListBoxModel()\n .includeEmptyValue();\n }\n }\n\n public Optional<FileCredentials> getAuthenticationKeyFileCredentialsById(String credentialsId) {\n return getCredentialsById(AUTH_KEY_FILE_CREDENTIALS_CLASS, credentialsId);\n }\n\n public Optional<String> getCoverityUsernameById(String credentialsId) {\n return getIntegrationCredentialsById(credentialsId)\n .getUsername();\n }\n\n public Optional<String> getCoverityPassphraseById(String credentialsId) {\n return getUsernamePasswordCredentialsById(credentialsId)\n .map(UsernamePasswordCredentialsImpl::getPassword)\n .map(Secret::getPlainText);\n }\n\n @Override\n public com.synopsys.integration.rest.credentials.Credentials getIntegrationCredentialsById(String credentialsId) {\n Optional<UsernamePasswordCredentialsImpl> possibleUsernamePasswordCredentials = getUsernamePasswordCredentialsById(credentialsId);\n Optional<FileCredentials> possibleAuthKeyCredentials = getAuthenticationKeyFileCredentialsById(credentialsId);\n CredentialsBuilder credentialsBuilder = com.synopsys.integration.rest.credentials.Credentials.newBuilder();\n\n if (possibleUsernamePasswordCredentials.isPresent()) {\n UsernamePasswordCredentialsImpl usernamePasswordCredentials = possibleUsernamePasswordCredentials.get();\n\n credentialsBuilder.setUsernameAndPassword(usernamePasswordCredentials.getUsername(), usernamePasswordCredentials.getPassword().getPlainText());\n }\n\n if (possibleAuthKeyCredentials.isPresent()) {\n FileCredentials fileCredentials = possibleAuthKeyCredentials.get();\n AuthenticationKeyFileUtility authenticationKeyFileUtility = AuthenticationKeyFileUtility.defaultUtility();\n\n try (InputStream keyFileInputStream = fileCredentials.getContent()) {\n AuthenticationKeyFile authenticationKeyFile = authenticationKeyFileUtility.readAuthenticationKeyFile(keyFileInputStream);\n\n credentialsBuilder.setUsernameAndPassword(authenticationKeyFile.username, authenticationKeyFile.key);\n } catch (IOException e) {\n logger.trace(\"Could not parse authentication key file with credentials id \" + credentialsId + \" because: \", e);\n }\n\n }\n\n return credentialsBuilder.build();\n }\n\n}\n\n## 内容2\n### 文件路径:\nsrc/main/java/com/synopsys/integration/jenkins/coverity/extensions/CheckForIssuesInView.java\n### 代码内容:\npackage com.synopsys.integration.jenkins.coverity.extensions;\n\nimport org.kohsuke.stapler.DataBoundConstructor;\nimport org.kohsuke.stapler.QueryParameter;\nimport org.slf4j.LoggerFactory;\n\nimport com.synopsys.integration.jenkins.annotations.HelpMarkdown;\nimport com.synopsys.integration.jenkins.coverity.extensions.buildstep.CoverityBuildStep;\nimport com.synopsys.integration.jenkins.coverity.extensions.utils.IssueViewFieldHelper;\nimport com.synopsys.integration.jenkins.extensions.JenkinsSelectBoxEnum;\nimport com.synopsys.integration.log.Slf4jIntLogger;\n\nimport hudson.Extension;\nimport hudson.RelativePath;\nimport hudson.model.AbstractDescribableImpl;\nimport hudson.model.Descriptor;\nimport hudson.util.ListBoxModel;\n\npublic class CheckForIssuesInView extends AbstractDescribableImpl<CheckForIssuesInView> {\n // Jenkins directly serializes the names of the fields, so they are an important part of the plugin's API.\n // Be aware by changing a field name, you will also need to change these strings and will likely break previous implementations.\n // Jenkins Common provides convenient access to the XSTREAM instance that Jenkins uses to serialize the classes, you can use the serialization methods on that class to rename fields without breaking them.\n // --rotte MAY 2021\n public static final String FIELD_VIEW_NAME = \"viewName\";\n public static final String FIELD_BUILD_STATUS_FOR_ISSUES = \"buildStatusForIssues\";\n public static final String PATH_TO_COVERITY_BUILD_STEP = \"..\";\n\n @HelpMarkdown(\"Specify the name of the Coverity view that you would like to check for issues. \\r\\n\"\n + \"The resulting view name is stored in the $COV_VIEW environment variable, and affects checking for issues in both the full and incremental analysis, if configured.\")\n private final String viewName;\n @HelpMarkdown(\"Specify the build status to set if issues are found in the configured view.\")\n private final BuildStatus buildStatusForIssues;\n\n @DataBoundConstructor\n public CheckForIssuesInView(String viewName, String buildStatusForIssues) {\n this.viewName = viewName;\n this.buildStatusForIssues = BuildStatus.valueOf(buildStatusForIssues);\n }\n\n public BuildStatus getBuildStatusForIssues() {\n return buildStatusForIssues;\n }\n\n public String getViewName() {\n return viewName;\n }\n\n @Override\n public DescriptorImpl getDescriptor() {\n return (DescriptorImpl) super.getDescriptor();\n }\n\n @Extension\n public static class DescriptorImpl extends Descriptor<CheckForIssuesInView> {\n private final IssueViewFieldHelper issueViewFieldHelper;\n\n public DescriptorImpl() {\n super(CheckForIssuesInView.class);\n load();\n Slf4jIntLogger slf4jIntLogger = new Slf4jIntLogger(LoggerFactory.getLogger(CheckForIssuesInView.class));\n issueViewFieldHelper = new IssueViewFieldHelper(slf4jIntLogger);\n }\n\n public ListBoxModel doFillViewNameItems(\n @RelativePath(PATH_TO_COVERITY_BUILD_STEP) @QueryParameter(CoverityBuildStep.FIELD_COVERITY_INSTANCE_URL) String coverityInstanceUrl,\n @RelativePath(PATH_TO_COVERITY_BUILD_STEP) @QueryParameter(CoverityBuildStep.FIELD_OVERRIDE_CREDENTIALS) Boolean overrideDefaultCredentials,\n @RelativePath(PATH_TO_COVERITY_BUILD_STEP) @QueryParameter(CoverityBuildStep.FIELD_CREDENTIALS_ID) String credentialsId,\n @QueryParameter(\"updateNow\") boolean updateNow\n ) throws InterruptedException {\n if (updateNow) {\n issueViewFieldHelper.updateNow(coverityInstanceUrl, overrideDefaultCredentials, credentialsId);\n }\n return issueViewFieldHelper.getViewNamesForListBox(coverityInstanceUrl, overrideDefaultCredentials, credentialsId);\n }\n\n public ListBoxModel doFillBuildStatusForIssuesItems() {\n return JenkinsSelectBoxEnum.toListBoxModel(BuildStatus.values());\n }\n\n }\n\n}\n\n## 内容3\n### 文件路径:\nsrc/main/java/com/synopsys/integration/jenkins/coverity/extensions/buildstep/CoverityBuildStep.java\n### 代码内容:\npackage com.synopsys.integration.jenkins.coverity.extensions.buildstep;\n\nimport java.io.IOException;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.kohsuke.stapler.DataBoundConstructor;\nimport org.kohsuke.stapler.DataBoundSetter;\nimport org.kohsuke.stapler.QueryParameter;\nimport org.slf4j.LoggerFactory;\n\nimport com.synopsys.integration.jenkins.annotations.HelpMarkdown;\nimport com.synopsys.integration.jenkins.coverity.SynopsysCoverityCredentialsHelper;\nimport com.synopsys.integration.jenkins.coverity.extensions.CheckForIssuesInView;\nimport com.synopsys.integration.jenkins.coverity.extensions.CleanUpAction;\nimport com.synopsys.integration.jenkins.coverity.extensions.ConfigureChangeSetPatterns;\nimport com.synopsys.integration.jenkins.coverity.extensions.OnCommandFailure;\nimport com.synopsys.integration.jenkins.coverity.extensions.global.CoverityConnectInstance;\nimport com.synopsys.integration.jenkins.coverity.extensions.utils.CoverityConnectionFieldHelper;\nimport com.synopsys.integration.jenkins.coverity.extensions.utils.ProjectStreamFieldHelper;\nimport com.synopsys.integration.jenkins.coverity.stepworkflow.CoverityWorkflowStepFactory;\nimport com.synopsys.integration.jenkins.extensions.JenkinsIntLogger;\nimport com.synopsys.integration.jenkins.extensions.JenkinsSelectBoxEnum;\nimport com.synopsys.integration.jenkins.wrapper.JenkinsVersionHelper;\nimport com.synopsys.integration.jenkins.wrapper.JenkinsWrapper;\nimport com.synopsys.integration.log.Slf4jIntLogger;\n\nimport hudson.Extension;\nimport hudson.FilePath;\nimport hudson.Launcher;\nimport hudson.model.AbstractBuild;\nimport hudson.model.AbstractProject;\nimport hudson.model.BuildListener;\nimport hudson.tasks.BuildStepDescriptor;\nimport hudson.tasks.BuildStepMonitor;\nimport hudson.tasks.Builder;\nimport hudson.util.ComboBoxModel;\nimport hudson.util.FormValidation;\nimport hudson.util.ListBoxModel;\n\npublic class CoverityBuildStep extends Builder {\n // Jenkins directly serializes the names of the fields, so they are an important part of the plugin's API.\n public static final String FIELD_COVERITY_INSTANCE_URL = \"coverityInstanceUrl\";\n public static final String FIELD_PROJECT_NAME = \"projectName\";\n public static final String FIELD_STREAM_NAME = \"streamName\";\n public static final String NESTED_DESCRIPTOR_CHECK_FOR_ISSUES = \"checkForIssuesInView\";\n public static final String NESTED_DESCRIPTOR_CONFIGURE_CHANGE_SET_PATTERNS = \"configureChangeSetPatterns\";\n public static final String NESTED_DESCRIPTOR_COVERITY_RUN_CONFIGURATION = \"coverityRunConfiguration\";\n public static final String FIELD_ON_COMMAND_FAILURE = \"onCommandFailure\";\n public static final String FIELD_CLEAN_UP_ACTION = \"cleanUpAction\";\n public static final String FIELD_OVERRIDE_CREDENTIALS = \"overrideDefaultCredentials\";\n public static final String FIELD_CREDENTIALS_ID = \"credentialsId\";\n\n @HelpMarkdown(\"Specify which Synopsys Coverity connect instance to run this job against. \\r\\n\"\n + \"The resulting Synopsys Coverity connect instance URL is stored in the $COV_URL environment variable, and will affect both the full and incremental analysis.\")\n private final String coverityInstanceUrl;\n\n @HelpMarkdown(\"Specify the name of the Coverity project. \\r\\n\"\n + \"The resulting project name is stored in the $COV_PROJECT environment variable, and will affect both the full and incremental analysis.\")\n private final String projectName;\n\n @HelpMarkdown(\"Specify the name of the Coverity stream that you would like to use for the commands. \\r\\n\"\n + \"The resulting stream name is stored in the $COV_STREAM environment variable, and will affect both the full and incremental analysis.\")\n private final String streamName;\n\n private final CheckForIssuesInView checkForIssuesInView;\n\n private final ConfigureChangeSetPatterns configureChangeSetPatterns;\n\n private final CoverityRunConfiguration coverityRunConfiguration;\n\n @HelpMarkdown(\"Specify the action to take if a Coverity static analysis command fails.\")\n private final OnCommandFailure onCommandFailure;\n\n // Any field set by a DataBoundSetter should be explicitly declared as @Nullable to avoid accidental NPEs -- rotte 10/21/2019\n @Nullable\n @HelpMarkdown(\"Specify the clean up action to perform on a successful execution. \\r\\n\"\n + \"Will either persist or delete the intermediate directory created by the specified capture type.\")\n private CleanUpAction cleanUpAction;\n\n @Nullable\n @HelpMarkdown(\"Specify the credentials to use with the Synopsys Coverity connect instance.\")\n private String credentialsId;\n\n @Nullable\n private Boolean overrideDefaultCredentials;\n\n @DataBoundConstructor\n public CoverityBuildStep(String coverityInstanceUrl, String onCommandFailure, String projectName, String streamName, CheckForIssuesInView checkForIssuesInView,\n ConfigureChangeSetPatterns configureChangeSetPatterns, CoverityRunConfiguration coverityRunConfiguration) {\n this.coverityInstanceUrl = coverityInstanceUrl;\n this.projectName = projectName;\n this.streamName = streamName;\n this.checkForIssuesInView = checkForIssuesInView;\n this.configureChangeSetPatterns = configureChangeSetPatterns;\n this.coverityRunConfiguration = coverityRunConfiguration;\n this.onCommandFailure = OnCommandFailure.valueOf(onCommandFailure);\n }\n\n public CleanUpAction getCleanUpAction() {\n return cleanUpAction;\n }\n\n @DataBoundSetter\n public void setCleanUpAction(CleanUpAction cleanUpAction) {\n this.cleanUpAction = cleanUpAction;\n }\n\n public String getCredentialsId() {\n return credentialsId;\n }\n\n @DataBoundSetter\n public void setCredentialsId(String credentialsId) {\n this.credentialsId = credentialsId;\n }\n\n public Boolean getOverrideDefaultCredentials() {\n return overrideDefaultCredentials;\n }\n\n @DataBoundSetter\n public void setOverrideDefaultCredentials(Boolean overrideDefaultCredentials) {\n this.overrideDefaultCredentials = overrideDefaultCredentials;\n }\n\n public String getCoverityInstanceUrl() {\n return coverityInstanceUrl;\n }\n\n public OnCommandFailure getOnCommandFailure() {\n return onCommandFailure;\n }\n\n public ConfigureChangeSetPatterns getConfigureChangeSetPatterns() {\n return configureChangeSetPatterns;\n }\n\n public CheckForIssuesInView getCheckForIssuesInView() {\n return checkForIssuesInView;\n }\n\n public String getProjectName() {\n return projectName;\n }\n\n public String getStreamName() {\n return streamName;\n }\n\n public CoverityRunConfiguration getCoverityRunConfiguration() {\n return coverityRunConfiguration;\n }\n\n @Override\n public BuildStepMonitor getRequiredMonitorService() {\n return BuildStepMonitor.NONE;\n }\n\n @Override\n public DescriptorImpl getDescriptor() {\n return (DescriptorImpl) super.getDescriptor();\n }\n\n @Override\n public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException {\n String remoteWorkingDirectoryPath = computeRemoteWorkingDirectory(coverityRunConfiguration, build.getWorkspace(), build.getProject());\n\n CoverityWorkflowStepFactory coverityWorkflowStepFactory = new CoverityWorkflowStepFactory(build.getEnvironment(listener), build.getBuiltOn(), launcher, listener);\n JenkinsIntLogger logger = coverityWorkflowStepFactory.getOrCreateLogger();\n JenkinsVersionHelper jenkinsVersionHelper = JenkinsWrapper.initializeFromJenkinsJVM().getVersionHelper();\n\n String resolvedCredentialsId;\n if (Boolean.TRUE.equals(overrideDefaultCredentials)) {\n resolvedCredentialsId = credentialsId;\n } else {\n CoverityConnectInstance coverityConnectInstance = coverityWorkflowStepFactory.getCoverityConnectInstanceFromUrl(coverityInstanceUrl);\n resolvedCredentialsId = coverityConnectInstance.getDefaultCredentialsId();\n }\n\n CoverityBuildStepWorkflow coverityBuildStepWorkflow = new CoverityBuildStepWorkflow(\n logger,\n jenkinsVersionHelper,\n () -> coverityWorkflowStepFactory.getWebServiceFactoryFromUrl(coverityInstanceUrl, resolvedCredentialsId),\n coverityWorkflowStepFactory,\n build,\n remoteWorkingDirectoryPath,\n coverityInstanceUrl,\n resolvedCredentialsId,\n projectName,\n streamName,\n coverityRunConfiguration,\n configureChangeSetPatterns,\n checkForIssuesInView,\n onCommandFailure,\n cleanUpAction\n );\n\n return coverityBuildStepWorkflow.perform();\n }\n\n private String computeRemoteWorkingDirectory(CoverityRunConfiguration coverityRunConfiguration, FilePath buildWorkspace, AbstractProject<?, ?> project) {\n boolean isDefaultCoverityWorkflow = CoverityRunConfiguration.RunConfigurationType.SIMPLE.equals(coverityRunConfiguration.getRunConFigurationType());\n String customWorkingDirectory = isDefaultCoverityWorkflow ? ((SimpleCoverityRunConfiguration) coverityRunConfiguration).getCustomWorkingDirectory() : null;\n\n if (StringUtils.isNotBlank(customWorkingDirectory)) {\n return customWorkingDirectory;\n } else if (buildWorkspace != null) {\n return buildWorkspace.getRemote();\n } else {\n return project.getCustomWorkspace();\n }\n }\n\n @Extension\n public static class DescriptorImpl extends BuildStepDescriptor<Builder> {\n private final CoverityConnectionFieldHelper coverityConnectionFieldHelper;\n private final ProjectStreamFieldHelper projectStreamFieldHelper;\n private final SynopsysCoverityCredentialsHelper credentialsHelper;\n\n public DescriptorImpl() {\n super(CoverityBuildStep.class);\n load();\n\n Slf4jIntLogger slf4jIntLogger = new Slf4jIntLogger(LoggerFactory.getLogger(this.getClass()));\n coverityConnectionFieldHelper = new CoverityConnectionFieldHelper(slf4jIntLogger);\n projectStreamFieldHelper = new ProjectStreamFieldHelper(slf4jIntLogger);\n credentialsHelper = new SynopsysCoverityCredentialsHelper(slf4jIntLogger, JenkinsWrapper.initializeFromJenkinsJVM());\n }\n\n @Override\n @Nonnull\n public String getDisplayName() {\n return \"Execute Synopsys Coverity static analysis\";\n }\n\n @Override\n public boolean isApplicable(Class<? extends AbstractProject> jobType) {\n return true;\n }\n\n public ListBoxModel doFillCoverityInstanceUrlItems() {\n return coverityConnectionFieldHelper.doFillCoverityInstanceUrlItems();\n }\n\n public ListBoxModel doFillCredentialsIdItems() {\n return credentialsHelper.listSupportedCredentials();\n }\n\n public FormValidation doCheckCoverityInstanceUrl(@QueryParameter(FIELD_COVERITY_INSTANCE_URL) String coverityInstanceUrl, @QueryParameter(FIELD_OVERRIDE_CREDENTIALS) Boolean overrideDefaultCredentials, @QueryParameter(FIELD_CREDENTIALS_ID) String credentialsId) {\n return coverityConnectionFieldHelper.doCheckCoverityInstanceUrl(coverityInstanceUrl, overrideDefaultCredentials, credentialsId);\n }\n\n public ComboBoxModel doFillProjectNameItems(@QueryParameter(FIELD_COVERITY_INSTANCE_URL) String coverityInstanceUrl, @QueryParameter(FIELD_OVERRIDE_CREDENTIALS) Boolean overrideDefaultCredentials, @QueryParameter(FIELD_CREDENTIALS_ID) String credentialsId, @QueryParameter(\"updateNow\") boolean updateNow) throws InterruptedException {\n if (updateNow) {\n projectStreamFieldHelper.updateNow(coverityInstanceUrl, overrideDefaultCredentials, credentialsId);\n }\n return projectStreamFieldHelper.getProjectNamesForComboBox(coverityInstanceUrl, overrideDefaultCredentials, credentialsId);\n }\n\n public FormValidation doCheckProjectName(@QueryParameter(FIELD_COVERITY_INSTANCE_URL) String coverityInstanceUrl, @QueryParameter(FIELD_OVERRIDE_CREDENTIALS) Boolean overrideDefaultCredentials, @QueryParameter(FIELD_CREDENTIALS_ID) String credentialsId, @QueryParameter(FIELD_PROJECT_NAME) String projectName) {\n FormValidation urlValidation = coverityConnectionFieldHelper.doCheckCoverityInstanceUrlIgnoreMessage(coverityInstanceUrl, overrideDefaultCredentials, credentialsId);\n if (urlValidation.kind == FormValidation.Kind.ERROR) {\n return urlValidation;\n } else {\n return projectStreamFieldHelper.checkForProjectInCache(coverityInstanceUrl, overrideDefaultCredentials, credentialsId, projectName);\n }\n }\n\n public ComboBoxModel doFillStreamNameItems(@QueryParameter(FIELD_COVERITY_INSTANCE_URL) String coverityInstanceUrl, @QueryParameter(FIELD_OVERRIDE_CREDENTIALS) Boolean overrideDefaultCredentials, @QueryParameter(FIELD_CREDENTIALS_ID) String credentialsId, @QueryParameter(FIELD_PROJECT_NAME) String projectName) throws InterruptedException {\n return projectStreamFieldHelper.getStreamNamesForComboBox(coverityInstanceUrl, overrideDefaultCredentials,credentialsId, projectName);\n }\n\n public FormValidation doCheckStreamName(@QueryParameter(FIELD_COVERITY_INSTANCE_URL) String coverityInstanceUrl, @QueryParameter(FIELD_OVERRIDE_CREDENTIALS) Boolean overrideDefaultCredentials, @QueryParameter(FIELD_CREDENTIALS_ID) String credentialsId, @QueryParameter(FIELD_PROJECT_NAME) String projectName, @QueryParameter(FIELD_STREAM_NAME) String streamName) {\n FormValidation urlValidation = coverityConnectionFieldHelper.doCheckCoverityInstanceUrlIgnoreMessage(coverityInstanceUrl, overrideDefaultCredentials, credentialsId);\n if (urlValidation.kind == FormValidation.Kind.ERROR) {\n return urlValidation;\n } else {\n return projectStreamFieldHelper.checkForStreamInCache(coverityInstanceUrl, overrideDefaultCredentials, credentialsId, projectName, streamName);\n }\n }\n\n public ListBoxModel doFillOnCommandFailureItems() {\n return JenkinsSelectBoxEnum.toListBoxModel(OnCommandFailure.values());\n }\n\n public ListBoxModel doFillCleanUpActionItems() {\n return JenkinsSelectBoxEnum.toListBoxModel(CleanUpAction.values());\n }\n\n public CoverityRunConfiguration getDefaultCoverityRunConfiguration() {\n return SimpleCoverityRunConfiguration.DEFAULT_CONFIGURATION();\n }\n\n }\n\n}\n\n## 内容4\n### 文件路径:\nsrc/main/java/com/synopsys/integration/jenkins/coverity/extensions/global/CoverityConnectInstance.java\n### 代码内容:\npackage com.synopsys.integration.jenkins.coverity.extensions.global;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Optional;\n\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jenkinsci.plugins.plaincredentials.FileCredentials;\nimport org.kohsuke.stapler.DataBoundConstructor;\nimport org.kohsuke.stapler.DataBoundSetter;\nimport org.kohsuke.stapler.QueryParameter;\nimport org.kohsuke.stapler.verb.POST;\nimport org.slf4j.LoggerFactory;\n\nimport com.synopsys.integration.coverity.config.CoverityServerConfig;\nimport com.synopsys.integration.jenkins.annotations.HelpMarkdown;\nimport com.synopsys.integration.jenkins.coverity.SynopsysCoverityCredentialsHelper;\nimport com.synopsys.integration.jenkins.coverity.exception.CoverityJenkinsAbortException;\nimport com.synopsys.integration.jenkins.coverity.extensions.utils.CoverityConnectionFieldHelper;\nimport com.synopsys.integration.jenkins.wrapper.JenkinsWrapper;\nimport com.synopsys.integration.log.IntLogger;\nimport com.synopsys.integration.log.Slf4jIntLogger;\nimport com.synopsys.integration.rest.credentials.Credentials;\n\nimport hudson.Extension;\nimport hudson.model.AbstractDescribableImpl;\nimport hudson.model.Descriptor;\nimport hudson.util.FormValidation;\nimport hudson.util.ListBoxModel;\nimport jenkins.model.Jenkins;\n\npublic class CoverityConnectInstance extends AbstractDescribableImpl<CoverityConnectInstance> {\n @HelpMarkdown(\"Specify the URL for your Coverity Connect instance. \\r\\n\"\n + \"Populates the $COV_HOST and $COV_PORT environment variables\")\n private final String url;\n\n @HelpMarkdown(\"Specify the default credentials or authentication key file for authenticating with your Coverity Connect instance. Credentials can be specified on a per-execution basis in the Freestyle or Pipeline job config. \\r\\n\"\n + \"The credentials specified here will be used if no other credentials are provided in the job config. \\r\\n\"\n + \"**Note:** \\\"Username with password\\\" and \\\"Secret File\\\" are the only kind of credentials supported.\")\n private final String defaultCredentialsId;\n\n // A backwards-compatibility field for defaultCredentialsId.\n // Ideally we could find a way to remove this and use XSTREAM aliasing instead, but previous attempts have not been successful.\n // --rotte JUN 2021\n private transient String credentialId;\n\n @DataBoundConstructor\n public CoverityConnectInstance(String url, String defaultCredentialsId) {\n this.url = url;\n this.defaultCredentialsId = defaultCredentialsId;\n }\n\n @DataBoundSetter\n public void setCredentialId(String credentialId){\n this.credentialId = credentialId;\n }\n\n public String getDefaultCredentialsId() {\n if (credentialId != null) {\n return credentialId;\n }\n return defaultCredentialsId;\n }\n\n public String getUrl() {\n return url;\n }\n\n public Optional<URL> getCoverityURL() {\n URL coverityUrl = null;\n if (url != null) {\n try {\n coverityUrl = new URL(url);\n } catch (MalformedURLException ignored) {\n // Handled by form validation in the global configuration\n }\n }\n\n return Optional.ofNullable(coverityUrl);\n }\n\n public CoverityServerConfig getCoverityServerConfig(IntLogger logger, String credentialsId) throws IllegalArgumentException, IllegalStateException {\n return CoverityServerConfig.newBuilder()\n .setUrl(url)\n .setCredentials(getCoverityServerCredentials(logger, credentialsId))\n .build();\n }\n\n public Credentials getCoverityServerCredentials(IntLogger logger, String credentialId) {\n SynopsysCoverityCredentialsHelper synopsysCoverityCredentialsHelper = new SynopsysCoverityCredentialsHelper(logger, JenkinsWrapper.initializeFromJenkinsJVM());\n return synopsysCoverityCredentialsHelper.getIntegrationCredentialsById(credentialId);\n }\n\n public Optional<String> getUsername(IntLogger logger, String credentialId) {\n SynopsysCoverityCredentialsHelper synopsysCoverityCredentialsHelper = new SynopsysCoverityCredentialsHelper(logger,JenkinsWrapper.initializeFromJenkinsJVM());\n return synopsysCoverityCredentialsHelper.getCoverityUsernameById(credentialId);\n }\n\n public Optional<String> getPassphrase(String credentialId) {\n SynopsysCoverityCredentialsHelper synopsysCoverityCredentialsHelper = SynopsysCoverityCredentialsHelper.silentHelper(JenkinsWrapper.initializeFromJenkinsJVM());\n return synopsysCoverityCredentialsHelper.getCoverityPassphraseById(credentialId);\n }\n\n public Optional<String> getAuthenticationKeyFileContents(IntLogger logger, String credentialId) throws CoverityJenkinsAbortException {\n SynopsysCoverityCredentialsHelper synopsysCoverityCredentialsHelper = new SynopsysCoverityCredentialsHelper(logger, JenkinsWrapper.initializeFromJenkinsJVM());\n Optional<FileCredentials> authenticationKeyFileCredentials = synopsysCoverityCredentialsHelper.getAuthenticationKeyFileCredentialsById(credentialId);\n String contents = null;\n\n if (authenticationKeyFileCredentials.isPresent()) {\n try (InputStream inputStream = authenticationKeyFileCredentials.get().getContent()) {\n contents = IOUtils.toString(inputStream, StandardCharsets.UTF_8);\n } catch (IOException e) {\n throw new CoverityJenkinsAbortException(\"Authentication Key File could not be read from the Synopsys Coverity for Jenkins global configuration.\");\n }\n }\n\n return Optional.ofNullable(contents);\n }\n\n public boolean isEmpty() {\n return null == url && null == defaultCredentialsId;\n }\n\n @Override\n public DescriptorImpl getDescriptor() {\n return (DescriptorImpl) super.getDescriptor();\n }\n\n @Extension\n public static class DescriptorImpl extends Descriptor<CoverityConnectInstance> {\n private final CoverityConnectionFieldHelper coverityConnectionFieldHelper;\n private final SynopsysCoverityCredentialsHelper credentialsHelper;\n\n public DescriptorImpl() {\n super(CoverityConnectInstance.class);\n load();\n Slf4jIntLogger slf4jIntLogger = new Slf4jIntLogger(LoggerFactory.getLogger(this.getClass()));\n coverityConnectionFieldHelper = new CoverityConnectionFieldHelper(slf4jIntLogger);\n credentialsHelper = new SynopsysCoverityCredentialsHelper(slf4jIntLogger, JenkinsWrapper.initializeFromJenkinsJVM());\n }\n\n public FormValidation doCheckUrl(@QueryParameter(\"url\") String url) {\n JenkinsWrapper.initializeFromJenkinsJVM().getJenkins().ifPresent(jenkins -> jenkins.checkPermission(Jenkins.ADMINISTER));\n if (StringUtils.isBlank(url)) {\n return FormValidation.error(\"Please provide a URL for the Coverity Connect instance.\");\n }\n try {\n new URL(url);\n } catch (MalformedURLException e) {\n return FormValidation.error(e, String.format(\"The provided URL for the Coverity Connect instance is not a valid URL. Error: %s\", e.getMessage()));\n }\n return FormValidation.ok();\n }\n\n public ListBoxModel doFillDefaultCredentialsIdItems() {\n return credentialsHelper.listSupportedCredentials();\n }\n\n @POST\n public FormValidation doTestConnection(@QueryParameter(\"url\") String url, @QueryParameter(\"defaultCredentialsId\") String defaultCredentialsId) {\n JenkinsWrapper.initializeFromJenkinsJVM().getJenkins().ifPresent(jenkins -> jenkins.checkPermission(Jenkins.ADMINISTER));\n FormValidation urlValidation = doCheckUrl(url);\n if (!FormValidation.Kind.OK.equals(urlValidation.kind)) {\n return urlValidation;\n }\n\n if (StringUtils.isBlank(defaultCredentialsId)) {\n return FormValidation.error(\"Please specify the credentials for the Coverity Connect instance.\");\n }\n\n CoverityConnectInstance coverityConnectInstance = new CoverityConnectInstance(url, defaultCredentialsId);\n return coverityConnectionFieldHelper.testConnectionToCoverityInstance(coverityConnectInstance, defaultCredentialsId);\n }\n }\n\n}\n\n## 内容5\n### 文件路径:\nsrc/main/java/com/synopsys/integration/jenkins/coverity/extensions/pipeline/CheckForIssuesStep.java\n### 代码内容:\npackage com.synopsys.integration.jenkins.coverity.extensions.pipeline;\n\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.UnaryOperator;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.jenkinsci.Symbol;\nimport org.jenkinsci.plugins.workflow.graph.FlowNode;\nimport org.jenkinsci.plugins.workflow.steps.Step;\nimport org.jenkinsci.plugins.workflow.steps.StepContext;\nimport org.jenkinsci.plugins.workflow.steps.StepDescriptor;\nimport org.jenkinsci.plugins.workflow.steps.StepExecution;\nimport org.jenkinsci.plugins.workflow.steps.SynchronousNonBlockingStepExecution;\nimport org.kohsuke.stapler.DataBoundConstructor;\nimport org.kohsuke.stapler.DataBoundSetter;\nimport org.kohsuke.stapler.QueryParameter;\nimport org.slf4j.LoggerFactory;\n\nimport com.synopsys.integration.jenkins.annotations.HelpMarkdown;\nimport com.synopsys.integration.jenkins.coverity.CoverityJenkinsIntLogger;\nimport com.synopsys.integration.jenkins.coverity.JenkinsCoverityEnvironmentVariable;\nimport com.synopsys.integration.jenkins.coverity.SynopsysCoverityCredentialsHelper;\nimport com.synopsys.integration.jenkins.coverity.extensions.global.CoverityConnectInstance;\nimport com.synopsys.integration.jenkins.coverity.extensions.utils.CoverityConnectionFieldHelper;\nimport com.synopsys.integration.jenkins.coverity.extensions.utils.IssueViewFieldHelper;\nimport com.synopsys.integration.jenkins.coverity.extensions.utils.ProjectStreamFieldHelper;\nimport com.synopsys.integration.jenkins.coverity.stepworkflow.CoverityWorkflowStepFactory;\nimport com.synopsys.integration.jenkins.wrapper.JenkinsVersionHelper;\nimport com.synopsys.integration.jenkins.wrapper.JenkinsWrapper;\nimport com.synopsys.integration.log.Slf4jIntLogger;\nimport com.synopsys.integration.util.IntEnvironmentVariables;\n\nimport hudson.AbortException;\nimport hudson.EnvVars;\nimport hudson.Extension;\nimport hudson.Launcher;\nimport hudson.Util;\nimport hudson.model.Node;\nimport hudson.model.Run;\nimport hudson.model.TaskListener;\nimport hudson.util.FormValidation;\nimport hudson.util.ListBoxModel;\n\npublic class CheckForIssuesStep extends Step implements Serializable {\n public static final String DISPLAY_NAME = \"Check for Issues in Coverity View\";\n public static final String PIPELINE_NAME = \"coverityIssueCheck\";\n private static final long serialVersionUID = 3602102048550370960L;\n\n // Jenkins directly serializes the names of the fields, so they are an important part of the plugin's API.\n // Be aware by changing a field name, you will also need to change these strings and will likely break previous implementations.\n // Jenkins Common provides convenient access to the XSTREAM instance that Jenkins uses to serialize the classes, you can use the serialization methods on that class to rename fields without breaking them.\n // --rotte MAY 2021\n public static final String FIELD_COVERITY_INSTANCE_URL = \"coverityInstanceUrl\";\n public static final String FIELD_PROJECT_NAME = \"projectName\";\n public static final String FIELD_STREAM_NAME = \"streamName\";\n public static final String FIELD_VIEW_NAME = \"viewName\";\n public static final String FIELD_CREDENTIALS_ID = \"credentialsId\";\n public static final String FIELD_RETURN_ISSUE_COUNT = \"returnIssueCount\";\n public static final String FIELD_MARK_UNSTABLE = \"markUnstable\";\n\n // Any field set by a DataBoundSetter should be explicitly declared as nullable to avoid NPEs\n @Nullable\n @HelpMarkdown(\"Specify which Synopsys Coverity connect instance to check for issues.\")\n private String coverityInstanceUrl;\n\n @Nullable\n @HelpMarkdown(\"Specify the credentials to use with the Synopsys Coverity connect instance.\")\n private String credentialsId;\n\n @Nullable\n @HelpMarkdown(\"Specify the name of the Coverity project the view is associated with.\")\n private String projectName;\n\n @Nullable\n @HelpMarkdown(\"Specify the name of the Coverity view that you would like to check for issues.\")\n private String viewName;\n\n @Nullable\n @HelpMarkdown(\"If checked, will return the number of issues discovered in the specified Coverity view instead of throwing an exception.\")\n private Boolean returnIssueCount;\n\n @Nullable\n @HelpMarkdown(\"If checked, will mark the stage Unstable but otherwise allow the pipeline to proceed instead of throwing an exception. \\r\\n\"\n + \"As a byproduct, this also allows the step to return the issue count. Use returnIssueCount if you want to make this behavior more explicit.\")\n private Boolean markUnstable;\n\n @DataBoundConstructor\n public CheckForIssuesStep() {\n // All fields are optional, so this constructor exists only to prevent some versions of the pipeline syntax generator from failing\n }\n\n public String getCredentialsId() {\n if (StringUtils.isNotBlank(credentialsId)) {\n return credentialsId;\n }\n return null;\n }\n\n @DataBoundSetter\n public void setCredentialsId(String credentialsId) {\n this.credentialsId = credentialsId;\n }\n\n public Boolean getReturnIssueCount() {\n if (Boolean.FALSE.equals(returnIssueCount)) {\n return null;\n }\n return returnIssueCount;\n }\n\n @DataBoundSetter\n public void setReturnIssueCount(Boolean returnIssueCount) {\n this.returnIssueCount = returnIssueCount;\n }\n\n public String getCoverityInstanceUrl() {\n if (StringUtils.isBlank(coverityInstanceUrl)) {\n return null;\n }\n return coverityInstanceUrl;\n }\n\n @DataBoundSetter\n public void setCoverityInstanceUrl(String coverityInstanceUrl) {\n this.coverityInstanceUrl = coverityInstanceUrl;\n }\n\n public String getProjectName() {\n if (StringUtils.isBlank(projectName)) {\n return null;\n }\n return projectName;\n }\n\n @DataBoundSetter\n public void setProjectName(String projectName) {\n this.projectName = projectName;\n }\n\n public String getViewName() {\n if (StringUtils.isBlank(viewName)) {\n return null;\n }\n return viewName;\n }\n\n @DataBoundSetter\n public void setViewName(String viewName) {\n this.viewName = viewName;\n }\n\n public Boolean getMarkUnstable() {\n if (Boolean.FALSE.equals(markUnstable)) {\n return null;\n }\n return markUnstable;\n }\n\n @DataBoundSetter\n public void setMarkUnstable(Boolean markUnstable) {\n this.markUnstable = markUnstable;\n }\n\n @Override\n public StepExecution start(StepContext context) throws Exception {\n return new Execution(context);\n }\n\n @Symbol(PIPELINE_NAME)\n @Extension(optional = true)\n public static final class DescriptorImpl extends StepDescriptor {\n private final CoverityConnectionFieldHelper coverityConnectionFieldHelper;\n private final ProjectStreamFieldHelper projectStreamFieldHelper;\n private final IssueViewFieldHelper issueViewFieldHelper;\n private final SynopsysCoverityCredentialsHelper credentialsHelper;\n\n public DescriptorImpl() {\n Slf4jIntLogger slf4jIntLogger = new Slf4jIntLogger(LoggerFactory.getLogger(this.getClass()));\n coverityConnectionFieldHelper = new CoverityConnectionFieldHelper(slf4jIntLogger);\n projectStreamFieldHelper = new ProjectStreamFieldHelper(slf4jIntLogger);\n issueViewFieldHelper = new IssueViewFieldHelper(slf4jIntLogger);\n credentialsHelper = new SynopsysCoverityCredentialsHelper(slf4jIntLogger, JenkinsWrapper.initializeFromJenkinsJVM());\n }\n\n @Override\n public Set<? extends Class<?>> getRequiredContext() {\n return new HashSet<>(Arrays.asList(TaskListener.class, EnvVars.class));\n }\n\n @Override\n public String getFunctionName() {\n return PIPELINE_NAME;\n }\n\n @Nonnull\n @Override\n public String getDisplayName() {\n return DISPLAY_NAME;\n }\n\n public ListBoxModel doFillCoverityInstanceUrlItems() {\n return coverityConnectionFieldHelper.doFillCoverityInstanceUrlItems();\n }\n\n public FormValidation doCheckCoverityInstanceUrl(\n @QueryParameter(FIELD_COVERITY_INSTANCE_URL) String coverityInstanceUrl,\n @QueryParameter(FIELD_CREDENTIALS_ID) String credentialsId\n ) {\n return coverityConnectionFieldHelper.doCheckCoverityInstanceUrl(coverityInstanceUrl, StringUtils.isNotBlank(credentialsId), credentialsId);\n }\n\n public ListBoxModel doFillCredentialsIdItems() {\n return credentialsHelper.listSupportedCredentials();\n }\n\n public ListBoxModel doFillProjectNameItems(\n @QueryParameter(FIELD_COVERITY_INSTANCE_URL) String coverityInstanceUrl,\n @QueryParameter(FIELD_CREDENTIALS_ID) String credentialsId,\n @QueryParameter(\"updateNow\") boolean updateNow\n ) throws InterruptedException {\n if (updateNow) {\n projectStreamFieldHelper.updateNow(coverityInstanceUrl, StringUtils.isNotBlank(credentialsId), credentialsId);\n }\n return projectStreamFieldHelper.getProjectNamesForListBox(coverityInstanceUrl, StringUtils.isNotBlank(credentialsId), credentialsId);\n }\n\n public FormValidation doCheckProjectName(\n @QueryParameter(FIELD_COVERITY_INSTANCE_URL) String coverityInstanceUrl,\n @QueryParameter(FIELD_CREDENTIALS_ID) String credentialsId\n ) {\n return coverityConnectionFieldHelper.doCheckCoverityInstanceUrlIgnoreMessage(coverityInstanceUrl, StringUtils.isNotBlank(credentialsId), credentialsId);\n }\n\n public ListBoxModel doFillViewNameItems(\n @QueryParameter(FIELD_COVERITY_INSTANCE_URL) String coverityInstanceUrl,\n @QueryParameter(FIELD_CREDENTIALS_ID) String credentialsId,\n @QueryParameter(\"updateNow\") boolean updateNow\n ) throws InterruptedException {\n if (updateNow) {\n issueViewFieldHelper.updateNow(coverityInstanceUrl, StringUtils.isNotBlank(credentialsId), credentialsId);\n }\n return issueViewFieldHelper.getViewNamesForListBox(coverityInstanceUrl, StringUtils.isNotBlank(credentialsId), credentialsId);\n }\n\n public FormValidation doCheckViewName(\n @QueryParameter(FIELD_COVERITY_INSTANCE_URL) String coverityInstanceUrl,\n @QueryParameter(FIELD_CREDENTIALS_ID) String credentialsId\n ) {\n return coverityConnectionFieldHelper.doCheckCoverityInstanceUrlIgnoreMessage(coverityInstanceUrl, StringUtils.isNotBlank(credentialsId), credentialsId);\n }\n\n }\n\n public class Execution extends SynchronousNonBlockingStepExecution<Integer> {\n private static final long serialVersionUID = -5807577350749324767L;\n private final transient TaskListener listener;\n private final transient EnvVars envVars;\n private final transient Node node;\n private final transient Launcher launcher;\n private final transient Run<?, ?> run;\n private final transient FlowNode flowNode;\n\n protected Execution(@Nonnull StepContext context) throws InterruptedException, IOException {\n super(context);\n listener = context.get(TaskListener.class);\n envVars = context.get(EnvVars.class);\n node = context.get(Node.class);\n launcher = context.get(Launcher.class);\n run = context.get(Run.class);\n flowNode = context.get(FlowNode.class);\n }\n\n @Override\n protected Integer run() throws Exception {\n CoverityWorkflowStepFactory coverityWorkflowStepFactory = new CoverityWorkflowStepFactory(envVars, node, launcher, listener);\n CoverityJenkinsIntLogger logger = coverityWorkflowStepFactory.getOrCreateLogger();\n IntEnvironmentVariables intEnvironmentVariables = coverityWorkflowStepFactory.getOrCreateEnvironmentVariables();\n String unresolvedCoverityInstanceUrl = getRequiredValueOrDie(coverityInstanceUrl, FIELD_COVERITY_INSTANCE_URL, JenkinsCoverityEnvironmentVariable.COVERITY_URL, intEnvironmentVariables::getValue);\n String resolvedCoverityInstanceUrl = Util.replaceMacro(unresolvedCoverityInstanceUrl, intEnvironmentVariables.getVariables());\n\n String unresolvedProjectName = getRequiredValueOrDie(projectName, FIELD_PROJECT_NAME, JenkinsCoverityEnvironmentVariable.COVERITY_PROJECT, intEnvironmentVariables::getValue);\n String resolvedProjectName = Util.replaceMacro(unresolvedProjectName, intEnvironmentVariables.getVariables());\n\n String unresolvedViewName = getRequiredValueOrDie(viewName, FIELD_VIEW_NAME, JenkinsCoverityEnvironmentVariable.COVERITY_VIEW, intEnvironmentVariables::getValue);\n String resolvedViewName = Util.replaceMacro(unresolvedViewName, intEnvironmentVariables.getVariables());\n\n JenkinsVersionHelper jenkinsVersionHelper = JenkinsWrapper.initializeFromJenkinsJVM().getVersionHelper();\n\n String resolvedCredentialsId;\n Optional<String> possibleCredentialsId = getValueFromStepOrEnvironment(credentialsId, JenkinsCoverityEnvironmentVariable.CREDENTIALS_ID, intEnvironmentVariables::getValue);\n if (possibleCredentialsId.isPresent()) {\n resolvedCredentialsId = possibleCredentialsId.get();\n } else {\n CoverityConnectInstance coverityConnectInstance = coverityWorkflowStepFactory.getCoverityConnectInstanceFromUrl(resolvedCoverityInstanceUrl);\n resolvedCredentialsId = coverityConnectInstance.getDefaultCredentialsId();\n }\n\n CheckForIssuesStepWorkflow checkForIssuesStepWorkflow = new CheckForIssuesStepWorkflow(logger,\n jenkinsVersionHelper,\n () -> coverityWorkflowStepFactory.getWebServiceFactoryFromUrl(resolvedCoverityInstanceUrl, resolvedCredentialsId),\n coverityWorkflowStepFactory,\n resolvedCoverityInstanceUrl,\n resolvedCredentialsId,\n resolvedProjectName,\n resolvedViewName,\n returnIssueCount,\n markUnstable,\n run,\n flowNode);\n return checkForIssuesStepWorkflow.perform();\n }\n\n private Optional<String> getValueFromStepOrEnvironment(String pipelineParameter, JenkinsCoverityEnvironmentVariable environmentVariable, UnaryOperator<String> getter) {\n if (StringUtils.isNotBlank(pipelineParameter)) {\n return Optional.of(pipelineParameter);\n }\n\n String valueFromEnvironmentVariable = getter.apply(environmentVariable.toString());\n if (StringUtils.isNotBlank(valueFromEnvironmentVariable)) {\n return Optional.of(valueFromEnvironmentVariable);\n }\n\n return Optional.empty();\n }\n\n private String getRequiredValueOrDie(String pipelineParameter, String parameterName, JenkinsCoverityEnvironmentVariable environmentVariable, UnaryOperator<String> getter) throws AbortException {\n return getValueFromStepOrEnvironment(pipelineParameter, environmentVariable, getter)\n .orElseThrow(() -> new AbortException( \"Coverity issue check failed because required parameter \" + parameterName + \" was not set. \"\n + \"Please set \" + parameterName + \" or populate $\" + environmentVariable.toString() + \" with the desired value.\"));\n }\n\n }\n}\n\n## 内容6\n### 文件路径:\nsrc/main/java/com/synopsys/integration/jenkins/coverity/extensions/wrap/CoverityEnvironmentWrapper.java\n### 代码内容:\npackage com.synopsys.integration.jenkins.coverity.extensions.wrap;\n\nimport static com.synopsys.integration.jenkins.coverity.JenkinsCoverityEnvironmentVariable.TEMPORARY_AUTH_KEY_PATH;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Optional;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.jenkinsci.Symbol;\nimport org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper;\nimport org.kohsuke.stapler.DataBoundConstructor;\nimport org.kohsuke.stapler.DataBoundSetter;\nimport org.kohsuke.stapler.QueryParameter;\nimport org.slf4j.LoggerFactory;\n\nimport com.synopsys.integration.jenkins.PasswordMaskingOutputStream;\nimport com.synopsys.integration.jenkins.annotations.HelpMarkdown;\nimport com.synopsys.integration.jenkins.coverity.CoverityJenkinsIntLogger;\nimport com.synopsys.integration.jenkins.coverity.GlobalValueHelper;\nimport com.synopsys.integration.jenkins.coverity.JenkinsCoverityEnvironmentVariable;\nimport com.synopsys.integration.jenkins.coverity.SynopsysCoverityCredentialsHelper;\nimport com.synopsys.integration.jenkins.coverity.extensions.ConfigureChangeSetPatterns;\nimport com.synopsys.integration.jenkins.coverity.extensions.global.CoverityConnectInstance;\nimport com.synopsys.integration.jenkins.coverity.extensions.utils.CoverityConnectionFieldHelper;\nimport com.synopsys.integration.jenkins.coverity.extensions.utils.IssueViewFieldHelper;\nimport com.synopsys.integration.jenkins.coverity.extensions.utils.ProjectStreamFieldHelper;\nimport com.synopsys.integration.jenkins.coverity.stepworkflow.CleanUpWorkflowService;\nimport com.synopsys.integration.jenkins.coverity.stepworkflow.CoverityWorkflowStepFactory;\nimport com.synopsys.integration.jenkins.wrapper.JenkinsVersionHelper;\nimport com.synopsys.integration.jenkins.wrapper.JenkinsWrapper;\nimport com.synopsys.integration.log.SilentIntLogger;\nimport com.synopsys.integration.log.Slf4jIntLogger;\nimport com.synopsys.integration.util.IntEnvironmentVariables;\n\nimport hudson.EnvVars;\nimport hudson.Extension;\nimport hudson.FilePath;\nimport hudson.Launcher;\nimport hudson.console.ConsoleLogFilter;\nimport hudson.model.AbstractProject;\nimport hudson.model.Computer;\nimport hudson.model.Node;\nimport hudson.model.Run;\nimport hudson.model.TaskListener;\nimport hudson.scm.ChangeLogSet;\nimport hudson.tasks.BuildWrapperDescriptor;\nimport hudson.util.ComboBoxModel;\nimport hudson.util.FormValidation;\nimport hudson.util.ListBoxModel;\nimport jenkins.tasks.SimpleBuildWrapper;\n\npublic class CoverityEnvironmentWrapper extends SimpleBuildWrapper {\n // Jenkins directly serializes the names of the fields, so they are an important part of the plugin's API.\n // Be aware by changing a field name, you will also need to change these strings and will likely break previous implementations.\n // Jenkins Common provides convenient access to the XSTREAM instance that Jenkins uses to serialize the classes, you can use the serialization methods on that class to rename fields without breaking them.\n // --rotte MAY 2021\n public static final String FIELD_COVERITY_INSTANCE_URL = \"coverityInstanceUrl\";\n public static final String FIELD_PROJECT_NAME = \"projectName\";\n public static final String FIELD_STREAM_NAME = \"streamName\";\n public static final String NESTED_DESCRIPTOR_CHECK_FOR_ISSUES = \"checkForIssuesInView\";\n public static final String NESTED_DESCRIPTOR_CONFIGURE_CHANGE_SET_PATTERNS = \"configureChangeSetPatterns\";\n public static final String NESTED_DESCRIPTOR_COVERITY_RUN_CONFIGURATION = \"coverityRunConfiguration\";\n public static final String FIELD_ON_COMMAND_FAILURE = \"onCommandFailure\";\n public static final String FIELD_CLEAN_UP_ACTION = \"cleanUpAction\";\n public static final String FIELD_CREDENTIALS_ID = \"credentialsId\";\n\n @HelpMarkdown(\"Specify which Synopsys Coverity connect instance to run this job against.\")\n private final String coverityInstanceUrl;\n private final String coverityPassphrase;\n\n // Any field set by a DataBoundSetter should be explicitly declared as nullable to avoid NPEs\n @Nullable\n @HelpMarkdown(\"Specify the name of the Coverity project. \\r\\n\"\n + \"The resulting project name is stored in the $COV_PROJECT environment variable, and will affect both the full and incremental analysis.\")\n private String projectName;\n\n @Nullable\n @HelpMarkdown(\"Specify the name of the Coverity stream that you would like to use for the commands. \\r\\n\"\n + \"The resulting stream name is stored in the $COV_STREAM environment variable, and will affect both the full and incremental analysis.\")\n private String streamName;\n\n @Nullable\n @HelpMarkdown(\"Specify the name of the Coverity view that you would like to check for issues. \\r\\n\"\n + \"The resulting view name is stored in the $COV_VIEW environment variable, and affects checking for issues in both the full and incremental analysis, if configured.\")\n private String viewName;\n\n @Nullable\n private ConfigureChangeSetPatterns configureChangeSetPatterns;\n\n @Nullable\n private Boolean createMissingProjectsAndStreams;\n\n @Nullable\n @HelpMarkdown(\"Specify the credentials to use with the Synopsys Coverity connect instance.\")\n private String credentialsId;\n\n @DataBoundConstructor\n public CoverityEnvironmentWrapper(String coverityInstanceUrl) {\n this.coverityInstanceUrl = coverityInstanceUrl;\n this.coverityPassphrase = GlobalValueHelper.getCoverityInstanceWithUrl(new SilentIntLogger(), coverityInstanceUrl)\n .flatMap(coverityConnectInstance -> coverityConnectInstance.getPassphrase(credentialsId))\n .orElse(StringUtils.EMPTY);\n }\n\n public Boolean getCreateMissingProjectsAndStreams() {\n if (Boolean.FALSE.equals(createMissingProjectsAndStreams)) {\n return null;\n }\n return createMissingProjectsAndStreams;\n }\n\n @DataBoundSetter\n public void setCreateMissingProjectsAndStreams(@QueryParameter(\"createMissingProjectsAndStreams\") Boolean createMissingProjectsAndStreams) {\n this.createMissingProjectsAndStreams = createMissingProjectsAndStreams;\n }\n\n public String getCoverityInstanceUrl() {\n return coverityInstanceUrl;\n }\n\n public String getProjectName() {\n return projectName;\n }\n\n @DataBoundSetter\n public void setProjectName(@QueryParameter(\"projectName\") String projectName) {\n this.projectName = projectName;\n }\n\n public String getStreamName() {\n return streamName;\n }\n\n @DataBoundSetter\n public void setStreamName(@QueryParameter(\"streamName\") String streamName) {\n this.streamName = streamName;\n }\n\n public String getViewName() {\n return viewName;\n }\n\n @DataBoundSetter\n public void setViewName(@QueryParameter(\"viewName\") String viewName) {\n this.viewName = viewName;\n }\n\n public ConfigureChangeSetPatterns getConfigureChangeSetPatterns() {\n return configureChangeSetPatterns;\n }\n\n @DataBoundSetter\n public void setConfigureChangeSetPatterns(@QueryParameter(\"configureChangeSetPatterns\") ConfigureChangeSetPatterns configureChangeSetPatterns) {\n this.configureChangeSetPatterns = configureChangeSetPatterns;\n }\n\n public String getCredentialsId() {\n if (StringUtils.isNotBlank(credentialsId)) {\n return credentialsId;\n }\n return null;\n }\n\n @DataBoundSetter\n public void setCredentialsId(String credentialsId) {\n this.credentialsId = credentialsId;\n }\n\n @Override\n public void setUp(Context context, Run<?, ?> build, FilePath workspace, Launcher launcher, TaskListener listener, EnvVars initialEnvironment) throws IOException, InterruptedException {\n Node node = Optional.ofNullable(workspace.toComputer())\n .map(Computer::getNode)\n .orElse(null);\n RunWrapper runWrapper = new RunWrapper(build, true);\n\n CoverityWorkflowStepFactory coverityWorkflowStepFactory = new CoverityWorkflowStepFactory(initialEnvironment, node, launcher, listener);\n CoverityJenkinsIntLogger logger = coverityWorkflowStepFactory.getOrCreateLogger();\n JenkinsVersionHelper jenkinsVersionHelper = JenkinsWrapper.initializeFromJenkinsJVM().getVersionHelper();\n List<ChangeLogSet<?>> changeLogSets;\n try {\n changeLogSets = runWrapper.getChangeSets();\n } catch (Exception e) {\n logger.warn(String.format(\"WARNING: Synopsys Coverity for Jenkins could not determine the change set, %s will be empty and %s will be 0.\",\n JenkinsCoverityEnvironmentVariable.CHANGE_SET,\n JenkinsCoverityEnvironmentVariable.CHANGE_SET_SIZE));\n\n changeLogSets = Collections.emptyList();\n }\n\n String resolvedCredentialsId;\n if (credentialsId != null) {\n resolvedCredentialsId = credentialsId;\n } else {\n CoverityConnectInstance coverityConnectInstance = coverityWorkflowStepFactory.getCoverityConnectInstanceFromUrl(coverityInstanceUrl);\n resolvedCredentialsId = coverityConnectInstance.getDefaultCredentialsId();\n }\n\n CoverityEnvironmentWrapperStepWorkflow coverityEnvironmentWrapperStepWorkflow = new CoverityEnvironmentWrapperStepWorkflow(\n logger,\n jenkinsVersionHelper,\n () -> coverityWorkflowStepFactory.getWebServiceFactoryFromUrl(coverityInstanceUrl, resolvedCredentialsId),\n coverityWorkflowStepFactory,\n context,\n workspace.getRemote(),\n coverityInstanceUrl,\n resolvedCredentialsId,\n projectName,\n streamName,\n viewName,\n createMissingProjectsAndStreams,\n changeLogSets,\n configureChangeSetPatterns\n );\n Boolean environmentInjectedSuccessfully = coverityEnvironmentWrapperStepWorkflow.perform();\n if (Boolean.TRUE.equals(environmentInjectedSuccessfully)) {\n logger.info(\"Coverity environment injected successfully.\");\n }\n\n context.setDisposer(new DisposerImpl((HashMap<String, String>) coverityWorkflowStepFactory.getOrCreateEnvironmentVariables().getVariables()));\n }\n\n @Override\n public DescriptorImpl getDescriptor() {\n return (DescriptorImpl) super.getDescriptor();\n }\n\n @Override\n public ConsoleLogFilter createLoggerDecorator(@Nonnull Run<?, ?> build) {\n return new FilterImpl(coverityPassphrase);\n }\n\n @Symbol(\"withCoverityEnvironment\")\n @Extension\n public static final class DescriptorImpl extends BuildWrapperDescriptor {\n private final CoverityConnectionFieldHelper coverityConnectionFieldHelper;\n private final ProjectStreamFieldHelper projectStreamFieldHelper;\n private final SynopsysCoverityCredentialsHelper credentialsHelper;\n private final IssueViewFieldHelper issueViewFieldHelper;\n\n public DescriptorImpl() {\n super(CoverityEnvironmentWrapper.class);\n load();\n\n Slf4jIntLogger slf4jIntLogger = new Slf4jIntLogger(LoggerFactory.getLogger(this.getClass()));\n coverityConnectionFieldHelper = new CoverityConnectionFieldHelper(slf4jIntLogger);\n projectStreamFieldHelper = new ProjectStreamFieldHelper(slf4jIntLogger);\n issueViewFieldHelper = new IssueViewFieldHelper(slf4jIntLogger);\n credentialsHelper = new SynopsysCoverityCredentialsHelper(slf4jIntLogger, JenkinsWrapper.initializeFromJenkinsJVM());\n }\n\n public ListBoxModel doFillCoverityInstanceUrlItems() {\n return coverityConnectionFieldHelper.doFillCoverityInstanceUrlItems();\n }\n\n public FormValidation doCheckCoverityInstanceUrl(\n @QueryParameter(FIELD_COVERITY_INSTANCE_URL) String coverityInstanceUrl,\n @QueryParameter(FIELD_CREDENTIALS_ID) String credentialsId\n ) {\n return coverityConnectionFieldHelper.doCheckCoverityInstanceUrl(coverityInstanceUrl, StringUtils.isNotBlank(credentialsId), credentialsId);\n }\n\n public ListBoxModel doFillCredentialsIdItems() {\n return credentialsHelper.listSupportedCredentials();\n }\n\n public ComboBoxModel doFillProjectNameItems(\n @QueryParameter(FIELD_COVERITY_INSTANCE_URL) String coverityInstanceUrl,\n @QueryParameter(FIELD_CREDENTIALS_ID) String credentialsId,\n @QueryParameter(\"updateNow\") boolean updateNow\n ) throws InterruptedException {\n if (updateNow) {\n projectStreamFieldHelper.updateNow(coverityInstanceUrl, StringUtils.isNotBlank(credentialsId), credentialsId);\n }\n return projectStreamFieldHelper.getProjectNamesForComboBox(coverityInstanceUrl, StringUtils.isNotBlank(credentialsId), credentialsId);\n }\n\n public FormValidation doCheckProjectName(\n @QueryParameter(FIELD_COVERITY_INSTANCE_URL) String coverityInstanceUrl,\n @QueryParameter(FIELD_CREDENTIALS_ID) String credentialsId\n ) {\n return coverityConnectionFieldHelper.doCheckCoverityInstanceUrlIgnoreMessage(coverityInstanceUrl, StringUtils.isNotBlank(credentialsId), credentialsId);\n }\n\n public ComboBoxModel doFillStreamNameItems(\n @QueryParameter(FIELD_COVERITY_INSTANCE_URL) String coverityInstanceUrl,\n @QueryParameter(FIELD_CREDENTIALS_ID) String credentialsId,\n @QueryParameter(FIELD_PROJECT_NAME) String projectName\n ) throws InterruptedException {\n return projectStreamFieldHelper.getStreamNamesForComboBox(coverityInstanceUrl, StringUtils.isNotBlank(credentialsId), credentialsId, projectName);\n }\n\n public FormValidation doCheckStreamName(\n @QueryParameter(FIELD_COVERITY_INSTANCE_URL) String coverityInstanceUrl,\n @QueryParameter(FIELD_CREDENTIALS_ID) String credentialsId) {\n return coverityConnectionFieldHelper.doCheckCoverityInstanceUrlIgnoreMessage(coverityInstanceUrl, StringUtils.isNotBlank(credentialsId), credentialsId);\n }\n\n public ListBoxModel doFillViewNameItems(\n @QueryParameter(FIELD_COVERITY_INSTANCE_URL) String coverityInstanceUrl,\n @QueryParameter(FIELD_CREDENTIALS_ID) String credentialsId,\n @QueryParameter(\"updateNow\") boolean updateNow\n ) throws InterruptedException {\n if (updateNow) {\n issueViewFieldHelper.updateNow(coverityInstanceUrl, StringUtils.isNotBlank(credentialsId), credentialsId);\n }\n return issueViewFieldHelper.getViewNamesForListBox(coverityInstanceUrl, StringUtils.isNotBlank(credentialsId), credentialsId);\n }\n\n public FormValidation doCheckViewName(\n @QueryParameter(FIELD_COVERITY_INSTANCE_URL) String coverityInstanceUrl,\n @QueryParameter(FIELD_CREDENTIALS_ID) String credentialsId) {\n return coverityConnectionFieldHelper.doCheckCoverityInstanceUrl(coverityInstanceUrl, StringUtils.isNotBlank(credentialsId), credentialsId);\n }\n\n @Override\n public boolean isApplicable(AbstractProject<?, ?> item) {\n return true;\n }\n\n @Override\n public String getDisplayName() {\n return \"Inject Coverity environment into the build process\";\n }\n }\n\n private static final class FilterImpl extends ConsoleLogFilter implements Serializable {\n private static final long serialVersionUID = 1787519634824445328L;\n private final String passwordToMask;\n\n public FilterImpl(String passwordToMask) {\n this.passwordToMask = passwordToMask;\n }\n\n @Override\n public OutputStream decorateLogger(Run ignored, OutputStream logger) {\n return new PasswordMaskingOutputStream(logger, passwordToMask);\n }\n }\n\n private static final class DisposerImpl extends SimpleBuildWrapper.Disposer {\n private static final long serialVersionUID = 4771346213830683656L;\n private final HashMap<String, String> environmentVariables;\n\n public DisposerImpl(HashMap<String, String> intEnvironmentVariables) {\n this.environmentVariables = intEnvironmentVariables;\n }\n\n @Override\n public void tearDown(Run<?, ?> build, FilePath workspace, Launcher launcher, TaskListener listener) throws IOException, InterruptedException {\n IntEnvironmentVariables intEnvironmentVariables = IntEnvironmentVariables.empty();\n intEnvironmentVariables.putAll(environmentVariables);\n CoverityJenkinsIntLogger logger = CoverityJenkinsIntLogger.initializeLogger(listener, intEnvironmentVariables);\n CleanUpWorkflowService cleanUpWorkflowService = new CleanUpWorkflowService(logger);\n\n String authKeyPath = intEnvironmentVariables.getValue(TEMPORARY_AUTH_KEY_PATH.toString());\n if (StringUtils.isNotBlank(authKeyPath)) {\n FilePath authKeyFile = new FilePath(launcher.getChannel(), authKeyPath);\n cleanUpWorkflowService.cleanUpAuthenticationFile(authKeyFile);\n }\n }\n }\n\n}\n\n", "input": "", "output": "【漏洞类型】权限检查缺失漏洞\n【严重性】中等\n【漏洞摘要】在多个文件中,用户能够通过Web界面访问和操作Coverity连接实例的URL、项目名称、流名称、视图名称和凭据ID,但并没有进行适当的权限检查。例如,在`CoverityEnvironmentWrapper.java`的`doCheckCoverityInstanceUrl`方法中,直接验证了Coverity实例URL,而没有检查调用者的权限,这可能导致未经授权的用户能够查看和操作这些敏感信息。\n【关键代码模式】`doCheckCoverityInstanceUrl`、`doFillCredentialsIdItems`、`doFillCoverityInstanceUrlItems`、`doFillProjectNameItems`、`doFillStreamNameItems`、`doFillViewNameItems`等方法,以及它们的调用者和参数。\n【修复方法说明】通过在每个相关方法中加入`@AncestorInPath Item item`参数,并在方法内部调用`credentialsHelper.checkPermissionToAccessCredentials(item)`来检查调用者的权限,确保只有有权限的用户能够访问和操作Coverity连接实例的配置信息。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\ninlong-manager/manager-workflow/src/main/java/org/apache/inlong/manager/workflow/core/impl/ProcessServiceImpl.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.workflow.core.impl;\n\nimport com.google.common.collect.Lists;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.inlong.manager.common.enums.ErrorCodeEnum;\nimport org.apache.inlong.manager.common.enums.ProcessStatus;\nimport org.apache.inlong.manager.common.enums.TaskStatus;\nimport org.apache.inlong.manager.common.util.Preconditions;\nimport org.apache.inlong.manager.dao.entity.WorkflowProcessEntity;\nimport org.apache.inlong.manager.dao.entity.WorkflowTaskEntity;\nimport org.apache.inlong.manager.dao.mapper.WorkflowTaskEntityMapper;\nimport org.apache.inlong.manager.pojo.workflow.form.process.ProcessForm;\nimport org.apache.inlong.manager.workflow.WorkflowAction;\nimport org.apache.inlong.manager.workflow.WorkflowContext;\nimport org.apache.inlong.manager.workflow.core.ProcessService;\nimport org.apache.inlong.manager.workflow.core.ProcessorExecutor;\nimport org.apache.inlong.manager.workflow.core.WorkflowContextBuilder;\nimport org.apache.inlong.manager.workflow.definition.WorkflowTask;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n/**\n * WorkflowProcess service\n */\n@Service\n@Slf4j\npublic class ProcessServiceImpl implements ProcessService {\n\n @Autowired\n private ProcessorExecutor processorExecutor;\n @Autowired\n private WorkflowTaskEntityMapper taskEntityMapper;\n @Autowired\n private WorkflowContextBuilder workflowContextBuilder;\n\n @Override\n public WorkflowContext start(String name, String applicant, ProcessForm form) {\n Preconditions.expectNotBlank(name, ErrorCodeEnum.INVALID_PARAMETER, \"process name cannot be null\");\n Preconditions.expectNotBlank(applicant, ErrorCodeEnum.INVALID_PARAMETER, \"applicant cannot be null\");\n Preconditions.expectNotNull(form, \"form cannot be null\");\n\n // build context\n WorkflowContext context = workflowContextBuilder.buildContextForProcess(name, applicant, form);\n this.processorExecutor.executeStart(context.getProcess().getStartEvent(), context);\n return context;\n }\n\n @Override\n public WorkflowContext continueProcess(Integer processId, String operator, String remark) {\n Preconditions.expectNotBlank(operator, ErrorCodeEnum.INVALID_PARAMETER, \"operator cannot be null\");\n Preconditions.expectNotNull(processId, \"processId cannot be null\");\n WorkflowContext context = workflowContextBuilder.buildContextForProcess(processId);\n WorkflowProcessEntity processEntity = context.getProcessEntity();\n ProcessStatus processStatus = ProcessStatus.valueOf(processEntity.getStatus());\n Preconditions.expectTrue(processStatus == ProcessStatus.PROCESSING,\n String.format(\"processId=%s should be in processing\", processId));\n List<WorkflowTaskEntity> startElements = Lists.newArrayList();\n startElements.addAll(taskEntityMapper.selectByProcess(processId, TaskStatus.PENDING));\n startElements.addAll(taskEntityMapper.selectByProcess(processId, TaskStatus.FAILED));\n for (WorkflowTaskEntity taskEntity : startElements) {\n String taskName = taskEntity.getName();\n WorkflowTask task = context.getProcess().getTaskByName(taskName);\n context.setActionContext(new WorkflowContext.ActionContext()\n .setAction(WorkflowAction.COMPLETE)\n .setTaskEntity(taskEntity)\n .setOperator(operator)\n .setRemark(remark)\n .setTask(task));\n this.processorExecutor.executeStart(task, context);\n }\n return context;\n }\n\n @Override\n public WorkflowContext cancel(Integer processId, String operator, String remark) {\n Preconditions.expectNotBlank(operator, ErrorCodeEnum.INVALID_PARAMETER, \"operator cannot be null\");\n Preconditions.expectNotNull(processId, \"processId cannot be null\");\n\n WorkflowContext context = workflowContextBuilder.buildContextForProcess(processId);\n List<WorkflowTaskEntity> pendingTasks = taskEntityMapper.selectByProcess(processId, TaskStatus.PENDING);\n for (WorkflowTaskEntity taskEntity : pendingTasks) {\n WorkflowTask task = context.getProcess().getTaskByName(taskEntity.getName());\n context.setActionContext(new WorkflowContext.ActionContext()\n .setAction(WorkflowAction.CANCEL)\n .setTaskEntity(taskEntity)\n .setOperator(operator)\n .setRemark(remark)\n .setTask(task));\n this.processorExecutor.executeComplete(task, context);\n }\n\n return context;\n }\n\n}\n\n", "input": "", "output": "【漏洞类型】权限检查缺失\n【严重性】高\n【漏洞摘要】原始代码在启动、继续和取消工作流时,没有对操作者进行权限检查。攻击者可以通过提供任意用户名来执行这些操作,从而绕过权限控制,拥有不当的访问权限。\n【关键代码模式】\n- `start` 方法中没有检查操作者权限。\n- `continueProcess` 方法中没有检查操作者权限。\n- `cancel` 方法中没有检查操作者权限。\n\n【修复方法说明】修复方法通过增加 `checkUser` 方法,在启动、继续和取消工作流之前检查操作者是否具有权限。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\ncli/ballerina-cli-module/src/main/java/org/ballerinalang/cli/module/Pull.java\n### 代码内容:\n// Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved.\n//\n// WSO2 Inc. licenses this file to you under the Apache License,\n// Version 2.0 (the \"License\"); you may not use this file except\n// in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing,\n// software distributed under the License is distributed on an\n// \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n// KIND, either express or implied. See the License for the\n// specific language governing permissions and limitations\n// under the License.\n\npackage org.ballerinalang.cli.module;\n\nimport me.tongfei.progressbar.ProgressBar;\nimport me.tongfei.progressbar.ProgressBarStyle;\nimport org.ballerinalang.cli.module.util.ErrorUtil;\nimport org.ballerinalang.cli.module.util.Utils;\nimport org.ballerinalang.jvm.JSONParser;\nimport org.ballerinalang.jvm.values.MapValue;\n\nimport java.io.BufferedReader;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.PrintStream;\nimport java.net.Authenticator;\nimport java.net.HttpURLConnection;\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\nimport javax.ws.rs.core.HttpHeaders;\n\nimport static org.ballerinalang.cli.module.util.CliModuleConstants.BALLERINA_PLATFORM;\nimport static org.ballerinalang.cli.module.util.CliModuleConstants.BAL_LANG_SPEC_VERSION;\nimport static org.ballerinalang.cli.module.util.CliModuleConstants.IDENTITY;\nimport static org.ballerinalang.cli.module.util.CliModuleConstants.RESOLVED_REQUESTED_URI;\nimport static org.ballerinalang.cli.module.util.Utils.convertToUrl;\nimport static org.ballerinalang.cli.module.util.Utils.createHttpUrlConnection;\nimport static org.ballerinalang.cli.module.util.Utils.getStatusCode;\nimport static org.ballerinalang.cli.module.util.Utils.initializeSsl;\nimport static org.ballerinalang.cli.module.util.Utils.setRequestMethod;\n\n/**\n * This class is pulling the modules from ballerina central.\n *\n * @since 1.2.0\n */\npublic class Pull {\n private static final String VERSION_REGEX = \"(\\\\d+\\\\.)(\\\\d+\\\\.)(\\\\d+)\";\n private static PrintStream outStream = System.out;\n private static DefaultLogFormatter logFormatter = new DefaultLogFormatter();\n\n private Pull() {\n }\n\n /**\n * Execute pull command.\n *\n * @param url remote uri of the central\n * @param modulePathInBaloCache module path in balo cache, user.home/.ballerina/balo_cache/org_name/module_name\n * @param moduleNameWithOrg module name with org, org_name/module_name\n * @param proxyHost proxy host\n * @param proxyPort proxy port\n * @param proxyUsername proxy username\n * @param proxyPassword proxy password\n * @param supportedVersionRange supported version range\n * @param isBuild pulling happens when building\n * @param isNightlyBuild is nightly build\n * @param langSpecVersion lang spec version\n * @param platform supported version\n */\n public static void execute(String url, String modulePathInBaloCache, String moduleNameWithOrg, String proxyHost,\n int proxyPort, String proxyUsername, String proxyPassword, String supportedVersionRange, boolean isBuild,\n boolean isNightlyBuild, String langSpecVersion, String platform) {\n if (isBuild) {\n logFormatter = new BuildLogFormatter();\n }\n\n HttpURLConnection conn = null;\n try {\n initializeSsl();\n conn = createHttpUrlConnection(convertToUrl(url + supportedVersionRange), proxyHost, proxyPort,\n proxyUsername, proxyPassword);\n\n conn.setInstanceFollowRedirects(false);\n setRequestMethod(conn, Utils.RequestMethod.GET);\n\n // Set headers\n conn.setRequestProperty(BALLERINA_PLATFORM, platform);\n conn.setRequestProperty(BAL_LANG_SPEC_VERSION, langSpecVersion);\n conn.setRequestProperty(HttpHeaders.ACCEPT_ENCODING, IDENTITY);\n\n boolean redirect = false;\n // 302 - Module is found\n // Other - Error occurred, json returned with the error message\n if (getStatusCode(conn) == HttpURLConnection.HTTP_MOVED_TEMP) {\n redirect = true;\n } else {\n handleErrorResponse(conn, url, moduleNameWithOrg);\n }\n\n if (redirect) {\n // get redirect url from \"location\" header field\n String newUrl = conn.getHeaderField(HttpHeaders.LOCATION);\n String contentDisposition = conn.getHeaderField(HttpHeaders.CONTENT_DISPOSITION);\n\n conn = createHttpUrlConnection(convertToUrl(newUrl), proxyHost, proxyPort, proxyUsername,\n proxyPassword);\n conn.setRequestProperty(HttpHeaders.CONTENT_DISPOSITION, contentDisposition);\n\n createBaloInHomeRepo(conn, modulePathInBaloCache, moduleNameWithOrg, isNightlyBuild, newUrl,\n contentDisposition);\n }\n } catch (Exception e) {\n throw ErrorUtil.createCommandException(e.getMessage());\n } finally {\n if (conn != null) {\n conn.disconnect();\n }\n Authenticator.setDefault(null);\n }\n }\n\n /**\n * Create the balo in home repo.\n *\n * @param conn http connection\n * @param modulePathInBaloCache module path in balo cache, <user.home>.ballerina/balo_cache/<org-name>/<module-name>\n * @param moduleNameWithOrg module name with org, <org-name>/<module-name>\n * @param isNightlyBuild is nightly build\n * @param newUrl new redirect url\n * @param contentDisposition content disposition header\n */\n private static void createBaloInHomeRepo(HttpURLConnection conn, String modulePathInBaloCache,\n String moduleNameWithOrg, boolean isNightlyBuild, String newUrl, String contentDisposition) {\n long responseContentLength = conn.getContentLengthLong();\n if (responseContentLength <= 0) {\n createError(\"invalid response from the server, please try again\");\n }\n String resolvedURI = conn.getHeaderField(RESOLVED_REQUESTED_URI);\n if (resolvedURI == null || resolvedURI.equals(\"\")) {\n resolvedURI = newUrl;\n }\n String[] uriParts = resolvedURI.split(\"/\");\n String moduleVersion = uriParts[uriParts.length - 3];\n\n validateModuleVersion(moduleVersion);\n String baloFile = getBaloFileName(contentDisposition, uriParts[uriParts.length - 1]);\n Path baloCacheWithModulePath = Paths.get(modulePathInBaloCache, moduleVersion);\n //<user.home>.ballerina/balo_cache/<org-name>/<module-name>/<module-version>\n\n Path baloPath = Paths.get(baloCacheWithModulePath.toString(), baloFile);\n if (baloPath.toFile().exists()) {\n createError(\"module already exists in the home repository: \" + baloPath.toString());\n }\n\n createBaloFileDirectory(baloCacheWithModulePath);\n writeBaloFile(conn, baloPath, moduleNameWithOrg + \":\" + moduleVersion, responseContentLength);\n handleNightlyBuild(isNightlyBuild, baloCacheWithModulePath);\n }\n\n /**\n * Write balo file to the home repo.\n *\n * @param conn http connection\n * @param baloPath path of the balo file\n * @param fullModuleName full module name, <org-name>/<module-name>:<module-version>\n * @param resContentLength response content length\n */\n private static void writeBaloFile(HttpURLConnection conn, Path baloPath, String fullModuleName,\n long resContentLength) {\n try (InputStream inputStream = conn.getInputStream();\n FileOutputStream outputStream = new FileOutputStream(baloPath.toString())) {\n writeAndHandleProgress(inputStream, outputStream, resContentLength / 1024, fullModuleName);\n } catch (IOException e) {\n createError(\"error occurred copying the balo file: \" + e.getMessage());\n }\n }\n\n /**\n * Show progress of the writing the balo file.\n *\n * @param inputStream response input stream\n * @param outputStream home repo balo file output stream\n * @param totalSizeInKB response input stream size in kb\n * @param fullModuleName full module name, <org-name>/<module-name>:<module-version>\n */\n private static void writeAndHandleProgress(InputStream inputStream, FileOutputStream outputStream,\n long totalSizeInKB, String fullModuleName) {\n int count;\n byte[] buffer = new byte[1024];\n\n try (ProgressBar progressBar = new ProgressBar(fullModuleName + \" [central.ballerina.io -> home repo] \",\n totalSizeInKB, 1000, outStream, ProgressBarStyle.ASCII, \" KB\", 1)) {\n while ((count = inputStream.read(buffer)) > 0) {\n outputStream.write(buffer, 0, count);\n progressBar.step();\n }\n } catch (IOException e) {\n outStream.println(logFormatter.formatLog(fullModuleName + \"pulling the module from central failed\"));\n } finally {\n outStream.println(logFormatter.formatLog(fullModuleName + \" pulled from central successfully\"));\n }\n }\n\n /**\n * Handle nightly build.\n *\n * @param isNightlyBuild is nightly build\n * @param baloCacheWithModulePath balo cache with module path\n */\n private static void handleNightlyBuild(boolean isNightlyBuild, Path baloCacheWithModulePath) {\n if (isNightlyBuild) {\n // If its a nightly build tag the file as a module from nightly\n Path nightlyBuildMetaFile = Paths.get(baloCacheWithModulePath.toString(), \"nightly.build\");\n if (!nightlyBuildMetaFile.toFile().exists()) {\n createNightlyBuildMetaFile(nightlyBuildMetaFile);\n }\n }\n }\n\n /**\n * Validate module version with the regex.\n *\n * @param moduleVersion module version\n */\n private static void validateModuleVersion(String moduleVersion) {\n if (!moduleVersion.matches(VERSION_REGEX)) {\n createError(\"module version could not be detected\");\n }\n }\n\n /**\n * Create nightly build meta file.\n *\n * @param nightlyBuildMetaFilePath nightly build meta file path\n */\n private static void createNightlyBuildMetaFile(Path nightlyBuildMetaFilePath) {\n try {\n Files.createFile(nightlyBuildMetaFilePath);\n } catch (Exception e) {\n createError(\"error occurred while creating nightly.build file.\");\n }\n }\n\n /**\n * Create balo file directory.\n *\n * @param fullPathToStoreBalo full path to store the balo file\n * <user.home>.ballerina/balo_cache/<org-name>/<module-name>/<module-version>\n */\n private static void createBaloFileDirectory(Path fullPathToStoreBalo) {\n try {\n Files.createDirectory(fullPathToStoreBalo);\n } catch (IOException e) {\n createError(\"error creating directory for balo file\");\n }\n }\n\n /**\n * Handle error response.\n *\n * @param conn http connection\n * @param url remote repository url\n * @param moduleFullName module name with org and version\n */\n private static void handleErrorResponse(HttpURLConnection conn, String url, String moduleFullName) {\n try (BufferedReader reader = new BufferedReader(\n new InputStreamReader(conn.getErrorStream(), Charset.defaultCharset()))) {\n StringBuilder result = new StringBuilder();\n String line;\n while ((line = reader.readLine()) != null) {\n result.append(line);\n }\n\n MapValue payload = (MapValue) JSONParser.parse(result.toString());\n createError(\"error: \" + payload.getStringValue(\"message\"));\n } catch (IOException e) {\n createError(\"failed to pull the module '\" + moduleFullName + \"' from the remote repository '\" + url + \"'\");\n }\n }\n\n /**\n * Create error using log formatter.\n *\n * @param errMessage error message\n */\n private static void createError(String errMessage) {\n throw ErrorUtil.createCommandException(logFormatter.formatLog(errMessage));\n }\n\n /**\n * Get balo file name from content disposition header.\n *\n * @param contentDisposition content disposition header value\n * @param baloFile balo file name taken from RESOLVED_REQUESTED_URI\n * @return balo file name\n */\n private static String getBaloFileName(String contentDisposition, String baloFile) {\n if (contentDisposition != null && !contentDisposition.equals(\"\")) {\n return contentDisposition.substring(\"attachment; filename=\".length());\n } else {\n return baloFile;\n }\n }\n}\n\n/**\n * Default log formatter class.\n */\nclass DefaultLogFormatter {\n String formatLog(String msg) {\n return msg;\n }\n}\n\n/**\n * Build log formatter class which used to log the message for build command.\n */\nclass BuildLogFormatter extends DefaultLogFormatter {\n @Override String formatLog(String msg) {\n return \"\\t\" + msg;\n }\n}\n\n## 内容2\n### 文件路径:\ncli/ballerina-cli-module/src/main/java/org/ballerinalang/cli/module/Push.java\n### 代码内容:\n// Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved.\n//\n// WSO2 Inc. licenses this file to you under the Apache License,\n// Version 2.0 (the \"License\"); you may not use this file except\n// in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing,\n// software distributed under the License is distributed on an\n// \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n// KIND, either express or implied. See the License for the\n// specific language governing permissions and limitations\n// under the License.\n\npackage org.ballerinalang.cli.module;\n\nimport me.tongfei.progressbar.ProgressBar;\nimport me.tongfei.progressbar.ProgressBarStyle;\nimport org.ballerinalang.cli.module.util.ErrorUtil;\nimport org.ballerinalang.cli.module.util.Utils;\nimport org.ballerinalang.jvm.JSONParser;\nimport org.ballerinalang.jvm.values.MapValue;\n\nimport java.io.BufferedReader;\nimport java.io.DataOutputStream;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.PrintStream;\nimport java.net.Authenticator;\nimport java.net.HttpURLConnection;\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nimport javax.ws.rs.core.HttpHeaders;\nimport javax.ws.rs.core.MediaType;\n\nimport static org.ballerinalang.cli.module.util.CliModuleConstants.PUSH_ORGANIZATION;\nimport static org.ballerinalang.cli.module.util.Utils.convertToUrl;\nimport static org.ballerinalang.cli.module.util.Utils.createHttpUrlConnection;\nimport static org.ballerinalang.cli.module.util.Utils.getStatusCode;\nimport static org.ballerinalang.cli.module.util.Utils.initializeSsl;\nimport static org.ballerinalang.cli.module.util.Utils.setRequestMethod;\n\n/**\n * This class is pushing the modules to ballerina central.\n *\n * @since 1.2.0\n */\npublic class Push {\n\n private static PrintStream errStream = System.err;\n private static PrintStream outStream = System.out;\n private static final int NO_OF_BYTES = 64;\n private static final int BUFFER_SIZE = 1024 * NO_OF_BYTES;\n\n private Push() {\n }\n\n /**\n * Execute push command.\n *\n * @param url remote uri of the central\n * @param proxyHost proxy host\n * @param proxyPort proxy port\n * @param proxyUsername proxy username\n * @param proxyPassword proxy password\n * @param accessToken access token of the org\n * @param orgName org name\n * @param moduleName module name\n * @param version module version\n * @param baloPath path to the balo\n */\n public static void execute(String url, String proxyHost, int proxyPort, String proxyUsername, String proxyPassword,\n String accessToken, String orgName, String moduleName, String version, Path baloPath) {\n initializeSsl();\n HttpURLConnection conn = createHttpUrlConnection(convertToUrl(url), proxyHost, proxyPort, proxyUsername,\n proxyPassword);\n conn.setInstanceFollowRedirects(false);\n setRequestMethod(conn, Utils.RequestMethod.POST);\n\n // Set headers\n conn.setRequestProperty(HttpHeaders.AUTHORIZATION, \"Bearer \" + accessToken);\n conn.setRequestProperty(PUSH_ORGANIZATION, orgName);\n conn.setRequestProperty(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM);\n\n conn.setDoOutput(true);\n conn.setChunkedStreamingMode(BUFFER_SIZE);\n\n try (DataOutputStream outputStream = new DataOutputStream(conn.getOutputStream())) {\n // Send balo content by 1 kb chunks\n byte[] buffer = new byte[BUFFER_SIZE];\n int count;\n try (ProgressBar progressBar = new ProgressBar(\n orgName + \"/\" + moduleName + \":\" + version + \" [project repo -> central]\",\n getTotalFileSizeInKB(baloPath), 1000, outStream, ProgressBarStyle.ASCII, \" KB\", 1);\n FileInputStream fis = new FileInputStream(baloPath.toFile())) {\n while ((count = fis.read(buffer)) > 0) {\n outputStream.write(buffer, 0, count);\n outputStream.flush();\n progressBar.stepBy((long) NO_OF_BYTES);\n }\n }\n } catch (IOException e) {\n throw ErrorUtil.createCommandException(\"error occurred while uploading balo to central: \" + e.getMessage());\n }\n\n handleResponse(conn, orgName, moduleName, version);\n Authenticator.setDefault(null);\n }\n\n /**\n * Handle the http response.\n *\n * @param conn http connection\n * @param orgName org name\n * @param moduleName module name\n * @param version module version\n */\n private static void handleResponse(HttpURLConnection conn, String orgName, String moduleName, String version) {\n try {\n int statusCode = getStatusCode(conn);\n // 200 - Module pushed successfully\n // Other - Error occurred, json returned with the error message\n if (statusCode == HttpURLConnection.HTTP_OK) {\n outStream.println(orgName + \"/\" + moduleName + \":\" + version + \" pushed to central successfully\");\n } else if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED) {\n errStream.println(\"unauthorized access token for organization: \" + orgName);\n } else if (statusCode == HttpURLConnection.HTTP_BAD_REQUEST) {\n try (BufferedReader reader = new BufferedReader(\n new InputStreamReader(conn.getErrorStream(), Charset.defaultCharset()))) {\n StringBuilder result = new StringBuilder();\n String line;\n while ((line = reader.readLine()) != null) {\n result.append(line);\n }\n\n MapValue payload = (MapValue) JSONParser.parse(result.toString());\n String message = payload.getStringValue(\"message\");\n if (message.contains(\"module md file cannot be empty\")) {\n errStream.println(message);\n } else {\n throw ErrorUtil.createCommandException(message);\n }\n } catch (IOException e) {\n throw ErrorUtil.createCommandException(\n \"failed to push the module '\" + orgName + \"/\" + moduleName + \":\" + version\n + \"' to the remote repository '\" + conn.getURL() + \"'\");\n }\n } else {\n throw ErrorUtil.createCommandException(\n \"failed to push the module '\" + orgName + \"/\" + moduleName + \":\" + version\n + \"' to the remote repository '\" + conn.getURL() + \"'\");\n }\n } finally {\n conn.disconnect();\n }\n }\n\n /**\n * Get total file size in kb.\n *\n * @param filePath path to the file\n * @return size of the file in kb\n */\n private static long getTotalFileSizeInKB(Path filePath) {\n byte[] baloContent;\n try {\n baloContent = Files.readAllBytes(filePath);\n return baloContent.length / 1024;\n } catch (IOException e) {\n throw ErrorUtil.createCommandException(\"cannot read the balo content\");\n }\n }\n}\n\n## 内容3\n### 文件路径:\ncli/ballerina-cli-module/src/main/java/org/ballerinalang/cli/module/Search.java\n### 代码内容:\n// Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved.\n//\n// WSO2 Inc. licenses this file to you under the Apache License,\n// Version 2.0 (the \"License\"); you may not use this file except\n// in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing,\n// software distributed under the License is distributed on an\n// \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n// KIND, either express or implied. See the License for the\n// specific language governing permissions and limitations\n// under the License.\n\npackage org.ballerinalang.cli.module;\n\nimport org.ballerinalang.cli.module.util.ErrorUtil;\nimport org.ballerinalang.cli.module.util.Utils;\nimport org.ballerinalang.jvm.JSONParser;\nimport org.ballerinalang.jvm.values.ArrayValue;\nimport org.ballerinalang.jvm.values.MapValue;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.PrintStream;\nimport java.net.Authenticator;\nimport java.net.HttpURLConnection;\nimport java.nio.charset.Charset;\nimport java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n\nimport static org.ballerinalang.cli.module.util.Utils.convertToUrl;\nimport static org.ballerinalang.cli.module.util.Utils.createHttpUrlConnection;\nimport static org.ballerinalang.cli.module.util.Utils.getStatusCode;\nimport static org.ballerinalang.cli.module.util.Utils.initializeSsl;\nimport static org.ballerinalang.cli.module.util.Utils.setRequestMethod;\n\n/**\n * This class is searching modules from ballerina central.\n *\n * @since 1.2.0\n */\npublic class Search {\n\n private static PrintStream outStream = System.out;\n\n private Search() {\n }\n\n /**\n * Execute search command.\n *\n * @param url remote uri of the central\n * @param proxyHost proxy host\n * @param proxyPort proxy port\n * @param proxyUsername proxy username\n * @param proxyPassword proxy password\n * @param terminalWidth terminal width of the CLI\n */\n public static void execute(String url, String proxyHost, int proxyPort, String proxyUsername, String proxyPassword,\n String terminalWidth) {\n initializeSsl();\n HttpURLConnection conn = createHttpUrlConnection(convertToUrl(url), proxyHost, proxyPort, proxyUsername,\n proxyPassword);\n conn.setInstanceFollowRedirects(false);\n setRequestMethod(conn, Utils.RequestMethod.GET);\n handleResponse(conn, getStatusCode(conn), terminalWidth);\n Authenticator.setDefault(null);\n }\n\n /**\n * Handle http response.\n *\n * @param conn http connection\n * @param statusCode status code of the response\n * @param terminalWidth terminal width of the CLI\n */\n private static void handleResponse(HttpURLConnection conn, int statusCode, String terminalWidth) {\n try {\n // 200 - modules found\n // Other - Error occurred, json returned with the error message\n MapValue payload;\n if (statusCode == HttpURLConnection.HTTP_OK) {\n try (BufferedReader reader = new BufferedReader(\n new InputStreamReader(conn.getInputStream(), Charset.defaultCharset()))) {\n StringBuilder result = new StringBuilder();\n String line;\n while ((line = reader.readLine()) != null) {\n result.append(line);\n }\n payload = (MapValue) JSONParser.parse(result.toString());\n } catch (IOException e) {\n throw ErrorUtil.createCommandException(e.getMessage());\n }\n\n if (payload.getIntValue(\"count\") > 0) {\n ArrayValue modules = payload.getArrayValue(\"modules\");\n printModules(modules, terminalWidth);\n } else {\n outStream.println(\"no modules found\");\n }\n } else {\n StringBuilder result = new StringBuilder();\n try (BufferedReader reader = new BufferedReader(\n new InputStreamReader(conn.getErrorStream(), Charset.defaultCharset()))) {\n String line;\n while ((line = reader.readLine()) != null) {\n result.append(line);\n }\n } catch (IOException e) {\n throw ErrorUtil.createCommandException(e.getMessage());\n }\n\n payload = (MapValue) JSONParser.parse(result.toString());\n throw ErrorUtil.createCommandException(payload.getStringValue(\"message\"));\n }\n } finally {\n conn.disconnect();\n }\n }\n\n /**\n * Print module information as a table in the CLI.\n *\n * @param modules modules array\n * @param terminalWidth terminal width of the CLI\n */\n public static void printModules(ArrayValue modules, String terminalWidth) {\n int rightMargin = 3;\n int width = Integer.parseInt(terminalWidth) - rightMargin;\n int dateColWidth = 15;\n int versionColWidth = 8;\n int authorsColWidth = 15;\n double nameColFactor = 9.0;\n double descColFactor = 16.0;\n int additionalSpace = 7;\n double remainingWidth = (double) width - (dateColWidth + versionColWidth + additionalSpace);\n int nameColWidth = (int) Math.round(remainingWidth * (nameColFactor / (nameColFactor + descColFactor)));\n int descColWidth = (int) Math.round(remainingWidth * (descColFactor / (nameColFactor + descColFactor)));\n int minDescColWidth = 60;\n\n printTitle();\n printTableHeader(dateColWidth, versionColWidth, nameColWidth, descColWidth, minDescColWidth, authorsColWidth);\n\n int i = 0;\n while (i < modules.size()) {\n printModule((MapValue) modules.get(i), dateColWidth, versionColWidth, authorsColWidth, nameColWidth,\n descColWidth, minDescColWidth);\n i = i + 1;\n outStream.println();\n }\n outStream.println();\n outStream.println(modules.size() + \" modules found\");\n }\n\n /**\n * Print module row.\n *\n * @param module module information\n * @param dateColWidth date column width\n * @param versionColWidth version column width\n * @param nameColWidth name column width\n * @param descColWidth description column width\n * @param minDescColWidth minimum description column width\n * @param authorsColWidth authors column width\n */\n private static void printModule(MapValue module, int dateColWidth, int versionColWidth, int authorsColWidth,\n int nameColWidth, int descColWidth, int minDescColWidth) {\n String orgName = module.getStringValue(\"orgName\");\n String packageName = module.getStringValue(\"name\");\n printInCLI(\"|\" + orgName + \"/\" + packageName, nameColWidth);\n\n String summary = module.getStringValue(\"summary\");\n\n if (descColWidth >= minDescColWidth) {\n printInCLI(summary, descColWidth - authorsColWidth);\n String authors = \"\";\n ArrayValue authorsArr = module.getArrayValue(\"authors\");\n\n if (authorsArr.size() > 0) {\n for (int j = 0; j < authorsArr.size(); j++) {\n if (j == 0) {\n authors = (String) authorsArr.get(j);\n } else if (j == authorsArr.size() - 1) {\n authors = (String) authorsArr.get(j);\n } else {\n authors = \", \" + authorsArr.get(j);\n }\n }\n }\n printInCLI(authors, authorsColWidth);\n } else {\n printInCLI(summary, descColWidth);\n }\n\n long createTimeJson = module.getIntValue(\"createdDate\");\n printInCLI(getDateCreated(createTimeJson), dateColWidth);\n\n String packageVersion = module.getStringValue(\"version\");\n printInCLI(packageVersion, versionColWidth);\n }\n\n /**\n * Print the tile.\n */\n private static void printTitle() {\n outStream.println();\n outStream.println(\"Ballerina Central\");\n outStream.println(\"=================\");\n outStream.println();\n }\n\n /**\n * Print in CLI.\n *\n * @param element printing element\n * @param charactersAllowed number of characters allowed\n */\n private static void printInCLI(String element, int charactersAllowed) {\n int lengthOfElement = element.length();\n if (lengthOfElement > charactersAllowed || lengthOfElement == charactersAllowed) {\n int margin = 3;\n String trimmedElement = element.substring(0, charactersAllowed - margin) + \"...\";\n outStream.print(trimmedElement + \" |\");\n } else {\n printCharacter(element, charactersAllowed, \" \", false);\n }\n }\n\n /**\n * Print characters.\n *\n * @param element printing element\n * @param charactersAllowed number of characters allowed\n * @param separator separating element\n * @param isDashElement is a dash element\n */\n private static void printCharacter(String element, int charactersAllowed, String separator, boolean isDashElement) {\n int lengthOfElement = element.length();\n StringBuilder print = new StringBuilder(element);\n int i = 0;\n while (i < charactersAllowed - lengthOfElement) {\n print.append(separator);\n i = i + 1;\n }\n if (isDashElement) {\n outStream.print(print + \"-|\");\n } else {\n outStream.print(print + \" |\");\n }\n }\n\n /**\n * Get date in simple date format.\n *\n * @param timeInMillis date in millis\n * @return date in simple date format\n */\n private static String getDateCreated(long timeInMillis) {\n Date date = new Date(timeInMillis);\n DateFormat df = new SimpleDateFormat(\"yyyy-MM-dd-E\");\n return df.format(date);\n }\n\n /**\n * Print table header.\n *\n * @param dateColWidth date column width\n * @param versionColWidth version column width\n * @param nameColWidth name column width\n * @param descColWidth description column width\n * @param minDescColWidth minimum description column width\n * @param authorsColWidth authors column width\n */\n private static void printTableHeader(int dateColWidth, int versionColWidth, int nameColWidth, int descColWidth,\n int minDescColWidth, int authorsColWidth) {\n printHeadingRow(dateColWidth, versionColWidth, nameColWidth, descColWidth, minDescColWidth, authorsColWidth);\n printDashRow(dateColWidth, versionColWidth, nameColWidth, descColWidth, minDescColWidth, authorsColWidth);\n }\n\n /**\n * Print heading row of the table header.\n *\n * @param dateColWidth date column width\n * @param versionColWidth version column width\n * @param nameColWidth name column width\n * @param descColWidth description column width\n * @param minDescColWidth minimum description column width\n * @param authorsColWidth authors column width\n */\n private static void printHeadingRow(int dateColWidth, int versionColWidth, int nameColWidth, int descColWidth,\n int minDescColWidth, int authorsColWidth) {\n printInCLI(\"|NAME\", nameColWidth);\n if (descColWidth >= minDescColWidth) {\n printInCLI(\"DESCRIPTION\", descColWidth - authorsColWidth);\n printInCLI(\"AUTHOR\", authorsColWidth);\n } else {\n printInCLI(\"DESCRIPTION\", descColWidth);\n }\n printInCLI(\"DATE\", dateColWidth);\n printInCLI(\"VERSION\", versionColWidth);\n outStream.println();\n }\n\n /**\n * Print dash row of the table header.\n *\n * @param dateColWidth date column width\n * @param versionColWidth version column width\n * @param nameColWidth name column width\n * @param descColWidth description column width\n * @param minDescColWidth minimum description column width\n * @param authorsColWidth authors column width\n */\n private static void printDashRow(int dateColWidth, int versionColWidth, int nameColWidth, int descColWidth,\n int minDescColWidth, int authorsColWidth) {\n printCharacter(\"|-\", nameColWidth, \"-\", true);\n\n if (descColWidth >= minDescColWidth) {\n printCharacter(\"-\", descColWidth - authorsColWidth, \"-\", true);\n printCharacter(\"-\", authorsColWidth, \"-\", true);\n } else {\n printCharacter(\"-\", descColWidth, \"-\", true);\n }\n\n printCharacter(\"-\", dateColWidth, \"-\", true);\n printCharacter(\"-\", versionColWidth, \"-\", true);\n\n outStream.println();\n }\n}\n\n## 内容4\n### 文件路径:\ncli/ballerina-cli-module/src/main/java/org/ballerinalang/cli/module/TokenUpdater.java\n### 代码内容:\n// Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved.\n//\n// WSO2 Inc. licenses this file to you under the Apache License,\n// Version 2.0 (the \"License\"); you may not use this file except\n// in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing,\n// software distributed under the License is distributed on an\n// \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n// KIND, either express or implied. See the License for the\n// specific language governing permissions and limitations\n// under the License.\n\npackage org.ballerinalang.cli.module;\n\nimport com.sun.net.httpserver.HttpExchange;\nimport com.sun.net.httpserver.HttpHandler;\nimport com.sun.net.httpserver.HttpServer;\nimport org.ballerinalang.cli.module.util.ErrorUtil;\n\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.io.PrintStream;\nimport java.net.HttpURLConnection;\nimport java.net.InetSocketAddress;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Paths;\nimport java.util.Collections;\n\nimport javax.ws.rs.core.HttpHeaders;\n\nimport static org.ballerinalang.cli.module.util.CliModuleConstants.SETTINGS_TOML_FILE;\n\n/**\n * This class has a service which updates the access token in the `Settings.toml` file.\n *\n * @since 1.2.0\n */\npublic class TokenUpdater {\n\n private static PrintStream errStream = System.err;\n private static PrintStream outStream = System.out;\n\n private TokenUpdater() {\n }\n\n /**\n * Creating the server which updates the access token in the `Settings.toml` file.\n *\n * @param settingsTomlFilePath `Settings.toml` file path\n */\n public static void execute(String settingsTomlFilePath) {\n HttpServer server;\n try {\n server = HttpServer.create(new InetSocketAddress(9295), 0);\n } catch (IOException e) {\n throw ErrorUtil.createCommandException(\"error occurred while creating the server: \" + e.getMessage() +\n \"Access token is missing in \" + settingsTomlFilePath +\n \"\\nAuto update failed. Please visit https://central.ballerina.io, get token and add it to the\" +\n settingsTomlFilePath + \" file.\");\n }\n server.createContext(\"/update-settings\", new TokenUpdateHandler());\n server.setExecutor(null); // creates a default executor\n server.start();\n }\n\n /**\n * Http handler of the server.\n */\n static class TokenUpdateHandler implements HttpHandler {\n\n @Override public void handle(HttpExchange httpExchange) {\n String token = getToken(httpExchange.getRequestURI().getPath());\n String currentUsersHomeDir = System.getProperty(\"user.home\");\n String settingsTomlPath = String.valueOf(Paths.get(currentUsersHomeDir, \".ballerina\", SETTINGS_TOML_FILE));\n FileOutputStream outputStream = null;\n try {\n outputStream = new FileOutputStream(settingsTomlPath);\n String str = \"[central]\\naccesstoken=\\\"\" + token + \"\\\"\";\n outputStream.write(str.getBytes(StandardCharsets.UTF_8));\n } catch (FileNotFoundException e) {\n throw ErrorUtil.createCommandException(\"Settings.toml file could not be found: \" + settingsTomlPath);\n } catch (IOException e) {\n throw ErrorUtil.createCommandException(\n \"error occurred while writing to the Settings.toml file: \" + e.getMessage());\n } finally {\n try {\n if (outputStream != null) {\n outputStream.close();\n }\n } catch (IOException e) {\n errStream.println(\"error occurred while closing the output stream: \" + e.getMessage());\n }\n }\n outStream.println(\"token updated\");\n\n OutputStream os = null;\n try {\n String response = \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\"/>\";\n httpExchange.getResponseHeaders()\n .put(HttpHeaders.CONTENT_TYPE, Collections.singletonList(\"image/svg+xml\"));\n httpExchange.sendResponseHeaders(HttpURLConnection.HTTP_OK,\n response.getBytes(StandardCharsets.UTF_8).length);\n os = httpExchange.getResponseBody();\n os.write(response.getBytes(StandardCharsets.UTF_8));\n } catch (IOException e) {\n throw ErrorUtil\n .createCommandException(\"error occurred while generating the response: \" + e.getMessage());\n } finally {\n try {\n if (os != null) {\n os.close();\n }\n } catch (IOException e) {\n errStream.println(\"error occurred while closing the output stream: \" + e.getMessage());\n }\n }\n }\n\n /**\n * Get access token from url.\n *\n * @param uri url with access token\n * @return access token\n */\n private static String getToken(String uri) {\n String[] uriParts = uri.split(\"/\");\n return uriParts[uriParts.length - 1];\n }\n }\n}\n\n## 内容5\n### 文件路径:\ncli/ballerina-cli-module/src/main/java/org/ballerinalang/cli/module/util/Utils.java\n### 代码内容:\n// Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved.\n//\n// WSO2 Inc. licenses this file to you under the Apache License,\n// Version 2.0 (the \"License\"); you may not use this file except\n// in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing,\n// software distributed under the License is distributed on an\n// \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n// KIND, either express or implied. See the License for the\n// specific language governing permissions and limitations\n// under the License.\n\npackage org.ballerinalang.cli.module.util;\n\nimport java.io.IOException;\nimport java.net.Authenticator;\nimport java.net.HttpURLConnection;\nimport java.net.InetSocketAddress;\nimport java.net.MalformedURLException;\nimport java.net.PasswordAuthentication;\nimport java.net.ProtocolException;\nimport java.net.Proxy;\nimport java.net.URL;\nimport java.security.KeyManagementException;\nimport java.security.NoSuchAlgorithmException;\n\nimport javax.net.ssl.HttpsURLConnection;\nimport javax.net.ssl.SSLContext;\nimport javax.net.ssl.TrustManager;\nimport javax.net.ssl.X509TrustManager;\n\nimport static org.ballerinalang.cli.module.util.CliModuleConstants.SSL;\n\n/**\n * Class contains miscellaneous utility methods.\n *\n * @since 1.2.0\n */\npublic class Utils {\n\n /**\n * Request method types.\n */\n public enum RequestMethod {\n GET, POST\n }\n\n private static TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {\n public java.security.cert.X509Certificate[] getAcceptedIssuers() {\n return new java.security.cert.X509Certificate[] {};\n }\n\n public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {\n //No need to implement.\n }\n\n public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {\n //No need to implement.\n }\n } };\n\n /**\n * Get proxy for http connection.\n *\n * @param proxyHost proxy host\n * @param proxyPort proxy port\n * @param proxyUsername proxy username\n * @param proxyPassword proxy password\n * @return The proxy object.\n */\n public static Proxy getProxy(String proxyHost, int proxyPort, String proxyUsername, String proxyPassword) {\n if (!\"\".equals(proxyHost)) {\n InetSocketAddress proxyInet = new InetSocketAddress(proxyHost, proxyPort);\n if (!\"\".equals(proxyUsername) && \"\".equals(proxyPassword)) {\n Authenticator authenticator = new Authenticator() {\n @Override public PasswordAuthentication getPasswordAuthentication() {\n return (new PasswordAuthentication(proxyUsername, proxyPassword.toCharArray()));\n }\n };\n Authenticator.setDefault(authenticator);\n }\n return new Proxy(Proxy.Type.HTTP, proxyInet);\n }\n return null;\n }\n\n /**\n * Convert string to URL.\n *\n * @param url string URL\n * @return URL\n */\n public static URL convertToUrl(String url) {\n try {\n return new URL(url);\n } catch (MalformedURLException e) {\n throw ErrorUtil.createCommandException(e.getMessage());\n }\n }\n\n /**\n * Initialize SSL.\n */\n public static void initializeSsl() {\n try {\n SSLContext sc = SSLContext.getInstance(SSL);\n sc.init(null, trustAllCerts, new java.security.SecureRandom());\n HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());\n } catch (NoSuchAlgorithmException | KeyManagementException e) {\n throw ErrorUtil.createCommandException(\"initializing SSL failed: \" + e.getMessage());\n }\n }\n\n /**\n * Create http URL connection.\n *\n * @param url connection URL\n * @param proxyHost proxy host\n * @param proxyPort proxy port\n * @param proxyUsername proxy username\n * @param proxyPassword proxy password\n * @return http URL connection\n */\n public static HttpURLConnection createHttpUrlConnection(URL url, String proxyHost, int proxyPort,\n String proxyUsername, String proxyPassword) {\n try {\n Proxy proxy = getProxy(proxyHost, proxyPort, proxyUsername, proxyPassword);\n // set proxy if exists.\n if (proxy == null) {\n return (HttpURLConnection) url.openConnection();\n } else {\n return (HttpURLConnection) url.openConnection(proxy);\n }\n } catch (IOException e) {\n throw ErrorUtil.createCommandException(e.getMessage());\n }\n }\n\n /**\n * Set request method of the http connection.\n *\n * @param conn http connection\n * @param method request method\n */\n public static void setRequestMethod(HttpURLConnection conn, RequestMethod method) {\n try {\n conn.setRequestMethod(getRequestMethodAsString(method));\n } catch (ProtocolException e) {\n throw ErrorUtil.createCommandException(e.getMessage());\n }\n }\n\n private static String getRequestMethodAsString(RequestMethod method) {\n switch (method) {\n case GET:\n return \"GET\";\n case POST:\n return \"POST\";\n default:\n throw ErrorUtil.createCommandException(\"invalid request method:\" + method);\n }\n }\n\n /**\n * Get status code of http response.\n *\n * @param conn http connection\n * @return status code\n */\n public static int getStatusCode(HttpURLConnection conn) {\n try {\n return conn.getResponseCode();\n } catch (IOException e) {\n throw ErrorUtil\n .createCommandException(\"connection to the remote repository host failed: \" + e.getMessage());\n }\n }\n}\n\n## 内容6\n### 文件路径:\ncompiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/packaging/converters/URIDryConverter.java\n### 代码内容:\npackage org.wso2.ballerinalang.compiler.packaging.converters;\n\nimport org.ballerinalang.jvm.JSONParser;\nimport org.ballerinalang.jvm.values.MapValue;\nimport org.ballerinalang.model.elements.PackageID;\nimport org.ballerinalang.repository.CompilerInput;\nimport org.ballerinalang.toml.model.Manifest;\nimport org.wso2.ballerinalang.compiler.util.Name;\nimport org.wso2.ballerinalang.util.TomlParserUtils;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.PrintStream;\nimport java.net.Authenticator;\nimport java.net.HttpURLConnection;\nimport java.net.InetSocketAddress;\nimport java.net.PasswordAuthentication;\nimport java.net.Proxy;\nimport java.net.URI;\nimport java.nio.charset.Charset;\nimport java.security.KeyManagementException;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Map;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport javax.net.ssl.HttpsURLConnection;\nimport javax.net.ssl.SSLContext;\nimport javax.net.ssl.TrustManager;\nimport javax.net.ssl.X509TrustManager;\n\n/**\n * Checks if there is a latest version in central if version is not mentioned. If there is then the version of the\n * module is updated with that version.\n */\npublic class URIDryConverter extends URIConverter {\n\n /**\n * This is to keep track that an error has already been logged so that it doesn't repeatedly log the same error.\n */\n private static boolean loggedError = false;\n private static final Pattern semVerPatchPattern = Pattern.compile(\"(\\\\d+)\\\\.(\\\\d+)\");\n private PrintStream errStream = System.err;\n private Proxy proxy;\n\n private static TrustManager[] trustAllCerts = new TrustManager[]{\n new X509TrustManager() {\n public java.security.cert.X509Certificate[] getAcceptedIssuers() {\n return new java.security.cert.X509Certificate[]{};\n }\n public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {\n //No need to implement.\n }\n public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {\n //No need to implement.\n }\n }\n };\n\n public URIDryConverter(URI base, Map<PackageID, Manifest> dependencyManifests) {\n this(base, dependencyManifests, false);\n }\n\n public URIDryConverter(URI base, Map<PackageID, Manifest> dependencyManifests, boolean isBuild) {\n super(base, dependencyManifests, isBuild);\n this.base = URI.create(base.toString() + \"/modules/info/\");\n try {\n SSLContext sc = SSLContext.getInstance(\"SSL\");\n sc.init(null, trustAllCerts, new java.security.SecureRandom());\n HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());\n proxy = getProxy();\n } catch (NoSuchAlgorithmException | KeyManagementException e) {\n // ignore errors\n }\n }\n\n public Stream<CompilerInput> finalize(URI remoteURI, PackageID moduleID) {\n try {\n // only continue if a fixed module version is not set. a module version may be set through Ballerina.toml or\n // Ballerina.lock already.\n Matcher matcher = semVerPatchPattern.matcher(moduleID.version.value);\n if (\"\".equals(moduleID.version.value) || \"*\".equals(moduleID.version.value) || matcher.matches()) {\n HttpURLConnection conn;\n // set proxy if exists.\n if (null == this.proxy) {\n conn = (HttpURLConnection) remoteURI.toURL().openConnection();\n } else {\n conn = (HttpURLConnection) remoteURI.toURL().openConnection(this.proxy);\n }\n conn.setInstanceFollowRedirects(false);\n conn.setRequestMethod(\"GET\");\n\n // status code and meaning\n //// 200 - module info found\n //// 400 - bad request sent\n //// 500 - backend is broken\n int statusCode = conn.getResponseCode();\n if (statusCode == 200) {\n try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(),\n Charset.defaultCharset()))) {\n StringBuilder result = new StringBuilder();\n String line;\n while ((line = reader.readLine()) != null) {\n result.append(line);\n }\n Object payload = JSONParser.parse(result.toString());\n if (payload instanceof MapValue) {\n MapValue moduleInfo = ((MapValue) payload).getMapValue(\"module\");\n String version = moduleInfo.getStringValue(\"version\");\n moduleID.version = new Name(version);\n }\n }\n } else if (statusCode == 400 && !loggedError) {\n try (BufferedReader errorStream = new BufferedReader(\n new InputStreamReader(conn.getInputStream(), Charset.defaultCharset()))) {\n String errorContent = errorStream.lines().collect(Collectors.joining(\"\\n\"));\n this.errStream.println(\"error: could not connect to remote repository to find the latest \" +\n \"version of module: \" + moduleID.toString() + \". use '--offline' \" +\n \"flag to build in offline mode. reason: \" + errorContent);\n setErrorLoggedStatusAsTrue();\n }\n } else if (statusCode == 500 && !loggedError) {\n this.errStream.println(\"error: could not connect to remote repository to find the latest \" +\n \"version of module: \" + moduleID.toString() + \". use '--offline' \" +\n \"flag to build in offline mode.\");\n setErrorLoggedStatusAsTrue();\n }\n conn.disconnect();\n Authenticator.setDefault(null);\n }\n } catch (IOException e) {\n // ignore error and don't set the version.\n }\n\n return Stream.empty();\n }\n\n /**\n * Set the status that an error has been logged.\n */\n private static void setErrorLoggedStatusAsTrue() {\n loggedError = true;\n }\n\n /**\n * Get proxy for http connection.\n *\n * @return The proxy object.\n */\n private Proxy getProxy() {\n org.ballerinalang.toml.model.Proxy proxy = TomlParserUtils.readSettings().getProxy();\n if (!\"\".equals(proxy.getHost())) {\n InetSocketAddress proxyInet = new InetSocketAddress(proxy.getHost(), proxy.getPort());\n if (!\"\".equals(proxy.getUserName()) && \"\".equals(proxy.getPassword())) {\n Authenticator authenticator = new RemoteAuthenticator();\n Authenticator.setDefault(authenticator);\n }\n return new Proxy(Proxy.Type.HTTP, proxyInet);\n }\n\n return null;\n }\n\n /**\n * Authenticator for the proxy server if provided.\n */\n static class RemoteAuthenticator extends Authenticator {\n org.ballerinalang.toml.model.Proxy proxy;\n public RemoteAuthenticator() {\n proxy = TomlParserUtils.readSettings().getProxy();\n }\n\n @Override\n protected PasswordAuthentication getPasswordAuthentication() {\n return (new PasswordAuthentication(this.proxy.getUserName(), this.proxy.getPassword().toCharArray()));\n }\n }\n}\n\n## 内容7\n### 文件路径:\nmisc/openapi-ballerina/modules/openapi-to-ballerina-generator/src/main/java/org/ballerinalang/openapi/CodeGenerator.java\n### 代码内容:\npackage org.ballerinalang.openapi;\n\nimport com.github.jknack.handlebars.Context;\nimport com.github.jknack.handlebars.Handlebars;\nimport com.github.jknack.handlebars.Template;\nimport com.github.jknack.handlebars.context.FieldValueResolver;\nimport com.github.jknack.handlebars.context.JavaBeanValueResolver;\nimport com.github.jknack.handlebars.context.MapValueResolver;\nimport com.github.jknack.handlebars.helper.StringHelpers;\nimport com.github.jknack.handlebars.io.ClassPathTemplateLoader;\nimport com.github.jknack.handlebars.io.FileTemplateLoader;\nimport io.swagger.v3.oas.models.OpenAPI;\nimport io.swagger.v3.parser.OpenAPIV3Parser;\nimport org.apache.commons.lang3.StringUtils;\nimport org.ballerinalang.openapi.exception.BallerinaOpenApiException;\nimport org.ballerinalang.openapi.model.BallerinaOpenApi;\nimport org.ballerinalang.openapi.model.GenSrcFile;\nimport org.ballerinalang.openapi.typemodel.BallerinaOpenApiType;\nimport org.ballerinalang.openapi.utils.CodegenUtils;\nimport org.ballerinalang.openapi.utils.GeneratorConstants;\nimport org.ballerinalang.openapi.utils.GeneratorConstants.GenType;\nimport org.ballerinalang.openapi.utils.TypeExtractorUtil;\nimport org.ballerinalang.tool.LauncherUtils;\nimport org.wso2.ballerinalang.compiler.util.ProjectDirs;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.io.PrintWriter;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport static org.ballerinalang.openapi.model.GenSrcFile.GenFileType;\nimport static org.ballerinalang.openapi.utils.GeneratorConstants.GenType.GEN_CLIENT;\nimport static org.ballerinalang.openapi.utils.GeneratorConstants.MODULE_MD;\n\n/**\n * This class generates Ballerina Services/Clients for a provided OAS definition.\n */\npublic class CodeGenerator {\n private String srcPackage;\n private String modelPackage;\n\n private static final PrintStream outStream = System.err;\n\n /**\n * Generates ballerina source for provided Open API Definition in {@code definitionPath}.\n * Generated source will be written to a ballerina module at {@code outPath}\n * <p>Method can be user for generating Ballerina mock services and clients</p>\n *\n * @param type Output type. Following types are supported\n * <ul>\n * <li>mock</li>\n * <li>client</li>\n * </ul>\n * @param executionPath Command execution path\n * @param definitionPath Input Open Api Definition file path\n * @param serviceName Output Service Name\n * @param outPath Destination file path to save generated source files. If not provided\n * {@code definitionPath} will be used as the default destination path\n * @throws IOException when file operations fail\n * @throws BallerinaOpenApiException when code generator fails\n */\n private void generate(GenType type, String executionPath, String definitionPath,\n String reldefinitionPath , String serviceName, String outPath)\n throws IOException, BallerinaOpenApiException {\n\n if (!CodegenUtils.isBallerinaProject(Paths.get(outPath))) {\n throw new BallerinaOpenApiException(OpenApiMesseges.GEN_CLIENT_PROJECT_ROOT);\n }\n\n //Check if the selected path is a ballerina root for service generation\n //TODO check with team for root check\n Path projectRoot = ProjectDirs.findProjectRoot(Paths.get(executionPath));\n if (type.equals(GenType.GEN_SERVICE) && projectRoot == null) {\n throw LauncherUtils.createUsageExceptionWithHelp(OpenApiMesseges.GEN_SERVICE_PROJECT_ROOT);\n }\n\n Path srcPath = CodegenUtils.getSourcePath(srcPackage, outPath);\n Path implPath = CodegenUtils.getImplPath(srcPackage, srcPath);\n\n if (type.equals(GEN_CLIENT)) {\n if (srcPackage.equals(\"\")) {\n srcPath = srcPath.resolve(\"client\");\n implPath = implPath.resolve(\"client\");\n }\n\n if (Files.notExists(srcPath)) {\n Files.createDirectory(srcPath);\n }\n\n if (Files.notExists(implPath)) {\n Files.createDirectory(implPath);\n }\n\n }\n\n List<GenSrcFile> genFiles = generateBalSource(type, definitionPath, reldefinitionPath, serviceName);\n writeGeneratedSources(genFiles, srcPath, implPath, type);\n }\n\n /**\n * Generates ballerina source for provided Open API Definition in {@code definitionPath}.\n * Generated source will be written to a ballerina module at {@code outPath}\n * Method can be user for generating Ballerina clients.\n *\n * @param executionPath Command execution path\n * @param definitionPath Input Open Api Definition file path\n * @param outPath Destination file path to save generated source files. If not provided\n * {@code definitionPath} will be used as the default destination path\n * @throws IOException when file operations fail\n * @throws BallerinaOpenApiException when code generator fails\n */\n public void generateClient(String executionPath, String definitionPath, String serviceName, String outPath)\n throws IOException, BallerinaOpenApiException {\n generate(GenType.GEN_CLIENT, executionPath, definitionPath, null, serviceName, outPath);\n }\n\n /**\n * Generates ballerina source for provided Open API Definition in {@code definitionPath}.\n * Generated source will be written to a ballerina module at {@code outPath}\n * Method can be user for generating Ballerina clients.\n *\n * @param executionPath Command execution path\n * @param definitionPath Input Open Api Definition file path\n * @param reldefinitionPath Relative definition path to be used in the generated ballerina code\n * @param serviceName service name for the generated service\n * @param outPath Destination file path to save generated source files. If not provided\n * {@code definitionPath} will be used as the default destination path\n * @throws IOException when file operations fail\n * @throws BallerinaOpenApiException when code generator fails\n */\n public void generateService(String executionPath, String definitionPath,\n String reldefinitionPath, String serviceName, String outPath)\n throws IOException, BallerinaOpenApiException {\n generate(GenType.GEN_SERVICE, executionPath, definitionPath, reldefinitionPath, serviceName, outPath);\n }\n\n /**\n * Generates ballerina source for provided Open API Definition in {@code definitionPath}.\n * Generated code will be returned as a list of source files\n * <p>Method can be user for generating Ballerina mock services and clients</p>\n *\n * @param type Output type. Following types are supported\n * <ul>\n * <li>mock</li>\n * <li>client</li>\n * </ul>\n * @param serviceName Out put service name\n * @param definitionPath Input Open Api Definition file path\n * @param reldefinitionPath Relative OpenApi File\n * @return a list of generated source files wrapped as {@link GenSrcFile}\n * @throws IOException when file operations fail\n * @throws BallerinaOpenApiException when open api context building fail\n */\n public List<GenSrcFile> generateBalSource(GenType type, String definitionPath,\n String reldefinitionPath, String serviceName)\n throws IOException, BallerinaOpenApiException {\n OpenAPI api = new OpenAPIV3Parser().read(definitionPath);\n\n if (api == null) {\n throw new BallerinaOpenApiException(\"Couldn't read the definition from file: \" + definitionPath);\n }\n\n if (serviceName != null) {\n api.getInfo().setTitle(serviceName);\n } else if (api.getInfo() == null || StringUtils.isEmpty(api.getInfo().getTitle())) {\n api.getInfo().setTitle(GeneratorConstants.UNTITLED_SERVICE);\n }\n\n List<GenSrcFile> sourceFiles;\n\n switch (type) {\n case GEN_CLIENT:\n // modelPackage is not in use at the moment. All models will be written into same package\n // as other src files.\n // Therefore value set to modelPackage is ignored here\n BallerinaOpenApi definitionContext = new BallerinaOpenApi().buildContext(api).srcPackage(srcPackage)\n .modelPackage(srcPackage);\n definitionContext.setDefinitionPath(reldefinitionPath);\n\n sourceFiles = generateClient(definitionContext);\n break;\n case GEN_SERVICE:\n\n final BallerinaOpenApiType openApi = TypeExtractorUtil.extractOpenApiObject(api);\n openApi.setBalServiceName(serviceName);\n openApi.setBalModule(srcPackage);\n openApi.setServers(api);\n openApi.setTags(api.getTags());\n\n if (reldefinitionPath == null) {\n openApi.setDefPath(definitionPath.replaceAll(Pattern.quote(\"\\\\\"),\n Matcher.quoteReplacement(\"\\\\\\\\\")));\n } else {\n openApi.setDefPath(reldefinitionPath.replaceAll(Pattern.quote(\"\\\\\"),\n Matcher.quoteReplacement(\"\\\\\\\\\")));\n }\n\n sourceFiles = generateBallerinaService(openApi);\n break;\n default:\n return null;\n }\n\n return sourceFiles;\n }\n\n /**\n * Write ballerina definition of a <code>object</code> to a file as described by <code>template.</code>\n *\n * @param object Context object to be used by the template parser\n * @param templateDir Directory with all the templates required for generating the source file\n * @param templateName Name of the parent template to be used\n * @param outPath Destination path for writing the resulting source file\n * @throws IOException when file operations fail\n * @deprecated This method is now deprecated.\n * Use {@link #generateBalSource(GeneratorConstants.GenType, String, String, String) generate}\n * and implement a file write functionality your self, if you need to customize file writing steps.\n * Otherwise use {@link #generate(GeneratorConstants.GenType, String, String, String, String, String) generate}\n * to directly write generated source to a ballerina module.\n */\n @Deprecated\n public void writeBallerina(Object object, String templateDir, String templateName, String outPath)\n throws IOException {\n PrintWriter writer = null;\n\n try {\n Template template = compileTemplate(templateDir, templateName);\n Context context = Context.newBuilder(object).resolver(\n MapValueResolver.INSTANCE,\n JavaBeanValueResolver.INSTANCE,\n FieldValueResolver.INSTANCE).build();\n writer = new PrintWriter(outPath, \"UTF-8\");\n writer.println(template.apply(context));\n } finally {\n if (writer != null) {\n writer.close();\n }\n }\n }\n\n private Template compileTemplate(String defaultTemplateDir, String templateName) throws IOException {\n defaultTemplateDir = defaultTemplateDir.replaceAll(\"\\\\\\\\\", \"/\");\n String templatesDirPath = System.getProperty(GeneratorConstants.TEMPLATES_DIR_PATH_KEY, defaultTemplateDir);\n ClassPathTemplateLoader cpTemplateLoader = new ClassPathTemplateLoader((templatesDirPath));\n FileTemplateLoader fileTemplateLoader = new FileTemplateLoader(templatesDirPath);\n cpTemplateLoader.setSuffix(GeneratorConstants.TEMPLATES_SUFFIX);\n fileTemplateLoader.setSuffix(GeneratorConstants.TEMPLATES_SUFFIX);\n\n Handlebars handlebars = new Handlebars().with(cpTemplateLoader, fileTemplateLoader);\n handlebars.setInfiniteLoops(true); //This will allow templates to call themselves with recursion.\n handlebars.registerHelpers(StringHelpers.class);\n handlebars.registerHelper(\"equals\", (object, options) -> {\n CharSequence result;\n Object param0 = options.param(0);\n\n if (param0 == null) {\n throw new IllegalArgumentException(\"found 'null', expected 'string'\");\n }\n if (object != null) {\n if (object.toString().equals(param0.toString())) {\n result = options.fn(options.context);\n } else {\n result = options.inverse();\n }\n } else {\n result = null;\n }\n\n return result;\n });\n\n return handlebars.compile(templateName);\n }\n\n private void writeGeneratedSources(List<GenSrcFile> sources, Path srcPath, Path implPath, GenType type)\n throws IOException {\n // Remove old generated files - if any - before regenerate\n // if srcPackage was not provided and source was written to main package nothing will be deleted.\n if (srcPackage != null && !srcPackage.isEmpty() && Files.exists(srcPath)) {\n final File[] listFiles = new File(String.valueOf(srcPath)).listFiles();\n if (listFiles != null) {\n Arrays.stream(listFiles).forEach(file -> {\n boolean deleteStatus = true;\n if (!file.isDirectory() && !file.getName().equals(MODULE_MD)) {\n deleteStatus = file.delete();\n }\n\n //Capture return value of file.delete() since if\n //unable to delete returns false from file.delete() without an exception.\n if (!deleteStatus) {\n outStream.println(\"Unable to clean module directory.\");\n }\n });\n }\n\n }\n\n for (GenSrcFile file : sources) {\n Path filePath;\n\n // We only overwrite files of overwritable type.\n // So non overwritable files will be written to disk only once.\n if (!file.getType().isOverwritable()) {\n filePath = implPath.resolve(file.getFileName());\n if (Files.notExists(filePath)) {\n CodegenUtils.writeFile(filePath, file.getContent());\n }\n } else {\n filePath = srcPath.resolve(file.getFileName());\n CodegenUtils.writeFile(filePath, file.getContent());\n }\n }\n\n //This will print the generated files to the console\n if (type.equals(GenType.GEN_SERVICE)) {\n outStream.println(\"Service generated successfully and the OpenApi contract is copied to \" + srcPackage\n + \"/resources. this location will be referenced throughout the ballerina project.\");\n } else if (type.equals(GEN_CLIENT)) {\n outStream.println(\"Client generated successfully.\");\n }\n outStream.println(\"Following files were created. \\n\" +\n \"src/ \\n- \" + srcPackage);\n Iterator<GenSrcFile> iterator = sources.iterator();\n while (iterator.hasNext()) {\n outStream.println(\"-- \" + iterator.next().getFileName());\n }\n }\n\n /**\n * Generate code for ballerina client.\n *\n * @param context model context to be used by the templates\n * @return generated source files as a list of {@link GenSrcFile}\n * @throws IOException when code generation with specified templates fails\n */\n private List<GenSrcFile> generateClient(BallerinaOpenApi context) throws IOException {\n if (srcPackage == null || srcPackage.isEmpty()) {\n srcPackage = GeneratorConstants.DEFAULT_CLIENT_PKG;\n }\n\n List<GenSrcFile> sourceFiles = new ArrayList<>();\n String srcFile = context.getInfo().getTitle().toLowerCase(Locale.ENGLISH)\n .replaceAll(\" \", \"_\") + \".bal\";\n\n // Generate ballerina service and resources.\n String mainContent = getContent(context, GeneratorConstants.DEFAULT_CLIENT_DIR,\n GeneratorConstants.CLIENT_TEMPLATE_NAME);\n sourceFiles.add(new GenSrcFile(GenFileType.GEN_SRC, srcPackage, srcFile, mainContent));\n\n // Generate ballerina records to represent schemas.\n String schemaContent = getContent(context, GeneratorConstants.DEFAULT_MODEL_DIR,\n GeneratorConstants.SCHEMA_TEMPLATE_NAME);\n sourceFiles.add(new GenSrcFile(GenFileType.MODEL_SRC, srcPackage, GeneratorConstants.SCHEMA_FILE_NAME,\n schemaContent));\n\n return sourceFiles;\n }\n\n private List<GenSrcFile> generateBallerinaService(BallerinaOpenApiType api) throws IOException {\n if (srcPackage == null || srcPackage.isEmpty()) {\n srcPackage = GeneratorConstants.DEFAULT_MOCK_PKG;\n }\n\n List<GenSrcFile> sourceFiles = new ArrayList<>();\n String concatTitle = api.getBalServiceName().toLowerCase(Locale.ENGLISH).replaceAll(\" \", \"_\");\n String srcFile = concatTitle + \".bal\";\n\n String mainContent = getContent(api, GeneratorConstants.DEFAULT_TEMPLATE_DIR + \"/service\",\n \"balService\");\n sourceFiles.add(new GenSrcFile(GenFileType.GEN_SRC, srcPackage, srcFile, mainContent));\n\n String schemaContent = getContent(api, GeneratorConstants.DEFAULT_TEMPLATE_DIR + \"/service\",\n \"schemaList\");\n sourceFiles.add(new GenSrcFile(GenFileType.GEN_SRC, srcPackage, GeneratorConstants.SCHEMA_FILE_NAME,\n schemaContent));\n\n return sourceFiles;\n }\n\n /**\n * Retrieve generated source content as a String value.\n *\n * @param object context to be used by template engine\n * @param templateDir templates directory\n * @param templateName name of the template to be used for this code generation\n * @return String with populated template\n * @throws IOException when template population fails\n */\n private String getContent(BallerinaOpenApiType object, String templateDir, String templateName) throws IOException {\n Template template = compileTemplate(templateDir, templateName);\n Context context = Context.newBuilder(object)\n .resolver(MapValueResolver.INSTANCE, JavaBeanValueResolver.INSTANCE, FieldValueResolver.INSTANCE)\n .build();\n return template.apply(context);\n }\n\n /**\n * Retrieve generated source content as a String value.\n *\n * @param object context to be used by template engine\n * @param templateDir templates directory\n * @param templateName name of the template to be used for this code generation\n * @return String with populated template\n * @throws IOException when template population fails\n */\n private String getContent(BallerinaOpenApi object, String templateDir, String templateName) throws IOException {\n Template template = compileTemplate(templateDir, templateName);\n Context context = Context.newBuilder(object)\n .resolver(MapValueResolver.INSTANCE, JavaBeanValueResolver.INSTANCE, FieldValueResolver.INSTANCE)\n .build();\n return template.apply(context);\n }\n\n public String getSrcPackage() {\n return srcPackage;\n }\n\n public void setSrcPackage(String srcPackage) {\n this.srcPackage = srcPackage;\n }\n\n public String getModelPackage() {\n return modelPackage;\n }\n\n public void setModelPackage(String modelPackage) {\n this.modelPackage = modelPackage;\n }\n}\n\n", "input": "", "output": "【漏洞类型】SSL/TLS配置不安全\n【严重性】High\n【漏洞摘要】原始代码中使用了`initializeSsl`方法来创建一个信任所有证书的SSLContext,这意味着代码会忽略服务器证书的有效性检查,从而容易受到中间人攻击(Man-in-the-Middle, MITM)的风险。修复代码中将`HttpURLConnection`替换为`HttpsURLConnection`,并移除了对`initializeSsl`方法的调用,这表明已经移除了信任所有证书的配置,但仍然需要确保安全的SSL/TLS连接建立。\n【关键代码模式】与安全问题相关的关键代码模式包括:\n- `initializeSsl`方法的使用,创建了一个信任所有证书的SSLContext。\n- `createHttpUrlConnection`方法的使用,无论是否使用代理,都会打开一个HttpURLConnection。\n- `HttpURLConnection`实例的使用,包括设置请求方法、读取响应内容等操作。\n【修复方法说明】修复方法通过将`HttpURLConnection`替换为`HttpsURLConnection`,并移除对`initializeSsl`方法的调用,解决了SSL/TLS配置不安全的问题。建议在实际应用中使用更安全的SSL/TLS配置,例如配置具体的信任证书管理器,以确保服务器证书的有效性。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\naws-android-sdk-core/src/main/java/com/amazonaws/regions/RegionMetadataParser.java\n### 代码内容:\npackage com.amazonaws.regions;\n\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\n\n/**\n * Parses a region metadata file to pull out information about the available\n * regions, names, IDs, and what service endpoints are available in each region.\n *\n * @deprecated The SDK no longer supports a regions.xml override\n */\n@Deprecated\npublic class RegionMetadataParser {\n\n private static final String REGION_TAG = \"Region\";\n private static final String REGION_ID_TAG = \"Name\";\n private static final String DOMAIN_TAG = \"Domain\";\n private static final String ENDPOINT_TAG = \"Endpoint\";\n private static final String SERVICE_TAG = \"ServiceName\";\n private static final String HTTP_TAG = \"Http\";\n private static final String HTTPS_TAG = \"Https\";\n private static final String HOSTNAME_TAG = \"Hostname\";\n\n /**\n * Parses the specified input stream and returns a {@code RegionMetadata}\n * object.\n *\n * @param input the input stream to parse\n * @return the parsed region metadata\n * @throws IOException on error\n */\n public static RegionMetadata parse(final InputStream input)\n throws IOException {\n\n return new RegionMetadata(internalParse(input, false));\n }\n\n /**\n * @deprecated since this object is stateless\n */\n @Deprecated\n public RegionMetadataParser() {\n }\n\n /**\n * Parses the specified input stream and returns a list of the regions\n * declared in it. By default, verification on the region endpoints is\n * disabled.\n *\n * @param input The stream containing the region metadata to parse.\n * @return The list of parsed regions.\n * @deprecated in favor of {@link #parse(InputStream)}\n */\n @Deprecated\n public List<Region> parseRegionMetadata(InputStream input)\n throws IOException {\n\n return internalParse(input, false);\n }\n\n /**\n * Parses the specified input stream and optionally verifies that all of the\n * endpoints end in \".amazonaws.com\". This method is deprecated, since not\n * all valid AWS endpoints end in \".amazonaws.com\" any more.\n *\n * @param input The stream containing the region metadata to parse.\n * @param endpointVerification Whether to verify each region endpoint\n * @return The list of parsed regions.\n * @deprecated in favor of {@link #parse(InputStream)}\n */\n @Deprecated\n public List<Region> parseRegionMetadata(final InputStream input,\n final boolean endpointVerification)\n throws IOException {\n\n return internalParse(input, endpointVerification);\n }\n\n @SuppressWarnings(\"checkstyle:emptyblock\")\n private static List<Region> internalParse(\n final InputStream input,\n final boolean endpointVerification) throws IOException {\n\n Document document;\n try {\n\n DocumentBuilderFactory factory =\n DocumentBuilderFactory.newInstance();\n\n DocumentBuilder documentBuilder = factory.newDocumentBuilder();\n document = documentBuilder.parse(input);\n\n } catch (IOException exception) {\n throw exception;\n } catch (Exception exception) {\n throw new IOException(\"Unable to parse region metadata file: \"\n + exception.getMessage(),\n exception);\n } finally {\n try {\n input.close();\n } catch (IOException exception) {\n }\n }\n\n NodeList regionNodes = document.getElementsByTagName(REGION_TAG);\n List<Region> regions = new ArrayList<Region>();\n for (int i = 0; i < regionNodes.getLength(); i++) {\n Node node = regionNodes.item(i);\n if (node.getNodeType() == Node.ELEMENT_NODE) {\n Element element = (Element) node;\n regions.add(parseRegionElement(element, endpointVerification));\n }\n }\n\n return regions;\n }\n\n private static Region parseRegionElement(\n final Element regionElement,\n final boolean endpointVerification) {\n\n String name = getChildElementValue(REGION_ID_TAG, regionElement);\n String domain = getChildElementValue(DOMAIN_TAG, regionElement);\n\n Region region = new Region(name, domain);\n\n NodeList endpointNodes =\n regionElement.getElementsByTagName(ENDPOINT_TAG);\n\n for (int i = 0; i < endpointNodes.getLength(); i++) {\n addRegionEndpoint(region,\n (Element) endpointNodes.item(i),\n endpointVerification);\n }\n\n return region;\n }\n\n private static void addRegionEndpoint(\n final Region region,\n final Element endpointElement,\n final boolean endpointVerification) {\n\n String serviceName = getChildElementValue(SERVICE_TAG, endpointElement);\n String hostname = getChildElementValue(HOSTNAME_TAG, endpointElement);\n String http = getChildElementValue(HTTP_TAG, endpointElement);\n String https = getChildElementValue(HTTPS_TAG, endpointElement);\n\n if (endpointVerification && !verifyLegacyEndpoint(hostname)) {\n throw new IllegalStateException(\"Invalid service endpoint (\"\n + hostname + \") is detected.\");\n }\n\n region.getServiceEndpoints().put(serviceName, hostname);\n region.getHttpSupport().put(serviceName, \"true\".equals(http));\n region.getHttpsSupport().put(serviceName, \"true\".equals(https));\n }\n\n private static String getChildElementValue(\n final String tagName,\n final Element element) {\n\n Node tagNode = element.getElementsByTagName(tagName).item(0);\n if (tagNode == null)\n return null;\n NodeList nodes = tagNode.getChildNodes();\n Node node = nodes.item(0);\n\n return node.getNodeValue();\n }\n\n /**\n * Returns whether the provided endpoint is a \"valid\" AWS service endpoint\n * ending in \".amazonaws.com\".\n */\n private static boolean verifyLegacyEndpoint(String endpoint) {\n return endpoint.endsWith(\".amazonaws.com\");\n }\n}\n\n## 内容2\n### 文件路径:\naws-android-sdk-core/src/main/java/com/amazonaws/util/XpathUtils.java\n### 代码内容:\npackage com.amazonaws.util;\n\nimport com.amazonaws.logging.Log;\nimport com.amazonaws.logging.LogFactory;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\nimport org.xml.sax.SAXException;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.nio.ByteBuffer;\nimport java.util.Date;\n\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.parsers.ParserConfigurationException;\nimport javax.xml.xpath.XPath;\nimport javax.xml.xpath.XPathConstants;\nimport javax.xml.xpath.XPathExpressionException;\nimport javax.xml.xpath.XPathFactory;\n\n/**\n * Utility methods for extracting data from XML documents using Xpath\n * expressions.\n */\npublic class XpathUtils {\n\n /** Shared logger */\n private static Log log = LogFactory.getLog(XpathUtils.class);\n\n private static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\n\n /**\n * InputStream to Document.\n * @param is the input stream.\n * @return the Document.\n * @throws SAXException\n * @throws IOException\n * @throws ParserConfigurationException\n */\n public static Document documentFrom(InputStream is)\n throws SAXException, IOException, ParserConfigurationException {\n is = new NamespaceRemovingInputStream(is);\n Document doc = factory.newDocumentBuilder().parse(is);\n is.close();\n\n return doc;\n }\n\n /**\n * Xml string to Document.\n * @param xml the xml string.\n * @return the Document.\n * @throws SAXException\n * @throws IOException\n * @throws ParserConfigurationException\n */\n public static Document documentFrom(String xml) throws SAXException,\n IOException, ParserConfigurationException {\n return documentFrom(new ByteArrayInputStream(xml.getBytes(StringUtils.UTF8)));\n }\n\n /**\n * URL to Document,\n * @param url the URL.\n * @return the Document.\n * @throws SAXException\n * @throws IOException\n * @throws ParserConfigurationException\n */\n public static Document documentFrom(URL url) throws SAXException,\n IOException, ParserConfigurationException {\n return documentFrom(url.openStream());\n }\n\n /**\n * Evaluates the specified XPath expression and returns the results as a\n * Double.\n *\n * @param expression The XPath expression to evaluate.\n * @param node The node to run the expression on.\n * @return The Double result.\n * @throws XPathExpressionException If there was a problem processing the\n * specified XPath expression.\n */\n public static Double asDouble(String expression, Node node)\n throws XPathExpressionException {\n String doubleString = evaluateAsString(expression, node);\n return (isEmptyString(doubleString)) ? null : Double.valueOf(doubleString);\n }\n\n /**\n * Evaluates the specified XPath expression and returns the result as a\n * string.\n *\n * @param expression The XPath expression to evaluate.\n * @param node The node to run the expression on.\n * @return The string result.\n * @throws XPathExpressionException If there was a problem processing the\n * specified XPath expression.\n */\n public static String asString(String expression, Node node)\n throws XPathExpressionException {\n return evaluateAsString(expression, node);\n }\n\n /**\n * Evaluates the specified XPath expression and returns the result as an\n * Integer.\n *\n * @param expression The XPath expression to evaluate.\n * @param node The node to run the expression on.\n * @return The Integer result.\n * @throws XPathExpressionException If there was a problem processing the\n * specified XPath expression.\n */\n public static Integer asInteger(String expression, Node node)\n throws XPathExpressionException {\n String intString = evaluateAsString(expression, node);\n return (isEmptyString(intString)) ? null : Integer.valueOf(intString);\n }\n\n /**\n * Evaluates the specified XPath expression and returns the result as a\n * Boolean.\n *\n * @param expression The XPath expression to evaluate.\n * @param node The node to run the expression on.\n * @return The Boolean result.\n * @throws XPathExpressionException If there was a problem processing the\n * specified XPath expression.\n */\n public static Boolean asBoolean(String expression, Node node)\n throws XPathExpressionException {\n String booleanString = evaluateAsString(expression, node);\n return (isEmptyString(booleanString)) ? null : Boolean.valueOf(booleanString);\n }\n\n /**\n * Evaluates the specified XPath expression and returns the result as a\n * Float.\n *\n * @param expression The XPath expression to evaluate.\n * @param node The node to run the expression on.\n * @return The Float result.\n * @throws XPathExpressionException If there was a problem processing the\n * specified XPath expression.\n */\n public static Float asFloat(String expression, Node node)\n throws XPathExpressionException {\n String floatString = evaluateAsString(expression, node);\n return (isEmptyString(floatString)) ? null : Float.valueOf(floatString);\n }\n\n /**\n * Evaluates the specified XPath expression and returns the result as a\n * Long.\n *\n * @param expression The XPath expression to evaluate.\n * @param node The node to run the expression on.\n * @return The Long result.\n * @throws XPathExpressionException If there was a problem processing the\n * specified XPath expression.\n */\n public static Long asLong(String expression, Node node)\n throws XPathExpressionException {\n String longString = evaluateAsString(expression, node);\n return (isEmptyString(longString)) ? null : Long.valueOf(longString);\n }\n\n /**\n * Evaluates the specified XPath expression and returns the result as a\n * Byte.\n *\n * @param expression The XPath expression to evaluate.\n * @param node The node to run the expression on.\n * @return The Byte result.\n * @throws XPathExpressionException If there was a problem processing the\n * specified XPath expression.\n */\n public static Byte asByte(String expression, Node node)\n throws XPathExpressionException {\n String byteString = evaluateAsString(expression, node);\n return (isEmptyString(byteString)) ? null : Byte.valueOf(byteString);\n }\n\n /**\n * Evaluates the specified XPath expression and returns the result as a\n * Date. Assumes that the node's text is formatted as an ISO 8601 date, as\n * specified by xs:dateTime.\n *\n * @param expression The XPath expression to evaluate.\n * @param node The node to run the expression on.\n * @return The Date result.\n * @throws XPathExpressionException If there was a problem processing the\n * specified XPath expression.\n */\n public static Date asDate(String expression, Node node)\n throws XPathExpressionException {\n String dateString = evaluateAsString(expression, node);\n if (isEmptyString(dateString))\n return null;\n\n return DateUtils.parseISO8601Date(dateString);\n }\n\n /**\n * Evaluates the specified xpath expression, base64 decodes the data and\n * returns the result as a ByteBuffer.\n *\n * @param expression The Xpath expression to evaluate.\n * @param node The node on which to evaluate the expression.\n * @return A ByteBuffer of base64 decoded data from the result of evaluating\n * the specified Xpath expression.\n * @throws XPathExpressionException If there are any problems evaluating the\n * Xpath expression.\n */\n public static ByteBuffer asByteBuffer(String expression, Node node)\n throws XPathExpressionException {\n String base64EncodedString = evaluateAsString(expression, node);\n if (isEmptyString(base64EncodedString))\n return null;\n\n if (!isEmpty(node)) {\n byte[] decodedBytes = Base64.decode(base64EncodedString);\n return ByteBuffer.wrap(decodedBytes);\n }\n return null;\n }\n\n /**\n * Returns true if the specified node is null or has no children.\n *\n * @param node The node to test.\n * @return True if the specified node is null or has no children.\n */\n public static boolean isEmpty(Node node) {\n return (node == null);\n }\n\n /**\n * Returns the length of the specified node list.\n *\n * @param list The node list to measure.\n * @return The length of the specified node list.\n */\n public static int nodeLength(NodeList list) {\n return list == null ? 0 : list.getLength();\n }\n\n /**\n * Evaluates the specified expression on the specified node and returns the\n * result as a String.\n *\n * @param expression The Xpath expression to evaluate.\n * @param node The node on which to evaluate the expression.\n * @return The result of evaluating the specified expression, or null if the\n * evaluation didn't return any result.\n * @throws XPathExpressionException If there are any problems evaluating the\n * Xpath expression.\n */\n private static String evaluateAsString(String expression, Node node)\n throws XPathExpressionException {\n if (isEmpty(node))\n return null;\n\n if (!\".\".equals(expression)) {\n /*\n * If the expression being evaluated doesn't select a node, we want\n * to return null to distinguish between cases where a node isn't\n * present (which should be represented as null) and when a node is\n * present, but empty (which should be represented as the empty\n * string). We skip this test if the expression is \".\" since we've\n * already checked that the node exists.\n */\n if (asNode(expression, node) == null)\n return null;\n }\n\n String s = xpath().evaluate(expression, node);\n\n return s.trim();\n }\n\n /**\n * @param nodeName the node name.\n * @param node the node.\n * @return the Node.\n * @throws XPathExpressionException\n */\n public static Node asNode(String nodeName, Node node)\n throws XPathExpressionException {\n if (node == null)\n return null;\n return (Node) xpath().evaluate(nodeName, node, XPathConstants.NODE);\n }\n\n /**\n * @return true if the specified string is null or empty.\n *\n * @param s The string to test.\n * @return True if the specified string is null or empty.\n */\n private static boolean isEmptyString(String s) {\n if (s == null)\n return true;\n if (\"\".equals(s.trim()))\n return true;\n\n return false;\n }\n\n /**\n * @return a new instance of XPath, which is not thread safe and not\n * reentrant.\n */\n public static XPath xpath() {\n return XPathFactory.newInstance().newXPath();\n }\n\n}\n\n", "input": "", "output": "【漏洞类型】XML外部实体(XXE)注入\n\n【严重性】高\n\n【漏洞摘要】此代码存在XML外部实体(XXE)注入漏洞,因为未设置XML解析器的相关安全特性。在`RegionMetadataParser.java`和`XpathUtils.java`中,`DocumentBuilderFactory`实例被创建但没有禁用外部实体解析(如DOCTYPE声明、XInclude以及实体引用扩展)。这可能导致攻击者通过操控XML输入来泄露敏感信息或执行拒绝服务攻击(如读取文件、发起网络请求等)。\n\n【关键代码模式】`DocumentBuilderFactory`实例化及使用,未设置安全特性:\n- `DocumentBuilderFactory.newInstance()`\n- `documentBuilder.parse(input)`\n\n【修复方法说明】通过设置`DocumentBuilderFactory`的`disallow-doctype-decl`特性为`true`,禁用XInclude支持,并禁用实体引用扩展来防止XXE注入。最佳实践是始终对XML解析器进行安全配置,以防止外部实体解析带来的潜在安全风险。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/main/java/spark/resource/ClassPathResource.java\n### 代码内容:\npackage spark.resource;\n\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URL;\n\nimport spark.utils.Assert;\nimport spark.utils.ClassUtils;\nimport spark.utils.StringUtils;\n\n/**\n * {@link Resource} implementation for class path resources.\n * Uses either a given ClassLoader or a given Class for loading resources.\n * <p>Supports resolution as {@code java.io.File} if the class path\n * resource resides in the file system, but not for resources in a JAR.\n * Always supports resolution as URL.\n *\n * @author Juergen Hoeller\n * @author Sam Brannen\n * @see ClassLoader#getResourceAsStream(String)\n * @see Class#getResourceAsStream(String)\n * Code copied from Spring source. Modifications made (mostly removal of methods) by Per Wendel.\n */\npublic class ClassPathResource extends AbstractFileResolvingResource {\n\n private final String path;\n\n private ClassLoader classLoader;\n\n private Class<?> clazz;\n\n /**\n * Create a new ClassPathResource for ClassLoader usage.\n * A leading slash will be removed, as the ClassLoader\n * resource access methods will not accept it.\n * <p>The thread context class loader will be used for\n * loading the resource.\n *\n * @param path the absolute path within the class path\n * @see java.lang.ClassLoader#getResourceAsStream(String)\n * @see spark.utils.ClassUtils#getDefaultClassLoader()\n */\n public ClassPathResource(String path) {\n this(path, null);\n }\n\n /**\n * Create a new ClassPathResource for ClassLoader usage.\n * A leading slash will be removed, as the ClassLoader\n * resource access methods will not accept it.\n *\n * @param path the absolute path within the classpath\n * @param classLoader the class loader to load the resource with,\n * or {@code null} for the thread context class loader\n * @see ClassLoader#getResourceAsStream(String)\n */\n public ClassPathResource(String path, ClassLoader classLoader) {\n Assert.notNull(path, \"Path must not be null\");\n Assert.state(doesNotContainFileColon(path), \"Path must not contain 'file:'\");\n\n String pathToUse = StringUtils.cleanPath(path);\n\n if (pathToUse.startsWith(\"/\")) {\n pathToUse = pathToUse.substring(1);\n }\n\n this.path = pathToUse;\n this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());\n }\n\n private static boolean doesNotContainFileColon(String path) {\n return !path.contains(\"file:\");\n }\n\n /**\n * Create a new ClassPathResource with optional ClassLoader and Class.\n * Only for internal usage.\n *\n * @param path relative or absolute path within the classpath\n * @param classLoader the class loader to load the resource with, if any\n * @param clazz the class to load resources with, if any\n */\n protected ClassPathResource(String path, ClassLoader classLoader, Class<?> clazz) {\n this.path = StringUtils.cleanPath(path);\n this.classLoader = classLoader;\n this.clazz = clazz;\n }\n\n /**\n * Return the path for this resource (as resource path within the class path).\n *\n * @return the path\n */\n public final String getPath() {\n return this.path;\n }\n\n /**\n * This implementation checks for the resolution of a resource URL.\n *\n * @return if exists.\n * @see java.lang.ClassLoader#getResource(String)\n * @see java.lang.Class#getResource(String)\n */\n @Override\n public boolean exists() {\n URL url;\n if (this.clazz != null) {\n url = this.clazz.getResource(this.path);\n } else {\n url = this.classLoader.getResource(this.path);\n }\n return (url != null);\n }\n\n /**\n * This implementation opens an InputStream for the given class path resource.\n *\n * @return the input stream.\n * @see java.lang.ClassLoader#getResourceAsStream(String)\n * @see java.lang.Class#getResourceAsStream(String)\n */\n @Override\n public InputStream getInputStream() throws IOException {\n InputStream is;\n if (this.clazz != null) {\n is = this.clazz.getResourceAsStream(this.path);\n } else {\n is = this.classLoader.getResourceAsStream(this.path);\n }\n if (is == null) {\n throw new FileNotFoundException(getDescription() + \" cannot be opened because it does not exist\");\n }\n return is;\n }\n\n /**\n * This implementation returns a URL for the underlying class path resource.\n *\n * @return the url.\n * @see java.lang.ClassLoader#getResource(String)\n * @see java.lang.Class#getResource(String)\n */\n @Override\n public URL getURL() throws IOException {\n URL url;\n if (this.clazz != null) {\n url = this.clazz.getResource(this.path);\n } else {\n url = this.classLoader.getResource(this.path);\n }\n if (url == null) {\n throw new FileNotFoundException(getDescription() + \" cannot be resolved to URL because it does not exist\");\n }\n return url;\n }\n\n /**\n * This implementation creates a ClassPathResource, applying the given path\n * relative to the path of the underlying resource of this descriptor.\n *\n * @return the resource.\n * @see spark.utils.StringUtils#applyRelativePath(String, String)\n */\n @Override\n public Resource createRelative(String relativePath) {\n String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);\n return new ClassPathResource(pathToUse, this.classLoader, this.clazz);\n }\n\n /**\n * This implementation returns the name of the file that this class path\n * resource refers to.\n *\n * @return the file name.\n * @see spark.utils.StringUtils#getFilename(String)\n */\n @Override\n public String getFilename() {\n return StringUtils.getFilename(this.path);\n }\n\n /**\n * This implementation returns a description that includes the class path location.\n *\n * @return the description.\n */\n @Override\n public String getDescription() {\n StringBuilder builder = new StringBuilder(\"class path resource [\");\n String pathToUse = path;\n if (this.clazz != null && !pathToUse.startsWith(\"/\")) {\n builder.append(ClassUtils.classPackageAsResourcePath(this.clazz));\n builder.append('/');\n }\n if (pathToUse.startsWith(\"/\")) {\n pathToUse = pathToUse.substring(1);\n }\n builder.append(pathToUse);\n builder.append(']');\n return builder.toString();\n }\n\n /**\n * This implementation compares the underlying class path locations.\n *\n * @return if equals.\n */\n @Override\n public boolean equals(Object obj) {\n if (obj == this) {\n return true;\n }\n if (obj instanceof ClassPathResource) {\n ClassPathResource otherRes = (ClassPathResource) obj;\n\n ClassLoader thisLoader = this.classLoader;\n ClassLoader otherLoader = otherRes.classLoader;\n\n return (this.path.equals(otherRes.path) &&\n thisLoader.equals(otherLoader) &&\n this.clazz.equals(otherRes.clazz));\n }\n return false;\n }\n\n /**\n * This implementation returns the hash code of the underlying\n * class path location.\n *\n * @return the hash code.\n */\n @Override\n public int hashCode() {\n return this.path.hashCode();\n }\n\n}\n\n", "input": "", "output": "【漏洞类型】路径遍历漏洞\n【严重性】High\n【漏洞摘要】此代码在加载资源时没有对路径的合法性进行充分验证,可能导致攻击者通过构造特定的路径字符串来访问不应被访问的文件。具体表现为`ClassPathResource`构造函数中未对路径的合法性进行验证。\n【关键代码模式】`ClassLoader.getResourceAsStream(String)`、`ClassLoader.getResource(String)`、`Class.getResourceAsStream(String)`、`Class.getResource(String)`、`StringUtils.cleanPath(String)`、`doesNotContainFileColon(String)`。\n【修复方法说明】修复方法通过引入`isValid`函数和`isInvalidPath`函数来验证路径的合法性,避免了路径遍历漏洞。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nxwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/internal/mandatory/XWikiCommentsDocumentInitializer.java\n### 代码内容:\n/*\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n */\npackage com.xpn.xwiki.internal.mandatory;\n\nimport javax.inject.Named;\nimport javax.inject.Singleton;\n\nimport org.xwiki.component.annotation.Component;\nimport org.xwiki.model.reference.LocalDocumentReference;\nimport org.xwiki.model.reference.RegexEntityReference;\n\nimport com.xpn.xwiki.XWiki;\nimport com.xpn.xwiki.objects.BaseObjectReference;\nimport com.xpn.xwiki.objects.classes.BaseClass;\nimport com.xpn.xwiki.objects.classes.TextAreaClass;\n\n/**\n * Update XWiki.XWikiComments document with all required informations.\n *\n * @version $Id$\n * @since 4.3M1\n */\n@Component\n@Named(XWikiCommentsDocumentInitializer.LOCAL_REFERENCE_STRING)\n@Singleton\npublic class XWikiCommentsDocumentInitializer extends AbstractCommentsDocumentInitializer\n{\n /**\n * The name of the initialized document.\n * \n * @since 9.4RC1\n */\n public static final String NAME = \"XWikiComments\";\n\n /**\n * The local reference of the initialized document as String.\n * \n * @since 9.4RC1\n */\n public static final String LOCAL_REFERENCE_STRING = XWiki.SYSTEM_SPACE + '.' + NAME;\n\n /**\n * The local reference of the initialized document as String.\n * \n * @since 9.4RC1\n */\n public static final LocalDocumentReference LOCAL_REFERENCE = new LocalDocumentReference(XWiki.SYSTEM_SPACE, NAME);\n\n /**\n * A regex to match any object reference with initialized class.\n * \n * @since 9.4RC1\n */\n public static final RegexEntityReference OBJECT_REFERENCE = BaseObjectReference.any(LOCAL_REFERENCE_STRING);\n\n /**\n * Default constructor.\n */\n public XWikiCommentsDocumentInitializer()\n {\n super(LOCAL_REFERENCE);\n }\n\n @Override\n protected void createClass(BaseClass xclass)\n {\n super.createClass(xclass);\n\n xclass.addTextAreaField(\"highlight\", \"Highlighted Text\", 40, 2);\n xclass.addNumberField(\"replyto\", \"Reply To\", 5, \"integer\");\n\n String commentPropertyName = \"comment\";\n xclass.addTextAreaField(commentPropertyName, \"Comment\", 40, 5);\n\n // FIXME: Ensure that the comment text editor is set to its default value after an upgrade. This should be\n // handled in a cleaner way in BaseClass#addTextAreaField. See: https://jira.xwiki.org/browse/XWIKI-17605\n TextAreaClass comment = (TextAreaClass) xclass.getField(commentPropertyName);\n comment.setEditor((String) null);\n }\n}\n\n", "input": "", "output": "【漏洞类型】业务逻辑安全漏洞\n【严重性】Medium\n【漏洞摘要】原始代码在创建XWikiComments文档的类时,调用`xclass.addTextAreaField`方法时没有设置`restricted`参数。这意味着在XWiki中,用户可以上传任何内容到这个字段中,包括潜在的恶意代码或脚本。这可能会导致跨站脚本攻击(XSS)或其他业务逻辑安全问题。\n【关键代码模式】`xclass.addTextAreaField`方法在调用时没有设置`restricted`参数。\n【修复方法说明】修复方法通过在`xclass.addTextAreaField`方法中添加`true`参数来设置`restricted`,从而防止用户上传恶意内容,减少了XSS攻击的风险。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nreadme.md\n### 代码内容:\n# Markdown to PDF\n\n[](https://github.com/simonhaenisch/md-to-pdf/actions)\n[](https://www.npmjs.com/md-to-pdf)\n[](https://github.com/xojs/xo)\n\n\n\n**A simple and hackable CLI tool for converting markdown to pdf**. It uses [Marked](https://github.com/markedjs/marked) to convert `markdown` to `html` and [Puppeteer](https://github.com/GoogleChrome/puppeteer) (headless Chromium) to further convert the `html` to `pdf`. It also uses [highlight.js](https://github.com/isagalaev/highlight.js) for code highlighting. The whole source code of this tool is ~only \\~250 lines of JS~ ~400 lines of Typescript and ~100 lines of CSS, so it is easy to clone and customize.\n\n**Highlights:**\n\n- Concurrently convert multiple Markdown files\n- Watch mode\n- Use your own or remote stylesheets/scripts\n- Front-matter for configuration\n- Headers and Footers\n- Page Breaks\n- Syntax highlighting in code blocks\n- Extend the options of the underlying tools\n- Programmatic API\n- Supports `stdio`\n\n## Installation\n\n**Option 1: NPM**\n\n```sh\nnpm i -g md-to-pdf\n```\n\n**Option 2: Git**\n\nIf you want to have your own copy to hack around with, clone the repository instead:\n\n```sh\ngit clone \"https://github.com/simonhaenisch/md-to-pdf\"\ncd md-to-pdf\nnpm link # or npm i -g\n```\n\nThen the commands `md-to-pdf` and `md2pdf` (as a shorthand) will be globally available in your cli. Use `npm start` to start the TypeScript compiler (`tsc`) in watch mode.\n\n## Update\n\nIf you installed via npm, run `npm i -g md-to-pdf@latest` in your CLI. If you cloned this repository instead, you can simply do a `git pull` to get the latest changes from the master branch, then do `npm run build` to re-build. Unless there have been changes to packages (i. e. `package-lock.json`), you don't need to re-install the package (because NPM 5+ uses symlinks, at least on Unix systems).\n\n## Usage\n\n```\n$ md-to-pdf [options] path/to/file.md\n\nOptions:\n\n -h, --help ............... Output usage information\n -v, --version ............ Output version\n -w, --watch .............. Watch the current file(s) for changes\n --watch-options .......... Options for Chokidar's watch call\n --basedir ................ Base directory to be served by the file server\n --stylesheet ............. Path to a local or remote stylesheet (can be passed multiple times)\n --css .................... String of styles\n --document-title ......... Name of the HTML Document.\n --body-class ............. Classes to be added to the body tag (can be passed multiple times)\n --page-media-type ........ Media type to emulate the page with (default: screen)\n --highlight-style ........ Style to be used by highlight.js (default: github)\n --marked-options ......... Set custom options for marked (as a JSON string)\n --pdf-options ............ Set custom options for the generated PDF (as a JSON string)\n --launch-options ......... Set custom launch options for Puppeteer\n --port ................... Set the port to run the http server on\n --md-file-encoding ....... Set the file encoding for the markdown file\n --stylesheet-encoding .... Set the file encoding for the stylesheet\n --as-html ................ Output as HTML instead\n --config-file ............ Path to a JSON or JS configuration file\n --devtools ............... Open the browser with devtools instead of creating PDF\n```\n\nThe pdf is generated into the same directory as the source file and uses the same filename (with `.pdf` extension) by default. Multiple files can be specified by using shell globbing, e. g.:\n\n```sh\nmd-to-pdf ./**/*.md\n```\n\n_(If you use bash, you might need to enable the `globstar` shell option to make recursive globbing work.)_\n\nAlternatively, you can pass the markdown in from `stdin` and pipe its `stdout` into a target file:\n\n```sh\ncat file.md | md-to-pdf > path/to/output.pdf\n```\n\n_Tip: You can concatenate multiple files using `cat file1.md file2.md`._\n\nThe current working directory (`process.cwd()`) serves as the base directory of the file server by default. This can be adjusted with the `--basedir` flag (or equivalent config option).\n\n#### Watch Mode\n\nWatch mode (`--watch`) uses Chokidar's `watch` method on the markdown file. If you're having issues, you can adjust the watch options via the config (`watch_options`) or `--watch-options` CLI arg. The `awaitWriteFinish` option might be particularly useful if you use editor plugins (e. g. TOC generators) that modify and save the file after the initial save. Check out the [Chokidar docs](https://github.com/paulmillr/chokidar#api) for a full list of options.\n\nNote that Preview on macOS does not automatically reload the preview when the file has changed (or at least not reliably). There are PDF viewers available that can check for file changes and offer auto-reload (e. g. [Skim](https://skim-app.sourceforge.io/)'s \"Sync\" feature).\n\n#### Programmatic API\n\nThe programmatic API is very simple: it only exposes one function that accepts either a `path` to or `content` of a markdown file, and an optional config object (which can be used to specify the output file destination).\n\n```js\nconst fs = require('fs');\nconst { mdToPdf } = require('md-to-pdf');\n\n(async () => {\n\tconst pdf = await mdToPdf({ path: 'readme.md' }).catch(console.error);\n\n\tif (pdf) {\n\t\tfs.writeFileSync(pdf.filename, pdf.content);\n\t}\n})();\n```\n\nThe function throws an error if anything goes wrong, which can be handled by catching the rejected promise. If you set the `dest` option in the config, the file will be written to the specified location straight away:\n\n```js\nawait mdToPdf({ content: '# Hello, World' }, { dest: 'path/to/output.pdf' });\n```\n\n#### Page Break\n\nPlace an element with class `page-break` to force a page break at a certain point of the document (uses the CSS rule `page-break-after: always`), e. g.:\n\n```html\n<div class=\"page-break\"></div>\n```\n\n#### Header/Footer\n\nUse `headerTemplate` and `footerTemplate` of Puppeteer's [`page.pdf()` options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagepdfoptions). If either of the two is set, then `displayHeaderFooter` will be enabled by default. It's possible to inject a few dynamic values like page numbers by using certain class names, as stated in the Puppeteer docs. Please note that for some reason the font-size defaults to 1pt, and you need to make sure to have enough page margin, otherwise your header/footer might be overlayed by your content. If you add a `<style/>` tag in either of the templates, it will be applied to both header and footer.\n\nExample markdown frontmatter config that prints the date in the header and the page number in the footer:\n\n```markdown\n---\npdf_options:\n format: a4\n margin: 30mm 20mm\n printBackground: true\n headerTemplate: |-\n <style>\n section {\n margin: 0 auto;\n font-family: system-ui;\n font-size: 11px;\n }\n </style>\n <section>\n <span class=\"title\"></span>\n <span class=\"date\"></span>\n </section>\n footerTemplate: |-\n <section>\n <div>\n Page <span class=\"pageNumber\"></span>\n of <span class=\"totalPages\"></span>\n </div>\n </section>\n---\n```\n\nRefer to the [Puppeteer docs](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagepdfoptions) for more info about headers and footers.\n\n#### Formulas\n\nThis can be achieved with [MathJax](https://www.mathjax.org/). A simple example can be found in [`/src/test/mathjax`](/src/test/mathjax).\n\n#### Default and Advanced Options\n\nFor default and advanced options see the following links. The default highlight.js styling for code blocks is `github`. The default PDF options are the A4 format and some margin (see `lib/config.ts` for the full default config).\n\n- [Marked Advanced Options](https://github.com/markedjs/marked/blob/master/docs/USING_ADVANCED.md)\n- [Puppeteer PDF Options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagepdfoptions)\n- [Puppeteer Launch Options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions)\n- [highlight.js Styles](https://github.com/isagalaev/highlight.js/tree/master/src/styles)\n\n## Options\n\n| Option | Examples |\n| ----------------------- | --------------------------------------------------------------------- |\n| `--basedir` | `path/to/folder` |\n| `--stylesheet` | `path/to/style.css`, `https://example.org/stylesheet.css` |\n| `--css` | `body { color: tomato; }` |\n| `--document-title` | `Read me` |\n| `--body-class` | `markdown-body` |\n| `--page-media-type` | `print` |\n| `--highlight-style` | `monokai`, `solarized-light` |\n| `--marked-options` | `'{ \"gfm\": false }'` |\n| `--pdf-options` | `'{ \"format\": \"Letter\", \"margin\": \"20mm\", \"printBackground\": true }'` |\n| `--launch-options` | `'{ \"args\": [\"--no-sandbox\"] }'` |\n| `--port` | `3000` |\n| `--md-file-encoding` | `utf-8`, `windows1252` |\n| `--stylesheet-encoding` | `utf-8`, `windows1252` |\n| `--config-file` | `path/to/config.json` |\n\n**`margin`:** instead of an object (as stated in the Puppeteer docs), it is also possible to pass a CSS-like string, e. g. `1em` (all), `1in 2in` (top/bottom right/left), `10mm 20mm 30mm` (top right/left bottom) or `1px 2px 3px 4px` (top right bottom left).\n\n**`highlight-style`:** if you set a highlight style with a background color, make sure that `\"printBackground\": true` is set in the pdf options.\n\nThe options can also be set with front-matter or a config file (except `--md-file-encoding` can't be set by front-matter). In that case, remove the leading two hyphens (`--`) from the cli argument name and replace the hyphens (`-`) with underscores (`_`). `--stylesheet` and `--body-class` can be passed multiple times (i. e. to create an array). It's possible to set the output path for the PDF as `dest` in the config. If the same config option exists in multiple places, the priority (from low to high) is: defaults, config file, front-matter, cli arguments.\n\nExample front-matter:\n\n```markdown\n---\ndest: ./path/to/output.pdf\nstylesheet:\n - path/to/style.css\nbody_class: markdown-body\nhighlight_style: monokai\npdf_options:\n format: A5\n margin: 10mm\n printBackground: true\n---\n\n# Content\n```\n\nThe config file can be a Javascript file that exports a config object, which gives you the full power of the eco-system (e. g. for advanced header/footer templates); or it can also be a `.json` if you like it simple.\n\nExample `config.js`:\n\n```js\nmodule.exports = {\n\tstylesheet: ['path/to/style.css', 'https://example.org/stylesheet.css'],\n\tcss: `body { color: tomato; }`,\n\tbody_class: 'markdown-body',\n\tmarked_options: {\n\t\theaderIds: false,\n\t\tsmartypants: true,\n\t},\n\tpdf_options: {\n\t\tformat: 'A5',\n\t\tmargin: '20mm',\n\t\tprintBackground: true,\n\t},\n\tstylesheet_encoding: 'utf-8',\n};\n```\n\nExample `config.json`:\n\n```json\n{\n\t\"highlight_style\": \"monokai\",\n\t\"body_class\": [\"dark\", \"content\"]\n}\n```\n\n#### Github Styles\n\nHere is an example front-matter for how to get Github-like output:\n\n```markdown\n---\nstylesheet: https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/2.10.0/github-markdown.min.css\nbody_class: markdown-body\ncss: |-\n .page-break { page-break-after: always; }\n .markdown-body { font-size: 11px; }\n .markdown-body pre > code { white-space: pre-wrap; }\n---\n```\n\n## Security Consideration\n\nBy default, this tool serves the current working directory via a http server on `localhost` on a relatively random port (or the port you specify), and that server gets shut down when the process exits (or as soon as it is killed). Please be aware that for the duration of the process this server will be accessible on your local network, and therefore all files within the served folder that the process has permission to read. So as a suggestion, maybe don't run this in watch mode in your system's root folder. 😉\n\n## Customization/Development\n\nAfter cloning and linking/installing globally (`npm link`), just run the transpiler in watch mode (`npm start`). Then you can start making changes to the files and Typescript will transpile them on save. NPM 5+ uses symlinks for locally installed global packages, so all changes are reflected immediately without needing to re-install the package (except when there have been changes to required packages, then re-install using `npm i`). This also means that you can just do a `git pull` to get the latest version onto your machine.\n\nIdeas, feature requests and PRs are welcome. Just keep it simple! 🤓\n\n## Credits\n\nI want to thank the following people:\n\n- [imcvampire](https://github.com/imcvampire) for handing over the npm package name.\n- [Sindre Sorhus](https://github.com/sindresorhus) and [Zeit](https://github.com/zeit) for inspiration on how to write cli tools.\n- [Josh Bruce](https://github.com/joshbruce) for [reviving Marked](https://github.com/markedjs/marked/issues/1106).\n\n## License\n\n[MIT](/license).\n\n## 内容2\n### 文件路径:\nsrc/cli.ts\n### 代码内容:\n#!/usr/bin/env node\n\n// --\n// Packages\n\nimport arg from 'arg';\nimport chalk from 'chalk';\nimport { watch, WatchOptions } from 'chokidar';\nimport getPort from 'get-port';\nimport getStdin from 'get-stdin';\nimport Listr from 'listr';\nimport path from 'path';\nimport { PackageJson } from '.';\nimport { Config, defaultConfig } from './lib/config';\nimport { help } from './lib/help';\nimport { setProcessAndTermTitle } from './lib/helpers';\nimport { convertMdToPdf } from './lib/md-to-pdf';\nimport { closeServer, serveDirectory } from './lib/serve-dir';\nimport { validateNodeVersion } from './lib/validate-node-version';\n\n// --\n// Configure CLI Arguments\n\nexport const cliFlags = arg({\n\t'--help': Boolean,\n\t'--version': Boolean,\n\t'--basedir': String,\n\t'--watch': Boolean,\n\t'--watch-options': String,\n\t'--stylesheet': [String],\n\t'--css': String,\n\t'--document-title': String,\n\t'--body-class': [String],\n\t'--page-media-type': String,\n\t'--highlight-style': String,\n\t'--marked-options': String,\n\t'--html-pdf-options': String,\n\t'--pdf-options': String,\n\t'--launch-options': String,\n\t'--port': Number,\n\t'--md-file-encoding': String,\n\t'--stylesheet-encoding': String,\n\t'--as-html': Boolean,\n\t'--config-file': String,\n\t'--devtools': Boolean,\n\n\t// aliases\n\t'-h': '--help',\n\t'-v': '--version',\n\t'-w': '--watch',\n});\n\n// --\n// Run\n\nmain(cliFlags, defaultConfig).catch((error) => {\n\tconsole.error(error);\n\tprocess.exit(1);\n});\n\n// --\n// Define Main Function\n\nasync function main(args: typeof cliFlags, config: Config) {\n\tsetProcessAndTermTitle('md-to-pdf');\n\n\tif (!validateNodeVersion()) {\n\t\tthrow new Error('Please use a Node.js version that satisfies the version specified in the engines field.');\n\t}\n\n\tif (args['--version']) {\n\t\treturn console.log((require('../package.json') as PackageJson).version);\n\t}\n\n\tif (args['--help']) {\n\t\treturn help();\n\t}\n\n\t/**\n\t * 1. Get input.\n\t */\n\n\tconst files = args._;\n\n\tconst stdin = await getStdin();\n\n\tif (files.length === 0 && !stdin) {\n\t\treturn help();\n\t}\n\n\t/**\n\t * 2. Read config file and merge it into the config object.\n\t */\n\n\tif (args['--config-file']) {\n\t\ttry {\n\t\t\tconst configFile: Partial<Config> = require(path.resolve(args['--config-file']));\n\n\t\t\tconfig = {\n\t\t\t\t...config,\n\t\t\t\t...configFile,\n\t\t\t\tpdf_options: { ...config.pdf_options, ...configFile.pdf_options },\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconsole.warn(chalk.red(`Warning: couldn't read config file: ${path.resolve(args['--config-file'])}`));\n\t\t\tconsole.warn(error instanceof SyntaxError ? error.message : error);\n\t\t}\n\t}\n\n\t/**\n\t * 3. Start the file server.\n\t */\n\n\tif (args['--basedir']) {\n\t\tconfig.basedir = args['--basedir'];\n\t}\n\n\tconfig.port = args['--port'] ?? (await getPort());\n\n\tconst server = await serveDirectory(config);\n\n\t/**\n\t * 4. Either process stdin or create a Listr task for each file.\n\t */\n\n\tif (stdin) {\n\t\tawait convertMdToPdf({ content: stdin }, config, args)\n\t\t\t.then(async () => closeServer(server))\n\t\t\t.catch(async (error: Error) => {\n\t\t\t\tawait closeServer(server);\n\n\t\t\t\tthrow error;\n\t\t\t});\n\n\t\treturn;\n\t}\n\n\tconst getListrTask = (file: string) => ({\n\t\ttitle: `generating ${args['--as-html'] ? 'HTML' : 'PDF'} from ${chalk.underline(file)}`,\n\t\ttask: async () => convertMdToPdf({ path: file }, config, args),\n\t});\n\n\tawait new Listr(files.map(getListrTask), { concurrent: true, exitOnError: false })\n\t\t.run()\n\t\t.then(() => {\n\t\t\tif (args['--watch']) {\n\t\t\t\tconsole.log(chalk.bgBlue('\\n watching for changes \\n'));\n\n\t\t\t\tconst watchOptions = args['--watch-options']\n\t\t\t\t\t? (JSON.parse(args['--watch-options']) as WatchOptions)\n\t\t\t\t\t: config.watch_options;\n\n\t\t\t\twatch(files, watchOptions).on('change', async (file) =>\n\t\t\t\t\tnew Listr([getListrTask(file)], { exitOnError: false }).run().catch(console.error),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tserver.close();\n\t\t\t}\n\t\t})\n\t\t.catch((error: Error) => {\n\t\t\t/**\n\t\t\t * In watch mode the error needs to be shown immediately because the `main` function's catch handler will never execute.\n\t\t\t *\n\t\t\t * @todo is this correct or does `main` actually finish and the process is just kept alive because of the file server?\n\t\t\t */\n\t\t\tif (args['--watch']) {\n\t\t\t\treturn console.error(error);\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t});\n}\n\n## 内容3\n### 文件路径:\nsrc/lib/config.ts\n### 代码内容:\nimport { WatchOptions } from 'chokidar';\nimport { MarkedOptions } from 'marked';\nimport { resolve } from 'path';\nimport { FrameAddScriptTagOptions, launch, PDFOptions } from 'puppeteer';\n\nexport const defaultConfig: Config = {\n\tbasedir: process.cwd(),\n\tstylesheet: [resolve(__dirname, '..', '..', 'markdown.css')],\n\tscript: [],\n\tcss: '',\n\tdocument_title: '',\n\tbody_class: [],\n\tpage_media_type: 'screen',\n\thighlight_style: 'github',\n\tmarked_options: {},\n\tpdf_options: {\n\t\tprintBackground: true,\n\t\tformat: 'a4',\n\t\tmargin: {\n\t\t\ttop: '30mm',\n\t\t\tright: '40mm',\n\t\t\tbottom: '30mm',\n\t\t\tleft: '20mm',\n\t\t},\n\t},\n\tlaunch_options: {},\n\tmd_file_encoding: 'utf-8',\n\tstylesheet_encoding: 'utf-8',\n\tas_html: false,\n\tdevtools: false,\n};\n\n/**\n * In config keys, dashes of cli flag names are replaced with underscores.\n */\nexport type Config = PdfConfig | HtmlConfig;\n\nexport interface PdfConfig extends BasicConfig {\n\t/**\n\t * If true, generate HTML output instead of PDF output. Default: `false`.\n\t */\n\tas_html?: false;\n}\n\nexport interface HtmlConfig extends BasicConfig {\n\t/**\n\t * If true, generate HTML output instead of PDF output. Default: `false`.\n\t */\n\tas_html: true;\n}\n\ninterface BasicConfig {\n\t/**\n\t * Base directory to be served by the file server.\n\t */\n\tbasedir: string;\n\n\t/**\n\t * Optional destination path for the output file (including the extension).\n\t */\n\tdest?: string;\n\n\t/**\n\t * List of css files to use for styling.\n\t *\n\t * @todo change to `FrameAddStyleTagOptions` (will be a breaking change)\n\t */\n\tstylesheet: string[];\n\n\t/**\n\t * Custom css styles.\n\t */\n\tcss: string;\n\n\t/**\n\t * List of scripts to load into the page.\n\t *\n\t * @see https://github.com/puppeteer/puppeteer/blob/main/docs/api.md#pageaddscripttagoptions\n\t */\n\tscript: FrameAddScriptTagOptions[];\n\n\t/**\n\t * Name of the HTML Document.\n\t */\n\tdocument_title: string;\n\n\t/**\n\t * List of classes for the body tag.\n\t */\n\tbody_class: string[];\n\n\t/**\n\t * Media type to emulate the page with.\n\t */\n\tpage_media_type: 'screen' | 'print';\n\n\t/**\n\t * Highlight.js stylesheet to use (without the .css extension).\n\t *\n\t * @see https://github.com/isagalaev/highlight.js/tree/master/src/styles\n\t */\n\thighlight_style: string;\n\n\t/**\n\t * Options for the Marked parser.\n\t *\n\t * @see https://marked.js.org/#/USING_ADVANCED.md\n\t */\n\tmarked_options: MarkedOptions;\n\n\t/**\n\t * PDF options for Puppeteer.\n\t *\n\t * @see https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagepdfoptions\n\t */\n\tpdf_options: PDFOptions;\n\n\t/**\n\t * Launch options for Puppeteer.\n\t *\n\t * @see https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions\n\t */\n\tlaunch_options: PuppeteerLaunchOptions;\n\n\t/**\n\t * Markdown file encoding. Default: `utf-8`.\n\t */\n\tmd_file_encoding: string;\n\n\t/**\n\t * CSS stylesheet encoding. Default: `utf-8`.\n\t */\n\tstylesheet_encoding: string;\n\n\t/**\n\t * If true, open chromium with devtools instead of saving the pdf. This is\n\t * meant for development only, to inspect the rendered HTML.\n\t */\n\tdevtools: boolean;\n\n\t/**\n\t * Port to run the local server on.\n\t */\n\tport?: number;\n\n\t/**\n\t * Options to pass to Chokidar's `watch` call.\n\t *\n\t * This is specifically useful when running into issues when editor plugins trigger additional saves after the initial save.\n\t */\n\twatch_options?: WatchOptions;\n}\n\nexport type PuppeteerLaunchOptions = Parameters<typeof launch>[0];\n\n## 内容4\n### 文件路径:\nsrc/lib/help.ts\n### 代码内容:\nimport chalk from 'chalk';\n\nconst helpText = `\n ${chalk.bold('$ md-to-pdf')} [options] path/to/file.md\n\n ${chalk.dim.underline.bold('Options:')}\n\n -h, --help ${chalk.dim('...............')} Output usage information\n -v, --version ${chalk.dim('............')} Output version\n -w, --watch ${chalk.dim('..............')} Watch the current file(s) for changes\n\t\t--watch-options ${chalk.dim('..........')} Options for Chokidar's watch call\n --basedir ${chalk.dim('................')} Base directory to be served by the file server\n --stylesheet ${chalk.dim('.............')} Path to a local or remote stylesheet (can be passed multiple times)\n --css ${chalk.dim('....................')} String of styles\n --document-title ${chalk.dim('.........')} Name of the HTML Document.\n --body-class ${chalk.dim('.............')} Classes to be added to the body tag (can be passed multiple times)\n --page-media-type ${chalk.dim('........')} Media type to emulate the page with (default: screen)\n --highlight-style ${chalk.dim('........')} Style to be used by highlight.js (default: github)\n --marked-options ${chalk.dim('.........')} Set custom options for marked (as a JSON string)\n --pdf-options ${chalk.dim('............')} Set custom options for the generated PDF (as a JSON string)\n --launch-options ${chalk.dim('.........')} Set custom launch options for Puppeteer\n --port ${chalk.dim('...................')} Set the port to run the http server on\n --md-file-encoding ${chalk.dim('.......')} Set the file encoding for the markdown file\n --stylesheet-encoding ${chalk.dim('....')} Set the file encoding for the stylesheet\n --as-html ${chalk.dim('................')} Output as HTML instead\n --config-file ${chalk.dim('............')} Path to a JSON or JS configuration file\n --devtools ${chalk.dim('...............')} Open the browser with devtools instead of creating PDF\n\n ${chalk.dim.underline.bold('Examples:')}\n\n ${chalk.gray('–')} Convert ./file.md and save to ./file.pdf\n\n ${chalk.cyan('$ md-to-pdf file.md')}\n\n ${chalk.gray('–')} Convert all markdown files in current directory\n\n ${chalk.cyan('$ md-to-pdf ./*.md')}\n\n ${chalk.gray('–')} Convert all markdown files in current directory recursively\n\n ${chalk.cyan('$ md-to-pdf ./**/*.md')}\n\n ${chalk.gray('–')} Convert and enable watch mode\n\n ${chalk.cyan('$ md-to-pdf ./*.md -w')}\n\n ${chalk.gray('–')} Convert and enable watch mode with custom options\n\n ${chalk.cyan('$ md-to-pdf ./*.md --watch --watch-options \\'{ \"atomic\": true }\\'')}\n\n ${chalk.gray('–')} Convert path/to/file.md with a different base directory\n\n ${chalk.cyan('$ md-to-pdf path/to/file.md --basedir path')}\n\n ${chalk.gray('–')} Convert file.md using custom-markdown.css\n\n ${chalk.cyan('$ md-to-pdf file.md --stylesheet custom-markdown.css')}\n\n ${chalk.gray('–')} Convert file.md using the Monokai theme for code highlighting\n\n ${chalk.cyan('$ md-to-pdf file.md --highlight-style monokai')}\n\n ${chalk.gray('–')} Convert file.md using custom page options\n\n ${chalk.cyan('$ md-to-pdf file.md --pdf-options \\'{ \"format\": \"Letter\" }\\'')}\n\n ${chalk.gray('–')} Convert file.md but save the intermediate HTML instead\n\n ${chalk.cyan('$ md-to-pdf file.md --as-html')}\n`;\n\nexport const help = () => console.log(helpText);\n\n## 内容5\n### 文件路径:\nsrc/lib/md-to-pdf.ts\n### 代码内容:\nimport { promises as fs } from 'fs';\nimport grayMatter from 'gray-matter';\nimport { dirname, resolve } from 'path';\nimport { Config } from './config';\nimport { generateOutput } from './generate-output';\nimport { getHtml } from './get-html';\nimport { getOutputFilePath } from './get-output-file-path';\nimport { getMarginObject } from './helpers';\nimport { readFile } from './read-file';\n\ntype CliArgs = typeof import('../cli').cliFlags;\n\n/**\n * Convert markdown to pdf.\n */\nexport const convertMdToPdf = async (\n\tinput: { path: string } | { content: string },\n\tconfig: Config,\n\targs: CliArgs = {} as CliArgs,\n) => {\n\tconst mdFileContent =\n\t\t'content' in input\n\t\t\t? input.content\n\t\t\t: await readFile(input.path, args['--md-file-encoding'] ?? config.md_file_encoding);\n\n\tconst { content: md, data: frontMatterConfig } = grayMatter(mdFileContent);\n\n\t// merge front-matter config\n\tconfig = {\n\t\t...config,\n\t\t...(frontMatterConfig as Config),\n\t\tpdf_options: { ...config.pdf_options, ...frontMatterConfig.pdf_options },\n\t};\n\n\tconst { headerTemplate, footerTemplate, displayHeaderFooter } = config.pdf_options;\n\n\tif ((headerTemplate || footerTemplate) && displayHeaderFooter === undefined) {\n\t\tconfig.pdf_options.displayHeaderFooter = true;\n\t}\n\n\tconst arrayOptions = ['body_class', 'script', 'stylesheet'] as const;\n\n\t// sanitize frontmatter array options\n\tfor (const option of arrayOptions) {\n\t\tif (!Array.isArray(config[option])) {\n\t\t\tconfig[option] = [config[option]].filter(Boolean) as any;\n\t\t}\n\t}\n\n\tconst jsonArgs = new Set(['--marked-options', '--pdf-options', '--launch-options']);\n\n\t// merge cli args into config\n\tfor (const arg of Object.entries(args)) {\n\t\tconst [argKey, argValue] = arg as [string, string];\n\t\tconst key = argKey.slice(2).replace(/-/g, '_');\n\n\t\t(config as Record<string, any>)[key] = jsonArgs.has(argKey) ? JSON.parse(argValue) : argValue;\n\t}\n\n\t// sanitize the margin in pdf_options\n\tif (typeof config.pdf_options.margin === 'string') {\n\t\tconfig.pdf_options.margin = getMarginObject(config.pdf_options.margin);\n\t}\n\n\t// set output destination\n\tif (config.dest === undefined) {\n\t\tconfig.dest = 'path' in input ? getOutputFilePath(input.path, config.as_html ? 'html' : 'pdf') : 'stdout';\n\t}\n\n\tconst highlightStylesheet = resolve(\n\t\tdirname(require.resolve('highlight.js')),\n\t\t'..',\n\t\t'styles',\n\t\t`${config.highlight_style}.css`,\n\t);\n\n\tconfig.stylesheet = [...new Set([...config.stylesheet, highlightStylesheet])];\n\n\tconst html = getHtml(md, config);\n\n\tconst relativePath = 'path' in input ? resolve(input.path).replace(config.basedir, '') : '/';\n\n\tconst output = await generateOutput(html, relativePath, config);\n\n\tif (!output) {\n\t\tif (config.devtools) {\n\t\t\tthrow new Error('No file is generated with --devtools.');\n\t\t}\n\n\t\tthrow new Error(`Failed to create ${config.as_html ? 'HTML' : 'PDF'}.`);\n\t}\n\n\tif (output.filename) {\n\t\tif (output.filename === 'stdout') {\n\t\t\tprocess.stdout.write(output.content);\n\t\t} else {\n\t\t\tawait fs.writeFile(output.filename, output.content);\n\t\t}\n\t}\n\n\treturn output;\n};\n\n", "input": "", "output": "【漏洞类型】远程代码执行(RCE)\n【严重性】High\n【漏洞摘要】此代码存在远程代码执行漏洞,主要问题在于`gray-matter`库的默认配置允许执行JavaScript代码。攻击者可以通过在Markdown文件中注入恶意的front-matter来执行任意JavaScript代码,从而导致潜在的RCE风险。具体表现为在`src/lib/md-to-pdf.ts`文件中,`grayMatter`函数没有经过任何验证就直接解析了Markdown文件的内容,允许执行JavaScript。\n\n【关键代码模式】\n- `gray-matter`库的使用\n- front-matter的解析和执行\n\n【修复方法说明】修复方法通过默认禁用`gray-matter`库的JavaScript引擎来防止RCE攻击。具体实现是通过在`defaultConfig`中设置`gray_matter_options`来禁用JS引擎,并在`convertMdToPdf`函数中检查并处理front-matter解析的结果。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\n.gitignore\n### 代码内容:\ntarget\n.classpath\n.metadata\n.project\ndoc\n\n## 内容2\n### 文件路径:\nsrc/main/java/com/southernstorm/noise/protocol/AESGCMFallbackCipherState.java\n### 代码内容:\npackage com.southernstorm.noise.protocol;\n\nimport java.util.Arrays;\n\nimport javax.crypto.BadPaddingException;\nimport javax.crypto.ShortBufferException;\n\nimport com.southernstorm.noise.crypto.GHASH;\nimport com.southernstorm.noise.crypto.RijndaelAES;\n\n/**\n * Fallback implementation of \"AESGCM\" on platforms where\n * the JCA/JCE does not have a suitable GCM or CTR provider.\n */\nclass AESGCMFallbackCipherState implements CipherState {\n\n\tprivate RijndaelAES aes;\n\tprivate long n;\n\tprivate byte[] iv;\n\tprivate byte[] enciv;\n\tprivate byte[] hashKey;\n\tprivate GHASH ghash;\n\tprivate boolean haskey;\n\n\t/**\n\t * Constructs a new cipher state for the \"AESGCM\" algorithm.\n\t */\n\tpublic AESGCMFallbackCipherState()\n\t{\n\t\taes = new RijndaelAES();\n\t\tn = 0;\n\t\tiv = new byte [16];\n\t\tenciv = new byte [16];\n\t\thashKey = new byte [16];\n\t\tghash = new GHASH();\n\t\thaskey = false;\n\t}\n\n\t@Override\n\tpublic void destroy() {\n\t\taes.destroy();\n\t\tghash.destroy();\n\t\tNoise.destroy(hashKey);\n\t\tNoise.destroy(iv);\n\t\tNoise.destroy(enciv);\n\t}\n\n\t@Override\n\tpublic String getCipherName() {\n\t\treturn \"AESGCM\";\n\t}\n\n\t@Override\n\tpublic int getKeyLength() {\n\t\treturn 32;\n\t}\n\n\t@Override\n\tpublic int getMACLength() {\n\t\treturn haskey ? 16 : 0;\n\t}\n\n\t@Override\n\tpublic void initializeKey(byte[] key, int offset) {\n\t\t// Set up the AES key.\n\t\taes.setupEnc(key, offset, 256);\n\t\thaskey = true;\n\n\t\t// Generate the hashing key by encrypting a block of zeroes.\n\t\tArrays.fill(hashKey, (byte)0);\n\t\taes.encrypt(hashKey, 0, hashKey, 0);\n\t\tghash.reset(hashKey, 0);\n\t\t\n\t\t// Reset the nonce.\n\t\tn = 0;\n\t}\n\n\t@Override\n\tpublic boolean hasKey() {\n\t\treturn haskey;\n\t}\n\t\n\t/**\n\t * Set up to encrypt or decrypt the next packet.\n\t * \n\t * @param ad The associated data for the packet.\n\t */\n\tprivate void setup(byte[] ad)\n\t{\n\t\t// Check for nonce wrap-around.\n\t\tif (n == -1L)\n\t\t\tthrow new IllegalStateException(\"Nonce has wrapped around\");\n\t\t\n\t\t// Format the counter/IV block.\n\t\tiv[0] = 0;\n\t\tiv[1] = 0;\n\t\tiv[2] = 0;\n\t\tiv[3] = 0;\n\t\tiv[4] = (byte)(n >> 56);\n\t\tiv[5] = (byte)(n >> 48);\n\t\tiv[6] = (byte)(n >> 40);\n\t\tiv[7] = (byte)(n >> 32);\n\t\tiv[8] = (byte)(n >> 24);\n\t\tiv[9] = (byte)(n >> 16);\n\t\tiv[10] = (byte)(n >> 8);\n\t\tiv[11] = (byte)n;\n\t\tiv[12] = 0;\n\t\tiv[13] = 0;\n\t\tiv[14] = 0;\n\t\tiv[15] = 1;\n\t\t++n;\n\t\t\n\t\t// Encrypt a block of zeroes to generate the hash key to XOR\n\t\t// the GHASH tag with at the end of the encrypt/decrypt operation.\n\t\tArrays.fill(hashKey, (byte)0);\n\t\taes.encrypt(iv, 0, hashKey, 0);\n\t\t\n\t\t// Initialize the GHASH with the associated data value.\n\t\tghash.reset();\n\t\tif (ad != null) {\n\t\t\tghash.update(ad, 0, ad.length);\n\t\t\tghash.pad();\n\t\t}\n\t}\n\n\t/**\n\t * Encrypts a block in CTR mode.\n\t * \n\t * @param plaintext The plaintext to encrypt.\n\t * @param plaintextOffset Offset of the first plaintext byte.\n\t * @param ciphertext The resulting ciphertext.\n\t * @param ciphertextOffset Offset of the first ciphertext byte.\n\t * @param length The number of bytes to encrypt.\n\t * \n\t * This function can also be used to decrypt.\n\t */\n\tprivate void encryptCTR(byte[] plaintext, int plaintextOffset, byte[] ciphertext, int ciphertextOffset, int length)\n\t{\n\t\twhile (length > 0) {\n\t\t\t// Increment the IV and encrypt it to get the next keystream block.\n\t\t\tif (++(iv[15]) == 0)\n\t\t\t\tif (++(iv[14]) == 0)\n\t\t\t\t\tif (++(iv[13]) == 0)\n\t\t\t\t\t\t++(iv[12]);\n\t\t\taes.encrypt(iv, 0, enciv, 0);\n\t\t\t\n\t\t\t// XOR the keystream block with the plaintext to create the ciphertext.\n\t\t\tint temp = length;\n\t\t\tif (temp > 16)\n\t\t\t\ttemp = 16;\n\t\t\tfor (int index = 0; index < temp; ++index)\n\t\t\t\tciphertext[ciphertextOffset + index] = (byte)(plaintext[plaintextOffset + index] ^ enciv[index]);\n\t\t\t\n\t\t\t// Advance to the next block.\n\t\t\tplaintextOffset += temp;\n\t\t\tciphertextOffset += temp;\n\t\t\tlength -= temp;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int encryptWithAd(byte[] ad, byte[] plaintext, int plaintextOffset,\n\t\t\tbyte[] ciphertext, int ciphertextOffset, int length)\n\t\t\tthrows ShortBufferException {\n\t\tint space;\n\t\tif (ciphertextOffset > ciphertext.length)\n\t\t\tspace = 0;\n\t\telse\n\t\t\tspace = ciphertext.length - ciphertextOffset;\n\t\tif (!haskey) {\n\t\t\t// The key is not set yet - return the plaintext as-is.\n\t\t\tif (length > space)\n\t\t\t\tthrow new ShortBufferException();\n\t\t\tif (plaintext != ciphertext || plaintextOffset != ciphertextOffset)\n\t\t\t\tSystem.arraycopy(plaintext, plaintextOffset, ciphertext, ciphertextOffset, length);\n\t\t\treturn length;\n\t\t}\n\t\tif (space < 16 || length > (space - 16))\n\t\t\tthrow new ShortBufferException();\n\t\tsetup(ad);\n\t\tencryptCTR(plaintext, plaintextOffset, ciphertext, ciphertextOffset, length);\n\t\tghash.update(ciphertext, ciphertextOffset, length);\n\t\tghash.pad(ad != null ? ad.length : 0, length);\n\t\tghash.finish(ciphertext, ciphertextOffset + length, 16);\n\t\tfor (int index = 0; index < 16; ++index)\n\t\t\tciphertext[ciphertextOffset + length + index] ^= hashKey[index];\n\t\treturn length + 16;\n\t}\n\n\t@Override\n\tpublic int decryptWithAd(byte[] ad, byte[] ciphertext,\n\t\t\tint ciphertextOffset, byte[] plaintext, int plaintextOffset,\n\t\t\tint length) throws ShortBufferException, BadPaddingException {\n\t\tint space;\n\t\tif (ciphertextOffset > ciphertext.length)\n\t\t\tspace = 0;\n\t\telse\n\t\t\tspace = ciphertext.length - ciphertextOffset;\n\t\tif (length > space)\n\t\t\tthrow new ShortBufferException();\n\t\tif (plaintextOffset > plaintext.length)\n\t\t\tspace = 0;\n\t\telse\n\t\t\tspace = plaintext.length - plaintextOffset;\n\t\tif (!haskey) {\n\t\t\t// The key is not set yet - return the ciphertext as-is.\n\t\t\tif (length > space)\n\t\t\t\tthrow new ShortBufferException();\n\t\t\tif (plaintext != ciphertext || plaintextOffset != ciphertextOffset)\n\t\t\t\tSystem.arraycopy(ciphertext, ciphertextOffset, plaintext, plaintextOffset, length);\n\t\t\treturn length;\n\t\t}\n\t\tif (length < 16)\n\t\t\tNoise.throwBadTagException();\n\t\tint dataLen = length - 16;\n\t\tif (dataLen > space)\n\t\t\tthrow new ShortBufferException();\n\t\tsetup(ad);\n\t\tghash.update(ciphertext, ciphertextOffset, dataLen);\n\t\tghash.pad(ad != null ? ad.length : 0, dataLen);\n\t\tghash.finish(enciv, 0, 16);\n\t\tint temp = 0;\n\t\tfor (int index = 0; index < 16; ++index)\n\t\t\ttemp |= (hashKey[index] ^ enciv[index] ^ ciphertext[ciphertextOffset + dataLen + index]);\n\t\tif ((temp & 0xFF) != 0)\n\t\t\tNoise.throwBadTagException();\n\t\tencryptCTR(ciphertext, ciphertextOffset, plaintext, plaintextOffset, dataLen);\n\t\treturn dataLen;\n\t}\n\n\t@Override\n\tpublic CipherState fork(byte[] key, int offset) {\n\t\tCipherState cipher;\n\t\tcipher = new AESGCMFallbackCipherState();\n\t\tcipher.initializeKey(key, offset);\n\t\treturn cipher;\n\t}\n\n\t@Override\n\tpublic void setNonce(long nonce) {\n\t\tn = nonce;\n\t}\n}\n\n## 内容3\n### 文件路径:\nsrc/main/java/com/southernstorm/noise/protocol/AESGCMOnCtrCipherState.java\n### 代码内容:\npackage com.southernstorm.noise.protocol;\n\nimport java.security.InvalidAlgorithmParameterException;\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Arrays;\n\nimport javax.crypto.BadPaddingException;\nimport javax.crypto.Cipher;\nimport javax.crypto.IllegalBlockSizeException;\nimport javax.crypto.NoSuchPaddingException;\nimport javax.crypto.ShortBufferException;\nimport javax.crypto.spec.IvParameterSpec;\nimport javax.crypto.spec.SecretKeySpec;\n\nimport com.southernstorm.noise.crypto.GHASH;\n\n/**\n * Emulates the \"AESGCM\" cipher for Noise using the \"AES/CTR/NoPadding\"\n * transformation from JCA/JCE.\n * \n * This class is used on platforms that don't have \"AES/GCM/NoPadding\",\n * but which do have the older \"AES/CTR/NoPadding\".\n */\nclass AESGCMOnCtrCipherState implements CipherState {\n\n\tprivate Cipher cipher;\n\tprivate SecretKeySpec keySpec;\n\tprivate long n;\n\tprivate byte[] iv;\n\tprivate byte[] hashKey;\n\tprivate GHASH ghash;\n\n\t/**\n\t * Constructs a new cipher state for the \"AESGCM\" algorithm.\n\t * \n\t * @throws NoSuchAlgorithmException The system does not have a\n\t * provider for this algorithm.\n\t */\n\tpublic AESGCMOnCtrCipherState() throws NoSuchAlgorithmException\n\t{\n\t\ttry {\n\t\t\tcipher = Cipher.getInstance(\"AES/CTR/NoPadding\");\n\t\t} catch (NoSuchPaddingException e) {\n\t\t\t// AES/CTR is available, but not the unpadded version? Huh?\n\t\t\tthrow new NoSuchAlgorithmException(\"AES/CTR/NoPadding not available\", e);\n\t\t}\n\t\tkeySpec = null;\n\t\tn = 0;\n\t\tiv = new byte [16];\n\t\thashKey = new byte [16];\n\t\tghash = new GHASH();\n\t\t\n\t\t// Try to set a 256-bit key on the cipher. Some JCE's are\n\t\t// configured to disallow 256-bit AES if an extra policy\n\t\t// file has not been installed.\n\t\ttry {\n\t\t\tSecretKeySpec spec = new SecretKeySpec(new byte [32], \"AES\");\n\t\t\tIvParameterSpec params = new IvParameterSpec(iv);\n\t\t\tcipher.init(Cipher.ENCRYPT_MODE, spec, params);\n\t\t} catch (InvalidKeyException e) {\n\t\t\tthrow new NoSuchAlgorithmException(\"AES/CTR/NoPadding does not support 256-bit keys\", e);\n\t\t} catch (InvalidAlgorithmParameterException e) {\n\t\t\tthrow new NoSuchAlgorithmException(\"AES/CTR/NoPadding does not support 256-bit keys\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void destroy() {\n\t\t// There doesn't seem to be a standard API to clean out a Cipher.\n\t\t// So we instead set the key and IV to all-zeroes to hopefully\n\t\t// destroy the sensitive data in the cipher instance.\n\t\tghash.destroy();\n\t\tNoise.destroy(hashKey);\n\t\tNoise.destroy(iv);\n\t\tkeySpec = new SecretKeySpec(new byte [32], \"AES\");\n\t\tIvParameterSpec params = new IvParameterSpec(iv);\n\t\ttry {\n\t\t\tcipher.init(Cipher.ENCRYPT_MODE, keySpec, params);\n\t\t} catch (InvalidKeyException e) {\n\t\t\t// Shouldn't happen.\n\t\t} catch (InvalidAlgorithmParameterException e) {\n\t\t\t// Shouldn't happen.\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getCipherName() {\n\t\treturn \"AESGCM\";\n\t}\n\n\t@Override\n\tpublic int getKeyLength() {\n\t\treturn 32;\n\t}\n\n\t@Override\n\tpublic int getMACLength() {\n\t\treturn keySpec != null ? 16 : 0;\n\t}\n\n\t@Override\n\tpublic void initializeKey(byte[] key, int offset) {\n\t\t// Set the encryption key.\n\t\tkeySpec = new SecretKeySpec(key, offset, 32, \"AES\");\n\t\t\n\t\t// Generate the hashing key by encrypting a block of zeroes.\n\t\tArrays.fill(iv, (byte)0);\n\t\tArrays.fill(hashKey, (byte)0);\n\t\ttry {\n\t\t\tcipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv));\n\t\t} catch (InvalidKeyException e) {\n\t\t\t// Shouldn't happen.\n\t\t\tthrow new IllegalStateException(e);\n\t\t} catch (InvalidAlgorithmParameterException e) {\n\t\t\t// Shouldn't happen.\n\t\t\tthrow new IllegalStateException(e);\n\t\t}\n\t\ttry {\n\t\t\tint result = cipher.update(hashKey, 0, 16, hashKey, 0);\n\t\t\tcipher.doFinal(hashKey, result);\n\t\t} catch (ShortBufferException e) {\n\t\t\t// Shouldn't happen.\n\t\t\tthrow new IllegalStateException(e);\n\t\t} catch (IllegalBlockSizeException e) {\n\t\t\t// Shouldn't happen.\n\t\t\tthrow new IllegalStateException(e);\n\t\t} catch (BadPaddingException e) {\n\t\t\t// Shouldn't happen.\n\t\t\tthrow new IllegalStateException(e);\n\t\t}\n\t\tghash.reset(hashKey, 0);\n\t\t\n\t\t// Reset the nonce.\n\t\tn = 0;\n\t}\n\n\t@Override\n\tpublic boolean hasKey() {\n\t\treturn keySpec != null;\n\t}\n\n\t/**\n\t * Set up to encrypt or decrypt the next packet.\n\t * \n\t * @param ad The associated data for the packet.\n\t */\n\tprivate void setup(byte[] ad) throws InvalidKeyException, InvalidAlgorithmParameterException\n\t{\n\t\t// Check for nonce wrap-around.\n\t\tif (n == -1L)\n\t\t\tthrow new IllegalStateException(\"Nonce has wrapped around\");\n\t\t\n\t\t// Format the counter/IV block for AES/CTR/NoPadding.\n\t\tiv[0] = 0;\n\t\tiv[1] = 0;\n\t\tiv[2] = 0;\n\t\tiv[3] = 0;\n\t\tiv[4] = (byte)(n >> 56);\n\t\tiv[5] = (byte)(n >> 48);\n\t\tiv[6] = (byte)(n >> 40);\n\t\tiv[7] = (byte)(n >> 32);\n\t\tiv[8] = (byte)(n >> 24);\n\t\tiv[9] = (byte)(n >> 16);\n\t\tiv[10] = (byte)(n >> 8);\n\t\tiv[11] = (byte)n;\n\t\tiv[12] = 0;\n\t\tiv[13] = 0;\n\t\tiv[14] = 0;\n\t\tiv[15] = 1;\n\t\t++n;\n\t\t\n\t\t// Initialize the CTR mode cipher with the key and IV.\n\t\tcipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv));\n\t\t\n\t\t// Encrypt a block of zeroes to generate the hash key to XOR\n\t\t// the GHASH tag with at the end of the encrypt/decrypt operation.\n\t\tArrays.fill(hashKey, (byte)0);\n\t\ttry {\n\t\t\tcipher.update(hashKey, 0, 16, hashKey, 0);\n\t\t} catch (ShortBufferException e) {\n\t\t\t// Shouldn't happen.\n\t\t\tthrow new IllegalStateException(e);\n\t\t}\n\t\t\n\t\t// Initialize the GHASH with the associated data value.\n\t\tghash.reset();\n\t\tif (ad != null) {\n\t\t\tghash.update(ad, 0, ad.length);\n\t\t\tghash.pad();\n\t\t}\n\t}\n\n\t@Override\n\tpublic int encryptWithAd(byte[] ad, byte[] plaintext, int plaintextOffset,\n\t\t\tbyte[] ciphertext, int ciphertextOffset, int length)\n\t\t\tthrows ShortBufferException {\n\t\tint space;\n\t\tif (ciphertextOffset > ciphertext.length)\n\t\t\tspace = 0;\n\t\telse\n\t\t\tspace = ciphertext.length - ciphertextOffset;\n\t\tif (keySpec == null) {\n\t\t\t// The key is not set yet - return the plaintext as-is.\n\t\t\tif (length > space)\n\t\t\t\tthrow new ShortBufferException();\n\t\t\tif (plaintext != ciphertext || plaintextOffset != ciphertextOffset)\n\t\t\t\tSystem.arraycopy(plaintext, plaintextOffset, ciphertext, ciphertextOffset, length);\n\t\t\treturn length;\n\t\t}\n\t\tif (space < 16 || length > (space - 16))\n\t\t\tthrow new ShortBufferException();\n\t\ttry {\n\t\t\tsetup(ad);\n\t\t\tint result = cipher.update(plaintext, plaintextOffset, length, ciphertext, ciphertextOffset);\n\t\t\tcipher.doFinal(ciphertext, ciphertextOffset + result);\n\t\t} catch (InvalidKeyException e) {\n\t\t\t// Shouldn't happen.\n\t\t\tthrow new IllegalStateException(e);\n\t\t} catch (InvalidAlgorithmParameterException e) {\n\t\t\t// Shouldn't happen.\n\t\t\tthrow new IllegalStateException(e);\n\t\t} catch (IllegalBlockSizeException e) {\n\t\t\t// Shouldn't happen.\n\t\t\tthrow new IllegalStateException(e);\n\t\t} catch (BadPaddingException e) {\n\t\t\t// Shouldn't happen.\n\t\t\tthrow new IllegalStateException(e);\n\t\t}\n\t\tghash.update(ciphertext, ciphertextOffset, length);\n\t\tghash.pad(ad != null ? ad.length : 0, length);\n\t\tghash.finish(ciphertext, ciphertextOffset + length, 16);\n\t\tfor (int index = 0; index < 16; ++index)\n\t\t\tciphertext[ciphertextOffset + length + index] ^= hashKey[index];\n\t\treturn length + 16;\n\t}\n\n\t@Override\n\tpublic int decryptWithAd(byte[] ad, byte[] ciphertext,\n\t\t\tint ciphertextOffset, byte[] plaintext, int plaintextOffset,\n\t\t\tint length) throws ShortBufferException, BadPaddingException {\n\t\tint space;\n\t\tif (ciphertextOffset > ciphertext.length)\n\t\t\tspace = 0;\n\t\telse\n\t\t\tspace = ciphertext.length - ciphertextOffset;\n\t\tif (length > space)\n\t\t\tthrow new ShortBufferException();\n\t\tif (plaintextOffset > plaintext.length)\n\t\t\tspace = 0;\n\t\telse\n\t\t\tspace = plaintext.length - plaintextOffset;\n\t\tif (keySpec == null) {\n\t\t\t// The key is not set yet - return the ciphertext as-is.\n\t\t\tif (length > space)\n\t\t\t\tthrow new ShortBufferException();\n\t\t\tif (plaintext != ciphertext || plaintextOffset != ciphertextOffset)\n\t\t\t\tSystem.arraycopy(ciphertext, ciphertextOffset, plaintext, plaintextOffset, length);\n\t\t\treturn length;\n\t\t}\n\t\tif (length < 16)\n\t\t\tNoise.throwBadTagException();\n\t\tint dataLen = length - 16;\n\t\tif (dataLen > space)\n\t\t\tthrow new ShortBufferException();\n\t\ttry {\n\t\t\tsetup(ad);\n\t\t} catch (InvalidKeyException e) {\n\t\t\t// Shouldn't happen.\n\t\t\tthrow new IllegalStateException(e);\n\t\t} catch (InvalidAlgorithmParameterException e) {\n\t\t\t// Shouldn't happen.\n\t\t\tthrow new IllegalStateException(e);\n\t\t}\n\t\tghash.update(ciphertext, ciphertextOffset, dataLen);\n\t\tghash.pad(ad != null ? ad.length : 0, dataLen);\n\t\tghash.finish(iv, 0, 16);\n\t\tint temp = 0;\n\t\tfor (int index = 0; index < 16; ++index)\n\t\t\ttemp |= (hashKey[index] ^ iv[index] ^ ciphertext[ciphertextOffset + dataLen + index]);\n\t\tif ((temp & 0xFF) != 0)\n\t\t\tNoise.throwBadTagException();\n\t\ttry {\n\t\t\tint result = cipher.update(ciphertext, ciphertextOffset, dataLen, plaintext, plaintextOffset);\n\t\t\tcipher.doFinal(plaintext, plaintextOffset + result);\n\t\t} catch (IllegalBlockSizeException e) {\n\t\t\t// Shouldn't happen.\n\t\t\tthrow new IllegalStateException(e);\n\t\t} catch (BadPaddingException e) {\n\t\t\t// Shouldn't happen.\n\t\t\tthrow new IllegalStateException(e);\n\t\t}\n\t\treturn dataLen;\n\t}\n\n\t@Override\n\tpublic CipherState fork(byte[] key, int offset) {\n\t\tCipherState cipher;\n\t\ttry {\n\t\t\tcipher = new AESGCMOnCtrCipherState();\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\t// Shouldn't happen.\n\t\t\treturn null;\n\t\t}\n\t\tcipher.initializeKey(key, offset);\n\t\treturn cipher;\n\t}\n\n\t@Override\n\tpublic void setNonce(long nonce) {\n\t\tn = nonce;\n\t}\n}\n\n## 内容4\n### 文件路径:\nsrc/main/java/com/southernstorm/noise/protocol/ChaChaPolyCipherState.java\n### 代码内容:\npackage com.southernstorm.noise.protocol;\n\nimport java.util.Arrays;\n\nimport javax.crypto.BadPaddingException;\nimport javax.crypto.ShortBufferException;\n\nimport com.southernstorm.noise.crypto.ChaChaCore;\nimport com.southernstorm.noise.crypto.Poly1305;\n\n/**\n * Implements the ChaChaPoly cipher for Noise.\n */\nclass ChaChaPolyCipherState implements CipherState {\n\n\tprivate Poly1305 poly;\n\tprivate int[] input;\n\tprivate int[] output;\n\tprivate byte[] polyKey;\n\tlong n;\n\tprivate boolean haskey;\n\t\n\t/**\n\t * Constructs a new cipher state for the \"ChaChaPoly\" algorithm.\n\t */\n\tpublic ChaChaPolyCipherState()\n\t{\n\t\tpoly = new Poly1305();\n\t\tinput = new int [16];\n\t\toutput = new int [16];\n\t\tpolyKey = new byte [32];\n\t\tn = 0;\n\t\thaskey = false;\n\t}\n\n\t@Override\n\tpublic void destroy() {\n\t\tpoly.destroy();\n\t\tArrays.fill(input, 0);\n\t\tArrays.fill(output, 0);\n\t\tNoise.destroy(polyKey);\n\t}\n\n\t@Override\n\tpublic String getCipherName() {\n\t\treturn \"ChaChaPoly\";\n\t}\n\n\t@Override\n\tpublic int getKeyLength() {\n\t\treturn 32;\n\t}\n\n\t@Override\n\tpublic int getMACLength() {\n\t\treturn haskey ? 16 : 0;\n\t}\n\n\t@Override\n\tpublic void initializeKey(byte[] key, int offset) {\n\t\tChaChaCore.initKey256(input, key, offset);\n\t\tn = 0;\n\t\thaskey = true;\n\t}\n\n\t@Override\n\tpublic boolean hasKey() {\n\t\treturn haskey;\n\t}\n\n\t/**\n\t * XOR's the output of ChaCha20 with a byte buffer.\n\t * \n\t * @param input The input byte buffer.\n\t * @param inputOffset The offset of the first input byte.\n\t * @param output The output byte buffer (can be the same as the input).\n\t * @param outputOffset The offset of the first output byte.\n\t * @param length The number of bytes to XOR between 1 and 64.\n\t * @param block The ChaCha20 output block.\n\t */\n\tprivate static void xorBlock(byte[] input, int inputOffset, byte[] output, int outputOffset, int length, int[] block)\n\t{\n\t\tint posn = 0;\n\t\tint value;\n\t\twhile (length >= 4) {\n\t\t\tvalue = block[posn++];\n\t\t\toutput[outputOffset] = (byte)(input[inputOffset] ^ value);\n\t\t\toutput[outputOffset + 1] = (byte)(input[inputOffset + 1] ^ (value >> 8));\n\t\t\toutput[outputOffset + 2] = (byte)(input[inputOffset + 2] ^ (value >> 16));\n\t\t\toutput[outputOffset + 3] = (byte)(input[inputOffset + 3] ^ (value >> 24));\n\t\t\tinputOffset += 4;\n\t\t\toutputOffset += 4;\n\t\t\tlength -= 4;\n\t\t}\n\t\tif (length == 3) {\n\t\t\tvalue = block[posn];\n\t\t\toutput[outputOffset] = (byte)(input[inputOffset] ^ value);\n\t\t\toutput[outputOffset + 1] = (byte)(input[inputOffset + 1] ^ (value >> 8));\n\t\t\toutput[outputOffset + 2] = (byte)(input[inputOffset + 2] ^ (value >> 16));\n\t\t} else if (length == 2) {\n\t\t\tvalue = block[posn];\n\t\t\toutput[outputOffset] = (byte)(input[inputOffset] ^ value);\n\t\t\toutput[outputOffset + 1] = (byte)(input[inputOffset + 1] ^ (value >> 8));\n\t\t} else if (length == 1) {\n\t\t\tvalue = block[posn];\n\t\t\toutput[outputOffset] = (byte)(input[inputOffset] ^ value);\n\t\t}\n\t}\n\t\n\t/**\n\t * Set up to encrypt or decrypt the next packet.\n\t * \n\t * @param ad The associated data for the packet.\n\t */\n\tprivate void setup(byte[] ad)\n\t{\n\t\tif (n == -1L)\n\t\t\tthrow new IllegalStateException(\"Nonce has wrapped around\");\n\t\tChaChaCore.initIV(input, n++);\n\t\tChaChaCore.hash(output, input);\n\t\tArrays.fill(polyKey, (byte)0);\n\t\txorBlock(polyKey, 0, polyKey, 0, 32, output);\n\t\tpoly.reset(polyKey, 0);\n\t\tif (ad != null) {\n\t\t\tpoly.update(ad, 0, ad.length);\n\t\t\tpoly.pad();\n\t\t}\n\t\tif (++(input[12]) == 0)\n\t\t\t++(input[13]);\n\t}\n\n\t/**\n\t * Puts a 64-bit integer into a buffer in little-endian order.\n\t * \n\t * @param output The output buffer.\n\t * @param offset The offset into the output buffer.\n\t * @param value The 64-bit integer value.\n\t */\n\tprivate static void putLittleEndian64(byte[] output, int offset, long value)\n\t{\n\t\toutput[offset] = (byte)value;\n\t\toutput[offset + 1] = (byte)(value >> 8);\n\t\toutput[offset + 2] = (byte)(value >> 16);\n\t\toutput[offset + 3] = (byte)(value >> 24);\n\t\toutput[offset + 4] = (byte)(value >> 32);\n\t\toutput[offset + 5] = (byte)(value >> 40);\n\t\toutput[offset + 6] = (byte)(value >> 48);\n\t\toutput[offset + 7] = (byte)(value >> 56);\n\t}\n\n\t/**\n\t * Finishes up the authentication tag for a packet.\n\t * \n\t * @param ad The associated data.\n\t * @param length The length of the plaintext data.\n\t */\n\tprivate void finish(byte[] ad, int length)\n\t{\n\t\tpoly.pad();\n\t\tputLittleEndian64(polyKey, 0, ad != null ? ad.length : 0);\n\t\tputLittleEndian64(polyKey, 8, length);\n\t\tpoly.update(polyKey, 0, 16);\n\t\tpoly.finish(polyKey, 0);\n\t}\n\n\t/**\n\t * Encrypts or decrypts a buffer of bytes for the active packet.\n\t * \n\t * @param plaintext The plaintext data to be encrypted.\n\t * @param plaintextOffset The offset to the first plaintext byte.\n\t * @param ciphertext The ciphertext data that results from encryption.\n\t * @param ciphertextOffset The offset to the first ciphertext byte.\n\t * @param length The number of bytes to encrypt.\n\t */\n\tprivate void encrypt(byte[] plaintext, int plaintextOffset,\n\t\t\tbyte[] ciphertext, int ciphertextOffset, int length) {\n\t\twhile (length > 0) {\n\t\t\tint tempLen = 64;\n\t\t\tif (tempLen > length)\n\t\t\t\ttempLen = length;\n\t\t\tChaChaCore.hash(output, input);\n\t\t\txorBlock(plaintext, plaintextOffset, ciphertext, ciphertextOffset, tempLen, output);\n\t\t\tif (++(input[12]) == 0)\n\t\t\t\t++(input[13]);\n\t\t\tplaintextOffset += tempLen;\n\t\t\tciphertextOffset += tempLen;\n\t\t\tlength -= tempLen;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int encryptWithAd(byte[] ad, byte[] plaintext, int plaintextOffset,\n\t\t\tbyte[] ciphertext, int ciphertextOffset, int length) throws ShortBufferException {\n\t\tint space;\n\t\tif (ciphertextOffset > ciphertext.length)\n\t\t\tspace = 0;\n\t\telse\n\t\t\tspace = ciphertext.length - ciphertextOffset;\n\t\tif (!haskey) {\n\t\t\t// The key is not set yet - return the plaintext as-is.\n\t\t\tif (length > space)\n\t\t\t\tthrow new ShortBufferException();\n\t\t\tif (plaintext != ciphertext || plaintextOffset != ciphertextOffset)\n\t\t\t\tSystem.arraycopy(plaintext, plaintextOffset, ciphertext, ciphertextOffset, length);\n\t\t\treturn length;\n\t\t}\n\t\tif (space < 16 || length > (space - 16))\n\t\t\tthrow new ShortBufferException();\n\t\tsetup(ad);\n\t\tencrypt(plaintext, plaintextOffset, ciphertext, ciphertextOffset, length);\n\t\tpoly.update(ciphertext, ciphertextOffset, length);\n\t\tfinish(ad, length);\n\t\tSystem.arraycopy(polyKey, 0, ciphertext, ciphertextOffset + length, 16);\n\t\treturn length + 16;\n\t}\n\n\t@Override\n\tpublic int decryptWithAd(byte[] ad, byte[] ciphertext,\n\t\t\tint ciphertextOffset, byte[] plaintext, int plaintextOffset,\n\t\t\tint length) throws ShortBufferException, BadPaddingException {\n\t\tint space;\n\t\tif (ciphertextOffset > ciphertext.length)\n\t\t\tspace = 0;\n\t\telse\n\t\t\tspace = ciphertext.length - ciphertextOffset;\n\t\tif (length > space)\n\t\t\tthrow new ShortBufferException();\n\t\tif (plaintextOffset > plaintext.length)\n\t\t\tspace = 0;\n\t\telse\n\t\t\tspace = plaintext.length - plaintextOffset;\n\t\tif (!haskey) {\n\t\t\t// The key is not set yet - return the ciphertext as-is.\n\t\t\tif (length > space)\n\t\t\t\tthrow new ShortBufferException();\n\t\t\tif (plaintext != ciphertext || plaintextOffset != ciphertextOffset)\n\t\t\t\tSystem.arraycopy(ciphertext, ciphertextOffset, plaintext, plaintextOffset, length);\n\t\t\treturn length;\n\t\t}\n\t\tif (length < 16)\n\t\t\tNoise.throwBadTagException();\n\t\tint dataLen = length - 16;\n\t\tif (dataLen > space)\n\t\t\tthrow new ShortBufferException();\n\t\tsetup(ad);\n\t\tpoly.update(ciphertext, ciphertextOffset, dataLen);\n\t\tfinish(ad, dataLen);\n\t\tint temp = 0;\n\t\tfor (int index = 0; index < 16; ++index)\n\t\t\ttemp |= (polyKey[index] ^ ciphertext[ciphertextOffset + dataLen + index]);\n\t\tif ((temp & 0xFF) != 0)\n\t\t\tNoise.throwBadTagException();\n\t\tencrypt(ciphertext, ciphertextOffset, plaintext, plaintextOffset, dataLen);\n\t\treturn dataLen;\n\t}\n\n\t@Override\n\tpublic CipherState fork(byte[] key, int offset) {\n\t\tCipherState cipher = new ChaChaPolyCipherState();\n\t\tcipher.initializeKey(key, offset);\n\t\treturn cipher;\n\t}\n\n\t@Override\n\tpublic void setNonce(long nonce) {\n\t\tn = nonce;\n\t}\n}\n\n## 内容5\n### 文件路径:\nsrc/main/java/com/southernstorm/noise/protocol/CipherState.java\n### 代码内容:\npackage com.southernstorm.noise.protocol;\n\nimport javax.crypto.BadPaddingException;\nimport javax.crypto.ShortBufferException;\n\n/**\n * Interface to an authenticated cipher for use in the Noise protocol.\n *\n * CipherState objects are used to encrypt or decrypt data during a\n * session. Once the handshake has completed, HandshakeState.split()\n * will create two CipherState objects for encrypting packets sent to\n * the other party, and decrypting packets received from the other party.\n */\npublic interface CipherState extends Destroyable {\n\n\t/**\n\t * Gets the Noise protocol name for this cipher.\n\t * \n\t * @return The cipher name.\n\t */\n\tString getCipherName();\n\n\t/**\n\t * Gets the length of the key values for this cipher.\n\t * \n\t * @return The length of the key in bytes; usually 32.\n\t */\n\tint getKeyLength();\n\t\n\t/**\n\t * Gets the length of the MAC values for this cipher.\n\t * \n\t * @return The length of MAC values in bytes, or zero if the\n\t * key has not yet been initialized.\n\t */\n\tint getMACLength();\n\n\t/**\n\t * Initializes the key on this cipher object.\n\t * \n\t * @param key Points to a buffer that contains the key.\n\t * @param offset The offset of the key in the key buffer.\n\t * \n\t * The key buffer must contain at least getKeyLength() bytes\n\t * starting at offset.\n\t * \n\t * @see #hasKey()\n\t */\n\tvoid initializeKey(byte[] key, int offset);\n\n\t/**\n\t * Determine if this cipher object has been configured with a key.\n\t * \n\t * @return true if this cipher object has a key; false if the\n\t * key has not yet been set with initializeKey().\n\t * \n\t * @see #initializeKey(byte[], int)\n\t */\n\tboolean hasKey();\n\t\n\t/**\n\t * Encrypts a plaintext buffer using the cipher and a block of associated data.\n\t * \n\t * @param ad The associated data, or null if there is none.\n\t * @param plaintext The buffer containing the plaintext to encrypt.\n\t * @param plaintextOffset The offset within the plaintext buffer of the\n\t * first byte or plaintext data.\n\t * @param ciphertext The buffer to place the ciphertext in. This can\n\t * be the same as the plaintext buffer.\n\t * @param ciphertextOffset The first offset within the ciphertext buffer\n\t * to place the ciphertext and the MAC tag.\n\t * @param length The length of the plaintext.\n\t * @return The length of the ciphertext plus the MAC tag, or -1 if the\n\t * ciphertext buffer is not large enough to hold the result.\n\t * \n\t * @throws ShortBufferException The ciphertext buffer does not have\n\t * enough space to hold the ciphertext plus MAC.\n\t * \n\t * @throws IllegalStateException The nonce has wrapped around.\n\t * \n\t * The plaintext and ciphertext buffers can be the same for in-place\n\t * encryption. In that case, plaintextOffset must be identical to\n\t * ciphertextOffset.\n\t * \n\t * There must be enough space in the ciphertext buffer to accomodate\n\t * length + getMACLength() bytes of data starting at ciphertextOffset.\n\t */\n\tint encryptWithAd(byte[] ad, byte[] plaintext, int plaintextOffset, byte[] ciphertext, int ciphertextOffset, int length) throws ShortBufferException;\n\n\t/**\n\t * Decrypts a ciphertext buffer using the cipher and a block of associated data.\n\t * \n\t * @param ad The associated data, or null if there is none.\n\t * @param ciphertext The buffer containing the ciphertext to decrypt.\n\t * @param ciphertextOffset The offset within the ciphertext buffer of\n\t * the first byte of ciphertext data.\n\t * @param plaintext The buffer to place the plaintext in. This can be\n\t * the same as the ciphertext buffer.\n\t * @param plaintextOffset The first offset within the plaintext buffer\n\t * to place the plaintext.\n\t * @param length The length of the incoming ciphertext plus the MAC tag.\n\t * @return The length of the plaintext with the MAC tag stripped off.\n\t * \n\t * @throws ShortBufferException The plaintext buffer does not have\n\t * enough space to store the decrypted data.\n\t * \n\t * @throws BadPaddingException The MAC value failed to verify.\n\t * \n\t * @throws IllegalStateException The nonce has wrapped around.\n\t * \n\t * The plaintext and ciphertext buffers can be the same for in-place\n\t * decryption. In that case, ciphertextOffset must be identical to\n\t * plaintextOffset.\n\t */\n\tint decryptWithAd(byte[] ad, byte[] ciphertext, int ciphertextOffset, byte[] plaintext, int plaintextOffset, int length) throws ShortBufferException, BadPaddingException;\n\n\t/**\n\t * Creates a new instance of this cipher and initializes it with a key.\n\t * \n\t * @param key The buffer containing the key.\n\t * @param offset The offset into the key buffer of the first key byte.\n\t * @return A new CipherState of the same class as this one.\n\t */\n\tCipherState fork(byte[] key, int offset);\n\t\n\t/**\n\t * Sets the nonce value.\n\t * \n\t * @param nonce The new nonce value, which must be greater than or equal\n\t * to the current value.\n\t * \n\t * This function is intended for testing purposes only. If the nonce\n\t * value goes backwards then security may be compromised.\n\t */\n\tvoid setNonce(long nonce);\n}\n\n", "input": "", "output": "【漏洞类型】数组越界漏洞\n【严重性】High\n【漏洞摘要】原始代码在处理加密和解密操作时,未对输入参数进行充分验证,可能导致数组越界异常。具体表现为在`encryptWithAd`和`decryptWithAd`方法中,`ciphertextOffset`和`plaintextOffset`未进行负数检查,这在某些情况下可能导致数组越界异常。根本原因在于对数组长度的检查不够全面,没有考虑到负数偏移的可能性。\n【关键代码模式】与漏洞相关的关键代码模式包括`ciphertextOffset`和`plaintextOffset`在`encryptWithAd`和`decryptWithAd`方法中的使用。关键词包括`Arrays.fill`、`System.arraycopy`、`aes.encrypt`、`cipher.update`等。\n【修复方法说明】修复方法通过在`encryptWithAd`和`decryptWithAd`方法中添加负数检查来解决数组越界问题。最佳实践是始终验证数组长度和偏移量,以防止潜在的数组越界异常。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/main/java/org/dom4j/Namespace.java\n### 代码内容:\npackage org.dom4j;\n\nimport org.dom4j.tree.AbstractNode;\nimport org.dom4j.tree.DefaultNamespace;\nimport org.dom4j.tree.NamespaceCache;\n\n\n## 内容2\n### 文件路径:\nsrc/main/java/org/dom4j/QName.java\n### 代码内容:\npackage org.dom4j;\n\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.Serializable;\n\nimport org.dom4j.tree.QNameCache;\nimport org.dom4j.util.SingletonStrategy;\n\n\n## 内容3\n### 文件路径:\nsrc/main/java/org/dom4j/tree/QNameCache.java\n### 代码内容:\npackage org.dom4j.tree;\n\nimport org.dom4j.DocumentFactory;\nimport org.dom4j.Namespace;\nimport org.dom4j.QName;\n\nimport java.util.*;\n\n\n", "input": "", "output": "【漏洞类型】非法输入验证漏洞\n【严重性】Medium\n【漏洞摘要】原始代码中,`Namespace` 和 `QName` 类并未对传入的名称(如 `prefix`、`uri`、`name`、`qualifiedName`)进行充分的验证,允许传入不符合 XML 规范的名称,这可能导致 XML 解析器在处理这些名称时出现问题,例如拒绝服务攻击(DoS)或 XML 外部实体(XXE)攻击。\n【关键代码模式】\n- `Namespace` 类的构造函数中对 `prefix` 和 `uri` 的检查(`if (prefix != null) { this.prefix = prefix; }` 和 `if (uri != null) { this.uri = uri; }`)。\n- `QName` 类的构造函数中对 `name` 和 `qualifiedName` 的检查(`this.name = (name == null) ? \"\" : name;` 和 `this.qualifiedName = qualifiedName;`)。\n- 未使用正则表达式或其他验证机制来确保传入的名称符合 XML 的命名规则。\n【修复方法说明】修复方法通过添加正则表达式验证来确保传入的名称符合 XML 的命名规则,从而防止非法输入导致的安全问题。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nframework/sdk-parent/sdk/src/main/java/io/metersphere/controller/BaseUserController.java\n### 代码内容:\npackage io.metersphere.controller;\n\nimport io.metersphere.base.domain.User;\nimport io.metersphere.commons.constants.OperLogConstants;\nimport io.metersphere.commons.constants.OperLogModule;\nimport io.metersphere.commons.utils.SessionUtils;\nimport io.metersphere.dto.UserDTO;\nimport io.metersphere.log.annotation.MsAuditLog;\nimport io.metersphere.request.AuthUserIssueRequest;\nimport io.metersphere.request.member.EditPassWordRequest;\nimport io.metersphere.request.member.EditSeleniumServerRequest;\nimport io.metersphere.request.member.QueryMemberRequest;\nimport io.metersphere.service.BaseUserService;\nimport org.springframework.web.bind.annotation.*;\n\nimport jakarta.annotation.Resource;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class BaseUserController {\n @Resource\n private BaseUserService baseUserService;\n\n @GetMapping(\"/ws/current/member/list\")\n public List<User> getCurrentWorkspaceMember() {\n QueryMemberRequest request = new QueryMemberRequest();\n request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());\n return baseUserService.getMemberList(request);\n }\n\n @GetMapping(\"/switch/source/ws/{sourceId}\")\n public UserDTO switchWorkspace(@PathVariable(value = \"sourceId\") String sourceId) {\n baseUserService.switchUserResource(\"workspace\", sourceId, Objects.requireNonNull(SessionUtils.getUser()));\n return SessionUtils.getUser();\n }\n\n @PostMapping(\"/update/current\")\n @MsAuditLog(module = OperLogModule.PERSONAL_INFORMATION_PERSONAL_SETTINGS, type = OperLogConstants.UPDATE, beforeEvent = \"#msClass.getLogDetails(#user.id)\", content = \"#msClass.getLogDetails(#user.id)\", msClass = BaseUserService.class)\n public UserDTO updateCurrentUser(@RequestBody User user) {\n return baseUserService.updateCurrentUser(user);\n }\n\n /*\n * 修改当前用户密码\n * */\n @PostMapping(\"/update/password\")\n @MsAuditLog(module = OperLogModule.SYSTEM_USER, type = OperLogConstants.UPDATE, title = \"个人密码\")\n public int updateCurrentUserPassword(@RequestBody EditPassWordRequest request) {\n return baseUserService.updateCurrentUserPassword(request);\n }\n\n @GetMapping(\"/project/member/list\")\n public List<User> getProjectMemberListAll() {\n QueryMemberRequest request = new QueryMemberRequest();\n request.setProjectId(SessionUtils.getCurrentProjectId());\n return baseUserService.getProjectMemberList(request);\n }\n\n @GetMapping(\"/project/member/option\")\n public List<User> getProjectMemberOption() {\n return baseUserService.getProjectMemberOption(SessionUtils.getCurrentProjectId());\n }\n\n @GetMapping(\"/project/member/{projectId}\")\n public List<User> getProjectMembers(@PathVariable String projectId) {\n QueryMemberRequest request = new QueryMemberRequest();\n request.setProjectId(projectId);\n return baseUserService.getProjectMemberList(request);\n }\n\n @GetMapping(\"/info\")\n public UserDTO getUserInfo() {\n return baseUserService.getUserDTO(SessionUtils.getUserId());\n }\n\n /**\n * 根据userId 获取 user 所属工作空间和所属工作项目\n */\n @GetMapping(\"/get/ws-pj/{userId}\")\n public Map<Object, Object> getWSAndProjectByUserId(@PathVariable String userId) {\n return baseUserService.getWSAndProjectByUserId(userId);\n }\n\n /**\n * 配置 用户的selenium-server 地址 ip:port\n */\n @PostMapping(\"/update/selenium-server\")\n @MsAuditLog(module = OperLogModule.SYSTEM_USER, type = OperLogConstants.UPDATE, title = \"selenium-server地址\")\n public int updateSeleniumServer(@RequestBody EditSeleniumServerRequest request) {\n return baseUserService.updateUserSeleniumServer(request);\n }\n\n @PostMapping(\"issue/auth\")\n public void userAuth(@RequestBody AuthUserIssueRequest authUserIssueRequest) {\n baseUserService.userIssueAuth(authUserIssueRequest);\n }\n\n @GetMapping(\"/update/current-by-resource/{resourceId}\")\n public void updateCurrentUserByResourceId(@PathVariable String resourceId) {\n baseUserService.updateCurrentUserByResourceId(resourceId);\n }\n\n @GetMapping(\"/is/super/{userid}\")\n public boolean isSuperUser(@PathVariable String userid) {\n return baseUserService.isSuperUser(userid);\n }\n}\n\n## 内容2\n### 文件路径:\nproject-management/backend/src/main/java/io/metersphere/controller/GroupController.java\n### 代码内容:\npackage io.metersphere.controller;\n\nimport com.github.pagehelper.Page;\nimport com.github.pagehelper.PageHelper;\nimport io.metersphere.base.domain.Group;\nimport io.metersphere.base.domain.User;\nimport io.metersphere.base.domain.Workspace;\nimport io.metersphere.commons.constants.OperLogConstants;\nimport io.metersphere.commons.constants.OperLogModule;\nimport io.metersphere.commons.constants.PermissionConstants;\nimport io.metersphere.commons.utils.PageUtils;\nimport io.metersphere.commons.utils.Pager;\nimport io.metersphere.commons.utils.SessionUtils;\nimport io.metersphere.dto.GroupDTO;\nimport io.metersphere.dto.GroupPermissionDTO;\nimport io.metersphere.log.annotation.MsAuditLog;\nimport io.metersphere.log.annotation.MsRequestLog;\nimport io.metersphere.request.GroupRequest;\nimport io.metersphere.request.group.EditGroupRequest;\nimport io.metersphere.request.group.EditGroupUserRequest;\nimport io.metersphere.service.GroupService;\nimport jakarta.annotation.Resource;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.shiro.authz.annotation.Logical;\nimport org.apache.shiro.authz.annotation.RequiresPermissions;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\n\n@RequestMapping(\"/user/group\")\n@RestController\npublic class GroupController {\n\n @Resource\n private GroupService groupService;\n\n @PostMapping(\"/get/current/project/{goPage}/{pageSize}\")\n @RequiresPermissions(value = {PermissionConstants.PROJECT_GROUP_READ}, logical = Logical.OR)\n public Pager<List<GroupDTO>> getCurrentProjectGroupList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody EditGroupRequest request) {\n request.setGoPage(goPage);\n request.setPageSize(pageSize);\n return groupService.getProjectGroupList(request);\n }\n\n @GetMapping(\"/get/all\")\n @RequiresPermissions(value = {PermissionConstants.SYSTEM_GROUP_READ, PermissionConstants.SYSTEM_USER_READ, PermissionConstants.WORKSPACE_USER_READ}, logical = Logical.OR)\n public List<GroupDTO> getAllGroup() {\n return groupService.getAllGroup();\n }\n\n @PostMapping(\"/get\")\n @RequiresPermissions(value = {PermissionConstants.SYSTEM_GROUP_READ, PermissionConstants.SYSTEM_USER_READ, PermissionConstants.WORKSPACE_USER_READ}, logical = Logical.OR)\n public List<Group> getGroupByType(@RequestBody EditGroupRequest request) {\n return groupService.getGroupByType(request);\n }\n\n @PostMapping(\"/add\")\n @RequiresPermissions(value = {PermissionConstants.SYSTEM_GROUP_READ_CREATE, PermissionConstants.PROJECT_GROUP_READ_CREATE}, logical = Logical.OR)\n @MsAuditLog(module = OperLogModule.GROUP_PERMISSION, type = OperLogConstants.CREATE, content = \"#msClass.getLogDetails(#request.id)\", msClass = GroupService.class)\n public Group addGroup(@RequestBody EditGroupRequest request) {\n request.setId(UUID.randomUUID().toString());\n return groupService.addGroup(request);\n }\n\n @PostMapping(\"/edit\")\n @RequiresPermissions(value = {PermissionConstants.SYSTEM_GROUP_READ_EDIT, PermissionConstants.PROJECT_GROUP_READ_EDIT}, logical = Logical.OR)\n @MsAuditLog(module = OperLogModule.GROUP_PERMISSION, type = OperLogConstants.UPDATE, beforeEvent = \"#msClass.getLogDetails(#request.id)\", content = \"#msClass.getLogDetails(#request.id)\", msClass = GroupService.class)\n public void editGroup(@RequestBody EditGroupRequest request) {\n groupService.editGroup(request);\n }\n\n @GetMapping(\"/delete/{id}\")\n @RequiresPermissions(value = {PermissionConstants.SYSTEM_GROUP_READ_DELETE, PermissionConstants.PROJECT_GROUP_READ_DELETE}, logical = Logical.OR)\n @MsAuditLog(module = OperLogModule.GROUP_PERMISSION, type = OperLogConstants.DELETE, beforeEvent = \"#msClass.getLogDetails(#id)\", msClass = GroupService.class)\n public void deleteGroup(@PathVariable String id) {\n groupService.deleteGroup(id);\n }\n\n @PostMapping(\"/permission\")\n public GroupPermissionDTO getGroupResource(@RequestBody Group group) {\n return groupService.getGroupResource(group);\n }\n\n @PostMapping(\"/permission/edit\")\n @RequiresPermissions(value = {PermissionConstants.SYSTEM_GROUP_READ_SETTING_PERMISSION, PermissionConstants.PROJECT_GROUP_READ_SETTING_PERMISSION}, logical = Logical.OR)\n @MsRequestLog(module = OperLogModule.GROUP_PERMISSION)\n public void editGroupPermission(@RequestBody EditGroupRequest editGroupRequest) {\n groupService.editGroupPermission(editGroupRequest);\n }\n\n @GetMapping(\"/all/{userId}\")\n public List<Map<String, Object>> getAllUserGroup(@PathVariable(\"userId\") String userId) {\n return groupService.getAllUserGroup(userId);\n }\n\n @PostMapping(\"/list\")\n public List<Group> getGroupsByType(@RequestBody GroupRequest request) {\n return groupService.getGroupsByType(request);\n }\n\n @GetMapping(\"/list/ws/{workspaceId}/{userId}\")\n public List<Group> getWorkspaceMemberGroups(@PathVariable String workspaceId, @PathVariable String userId) {\n return groupService.getWorkspaceMemberGroups(workspaceId, userId);\n }\n\n @GetMapping(\"/list/project/{projectId}/{userId}\")\n public List<Group> getProjectMemberGroups(@PathVariable String projectId, @PathVariable String userId) {\n return groupService.getProjectMemberGroups(projectId, userId);\n }\n\n @GetMapping(\"/ws/{userId}\")\n public List<Workspace> getWorkspace(@PathVariable String userId) {\n return groupService.getWorkspace(userId);\n }\n\n @GetMapping(\"/{type}/{id}\")\n public List<?> getResource(@PathVariable String type, @PathVariable String id) {\n return groupService.getResource(type, id);\n }\n\n @PostMapping(\"/current/project/user/{goPage}/{pageSize}\")\n @RequiresPermissions(value = {PermissionConstants.PROJECT_GROUP_READ}, logical = Logical.OR)\n public Pager<List<User>> getCurrentProjectGroupUser(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody EditGroupRequest editGroupRequest) {\n editGroupRequest.setOnlyQueryCurrentProject(true);\n if (StringUtils.isBlank(editGroupRequest.getProjectId())) {\n editGroupRequest.setProjectId(SessionUtils.getCurrentProjectId());\n }\n Page<Object> page = PageHelper.startPage(goPage, pageSize, true);\n return PageUtils.setPageInfo(page, groupService.getGroupUser(editGroupRequest));\n }\n\n @GetMapping(\"/rm/{userId}/{groupId}\")\n public void removeGroupMember(@PathVariable String userId, @PathVariable String groupId) {\n groupService.removeGroupMember(userId, groupId);\n }\n\n @GetMapping(\"/source/{userId}/{groupId}\")\n public List<?> getGroupSource(@PathVariable String userId, @PathVariable String groupId) {\n return groupService.getGroupSource(userId, groupId);\n }\n\n @PostMapping(\"/add/member\")\n @MsRequestLog(module = OperLogModule.GROUP_PERMISSION)\n public void addGroupUser(@RequestBody EditGroupUserRequest request) {\n groupService.addGroupUser(request);\n }\n\n @PostMapping(\"/edit/member\")\n @MsRequestLog(module = OperLogModule.GROUP_PERMISSION)\n @RequiresPermissions(value = {PermissionConstants.SYSTEM_GROUP_READ_EDIT, PermissionConstants.PROJECT_GROUP_READ_EDIT}, logical = Logical.OR)\n public void editGroupUser(@RequestBody EditGroupUserRequest request) {\n groupService.editGroupUser(request);\n }\n\n @GetMapping(\"/workspace/list/resource/{groupId}/{groupType}\")\n public Object getWorkspaceResourceByGroup(@PathVariable String groupId, @PathVariable String groupType) {\n return groupService.getWorkspaceResourceByGroup(groupId, groupType);\n }\n}\n\n## 内容3\n### 文件路径:\nsystem-setting/backend/src/main/java/io/metersphere/controller/GroupController.java\n### 代码内容:\npackage io.metersphere.controller;\n\nimport com.github.pagehelper.Page;\nimport com.github.pagehelper.PageHelper;\nimport io.metersphere.base.domain.Group;\nimport io.metersphere.base.domain.User;\nimport io.metersphere.base.domain.Workspace;\nimport io.metersphere.commons.constants.OperLogConstants;\nimport io.metersphere.commons.constants.OperLogModule;\nimport io.metersphere.commons.constants.PermissionConstants;\nimport io.metersphere.commons.utils.PageUtils;\nimport io.metersphere.commons.utils.Pager;\nimport io.metersphere.commons.utils.SessionUtils;\nimport io.metersphere.log.annotation.MsRequestLog;\nimport io.metersphere.request.GroupRequest;\nimport io.metersphere.request.group.EditGroupRequest;\nimport io.metersphere.request.group.EditGroupUserRequest;\nimport io.metersphere.dto.GroupDTO;\nimport io.metersphere.dto.GroupPermissionDTO;\nimport io.metersphere.log.annotation.MsAuditLog;\nimport io.metersphere.service.GroupService;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.shiro.authz.annotation.Logical;\nimport org.apache.shiro.authz.annotation.RequiresPermissions;\nimport org.springframework.web.bind.annotation.*;\n\nimport jakarta.annotation.Resource;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\n\n@RequestMapping(\"/user/group\")\n@RestController\npublic class GroupController {\n\n @Resource\n private GroupService groupService;\n\n @PostMapping(\"/get/{goPage}/{pageSize}\")\n @RequiresPermissions(value = {PermissionConstants.SYSTEM_GROUP_READ}, logical = Logical.OR)\n public Pager<List<GroupDTO>> getGroupList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody EditGroupRequest request) {\n request.setGoPage(goPage);\n request.setPageSize(pageSize);\n return groupService.getGroupList(request);\n }\n\n @GetMapping(\"/get/all\")\n public List<GroupDTO> getAllGroup() {\n return groupService.getAllGroup();\n }\n\n @PostMapping(\"/get\")\n @RequiresPermissions(value = {PermissionConstants.SYSTEM_GROUP_READ, PermissionConstants.SYSTEM_USER_READ, PermissionConstants.WORKSPACE_USER_READ}, logical = Logical.OR)\n public List<Group> getGroupByType(@RequestBody EditGroupRequest request) {\n return groupService.getGroupByType(request);\n }\n\n @PostMapping(\"/add\")\n @RequiresPermissions(value = {PermissionConstants.SYSTEM_GROUP_READ_CREATE, PermissionConstants.PROJECT_GROUP_READ_CREATE}, logical = Logical.OR)\n @MsAuditLog(module = OperLogModule.GROUP_PERMISSION, type = OperLogConstants.CREATE, content = \"#msClass.getLogDetails(#request.id)\", msClass = GroupService.class)\n public Group addGroup(@RequestBody EditGroupRequest request) {\n request.setId(UUID.randomUUID().toString());\n return groupService.addGroup(request);\n }\n\n @PostMapping(\"/edit\")\n @RequiresPermissions(value = {PermissionConstants.SYSTEM_GROUP_READ_EDIT, PermissionConstants.PROJECT_GROUP_READ_EDIT}, logical = Logical.OR)\n @MsAuditLog(module = OperLogModule.GROUP_PERMISSION, type = OperLogConstants.UPDATE, beforeEvent = \"#msClass.getLogDetails(#request.id)\", content = \"#msClass.getLogDetails(#request.id)\", msClass = GroupService.class)\n public void editGroup(@RequestBody EditGroupRequest request) {\n groupService.editGroup(request);\n }\n\n @GetMapping(\"/delete/{id}\")\n @RequiresPermissions(value = {PermissionConstants.SYSTEM_GROUP_READ_DELETE, PermissionConstants.PROJECT_GROUP_READ_DELETE}, logical = Logical.OR)\n @MsAuditLog(module = OperLogModule.GROUP_PERMISSION, type = OperLogConstants.DELETE, beforeEvent = \"#msClass.getLogDetails(#id)\", msClass = GroupService.class)\n public void deleteGroup(@PathVariable String id) {\n groupService.deleteGroup(id);\n }\n\n @PostMapping(\"/permission\")\n public GroupPermissionDTO getGroupResource(@RequestBody Group group) {\n return groupService.getGroupResource(group);\n }\n\n @PostMapping(\"/permission/edit\")\n @RequiresPermissions(value = {PermissionConstants.SYSTEM_GROUP_READ_SETTING_PERMISSION, PermissionConstants.PROJECT_GROUP_READ_SETTING_PERMISSION}, logical = Logical.OR)\n @MsRequestLog(module = OperLogModule.GROUP_PERMISSION)\n public void editGroupPermission(@RequestBody EditGroupRequest editGroupRequest) {\n groupService.editGroupPermission(editGroupRequest);\n }\n\n @GetMapping(\"/all/{userId}\")\n public List<Map<String, Object>> getAllUserGroup(@PathVariable(\"userId\") String userId) {\n return groupService.getAllUserGroup(userId);\n }\n\n @PostMapping(\"/list\")\n public List<Group> getGroupsByType(@RequestBody GroupRequest request) {\n return groupService.getGroupsByType(request);\n }\n\n @GetMapping(\"/list/ws/{workspaceId}/{userId}\")\n public List<Group> getWorkspaceMemberGroups(@PathVariable String workspaceId, @PathVariable String userId) {\n return groupService.getWorkspaceMemberGroups(workspaceId, userId);\n }\n\n @GetMapping(\"/list/project/{projectId}/{userId}\")\n public List<Group> getProjectMemberGroups(@PathVariable String projectId, @PathVariable String userId) {\n return groupService.getProjectMemberGroups(projectId, userId);\n }\n\n @GetMapping(\"/ws/{userId}\")\n public List<Workspace> getWorkspace(@PathVariable String userId) {\n return groupService.getWorkspace(userId);\n }\n\n @GetMapping(\"/{type}/{id}\")\n public List<?> getResource(@PathVariable String type, @PathVariable String id) {\n return groupService.getResource(type, id);\n }\n\n @PostMapping(\"/user/{goPage}/{pageSize}\")\n @RequiresPermissions(value = {PermissionConstants.SYSTEM_GROUP_READ}, logical = Logical.OR)\n public Pager<List<User>> getGroupUser(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody EditGroupRequest editGroupRequest) {\n Page<Object> page = PageHelper.startPage(goPage, pageSize, true);\n return PageUtils.setPageInfo(page, groupService.getGroupUser(editGroupRequest));\n }\n\n @GetMapping(\"/rm/{userId}/{groupId}\")\n @RequiresPermissions(PermissionConstants.SYSTEM_GROUP_READ)\n public void removeGroupMember(@PathVariable String userId, @PathVariable String groupId) {\n groupService.removeGroupMember(userId, groupId);\n }\n\n @GetMapping(\"/source/{userId}/{groupId}\")\n public List<?> getGroupSource(@PathVariable String userId, @PathVariable String groupId) {\n return groupService.getGroupSource(userId, groupId);\n }\n\n @PostMapping(\"/add/member\")\n @MsRequestLog(module = OperLogModule.GROUP_PERMISSION)\n public void addGroupUser(@RequestBody EditGroupUserRequest request) {\n groupService.addGroupUser(request);\n }\n\n @PostMapping(\"/edit/member\")\n @MsRequestLog(module = OperLogModule.GROUP_PERMISSION)\n public void editGroupUser(@RequestBody EditGroupUserRequest request) {\n groupService.editGroupUser(request);\n }\n}\n\n", "input": "", "output": "【漏洞类型】权限绕过漏洞\n\n【严重性】High\n\n【漏洞摘要】原始代码中,`BaseUserController` 和 `GroupController` 中的某些方法没有进行足够的权限校验,允许用户访问或修改他们无权访问或修改的资源。例如,在 `BaseUserController` 的 `getCurrentWorkspaceMember`、`getProjectMemberListAll` 和 `getProjectMembers` 方法中,用户可以请求任意工作空间或项目的成员列表,而没有检查用户是否属于该工作空间或项目。在 `GroupController` 中,`getAllUserGroup` 方法没有权限校验,允许任何用户获取任意用户的组信息。\n\n【关键代码模式】\n- 未进行权限校验的方法,例如 `getCurrentWorkspaceMember`、`getProjectMemberListAll`、`getProjectMembers` 和 `getAllUserGroup`。\n- 使用 `SessionUtils` 获取当前用户信息,但未进一步验证用户对请求资源的访问权限。\n- 缺少对用户输入的验证或限制,允许用户请求任意资源。\n\n【修复方法说明】\n- 修复方法通过在关键代码中添加权限校验逻辑,确保用户只能访问或修改他们有权限的资源。\n- 适用的最佳实践是使用细粒度的权限控制,确保每个用户只能操作其有权限的数据。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/main/java/com/cronutils/validation/CronValidator.java\n### 代码内容:\npackage com.cronutils.validation;\n\nimport com.cronutils.model.CronType;\nimport com.cronutils.model.definition.CronDefinition;\nimport com.cronutils.model.definition.CronDefinitionBuilder;\nimport com.cronutils.parser.CronParser;\n\nimport javax.validation.ConstraintValidator;\nimport javax.validation.ConstraintValidatorContext;\n\npublic class CronValidator implements ConstraintValidator<Cron, String> {\n\n private CronType type;\n\n @Override\n public void initialize(Cron constraintAnnotation) {\n this.type = constraintAnnotation.type();\n }\n\n @Override\n public boolean isValid(String value, ConstraintValidatorContext context) {\n if (value == null) {\n return true;\n }\n\n CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(type);\n CronParser cronParser = new CronParser(cronDefinition);\n try {\n cronParser.parse(value).validate();\n return true;\n } catch (IllegalArgumentException e) {\n context.disableDefaultConstraintViolation();\n context.buildConstraintViolationWithTemplate(\"Error parsing the Cron expression\").addConstraintViolation();\n return false;\n }\n }\n}\n\n", "input": "", "output": "【漏洞类型】安全漏洞类型名称\n【严重性】Medium\n【漏洞摘要】这是一个安全漏洞,具体表现为在代码中使用了用户输入作为错误信息模板。这可能导致任意表达式语言(EL)表达式执行,因为某些验证框架允许在错误信息中嵌入表达式,而这些表达式可以被恶意用户操纵。问题行在修复代码中是`context.buildConstraintViolationWithTemplate(\"Error parsing the Cron expression\").addConstraintViolation();`,在原始代码中则是`context.buildConstraintViolationWithTemplate(e.getMessage()).addConstraintViolation();`。\n【关键代码模式】与漏洞相关的关键代码模式包括用户输入的使用、错误信息的构建以及可能的EL表达式执行。\n【修复方法说明】修复方法通过将具体的异常信息替换为通用的错误信息,防止了恶意用户通过错误信息中的表达式执行任意代码。最佳实践包括对用户输入进行严格的验证和对错误信息进行安全处理,以避免潜在的安全风险。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/changes/changes.xml\n### 代码内容:\n<document xmlns=\"http://maven.apache.org/changes/1.0.0\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:schemaLocation=\"http://maven.apache.org/changes/1.0.0 http://maven.apache.org/plugins/maven-changes-plugin/xsd/changes-1.0.0.xsd\">\n <properties>\n <title>YAML 1.1 parser and emitter</title>\n </properties>\n <body>\n <release version=\"1.31\" date=\"in Git\" description=\"Maintenance\">\n <action dev=\"asomov\" type=\"add\" issue=\"525\">\n Add test for stackoverflow\n </action>\n <action dev=\"maslovalex\" type=\"fix\" issue=\"522\">\n Deserializing key \"on\" fails with Exception\n </action>\n <action dev=\"asomov\" type=\"update\">\n Merged PR #7: https://bitbucket.org/snakeyaml/snakeyaml/pull-requests/7 (2022-04-04)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Do not emit global TAGs by not keeping them in Parser\n (breaks https://code.google.com/archive/p/snakeyaml/issues/149)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add an example with Lombok and ENV variable substitution (2021-12-29)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Fix issues with comments\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"517\">\n Recognise issue with trailing TAB\n </action>\n </release>\n <release version=\"1.30\" date=\"2021-12-14\" description=\"Maintenance\">\n <action dev=\"asomov\" type=\"fix\" issue=\"516\">\n Remove snakeyaml.org\n </action>\n <action dev=\"asomov\" type=\"update\">\n Migrate to https://bitbucket.org/snakeyaml/snakeyaml (2021-10-23)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Fix issues with comments\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"506\">\n Improve parsing a number starting with 0x (2021-06-28)\n Take the Float pattern from ruamel: https://pypi.org/project/ruamel.yaml\n </action>\n </release>\n <release version=\"1.29\" date=\"2021-06-08\" description=\"Maintenance\">\n <action dev=\"Wolf2323\" type=\"fix\">\n Fix Events ordering and improve Node-Comment association (2021-05-13)\n </action>\n <action dev=\"asomov\" type=\"fix\">\n Fix corner case for comments where blank lines after plain token (2021-03-02)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add a test for issue 500 (2021-03-14)\n </action>\n <action dev=\"asomov\" type=\"remove\">\n Remove unused variable (2021-02-28)\n </action>\n </release>\n <release version=\"1.28\" date=\"2021-02-22\" description=\"Maintenance\">\n <action dev=\"maslovalex\" type=\"fix\">\n Fix tests to work on Windows (2021-02-19)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add enumCaseSensitive property to LoaderOptions to control constructing enum with case sensitivity (2021-01-05)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"493\">\n ENV substitution default can contain special characters (2021-01-03)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Allow for read and write YAML with Comments (2020-11-01)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"485\">\n Alias names are too permissive compared to libyaml and future spec.\n Follow https://github.com/yaml/yaml-spec/blob/master/rfc/RFC-0003.md (2020-10-06)\n </action>\n </release>\n <release version=\"1.27\" date=\"2020-08-31\" description=\"Maintenance\">\n <action dev=\"asomov\" type=\"fix\" issue=\"481\">\n Serialize anchors that are not used by any alias (2020-08-14)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"416\">\n Add indentWithIndicator option to improve dumping sequences (2020-07-14)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"480\">\n Anchor should allow non ascii characters while dumping (2020-06-29)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"remove\">\n Remove Spring related tests because they are outdated and upgrading to Spring 5\n makes the tests fail under JDK 7 (2020-05-02)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"476\">\n Make constructor of EnvScalarConstructor public (2020-04-24)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"474\">\n Parse the value of byte and short after a narrowing primitive conversion (2020-04-16)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add test for different Line Break Characters in the error message (2020-03-09)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Fix yet another OWASP false positive. It complains that the Spring controller makes SnakeYAML insecure\n even though SnakeYAML does not use Spring controller and does not depend on Spring (but the tests do).\n Bump spring.version from 3.2.17.RELEASE to 5.2.4.RELEASE (2020-03-08)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Migrated to GIT (2020-02-29)\n </action>\n </release>\n <release version=\"1.26\" date=\"2020-02-28\" description=\"Maintenance\">\n <action dev=\"asomov\" type=\"fix\" issue=\"377\">\n Allow configuration for preventing billion laughs attack (2020-02-24)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Update Maven wrapper (2020-01-18)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"468\">\n Parse ENV variables similar to how it works in docker-compose (2019-12-25)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"468\">\n Allow non ASCII characters in the anchor names (2019-12-23)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"462\">\n Clean up composer internal maps after construction. Should not have any impact but\n it does not harm (2019-10-25)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"459\">\n The '!' tag now works the same way as for PyYAML. (12 is parsed as int instead of string)\n The spec files are modified (spec-08-07.canonical spec-08-07.data)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add test for not implemented \"!\" tag (2019-10-11)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor Events: expose Event.ID via a getter (2019-10-05)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor ScalarAnalysis: make it immutable (2019-09-28)\n </action>\n <action dev=\"asomov\" type=\"add\" issue=\"454\">\n Add example for integer without time pattern (2019-08-22)\n </action>\n </release>\n <release version=\"1.25\" date=\"2019-08-13\" description=\"Maintenance\">\n <action dev=\"asomov\" type=\"update\">\n Deprecate setting tags in TypeDescription (2019-06-23)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"449\">\n Add test for issue 449 (2019-06-19)\n </action>\n <action dev=\"asomov\" type=\"add\" issue=\"441\">\n Restore the way to get anchor for a Node (2019-04-13)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"437\">\n Introduce setting to keep !!str tag for String even when it contains non-printable chars (2019-03-24)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Update plugin versions (2019-03-04)\n </action>\n </release>\n <release version=\"1.24\" date=\"2019-02-28\" description=\"Maintenance\">\n <action dev=\"asomov\" type=\"update\">\n Refactor BaseConstructor: Factored out postponed mapping logic so subclasses can effectively\n override constructMapping2ndStep() and delegate to the postponed mapping logic.\n This is backported from SnakeYAML engine (2019-01-26)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"431\">\n Customize simple key length when dumping (2019-01-17)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"430\">\n Wrap runtime exceptions into YAMLException (2019-01-14)\n </action>\n <action dev=\"asomov\" type=\"fix\">\n Fix: Null tag constructor not called when parsing top-level null value (2019-01-13)\n (issue reported for snakeyaml-engine)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"429\">\n Provide \"Automatic-Module-Name\" entry in MANIFEST (2018-12-16)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"426\">\n Fix NPE when duplicate keys are not allowed and the key is null (2018-12-02)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Support java.sql classes without the need to depend on java.sql module in java9+ (2018-11-06)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Require Java 7 (2018-11-06)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"423\">\n Date Serialization Fails for TimeZones on Daylight Savings Time (2018-11-06)\n </action>\n </release>\n <release version=\"1.23\" date=\"2018-08-27\" description=\"Maintenance\">\n <action dev=\"asomov\" type=\"fix\" issue=\"412\">\n Add another getter for ScalarStyle to support backwards compatibility (2018-08-27)\n </action>\n </release>\n <release version=\"1.22\" date=\"2018-08-25\" description=\"Maintenance\">\n <action dev=\"asomov\" type=\"update\">\n Run tests under Java 11 (2018-08-19)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Use Maven 3.5.4 (2018-08-19)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"412\">\n Restore the Boolean constructors for Events and Nodes for binary compatibility\n of dependent projects (2018-08-17)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"411\">\n System Property \"java.runtime.name\" is not required to be defined (2018-08-12)\n Ensure Compatibility with Graal\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"409\">\n Dumping Enum breaks when Enum value is Anonymous inner class (2018-08-07)\n Solution: refactor the way to detect if the full tag can be omitted (and !!str is used instead).\n Instead of checking whether the class for property and the class for the actual value is the same\n we can just check that the property class is not java.lang.Enum\n </action>\n </release>\n <release version=\"1.21\" date=\"2018-04-13\" description=\"Maintenance\">\n <action dev=\"asomov\" type=\"update\">\n Scanner throws IndexOutOfBoundsException if no more token left but getToken()\n or peekToken() called (2018-04-10)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Enhance output of token IDs (2018-04-06)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Mark: expose buffer and pointer (2018-04-06)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"401\">\n Restore index in Mark - it is used in JRuby (2018-03-26)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"397\">\n Plain scalars with colons in flow sequences/mappings are valid YAML (2018-03-03)\n </action>\n </release>\n <release version=\"1.20\" date=\"2018-02-28\" description=\"Maintenance\">\n <action dev=\"maslovalex\" type=\"fix\" issue=\"393\">\n Improve reflective access operation to avoid warning under Java 9 (2018-02-24)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor collection node - flow style must not be null (2018-02-16)\n </action>\n <action dev=\"asomov\" type=\"remove\">\n Refactor: remove unused method isResolved in Node (2018-02-15)\n </action>\n <action dev=\"asomov\" type=\"remove\">\n Refactor: remove unused code in Tag (2018-02-15)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor nodes and events - use enum FlowStyle instead of Boolean (2018-02-10)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor ScalarToken, ScalarNode and ScalarEvent - use enum ScalarStyle instead of Character (2018-02-10)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor Token - remove unused code (2018-02-08)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor Mark - remove unused code (2018-02-06)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"395\">\n Introduce DuplicateKeyException and report line number for duplicate keys\n when creating non-Javabeans (2017-12-15)\n </action>\n </release>\n <release version=\"1.19\" date=\"2017-10-14\" description=\"Maintenance\">\n <action dev=\"maslovalex\" type=\"update\">\n Only use FIELD access for Android in PropertyUtils (2017-08-20)\n </action>\n <action dev=\"maslovalex\" type=\"update\">\n Add getAnnotations() and getAnnotation() methods to Property (2017-08-20)\n </action>\n <action dev=\"maslovalex\" type=\"fix\" issue=\"383\">\n Some configuration properties of Representer are ignored.\n </action>\n <action dev=\"maslovalex\" type=\"fix\" issue=\"386\">\n Fix order of duplicate keys indices to prevent wrong removals.\n </action>\n <action dev=\"maslovalex\" type=\"fix\" issue=\"382\">\n Fix 382: same as 322\n </action>\n <action dev=\"maslovalex\" type=\"fix\" issue=\"322\">\n Fix 322: if there is no getter try to get type information from a setter\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"377\">\n Add test for 'billion laughs attack'\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"368\">\n Relax final restriction on TypeDescription (2017-05-05)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"375\">\n Empty yaml file must return null instead of throwing an exception when loading a JavaBean (2017-05-03)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"374\">\n Fix Number when it is used as a JavaBean property with a custom Locale (2017-04-30)\n </action>\n <action dev=\"asomov\" type=\"add\" issue=\"373\">\n Add redundant test (2017-04-06)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Provide access to node's anchor, pull request#20 (2017-03-30)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"370\">\n Remove redundant \"Bundle-RequiredExecutionEnvironment: J2SE-1.5\" (2017-03-16)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"364\">\n Serializing Calendar objects with certain timezone offsets renders invalid YAML (2017-02-28)\n </action>\n </release>\n <release version=\"1.18\" date=\"2017-02-22\" description=\"Maintenance\">\n <action dev=\"asomov\" type=\"fix\" issue=\"358\">\n Validate DumperOptions to prevent invalid YAML to be dumped (2017-01-12)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"355\">\n Fix for emitter to split long plain string scalars over multiple lines (2017-01-10)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Let Mark implement Serializable so that ParserException can be serialized (2016-12-15)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"354\">\n Add tests for issue 354 (2016-12-12)\n </action>\n <action dev=\"maslovalex\" type=\"fix\" issue=\"337\">\n Throw exception in case of duplicate keys in MappingNode and\n LoaderOptions.allowDuplicateKeys is false. (2016-11-25)\n </action>\n <action dev=\"maslovalex\" type=\"fix\" issue=\"351\">\n Keep same nodes order on merge (preprocess keys for MappingNode\n and remove duplicates keeping the last one). (2016-11-22)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"349\">\n Ignore white spaces for base64 encoded scalar (2016-11-17)\n </action>\n <action dev=\"maslovalex\" type=\"fix\" issue=\"348\">\n Not removing parent object when composed object is an anchor. (2016-10-13)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"323\">\n Support \"Miscellaneous Symbols and Pictographs\" (2016-09-05)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"341\">\n Fix NPE in BaseRepresenter.multiRepresenters if it contains 'null' as value (2016-05-03)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"335\">\n Add example for issue 335 - the same tag for different node kinds (2016-03-19)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"332\">\n Add example for issue 332 (2016-02-24)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Build SnakeYAML for different JDKs with docker without building images (2016-02-22)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Update plugin versions (2016-02-19)\n </action>\n </release>\n <release version=\"1.17\" date=\"2016-02-19\" description=\"Maintenance\">\n <action dev=\"maslovalex\" type=\"fix\" issue=\"318\" due-to=\"Rog Sigal\">\n Use Thread.currentThread.getContextClassLoader in Class.forName instead of default one (2016-02-15)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"329\">\n Add parameters to POM to change distribution server (2016-02-03)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"326\">\n Relax some of the modifiers to make integration easier (2016-02-02)\n </action>\n <action dev=\"maslovalex\" type=\"update\" issue=\"310\">\n Make use of private/protected constructors for Scalars and 'immutable' objects.\n Added 'src/test/java8' for Java8 specific tests (requires -Pwith-java8-tests) (2016-01-07)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"320\">\n Better support to customise BaseConstructor. Make 'composer' field and constructDocument()\n method protected (2015-10-03)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"306\">\n Better UUID support (2015-09-23)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Use Polyglot Maven: add support for YAML POM (2015-09-10)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"314\">\n Provide ability to customize anchor names (2015-08-25)\n </action>\n </release>\n <release version=\"1.16\" date=\"2015-08-18\" description=\"Maintenance\">\n <action dev=\"asomov\" type=\"fix\" issue=\"308\">\n Provide Docker image for testing under different Java 8 (2015-06-19)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"302\">\n Convert binary byte[] to String when it matches the JavaBean property (2015-05-26)\n </action>\n <action dev=\"asomov\" type=\"update\">\n The source code migrated to Bitbucket. Old commits (older then 6 years) have\n been removed. (2015-05-20)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"212\">\n Fix a typo in an error message in ScannerImpl (2015-05-19)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"209\">\n Do not print special characters in the error message (2015-04-16)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"199\">\n Evaluate implementations for duplicate mapping keys.\n </action>\n </release>\n <release version=\"1.15\" date=\"2015-02-19\" description=\"Maintenance\">\n <action dev=\"asomov\" type=\"update\">\n Apply FindBugs and PMD recommendations (2015-02-16)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Added split lines option to DumperOptions to allow line splitting to be disabled. (2015-02-08)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Use Maven 3 and update plugin versions (2015-02-05)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"205\">\n Add test to clarify iOS emoji character in the surrogate range (2015-02-03)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"201\">\n Fix grammar error in exception message (2014-11-18)\n </action>\n </release>\n <release version=\"1.14\" date=\"2014-08-29\" description=\"Maintenance\">\n <action dev=\"maslovalex\" type=\"fix\" issue=\"197\">\n Load JavaBean with fields using custom constructors (2014-08-18)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"192\">\n Drop support for \"Value Key Language-Independent Type\" and '=' a standard character (2014-05-22)\n </action>\n <action dev=\"maslovalex\" type=\"fix\" issue=\"191\">\n Improve error message for invalid YAML document (2014-05-21)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"188\">\n Improve error message for invalid YAML document (2014-03-26)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"183\">\n Support Number class (2013-11-07)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"182\">\n Double.POSITIVE_INFINITY applied to float fields (2013-11-07)\n </action>\n </release>\n <release version=\"1.13\" date=\"2013-08-29\" description=\"Maintenance\">\n <action dev=\"asomov\" type=\"fix\" issue=\"178\">\n OSGi: Specify version for exported packages (2013-06-27)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"177\">\n Improve error report while parsing a JavaBean (2013-05-14)\n </action>\n <action dev=\"Jordan\" type=\"fix\" issue=\"135\">\n Arrays of primitives are now fully supported (2013-04-16)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"174\">\n Duplicate anchors in an input document should not be an error (2013-04-03)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"172\">\n Using a locale with minimum number fraction digits breaks anchor generation (2013-03-30)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"171\">\n Use more generic generics in BaseRepresenter (2013-03-30)\n </action>\n </release>\n <release version=\"1.12\" date=\"2013-03-02\" description=\"Maintenance\">\n <action dev=\"asomov\" type=\"update\">\n The build is using JDK 1.6 (2013-03-02)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"169\">\n Make Constructor.typeDefinitions protected to be more flexible (2013-02-18)\n </action>\n <action dev=\"asomov\" type=\"remove\">\n Remove file AUTHORS because it does not reflect the actual situation (2012-11-09)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Improve the error message when a TAB character starts a token (2012-11-06)\n </action>\n </release>\n <release version=\"1.11\" date=\"2012-09-29\" description=\"Maintenance\">\n <action dev=\"asomov\" type=\"fix\" issue=\"158\">\n Fix issue 158: improve support for 32-bit characters (UTF-16 surrogate pairs) (2012-09-29)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"146\">\n Fix issue 146: empty tags should not force explicit document start (2012-09-29)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"156\">\n Fix issue 156: setSkipMissingProperties fails for non-scalar values (2012-09-05)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"155\">\n Fix issue 155: SnakeYAML must always eat its own food - a YAML document created by itself must\n be read without exceptions (2012-09-04)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"154\">\n Fix issue 154: Add option to skip missing properties when deserializing YAML into Java object (2012-07-25)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Add a test for issue 150 (2012-06-17)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Add a test for issue 151 (2012-06-14)\n </action>\n <action dev=\"asomov\" type=\"fix\">\n Fix issue 149: Directives are no longer lost between documents (2012-06-10)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: use Version enum instead of Array of Integers.\n This is done to simplify fixing issue 149 (2012-06-09)\n </action>\n <action dev=\"asomov\" type=\"fix\">\n Add tests for issue 148 (2012-06-07)\n </action>\n <action dev=\"asomov\" type=\"fix\">\n Fix issue 147: Serialized representation of character U+FFFD causes exception on deserialization (2012-06-05)\n </action>\n <action dev=\"asomov\" type=\"fix\">\n Fix issue 145: exception.getMessage() must show the line number as exception.toString() does (2012-04-03)\n </action>\n <action dev=\"maslovalex\" type=\"fix\">\n Fix issue 144: improve type inference for Compact Object Notation (2012-03-16)\n </action>\n <action dev=\"maslovalex\" type=\"add\">\n Improve Android support\n </action>\n </release>\n <release version=\"1.10\" date=\"2012-02-08\" description=\"Maintenance\">\n <action dev=\"asomov\" type=\"update\">\n Fix issue 141: TimeZone is configurable in DumperOptions (2012-02-03)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor with PMD: minor non-functional improvements (2012-01-28)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor with PMD: Avoid unused method parameter 'index' in\n Serializer and Emitter (2012-01-28)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor with PMD: Composer - Avoid unused method parameter 'index' in\n 'Composer.composeNode(Node parent, Object index)''. It was used\n in PyYAML for kind of XPath for YAML, but it was not imported from PyYAML (2012-01-28)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor Emitter: the SPACE mutable static field could be changed by malicious code or by accident.\n Boxed value is unboxed and then immediately reboxed (2012-01-28)\n </action>\n <action dev=\"asomov\" type=\"remove\">\n Refactor with FindBugs: remove unused ScalarAnalysis.allowDoubleQuoted (2012-01-28)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor with FindBugs: do not rely on default encoding (2012-01-28)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: apply FindBugs recommendations (2012-01-28)\n </action>\n <action dev=\"maslovalex\" type=\"fix\">\n Fix issue 139: merge should use last key in map (2012-01-24)\n </action>\n <action dev=\"asomov\" type=\"fix\">\n Fix issue 136: tabs are allowed in plain scalars. This is a deviation from PyYAML (2012-01-11)\n </action>\n <action dev=\"asomov\" type=\"add\" issue=\"138\">\n Expose internal data of ReaderException (2012-01-06)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"137\">\n Respect supplementary characters (2012-01-06)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Use http://mercurial.selenic.com/wiki/EolExtension to force LF as line separator\n for all operating systems (2011-12-20)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add a test for issue 136 (2011-12-14)\n </action>\n <action dev=\"asomov\" type=\"remove\">\n Deprecate the DumperOptions.calculateScalarStyle() method because it was introduced as a quick\n fix for issue 29. Now it should not be required at all (because of the fix for issue 66),\n or it should be implemented in the Representer (in RepresentString) (2011-10-10)\n </action>\n <action dev=\"asomov\" type=\"fix\">\n Fix issue 66: literal scalar style is used by default for multiline scalars (2011-10-10)\n </action>\n <action dev=\"asomov\" type=\"add\">\n An example added: how to dump multiline literal scalars (2011-10-04)\n </action>\n <action dev=\"asomov\" type=\"add\">\n An example added: how to dump recursive object for issue 133 (2011-09-14)\n </action>\n <action dev=\"asomov\" type=\"add\">\n A test added for issue 132 (2011-09-13)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Finish 1.9 release (2011-08-15)\n </action>\n </release>\n <release version=\"1.9\" date=\"2011-08-15\" description=\"Introduce Yaml.loadAs() and Yaml.dumpAs() methods\">\n <action dev=\"asomov\" type=\"update\">\n Add a test to prove that SnakeYAML is not affected by the problem reported for libyaml\n at http://pyyaml.org/ticket/196 (2011-07-28)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"128\">\n Since timestamp contains ':' characters it is dumped with single quoted scalar style\n in the flow context. The single quoted scalar style causes to dump the explicit tag.\n In the block context the plain scalar can be used and the tag is not required. It may cause\n unpredictable behaviour if the tag is required. See the comments in JodaTimeExampleTest (2011-07-25)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"130\">\n Fix scientific notation inconsistency in the YAML 1.1 specification:\n scalar '8e-06' should be parsed as a Double (2011-07-24)\n </action>\n <action dev=\"asomov\" type=\"remove\" issue=\"127\">\n Do not allow to override BaseRepresenter.representData(Object data) because\n users should instead implement Represent interface (2011-07-21)\n </action>\n <action dev=\"asomov\" type=\"remove\" issue=\"124\">\n Deprecate DumperOptions.explicitRoot (2011-07-20)\n </action>\n <action dev=\"asomov\" type=\"add\" issue=\"124\">\n Add Yaml.dumpAs(Object, Tag.MAP, FlowStyle) and Yaml.dumpAsMap(Object) methods. JavaBeanDumper is marked as deprecated (2011-07-16)\n </action>\n <action dev=\"asomov\" type=\"add\" issue=\"127\">\n Add example to show how to dump a custom class (2011-07-12)\n </action>\n <action dev=\"asomov\" type=\"add\" issue=\"129\">\n Add Yaml.serialize(Node) low level method to the public API (2011-07-14)\n </action>\n <action dev=\"asomov\" type=\"add\" issue=\"129\">\n Add Yaml.represent(Object) low level method to the public API (2011-07-14)\n </action>\n <action dev=\"asomov\" type=\"add\" issue=\"125\">\n Add support for Maven 3 via 'm3' profile (2011-07-10)\n </action>\n <action dev=\"asomov\" type=\"remove\" issue=\"124\">\n Remove deprecated JavaBeanParser (2011-07-05)\n </action>\n <action dev=\"asomov\" type=\"remove\" issue=\"124\">\n Remove redundant JavaBeanDumper.classTags set (2011-07-03)\n </action>\n <action dev=\"asomov\" type=\"add\" issue=\"124\">\n Add Yaml.loadAs() methods. JavaBeanLoader is marked as deprecated (2011-07-03)\n </action>\n <action dev=\"asomov\" type=\"remove\" issue=\"124\">\n Remove TypeDescription.root property to prepare issue 124. This is a minor backwards incompatible change.\n Now instead of setting as root, the TypeDescription must be passed to the Contructor's constructor\n to be taken as the root definition (2011-07-03)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"121\" due-to=\"Jaromir\">\n Fix: close files in tests to avoid a possible file handle limit (2011-06-09)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"116\" due-to=\"Jim Peterson\">\n Fix: Improved support for empty JavaBeans (2011-06-09)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"112\" due-to=\"Lethargish\">\n Fix: Improved support for parameterised types in collections (2011-05-25)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"115\" due-to=\"elkniwt\">\n Fix: parameterised JavaBeans fail to load and dump because they are treated as Maps (2011-05-16)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"114\" due-to=\"gileadis\">\n Fix: Do not remove root tags of JavaBeans when it is not explicitly requested (2011-04-20)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"111\" due-to=\"JordanAngold\">\n Fix: Long escaped tag URI sequences throw BufferOverflowException (2011-03-03)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"110\" due-to=\"dmitry.s.mamonov\">\n Fix: introduce a package for external libraries and move there the 64Coder\n and the Google's URL encoder (2011-02-24)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"109\" due-to=\"cjalmeida\">\n Fix: ancient years must be dumped with leading zeros (2011-02-19)\n </action>\n <action dev=\"asomov\" type=\"remove\" due-to=\"JordanAngold\">\n Remove unused code in Constructor: Modifier.isAbstract() is not needed any more (2011-02-18)\n </action>\n <action dev=\"JordanAngold\" type=\"fix\" issue=\"108\">\n Enum's name property shall be dumped instead of the 'toString()' output (2011-02-16)\n </action>\n </release>\n <release version=\"1.8\" date=\"2011-02-15\" description=\"Performance improvement\">\n <action dev=\"asomov\" type=\"add\">\n Add example to howto Wiki:\n How_to_substitute_object_in_YAML_document_with_a_custom_object (2011-01-27)\n </action>\n <action dev=\"asomov\" type=\"update\">\n When the YAML document to be loaded is provided as String parse it directly\n without making a Reader first (2011-01-23)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"106\">\n Immutable data structures in StreamReader allow to share the same buffer for all\n the Mark instances. It makes 'withMarkContext' setting redundant (2011-01-19)\n </action>\n <action dev=\"maslovalex\" type=\"update\" issue=\"100\">\n Merge JavaBean properties when an explicit tag is provided (2011-01-11)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"99\">\n Add an example for escaping line breaks in binary content (2011-01-03)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"97\">\n Propose a solution for JavaBeans to support SortedSet property when it is encoded\n as a sequence (2010-11-24)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"59\">\n Simplify the way how the order of JavaBean properties is specified. Introduce\n PropertyUtils.createPropertySet() method to be overridden when a specific order\n is expected (2010-11-23)\n </action>\n <action dev=\"maslovalex\" type=\"fix\" issue=\"95\">\n Fix: Loading of generic collections with Array parameter(s). (2010-11-16)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"94\">\n Add ChangeRuntimeClassTest as an example how to change a class for a global tag (2010-11-05)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Inner objects in Constructor become protected to be more flexible when Constructor\n is expended (2010-10-03)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"91\">\n Apply www.snakeyaml.org domain name (2010-09-20)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"90\">\n Move Base64Coder into another package to keep a separate copyright statement.\n Base64Coder is left unchanged (2010-09-19)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"69\">\n Iterable should not be serialised as sequence (2010-09-16)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Introduce 'fast' Maven profile to quickly build cobertura reports (2010-09-16)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"89\">\n Fix: Specify plugin versions in POM (2010-09-15)\n </action>\n <action dev=\"maslovalex\" type=\"fix\" issue=\"88\">\n Fix: Custom tag erased when referenced from generic collection (2010-09-15)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Minor refactoring in Emitter to improve performance: save calls to Constant.has() (2010-09-13)\n </action>\n <action dev=\"maslovalex\" type=\"update\">\n Minor refactorings in Emitter to improve performance: create less Strings [r9185e0b3] (2010-09-10)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"79\">\n Introduce LoaderOptions to be able to specify configuration while loading (2010-09-03)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"81\">\n Representer.representJavaBeanProperty() is given the wrong tag. Instead of the property tag,\n the tag for the JavaBean itself is provided. (2010-09-01)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Rename JvmDetector into GenericsBugDetector (2010-08-31)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"80\" due-to=\"SebastienRainville\">\n Fix: Timestamp is not parsed properly when milliseconds start with 0 (2010-08-24)\n </action>\n <action dev=\"maslovalex\" type=\"update\" issue=\"79\">\n Context for error reporting consumes a lot of resources (2010-08-21)\n </action>\n <action dev=\"asomov\" type=\"remove\">\n Cleanup unused code in deprecated Loader and Dumper (2010-08-13)\n </action>\n </release>\n <release version=\"1.7\" date=\"2010-08-12\" description=\"Simplify public API (drop Loader and Dumper)\">\n <action dev=\"asomov\" type=\"update\">\n Eclipse does not run JUnit 4 tests when they are launched for the whole project (2010-08-11)\n </action>\n <action dev=\"maslovalex\" type=\"update\" issue=\"55\">\n Share PropertyUtils if not explicitly set in Constructor or Representer\n (BeanAccess.FIELD works properly when JavaBean is identified by a root tag) (2010-08-11)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Create 1.7 Release Candidate 1 (2010-08-11)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"77\">\n Simplify public API: Drop Dumper (2010-08-06)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"77\">\n Simplify public API: Drop Loader (2010-08-05)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"75\" due-to=\"jon.p.hermes\">\n Add examples to create scalars that match custom regular expression:\n CustomImplicitResolverTest, CustomBeanResolverTest (2010-08-03)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"74\" due-to=\"Kevin Menard\">\n Do not use redundant tags for arrays which are JavaBean properties. (2010-07-21)\n </action>\n <action dev=\"asomov\" type=\"update\">\n RecursiveSetTest proves that it is possible to construct a recursive set (2010-07-20)\n </action>\n <action dev=\"asomov\" type=\"add\" issue=\"73\" due-to=\"birnbuazn\">\n Provide sequence support for loading java.util.Set. Also provide an example\n to serialise a java.util.Set as a sequence. (2010-07-19)\n </action>\n <action dev=\"asomov\" type=\"add\" issue=\"72\" due-to=\"birnbuazn\">\n Support java.util.Collection as a parent for List and Set (2010-07-09)\n </action>\n <action dev=\"maslovalex\" type=\"add\" issue=\"55\" due-to=\"birnbuazn\">\n Allow direct field access bypassing setters and getters. Empty constructor\n is required to support 2-step construction (2010-07-02)\n </action>\n <action dev=\"asomov\" type=\"update\" issue=\"69\">\n Serialise Iterator and Iterable as sequences (2010-06-25)\n </action>\n <action dev=\"asomov\" type=\"update\" due-to=\"maslovalex\">\n Change error message when 'No suitable constructor with N arguments found for class' (2010-06-23)\n </action>\n <action dev=\"asomov\" type=\"add\" due-to=\"Antony Stubbs\">\n Add JodaTime example (2010-06-04)\n </action>\n <action dev=\"asomov\" type=\"add\" issue=\"67\" due-to=\"Manuel Sugawara\">\n Add possibility to create a Tag out of an URI (2010-05-31)\n </action>\n <action dev=\"asomov\" type=\"update\">\n URLDecoder.decode() does not fail when UTF-8 is invalid. Use\n CodingErrorAction.REPORT to implement the failure (2010-05-21)\n </action>\n <action dev=\"maslovalex\" type=\"update\">\n Fix generic collections which contain other collections (2010-05-18)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"67\" due-to=\"Manuel Sugawara\">\n Fix: java classes containing non-ASCII characters in names are\n incorrectly encoded (2010-05-14)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"65\" due-to=\"lerch.johannes\">\n Fix: add checks for null arguments for JavaBeanDumper (2010-04-27)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add a test to see how stack trace is serialised (2010-04-27)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"64\" due-to=\"maxim.moschko\">\n ClassCastException in Representer when working with ParameterizedType (2010-04-25)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Improve toString() method for Node. Since scalars cannot be recursive\n they can be printed (2010-04-15)\n </action>\n <action dev=\"maslovalex\" type=\"fix\" issue=\"63\" due-to=\"Udo\">\n Refactor the way arrays are constructed (2010-04-15)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"62\">\n Add examples for dumping custom values for !!bool and !!null (2010-04-13)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"61\">\n Fix: ClassCastException when dumping generic bean (2010-04-11)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"59\">\n Provide an example for changing JavaBean properties order (2010-04-01)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"60\">\n Provide example for skipping null and empty collections (2010-03-29)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"58\" due-to=\"jeff.caulfield\">\n JavaBeanDumper.dump throws NullPointerException on list property\n with null element (2010-03-23)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"56\" due-to=\"DZeiss\">\n Make constructors in SafeConstructor public (2010-03-16)\n </action>\n <action dev=\"asomov\" type=\"update\" due-to=\"David Bernard\">\n Releases and snapshots are available in the Sonatype Maven repository.\n https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide\n </action>\n <action dev=\"obastard\" type=\"fix\" issue=\"53\" due-to=\"obastard\">\n Enhancement for a pretty format that combines BLOCK and FLOW (2010-03-03)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"50\" due-to=\"sualeh.fatehi\">\n Unable to dump JavaBean that inherits from a protected base class (2010-03-02)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Format source (2010-03-01)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Use Token.ID and Event.ID instead of just ID (2010-03-01)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Issue 50 fails in Eclipse but works with Maven (2010-03-01)\n </action>\n </release>\n <release version=\"1.6\" date=\"2010-02-26\" description=\"introduce Tag class\">\n <action dev=\"asomov\" type=\"update\">\n Release Candidate 2 is available (2010-02-24)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"47\" due-to=\"obastard\">\n Don't dump read-only properties by default. DumperOptions gets a setting to\n include read-only JavaBean properties.\n This is no backwards compatible change (2010-02-19)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"49\" due-to=\"obastard\">\n Support GregorianCalendar. Due to Daylight Saving Time parsing the timestamp with\n a TimeZone cannot determine the exact time (2010-02-19)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"51\" due-to=\"johann.Werner\">\n Some Unicode characters are wrongly replaced by \\x{fffd} during\n double quoted style dump (2010-02-15)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"48\" due-to=\"obastard\">\n Introduce representJavaBeanProperty() method in Representer. The method\n can be overridden to simplify custom JavaBean representation (2010-02-12)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Release Candidate 1 is available (2010-02-01)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Representer.representJavaBean() returns MappingNode (2010-01-26)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add example of serialising static fields (2010-01-26)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add example of serialising java.io.File as scalar node for issue 46 (2010-01-25)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: introduce Chomping to avoid using null as value for Boolean.\n Stay in line with Scala port where null is not allowed (2010-01-19)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: use Event.ID enum instead of classes (2010-01-15)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: use Token.ID enum instead of classes (2010-01-15)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: use generic classes for DirectiveToken (2010-01-14)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: rename Reader to StreamReader to avoid name conflict with java.io.Reader (2010-01-13)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: use StringBuilder instead of StringBuffer (2010-01-12)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: introduce Constant class to share String constants (2010-01-12)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Keep Tag.equals(String) to simplify transition to Tag class (2010-01-08)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: introduce Tag instead of Tags. Nodes use Tag class instead of String (2010-01-05)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"42\" due-to=\"Artem\">\n BaseConstructor shall give more flexibility to choose a constructor at runtime (2010-01-08)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: introduce TagTuple instead of String[] (2010-01-04)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"40\" due-to=\"sitrious\">\n Ignore tags when they are compatible with the runtime class (2010-01-04)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add example to ignore unknown tags (2009-12-08)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add Ruby example (2009-12-08)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Do not omit the tag for JavaBean properties when the tag is explicitly defined (2009-12-08)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"38\" due-to=\"gchpaco\">\n Fix ID format for numbers over 999 (2009-12-05)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"29\" due-to=\"grignaak\">\n Allow separate option in DumperOptions for long strings (2009-11-16)\n </action>\n <action dev=\"asomov\" type=\"add\">\n JavaBeanDumper: add possibility to define a custom Representer (2009-11-25)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"36\">\n Introduce multi contructors (tag prefix). A family of tags may be processed\n by a single constructor (2009-11-25)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor BaseConstructor: simplify second step for recursive structures (2009-11-25)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add FilterPropertyToDumpTest to show how to filter JavaBean properties (2009-11-24)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add FilterClassesConstructorTest to show how to filter created classes (2009-11-16)\n </action>\n <action dev=\"asomov\" type=\"update\" due-to=\"Stefan\">\n Improve JavaDoc (2009-11-12)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add Velocity example (2009-11-03)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: rename Tuple to RecursiveTuple and hide it inside BaseConstructor (2009-11-03)\n </action>\n </release>\n <release version=\"1.5\" date=\"2009-10-30\" description=\"Improve usage of generic collections in JavaBeans\">\n <action dev=\"asomov\" type=\"fix\" issue=\"27\" due-to=\"Polyglot Maven team\">\n Extend Resolver to support custom implicit types (2009-10-27)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Performance improvement: use ArrayStack instead of Stack which extends Vector (2009-10-20)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"25\" due-to=\"Benjamin Bentmann\">\n Improve usage of generic collections: while type erase makes no difference between\n Class< Foo> and Class< Bar> at runtime, the information about generics is still\n accessible via reflection from Method/Field. (2009-10-19)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Fix ConstructYamlObject: support recursive objects. Introduce 'resolved'\n property for Nodes. This property supposed to help to distinguish explicit tag\n from the resolved tag (2009-10-19)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: use rootTag instead of rootType (for Class) in BaseConstructor. This is done to\n solve the priority problem: normally explicit tag has more priority then runtime class but\n for the root tag it is the other way around (2009-10-19)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"24\" due-to=\"shrode\">\n Line numbers reported in Exceptions are Zero based, should be 1 based (2009-10-12)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"21\" due-to=\"ashwin.jayaprakash\">\n Support arrays of reference types as JavaBean properties (2009-09-22)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"17\" due-to=\"jcucurull\">\n Respect root tag for sequences (2009-09-04)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"18\" due-to=\"creiniger\">\n SafeRepresenter respects custom tags for standard Java classes where standard tag has\n more then one Java implementation available (Long, List, Map, Date etc) (2009-09-03)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add possibility to define a custom Class Loader. (2009-09-01)\n </action>\n <action dev=\"asomov\" type=\"fix\">\n Fixed an obscure scanner error not reported when there is no line break at the end\n of the stream. The fix is imported from PyYAML 3.09 {ticket 118} (2009-08-31)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"16\" due-to=\"infinity0x\">\n Cache JavaBean class properties. Tests show that the loading has become a few percents faster (2009-08-31)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Introduce ArrayStack to use push() and pop() instead of standard (and too verbose)\n 'remove(size()-1)' (2009-08-27)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"14\" due-to=\"infinity0x\">\n Fix: ArrayList is more efficient than LinkedList (2009-08-26)\n </action>\n </release>\n <release version=\"1.4\" date=\"2009-08-26\" description=\"better support for loading immutable objects\">\n <action dev=\"asomov\" type=\"update\">\n Apply Apache License Version 2.0 (2009-08-14)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"13\" due-to=\"infinity0x\">\n Provide javadocs link to Sun Java API (2009-08-10)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Build 1.4 Release Candidate 1 (2009-08-07)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Introduce Tags.getGlobalTagForClass() to simplify tag generation in custom constructors (2009-08-06)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: introduce ImplicitTuple (2009-08-06)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"11\" due-to=\"infinity0x\">\n Fix: create a Java instance with the following priority to choose the class:\n Explicit tag -> Runtime class (defined in JavaBean) -> implicit tag (2009-08-06)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"9\" due-to=\"wwagner4\">\n Fix: Bean with no property cannot be instantiated. This is implemented via better\n support for immutable objects. Custom Constructor may be used when there are more\n then 1 way to create an instance (2009-08-04)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Deliver possibility to load immutable instances with no global tags. Reflection for\n constructor arguments is used to get the runtime classes (2009-08-04)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Use more informative error message when a JavaBean property cannot\n be created (2009-08-02)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: Constructor is rewritten. Do not overwrite methods from BaseConstructor.\n Instead introduce ConstructScalar, ConstructSequence, ConstructMapping (2009-07-31)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Change Maven repository path: groupId='org.yaml', artifactId='snakeyaml' (2009-07-31)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"10\" due-to=\"derrick.rice\">\n Fix: dump omits JavaBean class name when used with an alias (2009-07-28)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Generate sources and Javadoc (2009-07-27)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Node does not have the value. It is delegated to the non-abstract classes (2009-07-27)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Extends JavaBeanDumper to allow skipping global tags inside type-safe collections.\n Introduce method setMapTagForBean() (2009-07-22)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add ConstructEmptyBeanTest to test JavaBean construction with no\n properties in the YAML document(2009-07-22)\n </action>\n <action dev=\"asomov\" type=\"remove\">\n Refactor: redesign tag management for JavaBeans in Representer.\n Drop dynamic root tag concept (2009-07-22)\n </action>\n <action dev=\"asomov\" type=\"remove\">\n Remove unused TypeDescription in Representer (2009-07-21)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Use NodeTuple instead of Node[] for mappings (2009-07-21)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Introduce JavaBeanLoader and JavaBeanDumper. Deprecate JavaBeanParser (2009-07-21)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"8\" due-to=\"Alan Gutierrez\">\n Fix: Representer was keeping state between invocations (2009-07-21)\n </action>\n </release>\n <release version=\"1.3\" date=\"2009-07-20\" description=\"complete support for recursive objects\">\n <action dev=\"asomov\" type=\"fix\" issue=\"6\" due-to=\"infinity0x\">\n Fix: values returned by System.identityHashCode() are not guaranteed to be unique (2009-07-14)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add a simple test for Java Generics (BirdTest). Unfortunately it shows that some JVM\n implementations do not recognise classes for JavaBean properties at runtime.\n It leads to unnecessary global tags. See http://code.google.com/p/snakeyaml/wiki/Documentation#Generics\n for details (2009-07-13)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"5\" due-to=\"infinity0x\">\n Fix: set the \"cause\" field for MarkedYAMLException (2009-07-10)\n </action>\n <action dev=\"maslovalex\" type=\"fix\" issue=\"1\">\n Fix: Recursive objects are now fully supported (2009-07-09)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add support for BigDecimal as a JavaBean property (2009-07-07)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Improve test coverage for Constructor. Allow construction of JavaBeans\n with only setter without the corresponding getter (2009-07-07)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add a test to check the proper report for IOException (2009-07-03)\n </action>\n <action dev=\"asomov\" type=\"fix\" issue=\"3\" due-to=\"infinity0x\">\n Fix: represent proper tags for JavaBeans when they are not the root of the YAML\n document but a member of a collection (2009-07-03)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: run PMD and apply some of the recommendations (2009-06-18)\n </action>\n <action dev=\"asomov\" type=\"add\" issue=\"1\">\n Create an issue for Recursive objects to be remembered (2009-06-08)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Migrate project hosting from Assembla to Google code (2009-06-08)\n </action>\n <action dev=\"asomov\" type=\"fix\" due-to=\"Magne\">\n Fix: null as a JavaBean property was not handled properly (2009-06-04)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Validate changes.xml file (2009-05-25)\n </action>\n <action dev=\"asomov\" type=\"fix\" due-to=\"Magne\">\n Fix ticket 40 in Assembla: getting an error when javabean contains java.sql.Timestamp fields (2009-05-25)\n </action>\n </release>\n <release version=\"1.2\" date=\"2009-04-27\" description=\"expose low-level API\">\n <action dev=\"asomov\" type=\"add\">\n Add 'Yaml.parse()' method which return Events to support low level YAML processing (2009-04-20)\n </action>\n <action dev=\"asomov\" type=\"add\" due-to=\"Bob Jalex\">\n Introduce LineBreak.getPlatformLineBreak (ticket 5 in Assembla) (2009-04-18)\n </action>\n <action dev=\"asomov\" type=\"update\" due-to=\"Bob Jalex\">\n Rename LineBreak.LINUX to LineBreak.UNIX (ticket 5 in Assembla) (2009-04-18)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add 'Yaml.compose()' methods which return Nodes to support YEdit (2009-04-17)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: rename enums in DumperOptions to make the names consistent (2009-04-07)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: use Character instead of char primitive for style in Emitter (2009-04-07)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add possibility to parse all scalars as Strings (2009-03-30)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Merge changeset 347 from PyYAML (2009-03-30)\n </action>\n <action dev=\"asomov\" type=\"fix\">\n Respect DumperOptions with a custom Representer (2009-03-18)\n </action>\n <action dev=\"asomov\" type=\"fix\">\n Represent TAB as '\\t' instead of '(9' in the error message (2009-03-17)\n </action>\n </release>\n <release version=\"1.1\" date=\"2009-03-14\" description=\"improve performance and test coverage\">\n <action dev=\"asomov\" type=\"add\">\n Introduce JavaBeanParser (2009-03-14)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Introduce DumperOptions.Version enum (2009-03-13)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Introduce DumperOptions.LineBreak enum (2009-03-10)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Use byte[] for binary type. (2009-03-09)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Restore Regular Expressions in Resolver. Ragel gives only 5% performance increase.\n Fix a bug in Resolver with expanded regular expressions which caused the\n performance problem. (2009-03-06)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Better Spring support: it is now possible to create a constructor with a String\n as the class name. (2009-03-05)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Throw an exception when the same Loader or Dumper instance is shared between\n different Yaml instances. Because they are statefull it is not Thread-safe. (2009-03-05)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add possibility to set a meaningful name for Yaml instance to be shown in toString(). (2009-03-05)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: declare classes which are not expected to be extended as final. (2009-03-04)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: use 'final' keyword to identify immutable fields. (2009-03-04)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: do not use 'final' keyword for local variables. (2009-03-04)\n </action>\n <action dev=\"asomov\" type=\"fix\">\n Fix: respect implicit resolvers with 'null' as a first character. (2009-03-02)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: use Character instead of String as a key for implicit resolvers. (2009-03-02)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Use Ragel instead of Regular Expressions for implicit types. (2009-03-02)\n </action>\n <action dev=\"asomov\" type=\"fix\" due-to=\"Christophe Desguez\">\n Fix ticket #4 (in Assembla): java.sql.Date not handled. (2009-02-28)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Introduce DumperOptions.DefaultFlowStyle enum (2009-02-24)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Introduce DumperOptions.DefaultScalarStyle enum (2009-02-24)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: use 'switch' with Enum instead of multiple 'if' statements to distinguish nodes (2009-02-19)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: use Enum instead of String as NodeId (2009-02-19)\n </action>\n </release>\n <release version=\"1.0.1\" date=\"2009-02-18\" description=\"implement Enum support\">\n <action dev=\"asomov\" type=\"fix\">\n Do not emit anchors for Enum (2009-02-18)\n </action>\n <action dev=\"asomov\" type=\"fix\">\n Enum as a JavaBean property (when the Enum class is implicitly defined) does\n not need tags for both loading and dumping (2009-02-17)\n </action>\n <action dev=\"asomov\" type=\"fix\">\n Enum is emitted as a scalar node (2009-02-17)\n </action>\n <action dev=\"asomov\" type=\"fix\" due-to=\"James Nissel\">\n Enum is parsed as a scalar node or as a JavaBean property (2009-02-17)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Refactor: for performance ScannerImpl.stalePossibleSimpleKeys() does not copy key Set (2009-02-10)\n </action>\n <action dev=\"asomov\" type=\"update\">\n By default allowUnicode=true. If it is necessary to escape Unicode use\n DumperOptions.setAllowUnicode(false) (2009-02-09)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Implement allowUnicode setting (to escape Unicode characters on non UTF-8 terminals) (2009-02-09)\n </action>\n <action dev=\"asomov\" type=\"add\">\n Add possibility to specify tags for dumping (2009-02-09)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Rename getExpRoot to getExplicitRoot to conform with\n standard JavaBean naming convention (2009-02-09)\n </action>\n <action dev=\"asomov\" type=\"update\">\n Rename explictStart and explicitEnd to standard setters to conform with\n standard JavaBean naming convention (2009-02-09)\n </action>\n <action dev=\"asomov\" type=\"fix\">\n Add possibility to specify a line break (2009-02-09)\n </action>\n </release>\n <release version=\"1.0\" date=\"2009-02-06\" description=\"final 1.0 release\">\n <action dev=\"asomov\" type=\"add\">\n Deliver first release (2009-02-06)\n </action>\n </release>\n </body>\n</document>\n\n## 内容2\n### 文件路径:\nsrc/main/java/org/yaml/snakeyaml/LoaderOptions.java\n### 代码内容:\npackage org.yaml.snakeyaml;\n\npublic class LoaderOptions {\n\n private boolean allowDuplicateKeys = true;\n private boolean wrappedToRootException = false;\n private int maxAliasesForCollections = 50; //to prevent YAML at https://en.wikipedia.org/wiki/Billion_laughs_attack\n private boolean allowRecursiveKeys = false;\n private boolean processComments = false;\n private boolean enumCaseSensitive = true;\n\n public boolean isAllowDuplicateKeys() {\n return allowDuplicateKeys;\n }\n\n /**\n * Allow/Reject duplicate map keys in the YAML file.\n *\n * Default is to allow.\n *\n * YAML 1.1 is slightly vague around duplicate entries in the YAML file. The\n * best reference is <a href=\"http://www.yaml.org/spec/1.1/#id862121\">\n * 3.2.1.3. Nodes Comparison</a> where it hints that a duplicate map key is\n * an error.\n *\n * For future reference, YAML spec 1.2 is clear. The keys MUST be unique.\n * <a href=\"http://www.yaml.org/spec/1.2/spec.html#id2759572\">1.3. Relation\n * to JSON</a>\n * @param allowDuplicateKeys false to reject duplicate mapping keys\n */\n public void setAllowDuplicateKeys(boolean allowDuplicateKeys) {\n this.allowDuplicateKeys = allowDuplicateKeys;\n }\n\n public boolean isWrappedToRootException() {\n return wrappedToRootException;\n }\n\n /**\n * Wrap runtime exception to YAMLException during parsing or leave them as they are\n *\n * Default is to leave original exceptions\n *\n * @param wrappedToRootException - true to convert runtime exception to YAMLException\n */\n public void setWrappedToRootException(boolean wrappedToRootException) {\n this.wrappedToRootException = wrappedToRootException;\n }\n\n public int getMaxAliasesForCollections() {\n return maxAliasesForCollections;\n }\n\n /**\n * Restrict the amount of aliases for collections (sequences and mappings) to avoid https://en.wikipedia.org/wiki/Billion_laughs_attack\n * @param maxAliasesForCollections set max allowed value (50 by default)\n */\n public void setMaxAliasesForCollections(int maxAliasesForCollections) {\n \tthis.maxAliasesForCollections = maxAliasesForCollections;\n }\n\n /**\n * Allow recursive keys for mappings. By default it is not allowed.\n * This setting only prevents the case when the key is the value. If the key is only a part of the value\n * (the value is a sequence or a mapping) then this case is not recognized and always allowed.\n * @param allowRecursiveKeys - false to disable recursive keys\n */\n public void setAllowRecursiveKeys(boolean allowRecursiveKeys) {\n \tthis.allowRecursiveKeys = allowRecursiveKeys;\n }\n\n public boolean getAllowRecursiveKeys() {\n return allowRecursiveKeys;\n }\n\n /**\n * Set the comment processing. By default, comments are ignored.\n *\n * @param processComments <code>true</code> to process; <code>false</code> to ignore\n */\n public void setProcessComments(boolean processComments) {\n this.processComments = processComments;\n }\n\n public boolean isProcessComments() {\n return processComments;\n }\n\n public boolean isEnumCaseSensitive() {\n return enumCaseSensitive;\n }\n\n /**\n * Disables or enables case sensitivity during construct enum constant from string value\n * Default is false.\n *\n * @param enumCaseSensitive - true to set enum case sensitive, false the reverse\n */\n public void setEnumCaseSensitive(boolean enumCaseSensitive) {\n this.enumCaseSensitive = enumCaseSensitive;\n }\n}\n\n## 内容3\n### 文件路径:\nsrc/main/java/org/yaml/snakeyaml/composer/Composer.java\n### 代码内容:\npackage org.yaml.snakeyaml.composer;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.yaml.snakeyaml.DumperOptions.FlowStyle;\nimport org.yaml.snakeyaml.LoaderOptions;\nimport org.yaml.snakeyaml.comments.CommentEventsCollector;\nimport org.yaml.snakeyaml.comments.CommentLine;\nimport org.yaml.snakeyaml.comments.CommentType;\nimport org.yaml.snakeyaml.error.Mark;\nimport org.yaml.snakeyaml.error.YAMLException;\nimport org.yaml.snakeyaml.events.AliasEvent;\nimport org.yaml.snakeyaml.events.Event;\nimport org.yaml.snakeyaml.events.MappingStartEvent;\nimport org.yaml.snakeyaml.events.NodeEvent;\nimport org.yaml.snakeyaml.events.ScalarEvent;\nimport org.yaml.snakeyaml.events.SequenceStartEvent;\nimport org.yaml.snakeyaml.nodes.AnchorNode;\nimport org.yaml.snakeyaml.nodes.MappingNode;\nimport org.yaml.snakeyaml.nodes.Node;\nimport org.yaml.snakeyaml.nodes.NodeId;\nimport org.yaml.snakeyaml.nodes.NodeTuple;\nimport org.yaml.snakeyaml.nodes.ScalarNode;\nimport org.yaml.snakeyaml.nodes.SequenceNode;\nimport org.yaml.snakeyaml.nodes.Tag;\nimport org.yaml.snakeyaml.parser.Parser;\nimport org.yaml.snakeyaml.resolver.Resolver;\n\n/**\n * Creates a node graph from parser events.\n * <p>\n * Corresponds to the 'Compose' step as described in chapter 3.1 of the\n * <a href=\"http://yaml.org/spec/1.1/\">YAML Specification</a>.\n * </p>\n */\npublic class Composer {\n protected final Parser parser;\n private final Resolver resolver;\n private final Map<String, Node> anchors;\n private final Set<Node> recursiveNodes;\n private int nonScalarAliasesCount = 0;\n private final LoaderOptions loadingConfig;\n private final CommentEventsCollector blockCommentsCollector;\n private final CommentEventsCollector inlineCommentsCollector;\n\n public Composer(Parser parser, Resolver resolver) {\n this(parser, resolver, new LoaderOptions());\n }\n\n public Composer(Parser parser, Resolver resolver, LoaderOptions loadingConfig) {\n this.parser = parser;\n this.resolver = resolver;\n this.anchors = new HashMap<String, Node>();\n this.recursiveNodes = new HashSet<Node>();\n this.loadingConfig = loadingConfig;\n this.blockCommentsCollector = new CommentEventsCollector(parser,\n CommentType.BLANK_LINE, CommentType.BLOCK);\n this.inlineCommentsCollector = new CommentEventsCollector(parser,\n CommentType.IN_LINE);\n }\n\n /**\n * Checks if further documents are available.\n *\n * @return <code>true</code> if there is at least one more document.\n */\n public boolean checkNode() {\n // Drop the STREAM-START event.\n if (parser.checkEvent(Event.ID.StreamStart)) {\n parser.getEvent();\n }\n // If there are more documents available?\n return !parser.checkEvent(Event.ID.StreamEnd);\n }\n\n /**\n * Reads and composes the next document.\n *\n * @return The root node of the document or <code>null</code> if no more documents are available.\n */\n public Node getNode() {\n // Collect inter-document start comments\n blockCommentsCollector.collectEvents();\n if (parser.checkEvent(Event.ID.StreamEnd)) {\n List<CommentLine> commentLines = blockCommentsCollector.consume();\n Mark startMark = commentLines.get(0).getStartMark();\n List<NodeTuple> children = Collections.emptyList();\n Node node = new MappingNode(Tag.COMMENT, false, children, startMark, null, FlowStyle.BLOCK);\n node.setBlockComments(commentLines);\n return node;\n }\n // Drop the DOCUMENT-START event.\n parser.getEvent();\n // Compose the root node.\n Node node = composeNode(null);\n // Drop the DOCUMENT-END event.\n blockCommentsCollector.collectEvents();\n if(!blockCommentsCollector.isEmpty()) {\n node.setEndComments(blockCommentsCollector.consume());\n }\n parser.getEvent();\n this.anchors.clear();\n this.recursiveNodes.clear();\n return node;\n }\n\n /**\n * Reads a document from a source that contains only one document.\n * <p>\n * If the stream contains more than one document an exception is thrown.\n * </p>\n *\n * @return The root node of the document or <code>null</code> if no document\n * is available.\n */\n public Node getSingleNode() {\n // Drop the STREAM-START event.\n parser.getEvent();\n // Compose a document if the stream is not empty.\n Node document = null;\n if (!parser.checkEvent(Event.ID.StreamEnd)) {\n document = getNode();\n }\n // Ensure that the stream contains no more documents.\n if (!parser.checkEvent(Event.ID.StreamEnd)) {\n Event event = parser.getEvent();\n Mark contextMark = document != null ? document.getStartMark(): null;\n throw new ComposerException(\"expected a single document in the stream\",\n contextMark, \"but found another document\", event.getStartMark());\n }\n // Drop the STREAM-END event.\n parser.getEvent();\n return document;\n }\n\n private Node composeNode(Node parent) {\n blockCommentsCollector.collectEvents();\n if (parent != null)\n recursiveNodes.add(parent);\n final Node node;\n if (parser.checkEvent(Event.ID.Alias)) {\n AliasEvent event = (AliasEvent) parser.getEvent();\n String anchor = event.getAnchor();\n if (!anchors.containsKey(anchor)) {\n throw new ComposerException(null, null, \"found undefined alias \" + anchor,\n event.getStartMark());\n }\n node = anchors.get(anchor);\n if (!(node instanceof ScalarNode)) {\n this.nonScalarAliasesCount++;\n if (this.nonScalarAliasesCount > loadingConfig.getMaxAliasesForCollections()) {\n throw new YAMLException(\"Number of aliases for non-scalar nodes exceeds the specified max=\" + loadingConfig.getMaxAliasesForCollections());\n }\n }\n if (recursiveNodes.remove(node)) {\n node.setTwoStepsConstruction(true);\n }\n // drop comments, they can not be supported here\n blockCommentsCollector.consume();\n inlineCommentsCollector.collectEvents().consume();\n } else {\n NodeEvent event = (NodeEvent) parser.peekEvent();\n String anchor = event.getAnchor();\n // the check for duplicate anchors has been removed (issue 174)\n if (parser.checkEvent(Event.ID.Scalar)) {\n node = composeScalarNode(anchor, blockCommentsCollector.consume());\n } else if (parser.checkEvent(Event.ID.SequenceStart)) {\n node = composeSequenceNode(anchor);\n } else {\n node = composeMappingNode(anchor);\n }\n }\n recursiveNodes.remove(parent);\n return node;\n }\n\n protected Node composeScalarNode(String anchor, List<CommentLine> blockComments) {\n ScalarEvent ev = (ScalarEvent) parser.getEvent();\n String tag = ev.getTag();\n boolean resolved = false;\n Tag nodeTag;\n if (tag == null || tag.equals(\"!\")) {\n nodeTag = resolver.resolve(NodeId.scalar, ev.getValue(),\n ev.getImplicit().canOmitTagInPlainScalar());\n resolved = true;\n } else {\n nodeTag = new Tag(tag);\n }\n Node node = new ScalarNode(nodeTag, resolved, ev.getValue(), ev.getStartMark(),\n ev.getEndMark(), ev.getScalarStyle());\n if (anchor != null) {\n node.setAnchor(anchor);\n anchors.put(anchor, node);\n }\n node.setBlockComments(blockComments);\n node.setInLineComments(inlineCommentsCollector.collectEvents().consume());\n return node;\n }\n\n protected Node composeSequenceNode(String anchor) {\n SequenceStartEvent startEvent = (SequenceStartEvent) parser.getEvent();\n String tag = startEvent.getTag();\n Tag nodeTag;\n \n boolean resolved = false;\n if (tag == null || tag.equals(\"!\")) {\n nodeTag = resolver.resolve(NodeId.sequence, null, startEvent.getImplicit());\n resolved = true;\n } else {\n nodeTag = new Tag(tag);\n }\n final ArrayList<Node> children = new ArrayList<Node>();\n SequenceNode node = new SequenceNode(nodeTag, resolved, children, startEvent.getStartMark(),\n null, startEvent.getFlowStyle());\n if (startEvent.isFlow()) {\n node.setBlockComments(blockCommentsCollector.consume());\n }\n if (anchor != null) {\n node.setAnchor(anchor);\n anchors.put(anchor, node);\n }\n while (!parser.checkEvent(Event.ID.SequenceEnd)) {\n blockCommentsCollector.collectEvents();\n if (parser.checkEvent(Event.ID.SequenceEnd)) {\n break;\n }\n children.add(composeNode(node));\n }\n if (startEvent.isFlow()) {\n node.setInLineComments(inlineCommentsCollector.collectEvents().consume());\n }\n Event endEvent = parser.getEvent();\n node.setEndMark(endEvent.getEndMark());\n inlineCommentsCollector.collectEvents();\n if(!inlineCommentsCollector.isEmpty()) {\n node.setInLineComments(inlineCommentsCollector.consume());\n }\n return node;\n }\n\n protected Node composeMappingNode(String anchor) {\n MappingStartEvent startEvent = (MappingStartEvent) parser.getEvent();\n String tag = startEvent.getTag();\n Tag nodeTag;\n boolean resolved = false;\n if (tag == null || tag.equals(\"!\")) {\n nodeTag = resolver.resolve(NodeId.mapping, null, startEvent.getImplicit());\n resolved = true;\n } else {\n nodeTag = new Tag(tag);\n }\n\n final List<NodeTuple> children = new ArrayList<NodeTuple>();\n MappingNode node = new MappingNode(nodeTag, resolved, children, startEvent.getStartMark(),\n null, startEvent.getFlowStyle());\n if (startEvent.isFlow()) {\n node.setBlockComments(blockCommentsCollector.consume());\n }\n if (anchor != null) {\n node.setAnchor(anchor);\n anchors.put(anchor, node);\n }\n while (!parser.checkEvent(Event.ID.MappingEnd)) {\n blockCommentsCollector.collectEvents();\n if (parser.checkEvent(Event.ID.MappingEnd)) {\n break;\n }\n composeMappingChildren(children, node);\n }\n if (startEvent.isFlow()) {\n node.setInLineComments(inlineCommentsCollector.collectEvents().consume());\n }\n Event endEvent = parser.getEvent();\n node.setEndMark(endEvent.getEndMark());\n inlineCommentsCollector.collectEvents();\n if(!inlineCommentsCollector.isEmpty()) {\n node.setInLineComments(inlineCommentsCollector.consume());\n }\n return node;\n }\n\n protected void composeMappingChildren(List<NodeTuple> children, MappingNode node) {\n Node itemKey = composeKeyNode(node);\n if (itemKey.getTag().equals(Tag.MERGE)) {\n node.setMerged(true);\n }\n Node itemValue = composeValueNode(node);\n children.add(new NodeTuple(itemKey, itemValue));\n }\n\n protected Node composeKeyNode(MappingNode node) {\n return composeNode(node);\n }\n\n protected Node composeValueNode(MappingNode node) {\n return composeNode(node);\n }\n}\n\n", "input": "", "output": "【漏洞类型】深度递归漏洞\n【严重性】High\n【漏洞摘要】原始代码中没有限制集合(序列和映射)的嵌套深度,这可能导致深度递归,从而引发栈溢出(Stack Overflow)异常,最终导致服务拒绝(DoS)攻击。具体表现为`Composer.composeNode`函数中对嵌套节点的无限制递归处理。\n【关键代码模式】`composeNode`函数,`Parser.checkEvent`,`Parser.getEvent`,`Event.ID`,`LoaderOptions`,`maxAliasesForCollections`,`Nesting Depth`\n【修复方法说明】在`LoaderOptions`中添加了`nestingDepthLimit`属性,并在`Composer`类中增加了`increaseNestingDepth`和`decreaseNestingDepth`方法来限制嵌套深度,防止超过设定值时抛出异常。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\ninlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/consts/InlongConstants.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.common.consts;\n\n/**\n * Global constant for the Inlong system.\n */\npublic class InlongConstants {\n\n /**\n * Thread pool related config.\n */\n public static final int CORE_POOL_SIZE = 10;\n public static final int MAX_POOL_SIZE = 20;\n public static final long ALIVE_TIME_MS = 100L;\n public static final int QUEUE_SIZE = 10000;\n\n /**\n * Group config\n */\n public static final String COMMA = \",\";\n\n public static final String SLASH = \"/\";\n\n public static final String COLON = \":\";\n\n public static final String SEMICOLON = \";\";\n\n public static final String LEFT_BRACKET = \"(\";\n\n public static final String PERCENT = \"%\";\n\n public static final String QUESTION_MARK = \"?\";\n\n public static final String ADMIN_USER = \"admin\";\n\n public static final Integer AFFECTED_ONE_ROW = 1;\n\n public static final Integer INITIAL_VERSION = 1;\n\n public static final Integer UN_DELETED = 0;\n\n public static final Integer DELETED_STATUS = 10;\n\n public static final Integer STANDARD_MODE = 0;\n public static final Integer LIGHTWEIGHT_MODE = 1;\n\n public static final Integer DISABLE_ZK = 0;\n public static final Integer ENABLE_ZK = 1;\n\n public static final Integer DISABLE_CREATE_RESOURCE = 0;\n public static final Integer ENABLE_CREATE_RESOURCE = 1;\n\n /**\n * Data report type, support:\n * <pre>\n * 0: report to DataProxy and respond when the DataProxy received data.\n * 1: report to DataProxy and respond after DataProxy sends data.\n * 2: report to MQ and respond when the MQ received data.\n * </pre>\n */\n public static final Integer REPORT_TO_DP_RECEIVED = 0;\n public static final Integer REPORT_TO_DP_SENT = 1;\n public static final Integer REPORT_TO_MQ_RECEIVED = 2;\n\n public static final Integer UN_SYNC_SEND = 0;\n public static final Integer SYNC_SEND = 1;\n\n public static final String BATCH_TASK = \"batch.task\";\n\n /**\n * Pulsar config\n */\n public static final String PULSAR_AUTHENTICATION = \"pulsar.authentication\";\n\n public static final String PULSAR_AUTHENTICATION_TYPE = \"pulsar.authentication.type\";\n\n public static final String DEFAULT_PULSAR_TENANT = \"public\";\n\n public static final String DEFAULT_PULSAR_AUTHENTICATION_TYPE = \"token\";\n\n public static final String PULSAR_QUEUE_TYPE_SERIAL = \"SERIAL\";\n\n public static final String PULSAR_QUEUE_TYPE_PARALLEL = \"PARALLEL\";\n\n /**\n * Format of the Pulsar topic: \"persistent://tenant/namespace/topic\n */\n public static final String PULSAR_TOPIC_FORMAT = \"persistent://%s/%s/%s\";\n\n /**\n * Sort config\n */\n public static final String DATAFLOW = \"dataflow\";\n\n public static final String STREAMS = \"streams\";\n\n public static final String RELATIONS = \"relations\";\n\n public static final String INPUTS = \"inputs\";\n\n public static final String OUTPUTS = \"outputs\";\n\n public static final String NODES = \"nodes\";\n\n public static final String NODE_TYPE = \"type\";\n\n public static final String LOAD = \"Load\";\n\n public static final String EXTRACT = \"Extract\";\n\n public static final String SORT_JOB_ID = \"sort.job.id\";\n\n public static final String SORT_TYPE = \"sort.type\";\n\n public static final String DEFAULT_SORT_TYPE = \"flink\";\n\n public static final String SORT_NAME = \"sort.name\";\n\n public static final String SORT_URL = \"sort.url\";\n\n public static final String SORT_AUTHENTICATION = \"sort.authentication\";\n\n public static final String SORT_AUTHENTICATION_TYPE = \"sort.authentication.type\";\n\n public static final String DEFAULT_SORT_AUTHENTICATION_TYPE = \"secret_and_token\";\n\n public static final String SORT_PROPERTIES = \"sort.properties\";\n\n public static final String DATA_TYPE_RAW_PREFIX = \"RAW_\";\n\n public static final int DEFAULT_ENABLE_VALUE = 1;\n\n}\n\n## 内容2\n### 文件路径:\ninlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/ClusterRequest.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.pojo.cluster;\n\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport org.apache.inlong.manager.common.validation.SaveValidation;\nimport org.apache.inlong.manager.common.validation.UpdateByIdValidation;\nimport org.apache.inlong.manager.common.validation.UpdateByKeyValidation;\nimport org.hibernate.validator.constraints.Length;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Pattern;\n\n/**\n * Inlong cluster request\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@ApiModel(\"Inlong cluster request\")\n@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, visible = true, property = \"type\")\npublic abstract class ClusterRequest {\n\n @ApiModelProperty(value = \"Primary key\")\n @NotNull(groups = UpdateByIdValidation.class, message = \"id cannot be null\")\n private Integer id;\n\n @ApiModelProperty(value = \"Cluster name\")\n @NotBlank(groups = {SaveValidation.class, UpdateByKeyValidation.class}, message = \"cluster name cannot be blank\")\n @Length(min = 1, max = 128, message = \"length must be between 1 and 128\")\n @Pattern(regexp = \"^[a-z0-9_.-]{1,128}$\", message = \"only supports lowercase letters, numbers, '.', '-', or '_'\")\n private String name;\n\n @ApiModelProperty(value = \"Cluster type, including TUBEMQ, PULSAR, KAFKA, DATAPROXY, etc.\")\n @NotBlank(message = \"cluster type cannot be blank\")\n @Length(min = 1, max = 20, message = \"length must be between 1 and 20\")\n private String type;\n\n @ApiModelProperty(value = \"Cluster tags, separated by commas\")\n @NotBlank(groups = {SaveValidation.class, UpdateByKeyValidation.class}, message = \"clusterTags cannot be blank\")\n @Length(max = 512, message = \"length must be less than or equal to 512\")\n private String clusterTags;\n\n @ApiModelProperty(value = \"Cluster url\")\n @Length(max = 512, message = \"length must be less than or equal to 512\")\n private String url;\n\n @ApiModelProperty(value = \"Extension tag\")\n @Length(max = 128, message = \"length must be less than or equal to 128\")\n private String extTag = \"default=true\";\n\n @ApiModelProperty(value = \"Cluster token\")\n @Length(max = 512, message = \"length must be less than or equal to 512\")\n private String token;\n\n @ApiModelProperty(value = \"Cluster heartbeat info\")\n @Length(max = 163840, message = \"length must be less than or equal to 163840\")\n private String heartbeat;\n\n @ApiModelProperty(value = \"Extended params\")\n @Length(max = 163840, message = \"length must be less than or equal to 163840\")\n private String extParams;\n\n @ApiModelProperty(value = \"Description of the cluster\")\n @Length(max = 256, message = \"length must be less than or equal to 256\")\n private String description;\n\n @ApiModelProperty(value = \"Name of responsible person, separated by commas\")\n @Length(max = 512, message = \"length must be less than or equal to 512\")\n private String inCharges;\n\n @ApiModelProperty(value = \"Version number\")\n @NotNull(groups = {UpdateByIdValidation.class, UpdateByKeyValidation.class}, message = \"version cannot be null\")\n private Integer version;\n\n}\n\n## 内容3\n### 文件路径:\ninlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/pulsar/PulsarClusterRequest.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.pojo.cluster.pulsar;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.apache.inlong.manager.common.consts.InlongConstants;\nimport org.apache.inlong.manager.common.enums.ClusterType;\nimport org.apache.inlong.manager.common.util.JsonTypeDefine;\nimport org.apache.inlong.manager.pojo.cluster.ClusterRequest;\n\n/**\n * Inlong cluster request for Pulsar\n */\n@Data\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@JsonTypeDefine(value = ClusterType.PULSAR)\n@ApiModel(\"Inlong cluster request for Pulsar\")\npublic class PulsarClusterRequest extends ClusterRequest {\n\n @ApiModelProperty(value = \"Pulsar admin URL, such as: http://127.0.0.1:8080\", notes = \"Pulsar service URL is the 'url' field of the cluster\")\n private String adminUrl;\n\n @ApiModelProperty(value = \"Pulsar tenant, default is 'public'\")\n private String tenant = InlongConstants.DEFAULT_PULSAR_TENANT;\n\n public PulsarClusterRequest() {\n this.setType(ClusterType.PULSAR);\n }\n\n}\n\n## 内容4\n### 文件路径:\ninlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/tubemq/TubeClusterRequest.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.pojo.cluster.tubemq;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.apache.inlong.manager.common.enums.ClusterType;\nimport org.apache.inlong.manager.common.util.JsonTypeDefine;\nimport org.apache.inlong.manager.pojo.cluster.ClusterRequest;\n\n/**\n * Inlong cluster request for TubeMQ\n */\n@Data\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@JsonTypeDefine(value = ClusterType.TUBEMQ)\n@ApiModel(\"Inlong cluster request for TubeMQ\")\npublic class TubeClusterRequest extends ClusterRequest {\n\n @ApiModelProperty(value = \"Master Web URL http://120.0.0.1:8080\", notes = \"TubeMQ master RPC URL is the 'url' field of the cluster\")\n private String masterWebUrl;\n\n public TubeClusterRequest() {\n this.setType(ClusterType.TUBEMQ);\n }\n\n}\n\n## 内容5\n### 文件路径:\ninlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/node/DataNodeRequest.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.pojo.node;\n\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport org.apache.inlong.manager.common.validation.SaveValidation;\nimport org.apache.inlong.manager.common.validation.UpdateByIdValidation;\nimport org.apache.inlong.manager.common.validation.UpdateByKeyValidation;\nimport org.hibernate.validator.constraints.Length;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Pattern;\n\n/**\n * Data node request\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@ApiModel(\"Data node request\")\n@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, visible = true, property = \"type\")\npublic abstract class DataNodeRequest {\n\n @ApiModelProperty(value = \"Primary key\")\n @NotNull(groups = UpdateByIdValidation.class, message = \"id cannot be null\")\n private Integer id;\n\n @ApiModelProperty(value = \"Data node name\")\n @NotBlank(groups = {SaveValidation.class, UpdateByKeyValidation.class}, message = \"node name cannot be blank\")\n @Length(min = 1, max = 128, message = \"length must be between 1 and 128\")\n @Pattern(regexp = \"^[A-Za-z0-9_-]{1,128}$\", message = \"only supports letters, numbers, '-', or '_'\")\n private String name;\n\n @ApiModelProperty(value = \"Data node type, including MYSQL, HIVE, KAFKA, ES, etc.\")\n @NotBlank(message = \"node type cannot be blank\")\n @Length(max = 20, message = \"length must be less than or equal to 20\")\n private String type;\n\n @ApiModelProperty(value = \"Data node URL\")\n @Length(max = 512, message = \"length must be less than or equal to 512\")\n private String url;\n\n @ApiModelProperty(value = \"Data node username\")\n @Length(max = 128, message = \"length must be less than or equal to 128\")\n private String username;\n\n @ApiModelProperty(value = \"Data node token if needed\")\n @Length(max = 512, message = \"length must be less than or equal to 512\")\n private String token;\n\n @ApiModelProperty(value = \"Extended params\")\n @Length(min = 1, max = 16384, message = \"length must be between 1 and 16384\")\n private String extParams;\n\n @ApiModelProperty(value = \"Description of the data node\")\n @Length(max = 256, message = \"length must be less than or equal to 256\")\n private String description;\n\n @ApiModelProperty(value = \"Name of responsible person, separated by commas\")\n @NotBlank(groups = SaveValidation.class, message = \"inCharges cannot be blank\")\n @Length(max = 512, message = \"length must be less than or equal to 512\")\n private String inCharges;\n\n @ApiModelProperty(value = \"Version number\")\n @NotNull(groups = {UpdateByIdValidation.class, UpdateByKeyValidation.class}, message = \"version cannot be null\")\n private Integer version;\n\n}\n\n## 内容6\n### 文件路径:\ninlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/node/mysql/MySQLDataNodeRequest.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.pojo.node.mysql;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.apache.inlong.manager.common.consts.DataNodeType;\nimport org.apache.inlong.manager.common.util.JsonTypeDefine;\nimport org.apache.inlong.manager.pojo.node.DataNodeRequest;\n\n/**\n * MySQL data node request\n */\n@Data\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@JsonTypeDefine(value = DataNodeType.MYSQL)\n@ApiModel(\"MySQL data node request\")\npublic class MySQLDataNodeRequest extends DataNodeRequest {\n\n @ApiModelProperty(\"URL of backup DB server\")\n private String backupUrl;\n\n}\n\n## 内容7\n### 文件路径:\ninlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/sink/ck/ClickHouseSinkRequest.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.pojo.sink.ck;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.apache.inlong.manager.common.consts.SinkType;\nimport org.apache.inlong.manager.common.util.JsonTypeDefine;\nimport org.apache.inlong.manager.pojo.sink.SinkRequest;\n\n/**\n * ClickHouse sink request.\n */\n@Data\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@ApiModel(value = \"ClickHouse sink request\")\n@JsonTypeDefine(value = SinkType.CLICKHOUSE)\npublic class ClickHouseSinkRequest extends SinkRequest {\n\n @ApiModelProperty(\"JDBC URL of the ClickHouse server\")\n private String jdbcUrl;\n\n @ApiModelProperty(\"Username of the ClickHouse server\")\n private String username;\n\n @ApiModelProperty(\"User password of the ClickHouse server\")\n private String password;\n\n @ApiModelProperty(\"Target database name\")\n private String dbName;\n\n @ApiModelProperty(\"Target table name\")\n private String tableName;\n\n @ApiModelProperty(\"Flush interval, unit: second, default is 1s\")\n private Integer flushInterval = 1;\n\n @ApiModelProperty(\"Flush when record number reaches flushRecord\")\n private Integer flushRecord;\n\n @ApiModelProperty(\"Write max retry times, default is 3\")\n private Integer retryTimes = 3;\n\n @ApiModelProperty(\"Whether distributed table? 0: no, 1: yes\")\n private Integer isDistributed;\n\n @ApiModelProperty(\"Partition strategy, support: BALANCE, RANDOM, HASH\")\n private String partitionStrategy;\n\n @ApiModelProperty(value = \"Partition files, separate with commas\", notes = \"Necessary when partitionStrategy is HASH, must be one of the field list\")\n private String partitionFields;\n\n @ApiModelProperty(\"Key field names, separate with commas\")\n private String keyFieldNames;\n\n @ApiModelProperty(\"ClickHouse table engine, support MergeTree Mem and so on\")\n private String engine;\n\n @ApiModelProperty(\"Table partition information\")\n private String partitionBy;\n\n @ApiModelProperty(\"Table order information\")\n private String orderBy;\n\n @ApiModelProperty(value = \"Message time-to-live duration\")\n private Integer ttl;\n\n @ApiModelProperty(value = \"The unit of message's time-to-live duration\")\n private String ttlUnit;\n\n @ApiModelProperty(\"Table primary key\")\n private String primaryKey;\n\n}\n\n## 内容8\n### 文件路径:\ninlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/sink/greenplum/GreenplumSinkRequest.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.pojo.sink.greenplum;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.apache.inlong.manager.common.consts.SinkType;\nimport org.apache.inlong.manager.pojo.sink.SinkRequest;\nimport org.apache.inlong.manager.common.util.JsonTypeDefine;\n\n/**\n * Greenplum sink request.\n */\n@Data\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@ApiModel(value = \"Greenplum sink request\")\n@JsonTypeDefine(value = SinkType.GREENPLUM)\npublic class GreenplumSinkRequest extends SinkRequest {\n\n @ApiModelProperty(\"Greenplum JDBC URL such as:jdbc:postgresql://host:port/database\")\n private String jdbcUrl;\n\n @ApiModelProperty(\"Username for JDBC URL\")\n private String username;\n\n @ApiModelProperty(\"User password\")\n private String password;\n\n @ApiModelProperty(\"Target table name\")\n private String tableName;\n\n @ApiModelProperty(\"Primary key\")\n private String primaryKey;\n}\n\n## 内容9\n### 文件路径:\ninlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/sink/hive/HiveSinkRequest.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.pojo.sink.hive;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.apache.inlong.manager.common.consts.SinkType;\nimport org.apache.inlong.manager.common.enums.FileFormat;\nimport org.apache.inlong.manager.common.util.JsonTypeDefine;\nimport org.apache.inlong.manager.pojo.sink.SinkRequest;\n\nimport javax.validation.constraints.NotBlank;\nimport java.nio.charset.StandardCharsets;\nimport java.util.List;\n\n/**\n * Hive sink request.\n */\n@Data\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@ApiModel(value = \"Hive sink request\")\n@JsonTypeDefine(value = SinkType.HIVE)\npublic class HiveSinkRequest extends SinkRequest {\n\n @ApiModelProperty(\"Hive JDBC URL, such as jdbc:hive2://${ip}:${port}\")\n private String jdbcUrl;\n\n @ApiModelProperty(\"Username of the Hive server\")\n private String username;\n\n @ApiModelProperty(\"User password of the Hive server\")\n private String password;\n\n @NotBlank(message = \"dbName cannot be blank\")\n @ApiModelProperty(\"Target database name\")\n private String dbName;\n\n @NotBlank(message = \"tableName cannot be blank\")\n @ApiModelProperty(\"Target table name\")\n private String tableName;\n\n @ApiModelProperty(\"Data path, such as: hdfs://ip:port/user/hive/warehouse/test.db\")\n private String dataPath;\n\n @ApiModelProperty(\"Partition interval, support: 1 H, 1 D, 30 I, 10 I\")\n private Integer partitionInterval;\n\n @ApiModelProperty(\"Partition field list\")\n private List<HivePartitionField> partitionFieldList;\n\n @ApiModelProperty(\"Partition creation strategy, partition start, partition close\")\n private String partitionCreationStrategy;\n\n @ApiModelProperty(\"File format, support: TextFile, ORCFile, RCFile, SequenceFile, Avro, Parquet, etc\")\n private String fileFormat = FileFormat.TextFile.name();\n\n @ApiModelProperty(\"Data encoding format: UTF-8, GBK\")\n private String dataEncoding = StandardCharsets.UTF_8.toString();\n\n @ApiModelProperty(\"Data separator\")\n private String dataSeparator = String.valueOf((int) '\\001');\n\n @ApiModelProperty(\"Version for Hive, such as: 3.2.1\")\n private String hiveVersion;\n\n @ApiModelProperty(\"Config directory of Hive on HDFS, needed by sort in light mode, must include hive-site.xml\")\n private String hiveConfDir;\n\n}\n\n## 内容10\n### 文件路径:\ninlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/sink/hudi/HudiSinkRequest.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.pojo.sink.hudi;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport java.util.HashMap;\nimport java.util.List;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.apache.inlong.manager.common.consts.SinkType;\nimport org.apache.inlong.manager.common.util.JsonTypeDefine;\nimport org.apache.inlong.manager.pojo.sink.SinkRequest;\n\n/**\n * Hudi sink request.\n */\n@Data\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@ApiModel(value = \"Hudi sink request\")\n@JsonTypeDefine(value = SinkType.HUDI)\npublic class HudiSinkRequest extends SinkRequest {\n\n @ApiModelProperty(\"Catalog type, like: HIVE, HADOOP, default is HIVE\")\n private String catalogType = \"HIVE\";\n\n @ApiModelProperty(\"Catalog uri, such as hive metastore thrift://ip:port\")\n private String catalogUri;\n\n @ApiModelProperty(\"Hudi data warehouse dir\")\n private String warehouse;\n\n @ApiModelProperty(\"Target database name\")\n private String dbName;\n\n @ApiModelProperty(\"Target table name\")\n private String tableName;\n\n @ApiModelProperty(\"Data path, such as: hdfs://ip:port/user/hive/warehouse/test.db\")\n private String dataPath;\n\n @ApiModelProperty(\"File format, support: Parquet, Orc, Avro\")\n private String fileFormat;\n\n @ApiModelProperty(\"Extended properties\")\n private List<HashMap<String, String>> extList;\n\n @ApiModelProperty(\"Partition field list\")\n private String partitionKey;\n\n @ApiModelProperty(\"Primary key\")\n private String primaryKey;\n}\n\n## 内容11\n### 文件路径:\ninlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/sink/mysql/MySQLSinkDTO.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.pojo.sink.mysql;\n\nimport com.google.common.base.Strings;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.inlong.manager.common.consts.InlongConstants;\nimport org.apache.inlong.manager.common.enums.ErrorCodeEnum;\nimport org.apache.inlong.manager.common.exceptions.BusinessException;\nimport org.apache.inlong.manager.common.util.JsonUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.validation.constraints.NotNull;\nimport java.net.URLDecoder;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * MySQL sink info\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class MySQLSinkDTO {\n\n /**\n * The sensitive param may lead the attack.\n */\n private static final Map<String, String> SENSITIVE_PARAM_MAP = new HashMap<String, String>() {\n\n {\n put(\"autoDeserialize=true\", \"autoDeserialize=false\");\n put(\"allowLoadLocalInfile=true\", \"allowLoadLocalInfile=false\");\n put(\"allowUrlInLocalInfile=true\", \"allowUrlInLocalInfile=false\");\n put(\"allowLoadLocalInfileInPath=/\", \"allowLoadLocalInfileInPath=\");\n }\n };\n\n private static final Logger LOGGER = LoggerFactory.getLogger(MySQLSinkDTO.class);\n private static final String MYSQL_JDBC_PREFIX = \"jdbc:mysql://\";\n\n @ApiModelProperty(\"MySQL JDBC URL, such as jdbc:mysql://host:port\")\n private String jdbcUrl;\n\n @ApiModelProperty(\"Username for JDBC URL\")\n private String username;\n\n @ApiModelProperty(\"User password\")\n private String password;\n\n @ApiModelProperty(\"Target database name\")\n private String databaseName;\n\n @ApiModelProperty(\"Target table name\")\n private String tableName;\n\n @ApiModelProperty(\"Primary key\")\n private String primaryKey;\n\n @ApiModelProperty(\"Properties for MySQL\")\n private Map<String, Object> properties;\n\n /**\n * Get the dto instance from the request\n *\n * @param request MySQLSinkRequest\n * @return {@link MySQLSinkDTO}\n * @apiNote The config here will be saved to the database, so filter sensitive params before saving.\n */\n public static MySQLSinkDTO getFromRequest(MySQLSinkRequest request) {\n String url = filterSensitive(request.getJdbcUrl());\n return MySQLSinkDTO.builder()\n .jdbcUrl(url)\n .username(request.getUsername())\n .password(request.getPassword())\n .primaryKey(request.getPrimaryKey())\n .databaseName(request.getDatabaseName())\n .tableName(request.getTableName())\n .properties(request.getProperties())\n .build();\n }\n\n /**\n * Get MySQL sink info from JSON string\n *\n * @param extParams string ext params\n * @return {@link MySQLSinkDTO}\n */\n public static MySQLSinkDTO getFromJson(@NotNull String extParams) {\n try {\n return JsonUtils.parseObject(extParams, MySQLSinkDTO.class);\n } catch (Exception e) {\n throw new BusinessException(ErrorCodeEnum.SINK_INFO_INCORRECT,\n String.format(\"parse extParams of MySQL SinkDTO failure: %s\", e.getMessage()));\n }\n }\n\n /**\n * Get MySQL table info\n *\n * @param mySQLSink MySQL sink dto,{@link MySQLSinkDTO}\n * @param columnList MySQL column info list,{@link MySQLColumnInfo}\n * @return {@link MySQLTableInfo}\n */\n public static MySQLTableInfo getTableInfo(MySQLSinkDTO mySQLSink, List<MySQLColumnInfo> columnList) {\n MySQLTableInfo tableInfo = new MySQLTableInfo();\n tableInfo.setDbName(mySQLSink.getDatabaseName());\n tableInfo.setTableName(mySQLSink.getTableName());\n tableInfo.setPrimaryKey(mySQLSink.getPrimaryKey());\n tableInfo.setColumns(columnList);\n return tableInfo;\n }\n\n /**\n * Get DbName from jdbcUrl\n *\n * @param jdbcUrl MySQL JDBC url, such as jdbc:mysql://host:port/database\n * @return database name\n */\n private static String getDbNameFromUrl(String jdbcUrl) {\n String database = null;\n\n if (Strings.isNullOrEmpty(jdbcUrl)) {\n throw new IllegalArgumentException(\"Invalid JDBC url.\");\n }\n\n jdbcUrl = jdbcUrl.toLowerCase();\n if (jdbcUrl.startsWith(\"jdbc:impala\")) {\n jdbcUrl = jdbcUrl.replace(\":impala\", \"\");\n }\n\n int pos1;\n if (!jdbcUrl.startsWith(\"jdbc:\")\n || (pos1 = jdbcUrl.indexOf(':', 5)) == -1) {\n throw new IllegalArgumentException(\"Invalid JDBC url.\");\n }\n\n String connUri = jdbcUrl.substring(pos1 + 1);\n if (connUri.startsWith(\"//\")) {\n int pos = connUri.indexOf('/', 2);\n if (pos != -1) {\n database = connUri.substring(pos + 1);\n }\n } else {\n database = connUri;\n }\n\n if (Strings.isNullOrEmpty(database)) {\n throw new IllegalArgumentException(\"Invalid JDBC URL: \" + jdbcUrl);\n }\n\n if (database.contains(InlongConstants.QUESTION_MARK)) {\n database = database.substring(0, database.indexOf(InlongConstants.QUESTION_MARK));\n }\n if (database.contains(InlongConstants.SEMICOLON)) {\n database = database.substring(0, database.indexOf(InlongConstants.SEMICOLON));\n }\n return database;\n }\n\n public static String setDbNameToUrl(String jdbcUrl, String databaseName) {\n if (StringUtils.isBlank(jdbcUrl)) {\n return jdbcUrl;\n }\n String pattern = \"jdbc:mysql://(?<host>[a-zA-Z0-9-//.]+):(?<port>[0-9]+)?(?<ext>)\";\n Pattern namePattern = Pattern.compile(pattern);\n Matcher dataMatcher = namePattern.matcher(jdbcUrl);\n StringBuilder resultUrl;\n if (dataMatcher.find()) {\n String host = dataMatcher.group(\"host\");\n String port = dataMatcher.group(\"port\");\n resultUrl = new StringBuilder().append(MYSQL_JDBC_PREFIX)\n .append(host)\n .append(InlongConstants.COLON)\n .append(port)\n .append(InlongConstants.SLASH)\n .append(databaseName);\n } else {\n throw new BusinessException(ErrorCodeEnum.SINK_INFO_INCORRECT,\n \"MySQL JDBC URL was invalid, it should like jdbc:mysql://host:port\");\n }\n if (jdbcUrl.contains(InlongConstants.QUESTION_MARK)) {\n resultUrl.append(jdbcUrl.substring(jdbcUrl.indexOf(InlongConstants.QUESTION_MARK)));\n }\n return resultUrl.toString();\n }\n\n /**\n * Filter the sensitive params for the given URL.\n *\n * @param url str may have some sensitive params\n * @return str without sensitive param\n */\n public static String filterSensitive(String url) {\n if (StringUtils.isBlank(url)) {\n return url;\n }\n try {\n String resultUrl = url;\n while (resultUrl.contains(InlongConstants.PERCENT)) {\n resultUrl = URLDecoder.decode(resultUrl, \"UTF-8\");\n }\n for (String sensitiveParam : SENSITIVE_PARAM_MAP.keySet()) {\n if (StringUtils.containsIgnoreCase(resultUrl, sensitiveParam)) {\n resultUrl = StringUtils.replaceIgnoreCase(resultUrl, sensitiveParam,\n SENSITIVE_PARAM_MAP.get(sensitiveParam));\n }\n }\n LOGGER.info(\"the origin url [{}] was replaced to: [{}]\", url, resultUrl);\n return resultUrl;\n } catch (Exception e) {\n throw new BusinessException(ErrorCodeEnum.SINK_INFO_INCORRECT,\n ErrorCodeEnum.SINK_INFO_INCORRECT.getMessage() + \": \" + e.getMessage());\n }\n }\n\n}\n\n## 内容12\n### 文件路径:\ninlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/sink/mysql/MySQLSinkRequest.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.pojo.sink.mysql;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.apache.inlong.manager.common.consts.SinkType;\nimport org.apache.inlong.manager.pojo.sink.SinkRequest;\nimport org.apache.inlong.manager.common.util.JsonTypeDefine;\n\n/**\n * MySQL sink request.\n */\n@Data\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@ApiModel(value = \"MySQL sink request\")\n@JsonTypeDefine(value = SinkType.MYSQL)\npublic class MySQLSinkRequest extends SinkRequest {\n\n @ApiModelProperty(\"MySQL JDBC URL, such as jdbc:mysql://host:port\")\n private String jdbcUrl;\n\n @ApiModelProperty(\"Username for JDBC URL\")\n private String username;\n\n @ApiModelProperty(\"User password\")\n private String password;\n\n @ApiModelProperty(\"Target database name\")\n private String databaseName;\n\n @ApiModelProperty(\"Target table name\")\n private String tableName;\n\n @ApiModelProperty(\"Primary key\")\n private String primaryKey;\n}\n\n## 内容13\n### 文件路径:\ninlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/sink/oracle/OracleSinkRequest.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.pojo.sink.oracle;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.apache.inlong.manager.common.consts.SinkType;\nimport org.apache.inlong.manager.pojo.sink.SinkRequest;\nimport org.apache.inlong.manager.common.util.JsonTypeDefine;\n\n/**\n * Oracle sink request.\n */\n@Data\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@ApiModel(value = \"Oracle sink request\")\n@JsonTypeDefine(value = SinkType.ORACLE)\npublic class OracleSinkRequest extends SinkRequest {\n\n @ApiModelProperty(\"Oracle JDBC URL, such as jdbc:oracle:thin://host:port/database\")\n private String jdbcUrl;\n\n @ApiModelProperty(\"Username for JDBC URL\")\n private String username;\n\n @ApiModelProperty(\"User password\")\n private String password;\n\n @ApiModelProperty(\"Target table name\")\n private String tableName;\n\n @ApiModelProperty(\"Primary key\")\n private String primaryKey;\n}\n\n## 内容14\n### 文件路径:\ninlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/sink/postgresql/PostgreSQLSinkRequest.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.pojo.sink.postgresql;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.apache.inlong.manager.common.consts.SinkType;\nimport org.apache.inlong.manager.pojo.sink.SinkRequest;\nimport org.apache.inlong.manager.common.util.JsonTypeDefine;\n\n/**\n * PostgreSQL sink request.\n */\n@Data\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@ApiModel(value = \"PostgreSQL sink request\")\n@JsonTypeDefine(value = SinkType.POSTGRESQL)\npublic class PostgreSQLSinkRequest extends SinkRequest {\n\n @ApiModelProperty(\"JDBC URL of the PostgreSQL server\")\n private String jdbcUrl;\n\n @ApiModelProperty(\"Username for JDBC URL\")\n private String username;\n\n @ApiModelProperty(\"User password\")\n private String password;\n\n @ApiModelProperty(\"Target database name\")\n private String dbName;\n\n @ApiModelProperty(\"Target table name\")\n private String tableName;\n\n @ApiModelProperty(\"Primary key is required when serializationType is json, avro\")\n private String primaryKey;\n}\n\n## 内容15\n### 文件路径:\ninlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/sink/sqlserver/SQLServerSinkRequest.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.pojo.sink.sqlserver;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.apache.inlong.manager.common.consts.SinkType;\nimport org.apache.inlong.manager.pojo.sink.SinkRequest;\nimport org.apache.inlong.manager.common.util.JsonTypeDefine;\n\n/**\n * SQLServer sink request.\n */\n@Data\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@ApiModel(value = \"SQLServer sink request\")\n@JsonTypeDefine(value = SinkType.SQLSERVER)\npublic class SQLServerSinkRequest extends SinkRequest {\n\n @ApiModelProperty(\"Username of the SQLServer\")\n private String username;\n\n @ApiModelProperty(\"Password of the SQLServer\")\n private String password;\n\n @ApiModelProperty(\"SQLServer meta db URL, etc jdbc:sqlserver://host:port;databaseName=database\")\n private String jdbcUrl;\n\n @ApiModelProperty(\"Schema name of the SQLServer\")\n private String schemaName;\n\n @ApiModelProperty(\"Table name of the SQLServer\")\n private String tableName;\n\n @ApiModelProperty(\"Database time zone, Default is UTC\")\n private String serverTimezone;\n\n @ApiModelProperty(\"Whether to migrate all databases\")\n private boolean allMigration;\n\n @ApiModelProperty(value = \"Primary key must be shared by all tables\")\n private String primaryKey;\n\n}\n\n## 内容16\n### 文件路径:\ninlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/sink/starrocks/StarRocksSinkRequest.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.pojo.sink.starrocks;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.apache.inlong.manager.common.consts.SinkType;\nimport org.apache.inlong.manager.common.util.JsonTypeDefine;\nimport org.apache.inlong.manager.pojo.sink.SinkRequest;\n\n/**\n * StarRocks sink request.\n */\n@Data\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@ApiModel(value = \"StarRocks sink request\")\n@JsonTypeDefine(value = SinkType.STARROCKS)\npublic class StarRocksSinkRequest extends SinkRequest {\n\n @ApiModelProperty(\"StarRocks jdbc url\")\n private String jdbcUrl;\n\n @ApiModelProperty(\"StarRocks FE http address\")\n private String loadUrl;\n\n @ApiModelProperty(\"Username for StarRocks accessing\")\n private String username;\n\n @ApiModelProperty(\"Password for StarRocks accessing\")\n private String password;\n\n @ApiModelProperty(\"Database name\")\n private String databaseName;\n\n @ApiModelProperty(\"Table name\")\n private String tableName;\n\n @ApiModelProperty(\"The primary key of sink table\")\n private String primaryKey;\n\n @ApiModelProperty(\"The multiple enable of sink\")\n private Boolean sinkMultipleEnable = false;\n\n @ApiModelProperty(\"The multiple format of sink\")\n private String sinkMultipleFormat;\n\n @ApiModelProperty(\"The multiple database-pattern of sink\")\n private String databasePattern;\n\n @ApiModelProperty(\"The multiple table-pattern of sink\")\n private String tablePattern;\n\n @ApiModelProperty(\"The table engine, like: OLAP, MYSQL, ELASTICSEARCH, etc, default is OLAP\")\n private String tableEngine = \"OLAP\";\n\n @ApiModelProperty(\"The table replication num\")\n private Integer replicationNum = 3;\n\n @ApiModelProperty(\"The table barrel size\")\n private Integer barrelSize = 8;\n\n}\n\n## 内容17\n### 文件路径:\ninlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/sink/tdsqlpostgresql/TDSQLPostgreSQLSinkRequest.java\n### 代码内容:\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.inlong.manager.pojo.sink.tdsqlpostgresql;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.apache.inlong.manager.common.consts.SinkType;\nimport org.apache.inlong.manager.pojo.sink.SinkRequest;\nimport org.apache.inlong.manager.common.util.JsonTypeDefine;\n\n/**\n * TDSQLPostgreSQL sink request.\n */\n@Data\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@ApiModel(value = \"TDSQLPostgreSQL sink request\")\n@JsonTypeDefine(value = SinkType.TDSQLPOSTGRESQL)\npublic class TDSQLPostgreSQLSinkRequest extends SinkRequest {\n\n @ApiModelProperty(\"TDSQLPostgreSQL jdbc url, such as jdbc:postgresql://host:port/database\")\n private String jdbcUrl;\n\n @ApiModelProperty(\"Username for JDBC URL\")\n private String username;\n\n @ApiModelProperty(\"User password\")\n private String password;\n\n @ApiModelProperty(\"Target schema name\")\n private String schemaName;\n\n @ApiModelProperty(\"Target table name\")\n private String tableName;\n\n @ApiModelProperty(\"Primary key\")\n private String primaryKey;\n}\n\n", "input": "", "output": "【漏洞类型】路径遍历漏洞\n【严重性】High\n【漏洞摘要】这是一个路径遍历漏洞。在`MySQLSinkDTO`类的`filterSensitive`方法中,代码未对包含空白字符的URL进行适当的过滤或验证,可能导致攻击者通过构造包含空白字符的URL来绕过安全检查。具体来说,`filterSensitive`方法在循环解码URL中的百分号编码后,直接替换敏感参数,而未对空白字符进行处理,这使得攻击者可以插入空白字符来规避敏感参数的替换规则。\n【关键代码模式】`filterSensitive`方法中对URL的处理,特别是未验证和过滤空白字符。\n【修复方法说明】修复方法通过在`filterSensitive`方法中添加对空白字符的替换,确保所有空白字符都被移除,从而防止路径遍历攻击。适用的最佳实践包括对所有用户输入进行严格的验证和过滤,以防止注入攻击和其他安全问题。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nxwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/xpart.vm\n### 代码内容:\n## ---------------------------------------------------------------------------\n## See the NOTICE file distributed with this work for additional\n## information regarding copyright ownership.\n##\n## This is free software; you can redistribute it and/or modify it\n## under the terms of the GNU Lesser General Public License as\n## published by the Free Software Foundation; either version 2.1 of\n## the License, or (at your option) any later version.\n##\n## This software is distributed in the hope that it will be useful,\n## but WITHOUT ANY WARRANTY; without even the implied warranty of\n## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n## Lesser General Public License for more details.\n##\n## You should have received a copy of the GNU Lesser General Public\n## License along with this software; if not, write to the Free\n## Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n## 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n## ---------------------------------------------------------------------------\n#set ($vm = $request.vm)\n#template('xwikivars.vm')\n#if ($request.useLayoutVars == 'true')\n#template('layoutvars.vm')\n#end\n#template($vm)\n", "input": "", "output": "【漏洞类型】路径遍历漏洞\n【严重性】High\n【漏洞摘要】这是一个路径遍历漏洞。在原始代码中,`#template($vm)` 这一行直接使用了从用户请求中获取的 `$vm` 变量作为模板名称。如果攻击者能够控制 `$vm` 变量的内容,他们可能会注入恶意路径,导致系统加载和执行不应该被加载的模板文件。这可能允许攻击者读取、修改或执行任意文件,从而引发潜在的安全风险。\n【关键代码模式】未验证的用户输入\n【修复方法说明】修复方法通过检查 `$vm` 变量是否以 `.vm` 结尾来限制用户输入,从而防止路径遍历攻击。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\ncodec/src/main/java/io/netty/handler/codec/compression/Snappy.java\n### 代码内容:\npackage io.netty.handler.codec.compression;\n\nimport io.netty.buffer.ByteBuf;\n\n/**\n * Uncompresses an input {@link ByteBuf} encoded with Snappy compression into an\n * output {@link ByteBuf}.\n *\n * See <a href=\"https://github.com/google/snappy/blob/master/format_description.txt\">snappy format</a>.\n */\npublic final class Snappy {\n\n private static final int MAX_HT_SIZE = 1 << 14;\n private static final int MIN_COMPRESSIBLE_BYTES = 15;\n\n // used as a return value to indicate that we haven't yet read our full preamble\n private static final int PREAMBLE_NOT_FULL = -1;\n private static final int NOT_ENOUGH_INPUT = -1;\n\n // constants for the tag types\n private static final int LITERAL = 0;\n private static final int COPY_1_BYTE_OFFSET = 1;\n private static final int COPY_2_BYTE_OFFSET = 2;\n private static final int COPY_4_BYTE_OFFSET = 3;\n\n private State state = State.READY;\n private byte tag;\n private int written;\n\n private enum State {\n READY,\n READING_PREAMBLE,\n READING_TAG,\n READING_LITERAL,\n READING_COPY\n }\n\n public void reset() {\n state = State.READY;\n tag = 0;\n written = 0;\n }\n\n public void encode(final ByteBuf in, final ByteBuf out, final int length) {\n // Write the preamble length to the output buffer\n for (int i = 0;; i ++) {\n int b = length >>> i * 7;\n if ((b & 0xFFFFFF80) != 0) {\n out.writeByte(b & 0x7f | 0x80);\n } else {\n out.writeByte(b);\n break;\n }\n }\n\n int inIndex = in.readerIndex();\n final int baseIndex = inIndex;\n\n final short[] table = getHashTable(length);\n final int shift = Integer.numberOfLeadingZeros(table.length) + 1;\n\n int nextEmit = inIndex;\n\n if (length - inIndex >= MIN_COMPRESSIBLE_BYTES) {\n int nextHash = hash(in, ++inIndex, shift);\n outer: while (true) {\n int skip = 32;\n\n int candidate;\n int nextIndex = inIndex;\n do {\n inIndex = nextIndex;\n int hash = nextHash;\n int bytesBetweenHashLookups = skip++ >> 5;\n nextIndex = inIndex + bytesBetweenHashLookups;\n\n // We need at least 4 remaining bytes to read the hash\n if (nextIndex > length - 4) {\n break outer;\n }\n\n nextHash = hash(in, nextIndex, shift);\n\n candidate = baseIndex + table[hash];\n\n table[hash] = (short) (inIndex - baseIndex);\n }\n while (in.getInt(inIndex) != in.getInt(candidate));\n\n encodeLiteral(in, out, inIndex - nextEmit);\n\n int insertTail;\n do {\n int base = inIndex;\n int matched = 4 + findMatchingLength(in, candidate + 4, inIndex + 4, length);\n inIndex += matched;\n int offset = base - candidate;\n encodeCopy(out, offset, matched);\n in.readerIndex(in.readerIndex() + matched);\n insertTail = inIndex - 1;\n nextEmit = inIndex;\n if (inIndex >= length - 4) {\n break outer;\n }\n\n int prevHash = hash(in, insertTail, shift);\n table[prevHash] = (short) (inIndex - baseIndex - 1);\n int currentHash = hash(in, insertTail + 1, shift);\n candidate = baseIndex + table[currentHash];\n table[currentHash] = (short) (inIndex - baseIndex);\n }\n while (in.getInt(insertTail + 1) == in.getInt(candidate));\n\n nextHash = hash(in, insertTail + 2, shift);\n ++inIndex;\n }\n }\n\n // If there are any remaining characters, write them out as a literal\n if (nextEmit < length) {\n encodeLiteral(in, out, length - nextEmit);\n }\n }\n\n /**\n * Hashes the 4 bytes located at index, shifting the resulting hash into\n * the appropriate range for our hash table.\n *\n * @param in The input buffer to read 4 bytes from\n * @param index The index to read at\n * @param shift The shift value, for ensuring that the resulting value is\n * withing the range of our hash table size\n * @return A 32-bit hash of 4 bytes located at index\n */\n private static int hash(ByteBuf in, int index, int shift) {\n return in.getInt(index) * 0x1e35a7bd >>> shift;\n }\n\n /**\n * Creates an appropriately sized hashtable for the given input size\n *\n * @param inputSize The size of our input, ie. the number of bytes we need to encode\n * @return An appropriately sized empty hashtable\n */\n private static short[] getHashTable(int inputSize) {\n int htSize = 256;\n while (htSize < MAX_HT_SIZE && htSize < inputSize) {\n htSize <<= 1;\n }\n return new short[htSize];\n }\n\n /**\n * Iterates over the supplied input buffer between the supplied minIndex and\n * maxIndex to find how long our matched copy overlaps with an already-written\n * literal value.\n *\n * @param in The input buffer to scan over\n * @param minIndex The index in the input buffer to start scanning from\n * @param inIndex The index of the start of our copy\n * @param maxIndex The length of our input buffer\n * @return The number of bytes for which our candidate copy is a repeat of\n */\n private static int findMatchingLength(ByteBuf in, int minIndex, int inIndex, int maxIndex) {\n int matched = 0;\n\n while (inIndex <= maxIndex - 4 &&\n in.getInt(inIndex) == in.getInt(minIndex + matched)) {\n inIndex += 4;\n matched += 4;\n }\n\n while (inIndex < maxIndex && in.getByte(minIndex + matched) == in.getByte(inIndex)) {\n ++inIndex;\n ++matched;\n }\n\n return matched;\n }\n\n /**\n * Calculates the minimum number of bits required to encode a value. This can\n * then in turn be used to calculate the number of septets or octets (as\n * appropriate) to use to encode a length parameter.\n *\n * @param value The value to calculate the minimum number of bits required to encode\n * @return The minimum number of bits required to encode the supplied value\n */\n private static int bitsToEncode(int value) {\n int highestOneBit = Integer.highestOneBit(value);\n int bitLength = 0;\n while ((highestOneBit >>= 1) != 0) {\n bitLength++;\n }\n\n return bitLength;\n }\n\n /**\n * Writes a literal to the supplied output buffer by directly copying from\n * the input buffer. The literal is taken from the current readerIndex\n * up to the supplied length.\n *\n * @param in The input buffer to copy from\n * @param out The output buffer to copy to\n * @param length The length of the literal to copy\n */\n static void encodeLiteral(ByteBuf in, ByteBuf out, int length) {\n if (length < 61) {\n out.writeByte(length - 1 << 2);\n } else {\n int bitLength = bitsToEncode(length - 1);\n int bytesToEncode = 1 + bitLength / 8;\n out.writeByte(59 + bytesToEncode << 2);\n for (int i = 0; i < bytesToEncode; i++) {\n out.writeByte(length - 1 >> i * 8 & 0x0ff);\n }\n }\n\n out.writeBytes(in, length);\n }\n\n private static void encodeCopyWithOffset(ByteBuf out, int offset, int length) {\n if (length < 12 && offset < 2048) {\n out.writeByte(COPY_1_BYTE_OFFSET | length - 4 << 2 | offset >> 8 << 5);\n out.writeByte(offset & 0x0ff);\n } else {\n out.writeByte(COPY_2_BYTE_OFFSET | length - 1 << 2);\n out.writeByte(offset & 0x0ff);\n out.writeByte(offset >> 8 & 0x0ff);\n }\n }\n\n /**\n * Encodes a series of copies, each at most 64 bytes in length.\n *\n * @param out The output buffer to write the copy pointer to\n * @param offset The offset at which the original instance lies\n * @param length The length of the original instance\n */\n private static void encodeCopy(ByteBuf out, int offset, int length) {\n while (length >= 68) {\n encodeCopyWithOffset(out, offset, 64);\n length -= 64;\n }\n\n if (length > 64) {\n encodeCopyWithOffset(out, offset, 60);\n length -= 60;\n }\n\n encodeCopyWithOffset(out, offset, length);\n }\n\n public void decode(ByteBuf in, ByteBuf out) {\n while (in.isReadable()) {\n switch (state) {\n case READY:\n state = State.READING_PREAMBLE;\n // fall through\n case READING_PREAMBLE:\n int uncompressedLength = readPreamble(in);\n if (uncompressedLength == PREAMBLE_NOT_FULL) {\n // We've not yet read all of the preamble, so wait until we can\n return;\n }\n if (uncompressedLength == 0) {\n // Should never happen, but it does mean we have nothing further to do\n state = State.READY;\n return;\n }\n out.ensureWritable(uncompressedLength);\n state = State.READING_TAG;\n // fall through\n case READING_TAG:\n if (!in.isReadable()) {\n return;\n }\n tag = in.readByte();\n switch (tag & 0x03) {\n case LITERAL:\n state = State.READING_LITERAL;\n break;\n case COPY_1_BYTE_OFFSET:\n case COPY_2_BYTE_OFFSET:\n case COPY_4_BYTE_OFFSET:\n state = State.READING_COPY;\n break;\n }\n break;\n case READING_LITERAL:\n int literalWritten = decodeLiteral(tag, in, out);\n if (literalWritten != NOT_ENOUGH_INPUT) {\n state = State.READING_TAG;\n written += literalWritten;\n } else {\n // Need to wait for more data\n return;\n }\n break;\n case READING_COPY:\n int decodeWritten;\n switch (tag & 0x03) {\n case COPY_1_BYTE_OFFSET:\n decodeWritten = decodeCopyWith1ByteOffset(tag, in, out, written);\n if (decodeWritten != NOT_ENOUGH_INPUT) {\n state = State.READING_TAG;\n written += decodeWritten;\n } else {\n // Need to wait for more data\n return;\n }\n break;\n case COPY_2_BYTE_OFFSET:\n decodeWritten = decodeCopyWith2ByteOffset(tag, in, out, written);\n if (decodeWritten != NOT_ENOUGH_INPUT) {\n state = State.READING_TAG;\n written += decodeWritten;\n } else {\n // Need to wait for more data\n return;\n }\n break;\n case COPY_4_BYTE_OFFSET:\n decodeWritten = decodeCopyWith4ByteOffset(tag, in, out, written);\n if (decodeWritten != NOT_ENOUGH_INPUT) {\n state = State.READING_TAG;\n written += decodeWritten;\n } else {\n // Need to wait for more data\n return;\n }\n break;\n }\n }\n }\n }\n\n /**\n * Reads the length varint (a series of bytes, where the lower 7 bits\n * are data and the upper bit is a flag to indicate more bytes to be\n * read).\n *\n * @param in The input buffer to read the preamble from\n * @return The calculated length based on the input buffer, or 0 if\n * no preamble is able to be calculated\n */\n private static int readPreamble(ByteBuf in) {\n int length = 0;\n int byteIndex = 0;\n while (in.isReadable()) {\n int current = in.readUnsignedByte();\n length |= (current & 0x7f) << byteIndex++ * 7;\n if ((current & 0x80) == 0) {\n return length;\n }\n\n if (byteIndex >= 4) {\n throw new DecompressionException(\"Preamble is greater than 4 bytes\");\n }\n }\n\n return 0;\n }\n\n /**\n * Reads a literal from the input buffer directly to the output buffer.\n * A \"literal\" is an uncompressed segment of data stored directly in the\n * byte stream.\n *\n * @param tag The tag that identified this segment as a literal is also\n * used to encode part of the length of the data\n * @param in The input buffer to read the literal from\n * @param out The output buffer to write the literal to\n * @return The number of bytes appended to the output buffer, or -1 to indicate \"try again later\"\n */\n static int decodeLiteral(byte tag, ByteBuf in, ByteBuf out) {\n in.markReaderIndex();\n int length;\n switch(tag >> 2 & 0x3F) {\n case 60:\n if (!in.isReadable()) {\n return NOT_ENOUGH_INPUT;\n }\n length = in.readUnsignedByte();\n break;\n case 61:\n if (in.readableBytes() < 2) {\n return NOT_ENOUGH_INPUT;\n }\n length = in.readUnsignedShortLE();\n break;\n case 62:\n if (in.readableBytes() < 3) {\n return NOT_ENOUGH_INPUT;\n }\n length = in.readUnsignedMediumLE();\n break;\n case 63:\n if (in.readableBytes() < 4) {\n return NOT_ENOUGH_INPUT;\n }\n length = in.readIntLE();\n break;\n default:\n length = tag >> 2 & 0x3F;\n }\n length += 1;\n\n if (in.readableBytes() < length) {\n in.resetReaderIndex();\n return NOT_ENOUGH_INPUT;\n }\n\n out.writeBytes(in, length);\n return length;\n }\n\n /**\n * Reads a compressed reference offset and length from the supplied input\n * buffer, seeks back to the appropriate place in the input buffer and\n * writes the found data to the supplied output stream.\n *\n * @param tag The tag used to identify this as a copy is also used to encode\n * the length and part of the offset\n * @param in The input buffer to read from\n * @param out The output buffer to write to\n * @return The number of bytes appended to the output buffer, or -1 to indicate\n * \"try again later\"\n * @throws DecompressionException If the read offset is invalid\n */\n private static int decodeCopyWith1ByteOffset(byte tag, ByteBuf in, ByteBuf out, int writtenSoFar) {\n if (!in.isReadable()) {\n return NOT_ENOUGH_INPUT;\n }\n\n int initialIndex = out.writerIndex();\n int length = 4 + ((tag & 0x01c) >> 2);\n int offset = (tag & 0x0e0) << 8 >> 5 | in.readUnsignedByte();\n\n validateOffset(offset, writtenSoFar);\n\n out.markReaderIndex();\n if (offset < length) {\n int copies = length / offset;\n for (; copies > 0; copies--) {\n out.readerIndex(initialIndex - offset);\n out.readBytes(out, offset);\n }\n if (length % offset != 0) {\n out.readerIndex(initialIndex - offset);\n out.readBytes(out, length % offset);\n }\n } else {\n out.readerIndex(initialIndex - offset);\n out.readBytes(out, length);\n }\n out.resetReaderIndex();\n\n return length;\n }\n\n /**\n * Reads a compressed reference offset and length from the supplied input\n * buffer, seeks back to the appropriate place in the input buffer and\n * writes the found data to the supplied output stream.\n *\n * @param tag The tag used to identify this as a copy is also used to encode\n * the length and part of the offset\n * @param in The input buffer to read from\n * @param out The output buffer to write to\n * @throws DecompressionException If the read offset is invalid\n * @return The number of bytes appended to the output buffer, or -1 to indicate\n * \"try again later\"\n */\n private static int decodeCopyWith2ByteOffset(byte tag, ByteBuf in, ByteBuf out, int writtenSoFar) {\n if (in.readableBytes() < 2) {\n return NOT_ENOUGH_INPUT;\n }\n\n int initialIndex = out.writerIndex();\n int length = 1 + (tag >> 2 & 0x03f);\n int offset = in.readUnsignedShortLE();\n\n validateOffset(offset, writtenSoFar);\n\n out.markReaderIndex();\n if (offset < length) {\n int copies = length / offset;\n for (; copies > 0; copies--) {\n out.readerIndex(initialIndex - offset);\n out.readBytes(out, offset);\n }\n if (length % offset != 0) {\n out.readerIndex(initialIndex - offset);\n out.readBytes(out, length % offset);\n }\n } else {\n out.readerIndex(initialIndex - offset);\n out.readBytes(out, length);\n }\n out.resetReaderIndex();\n\n return length;\n }\n\n /**\n * Reads a compressed reference offset and length from the supplied input\n * buffer, seeks back to the appropriate place in the input buffer and\n * writes the found data to the supplied output stream.\n *\n * @param tag The tag used to identify this as a copy is also used to encode\n * the length and part of the offset\n * @param in The input buffer to read from\n * @param out The output buffer to write to\n * @return The number of bytes appended to the output buffer, or -1 to indicate\n * \"try again later\"\n * @throws DecompressionException If the read offset is invalid\n */\n private static int decodeCopyWith4ByteOffset(byte tag, ByteBuf in, ByteBuf out, int writtenSoFar) {\n if (in.readableBytes() < 4) {\n return NOT_ENOUGH_INPUT;\n }\n\n int initialIndex = out.writerIndex();\n int length = 1 + (tag >> 2 & 0x03F);\n int offset = in.readIntLE();\n\n validateOffset(offset, writtenSoFar);\n\n out.markReaderIndex();\n if (offset < length) {\n int copies = length / offset;\n for (; copies > 0; copies--) {\n out.readerIndex(initialIndex - offset);\n out.readBytes(out, offset);\n }\n if (length % offset != 0) {\n out.readerIndex(initialIndex - offset);\n out.readBytes(out, length % offset);\n }\n } else {\n out.readerIndex(initialIndex - offset);\n out.readBytes(out, length);\n }\n out.resetReaderIndex();\n\n return length;\n }\n\n /**\n * Validates that the offset extracted from a compressed reference is within\n * the permissible bounds of an offset (0 < offset < Integer.MAX_VALUE), and does not\n * exceed the length of the chunk currently read so far.\n *\n * @param offset The offset extracted from the compressed reference\n * @param chunkSizeSoFar The number of bytes read so far from this chunk\n * @throws DecompressionException if the offset is invalid\n */\n private static void validateOffset(int offset, int chunkSizeSoFar) {\n if (offset == 0) {\n throw new DecompressionException(\"Offset is less than minimum permissible value\");\n }\n\n if (offset < 0) {\n // Due to arithmetic overflow\n throw new DecompressionException(\"Offset is greater than maximum value supported by this implementation\");\n }\n\n if (offset > chunkSizeSoFar) {\n throw new DecompressionException(\"Offset exceeds size of chunk\");\n }\n }\n\n /**\n * Computes the CRC32C checksum of the supplied data and performs the \"mask\" operation\n * on the computed checksum\n *\n * @param data The input data to calculate the CRC32C checksum of\n */\n static int calculateChecksum(ByteBuf data) {\n return calculateChecksum(data, data.readerIndex(), data.readableBytes());\n }\n\n /**\n * Computes the CRC32C checksum of the supplied data and performs the \"mask\" operation\n * on the computed checksum\n *\n * @param data The input data to calculate the CRC32C checksum of\n */\n static int calculateChecksum(ByteBuf data, int offset, int length) {\n Crc32c crc32 = new Crc32c();\n try {\n crc32.update(data, offset, length);\n return maskChecksum(crc32.getValue());\n } finally {\n crc32.reset();\n }\n }\n\n /**\n * Computes the CRC32C checksum of the supplied data, performs the \"mask\" operation\n * on the computed checksum, and then compares the resulting masked checksum to the\n * supplied checksum.\n *\n * @param expectedChecksum The checksum decoded from the stream to compare against\n * @param data The input data to calculate the CRC32C checksum of\n * @throws DecompressionException If the calculated and supplied checksums do not match\n */\n static void validateChecksum(int expectedChecksum, ByteBuf data) {\n validateChecksum(expectedChecksum, data, data.readerIndex(), data.readableBytes());\n }\n\n /**\n * Computes the CRC32C checksum of the supplied data, performs the \"mask\" operation\n * on the computed checksum, and then compares the resulting masked checksum to the\n * supplied checksum.\n *\n * @param expectedChecksum The checksum decoded from the stream to compare against\n * @param data The input data to calculate the CRC32C checksum of\n * @throws DecompressionException If the calculated and supplied checksums do not match\n */\n static void validateChecksum(int expectedChecksum, ByteBuf data, int offset, int length) {\n final int actualChecksum = calculateChecksum(data, offset, length);\n if (actualChecksum != expectedChecksum) {\n throw new DecompressionException(\n \"mismatching checksum: \" + Integer.toHexString(actualChecksum) +\n \" (expected: \" + Integer.toHexString(expectedChecksum) + ')');\n }\n }\n\n /**\n * From the spec:\n *\n * \"Checksums are not stored directly, but masked, as checksumming data and\n * then its own checksum can be problematic. The masking is the same as used\n * in Apache Hadoop: Rotate the checksum by 15 bits, then add the constant\n * 0xa282ead8 (using wraparound as normal for unsigned integers).\"\n *\n * @param checksum The actual checksum of the data\n * @return The masked checksum\n */\n static int maskChecksum(long checksum) {\n return (int) ((checksum >> 15 | checksum << 17) + 0xa282ead8);\n }\n}\n\n## 内容2\n### 文件路径:\ncodec/src/main/java/io/netty/handler/codec/compression/SnappyFrameDecoder.java\n### 代码内容:\npackage io.netty.handler.codec.compression;\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.handler.codec.ByteToMessageDecoder;\n\nimport java.util.List;\n\nimport static io.netty.handler.codec.compression.Snappy.validateChecksum;\n\n/**\n * Uncompresses a {@link ByteBuf} encoded with the Snappy framing format.\n *\n * See <a href=\"https://github.com/google/snappy/blob/master/framing_format.txt\">Snappy framing format</a>.\n *\n * Note that by default, validation of the checksum header in each chunk is\n * DISABLED for performance improvements. If performance is less of an issue,\n * or if you would prefer the safety that checksum validation brings, please\n * use the {@link #SnappyFrameDecoder(boolean)} constructor with the argument\n * set to {@code true}.\n */\npublic class SnappyFrameDecoder extends ByteToMessageDecoder {\n\n private enum ChunkType {\n STREAM_IDENTIFIER,\n COMPRESSED_DATA,\n UNCOMPRESSED_DATA,\n RESERVED_UNSKIPPABLE,\n RESERVED_SKIPPABLE\n }\n\n private static final int SNAPPY_IDENTIFIER_LEN = 6;\n private static final int MAX_UNCOMPRESSED_DATA_SIZE = 65536 + 4;\n\n private final Snappy snappy = new Snappy();\n private final boolean validateChecksums;\n\n private boolean started;\n private boolean corrupted;\n\n /**\n * Creates a new snappy-framed decoder with validation of checksums\n * turned OFF. To turn checksum validation on, please use the alternate\n * {@link #SnappyFrameDecoder(boolean)} constructor.\n */\n public SnappyFrameDecoder() {\n this(false);\n }\n\n /**\n * Creates a new snappy-framed decoder with validation of checksums\n * as specified.\n *\n * @param validateChecksums\n * If true, the checksum field will be validated against the actual\n * uncompressed data, and if the checksums do not match, a suitable\n * {@link DecompressionException} will be thrown\n */\n public SnappyFrameDecoder(boolean validateChecksums) {\n this.validateChecksums = validateChecksums;\n }\n\n @Override\n protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {\n if (corrupted) {\n in.skipBytes(in.readableBytes());\n return;\n }\n\n try {\n int idx = in.readerIndex();\n final int inSize = in.readableBytes();\n if (inSize < 4) {\n // We need to be at least able to read the chunk type identifier (one byte),\n // and the length of the chunk (3 bytes) in order to proceed\n return;\n }\n\n final int chunkTypeVal = in.getUnsignedByte(idx);\n final ChunkType chunkType = mapChunkType((byte) chunkTypeVal);\n final int chunkLength = in.getUnsignedMediumLE(idx + 1);\n\n switch (chunkType) {\n case STREAM_IDENTIFIER:\n if (chunkLength != SNAPPY_IDENTIFIER_LEN) {\n throw new DecompressionException(\"Unexpected length of stream identifier: \" + chunkLength);\n }\n\n if (inSize < 4 + SNAPPY_IDENTIFIER_LEN) {\n break;\n }\n\n in.skipBytes(4);\n int offset = in.readerIndex();\n in.skipBytes(SNAPPY_IDENTIFIER_LEN);\n\n checkByte(in.getByte(offset++), (byte) 's');\n checkByte(in.getByte(offset++), (byte) 'N');\n checkByte(in.getByte(offset++), (byte) 'a');\n checkByte(in.getByte(offset++), (byte) 'P');\n checkByte(in.getByte(offset++), (byte) 'p');\n checkByte(in.getByte(offset), (byte) 'Y');\n\n started = true;\n break;\n case RESERVED_SKIPPABLE:\n if (!started) {\n throw new DecompressionException(\"Received RESERVED_SKIPPABLE tag before STREAM_IDENTIFIER\");\n }\n\n if (inSize < 4 + chunkLength) {\n // TODO: Don't keep skippable bytes\n return;\n }\n\n in.skipBytes(4 + chunkLength);\n break;\n case RESERVED_UNSKIPPABLE:\n // The spec mandates that reserved unskippable chunks must immediately\n // return an error, as we must assume that we cannot decode the stream\n // correctly\n throw new DecompressionException(\n \"Found reserved unskippable chunk type: 0x\" + Integer.toHexString(chunkTypeVal));\n case UNCOMPRESSED_DATA:\n if (!started) {\n throw new DecompressionException(\"Received UNCOMPRESSED_DATA tag before STREAM_IDENTIFIER\");\n }\n if (chunkLength > MAX_UNCOMPRESSED_DATA_SIZE) {\n throw new DecompressionException(\"Received UNCOMPRESSED_DATA larger than 65540 bytes\");\n }\n\n if (inSize < 4 + chunkLength) {\n return;\n }\n\n in.skipBytes(4);\n if (validateChecksums) {\n int checksum = in.readIntLE();\n validateChecksum(checksum, in, in.readerIndex(), chunkLength - 4);\n } else {\n in.skipBytes(4);\n }\n out.add(in.readRetainedSlice(chunkLength - 4));\n break;\n case COMPRESSED_DATA:\n if (!started) {\n throw new DecompressionException(\"Received COMPRESSED_DATA tag before STREAM_IDENTIFIER\");\n }\n\n if (inSize < 4 + chunkLength) {\n return;\n }\n\n in.skipBytes(4);\n int checksum = in.readIntLE();\n ByteBuf uncompressed = ctx.alloc().buffer();\n try {\n if (validateChecksums) {\n int oldWriterIndex = in.writerIndex();\n try {\n in.writerIndex(in.readerIndex() + chunkLength - 4);\n snappy.decode(in, uncompressed);\n } finally {\n in.writerIndex(oldWriterIndex);\n }\n validateChecksum(checksum, uncompressed, 0, uncompressed.writerIndex());\n } else {\n snappy.decode(in.readSlice(chunkLength - 4), uncompressed);\n }\n out.add(uncompressed);\n uncompressed = null;\n } finally {\n if (uncompressed != null) {\n uncompressed.release();\n }\n }\n snappy.reset();\n break;\n }\n } catch (Exception e) {\n corrupted = true;\n throw e;\n }\n }\n\n private static void checkByte(byte actual, byte expect) {\n if (actual != expect) {\n throw new DecompressionException(\"Unexpected stream identifier contents. Mismatched snappy \" +\n \"protocol version?\");\n }\n }\n\n /**\n * Decodes the chunk type from the type tag byte.\n *\n * @param type The tag byte extracted from the stream\n * @return The appropriate {@link ChunkType}, defaulting to {@link ChunkType#RESERVED_UNSKIPPABLE}\n */\n private static ChunkType mapChunkType(byte type) {\n if (type == 0) {\n return ChunkType.COMPRESSED_DATA;\n } else if (type == 1) {\n return ChunkType.UNCOMPRESSED_DATA;\n } else if (type == (byte) 0xff) {\n return ChunkType.STREAM_IDENTIFIER;\n } else if ((type & 0x80) == 0x80) {\n return ChunkType.RESERVED_SKIPPABLE;\n } else {\n return ChunkType.RESERVED_UNSKIPPABLE;\n }\n }\n}\n\n", "input": "", "output": "【漏洞类型】缓冲区溢出漏洞\n【严重性】High\n【漏洞摘要】原始代码中的SnappyFrameDecoder和Snappy类存在缓冲区溢出漏洞。缓冲区溢出漏洞可以通过输入一个非常大的压缩数据块,导致解码过程中分配过多内存,最终引发OutOfMemoryError (OOME)。具体表现为在处理COMPRESSED_DATA和UNCOMPRESSED_DATA时,没有对数据块的大小进行严格的限制,导致内部缓冲区可能被不当扩展。此外,处理RESERVED_SKIPPABLE标签时,代码没有立即跳过数据块,而是缓存整个数据块,直到接收完整个数据块,这可能导致内存过度使用。\n【关键代码模式】\n- `decode`函数在处理`COMPRESSED_DATA`和`UNCOMPRESSED_DATA`时,没有对数据块大小进行限制。\n- `readPreamble`函数在读取数据块长度时,没有对长度值进行验证。\n- `mapChunkType`函数在映射chunk类型时,使用了未验证的用户输入。\n- `getPreamble`函数在获取preamble时,也没有对数据块长度进行验证。\n【修复方法说明】\n- 修复方法通过添加对压缩和解压缩数据块大小的限制,避免了内存过度使用的问题。\n- 引入了`numBytesToSkip`变量,用于在处理RESERVED_SKIPPABLE标签时立即跳过数据块,而不是缓存整个数据块。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/core.js\n### 代码内容:\nvar\n\t// A central reference to the root jQuery(document)\n\trootjQuery,\n\n\t// The deferred used on DOM ready\n\treadyList,\n\n\t// Use the correct document accordingly with window argument (sandbox)\n\tdocument = window.document,\n\tlocation = window.location,\n\n\t// Map over jQuery in case of overwrite\n\t_jQuery = window.jQuery,\n\n\t// Map over the $ in case of overwrite\n\t_$ = window.$,\n\n\t// [[Class]] -> type pairs\n\tclass2type = {},\n\n\t// List of deleted data cache ids, so we can reuse them\n\tcore_deletedIds = [],\n\n\tcore_version = \"@VERSION\",\n\n\t// Save a reference to some core methods\n\tcore_concat = core_deletedIds.concat,\n\tcore_push = core_deletedIds.push,\n\tcore_slice = core_deletedIds.slice,\n\tcore_indexOf = core_deletedIds.indexOf,\n\tcore_toString = class2type.toString,\n\tcore_hasOwn = class2type.hasOwnProperty,\n\tcore_trim = core_version.trim,\n\n\t// Define a local copy of jQuery\n\tjQuery = function( selector, context ) {\n\t\t// The jQuery object is actually just the init constructor 'enhanced'\n\t\treturn new jQuery.fn.init( selector, context, rootjQuery );\n\t},\n\n\t// Used for matching numbers\n\tcore_pnum = /[+-]?(?:\\d*\\.|)\\d+(?:[eE][+-]?\\d+|)/.source,\n\n\t// Used for splitting on whitespace\n\tcore_rnotwhite = /\\S+/g,\n\n\t// Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE)\n\trtrim = /^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g,\n\n\t// A simple way to check for HTML strings\n\t// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)\n\trquickExpr = /^(?:[^#<]*(<[\\w\\W]+>)[^>]*|#([\\w-]*))$/,\n\n\t// Match a standalone tag\n\trsingleTag = /^<(\\w+)\\s*\\/?>(?:<\\/\\1>|)$/,\n\n\t// JSON RegExp\n\trvalidchars = /^[\\],:{}\\s]*$/,\n\trvalidbraces = /(?:^|:|,)(?:\\s*\\[)+/g,\n\trvalidescape = /\\\\(?:[\"\\\\\\/bfnrt]|u[\\da-fA-F]{4})/g,\n\trvalidtokens = /\"[^\"\\\\\\r\\n]*\"|true|false|null|-?(?:\\d+\\.|)\\d+(?:[eE][+-]?\\d+|)/g,\n\n\t// Matches dashed string for camelizing\n\trmsPrefix = /^-ms-/,\n\trdashAlpha = /-([\\da-z])/gi,\n\n\t// Used by jQuery.camelCase as callback to replace()\n\tfcamelCase = function( all, letter ) {\n\t\treturn letter.toUpperCase();\n\t},\n\n\t// The ready event handler and self cleanup method\n\tDOMContentLoaded = function() {\n\t\tif ( document.addEventListener ) {\n\t\t\tdocument.removeEventListener( \"DOMContentLoaded\", DOMContentLoaded, false );\n\t\t\tjQuery.ready();\n\t\t} else if ( document.readyState === \"complete\" ) {\n\t\t\t// we're here because readyState === \"complete\" in oldIE\n\t\t\t// which is good enough for us to call the dom ready!\n\t\t\tdocument.detachEvent( \"onreadystatechange\", DOMContentLoaded );\n\t\t\tjQuery.ready();\n\t\t}\n\t};\n\njQuery.fn = jQuery.prototype = {\n\t// The current version of jQuery being used\n\tjquery: core_version,\n\n\tconstructor: jQuery,\n\tinit: function( selector, context, rootjQuery ) {\n\t\tvar match, elem;\n\n\t\t// HANDLE: $(\"\"), $(null), $(undefined), $(false)\n\t\tif ( !selector ) {\n\t\t\treturn this;\n\t\t}\n\n\t\t// HANDLE: $(DOMElement)\n\t\tif ( selector.nodeType ) {\n\t\t\tthis.context = this[0] = selector;\n\t\t\tthis.length = 1;\n\t\t\treturn this;\n\t\t}\n\n\t\t// Handle HTML strings\n\t\tif ( typeof selector === \"string\" ) {\n\t\t\tif ( selector.charAt(0) === \"<\" && selector.charAt( selector.length - 1 ) === \">\" && selector.length >= 3 ) {\n\t\t\t\t// Assume that strings that start and end with <> are HTML and skip the regex check\n\t\t\t\tmatch = [ null, selector, null ];\n\n\t\t\t} else {\n\t\t\t\tmatch = rquickExpr.exec( selector );\n\t\t\t}\n\n\t\t\t// Match html or make sure no context is specified for #id\n\t\t\tif ( match && (match[1] || !context) ) {\n\n\t\t\t\t// HANDLE: $(html) -> $(array)\n\t\t\t\tif ( match[1] ) {\n\t\t\t\t\tcontext = context instanceof jQuery ? context[0] : context;\n\n\t\t\t\t\t// scripts is true for back-compat\n\t\t\t\t\tjQuery.merge( this, jQuery.parseHTML(\n\t\t\t\t\t\tmatch[1],\n\t\t\t\t\t\tcontext && context.nodeType ? context.ownerDocument || context : document,\n\t\t\t\t\t\ttrue\n\t\t\t\t\t) );\n\n\t\t\t\t\t// HANDLE: $(html, props)\n\t\t\t\t\tif ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {\n\t\t\t\t\t\tfor ( match in context ) {\n\t\t\t\t\t\t\t// Properties of context are called as methods if possible\n\t\t\t\t\t\t\tif ( jQuery.isFunction( this[ match ] ) ) {\n\t\t\t\t\t\t\t\tthis[ match ]( context[ match ] );\n\n\t\t\t\t\t\t\t// ...and otherwise set as attributes\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthis.attr( match, context[ match ] );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn this;\n\n\t\t\t\t// HANDLE: $(#id)\n\t\t\t\t} else {\n\t\t\t\t\telem = document.getElementById( match[2] );\n\n\t\t\t\t\t// Check parentNode to catch when Blackberry 4.6 returns\n\t\t\t\t\t// nodes that are no longer in the document #6963\n\t\t\t\t\tif ( elem && elem.parentNode ) {\n\t\t\t\t\t\t// Handle the case where IE and Opera return items\n\t\t\t\t\t\t// by name instead of ID\n\t\t\t\t\t\tif ( elem.id !== match[2] ) {\n\t\t\t\t\t\t\treturn rootjQuery.find( selector );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Otherwise, we inject the element directly into the jQuery object\n\t\t\t\t\t\tthis.length = 1;\n\t\t\t\t\t\tthis[0] = elem;\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.context = document;\n\t\t\t\t\tthis.selector = selector;\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\n\t\t\t// HANDLE: $(expr, $(...))\n\t\t\t} else if ( !context || context.jquery ) {\n\t\t\t\treturn ( context || rootjQuery ).find( selector );\n\n\t\t\t// HANDLE: $(expr, context)\n\t\t\t// (which is just equivalent to: $(context).find(expr)\n\t\t\t} else {\n\t\t\t\treturn this.constructor( context ).find( selector );\n\t\t\t}\n\n\t\t// HANDLE: $(function)\n\t\t// Shortcut for document ready\n\t\t} else if ( jQuery.isFunction( selector ) ) {\n\t\t\treturn rootjQuery.ready( selector );\n\t\t}\n\n\t\tif ( selector.selector !== undefined ) {\n\t\t\tthis.selector = selector.selector;\n\t\t\tthis.context = selector.context;\n\t\t}\n\n\t\treturn jQuery.makeArray( selector, this );\n\t},\n\n\t// Start with an empty selector\n\tselector: \"\",\n\n\t// The default length of a jQuery object is 0\n\tlength: 0,\n\n\t// The number of elements contained in the matched element set\n\tsize: function() {\n\t\treturn this.length;\n\t},\n\n\ttoArray: function() {\n\t\treturn core_slice.call( this );\n\t},\n\n\t// Get the Nth element in the matched element set OR\n\t// Get the whole matched element set as a clean array\n\tget: function( num ) {\n\t\treturn num == null ?\n\n\t\t\t// Return a 'clean' array\n\t\t\tthis.toArray() :\n\n\t\t\t// Return just the object\n\t\t\t( num < 0 ? this[ this.length + num ] : this[ num ] );\n\t},\n\n\t// Take an array of elements and push it onto the stack\n\t// (returning the new matched element set)\n\tpushStack: function( elems ) {\n\n\t\t// Build a new jQuery matched element set\n\t\tvar ret = jQuery.merge( this.constructor(), elems );\n\n\t\t// Add the old object onto the stack (as a reference)\n\t\tret.prevObject = this;\n\t\tret.context = this.context;\n\n\t\t// Return the newly-formed element set\n\t\treturn ret;\n\t},\n\n\t// Execute a callback for every element in the matched set.\n\t// (You can seed the arguments with an array of args, but this is\n\t// only used internally.)\n\teach: function( callback, args ) {\n\t\treturn jQuery.each( this, callback, args );\n\t},\n\n\tready: function( fn ) {\n\t\t// Add the callback\n\t\tjQuery.ready.promise().done( fn );\n\n\t\treturn this;\n\t},\n\n\tslice: function() {\n\t\treturn this.pushStack( core_slice.apply( this, arguments ) );\n\t},\n\n\tfirst: function() {\n\t\treturn this.eq( 0 );\n\t},\n\n\tlast: function() {\n\t\treturn this.eq( -1 );\n\t},\n\n\teq: function( i ) {\n\t\tvar len = this.length,\n\t\t\tj = +i + ( i < 0 ? len : 0 );\n\t\treturn this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );\n\t},\n\n\tmap: function( callback ) {\n\t\treturn this.pushStack( jQuery.map(this, function( elem, i ) {\n\t\t\treturn callback.call( elem, i, elem );\n\t\t}));\n\t},\n\n\tend: function() {\n\t\treturn this.prevObject || this.constructor(null);\n\t},\n\n\t// For internal use only.\n\t// Behaves like an Array's method, not like a jQuery method.\n\tpush: core_push,\n\tsort: [].sort,\n\tsplice: [].splice\n};\n\n// Give the init function the jQuery prototype for later instantiation\njQuery.fn.init.prototype = jQuery.fn;\n\njQuery.extend = jQuery.fn.extend = function() {\n\tvar options, name, src, copy, copyIsArray, clone,\n\t\ttarget = arguments[0] || {},\n\t\ti = 1,\n\t\tlength = arguments.length,\n\t\tdeep = false;\n\n\t// Handle a deep copy situation\n\tif ( typeof target === \"boolean\" ) {\n\t\tdeep = target;\n\t\ttarget = arguments[1] || {};\n\t\t// skip the boolean and the target\n\t\ti = 2;\n\t}\n\n\t// Handle case when target is a string or something (possible in deep copy)\n\tif ( typeof target !== \"object\" && !jQuery.isFunction(target) ) {\n\t\ttarget = {};\n\t}\n\n\t// extend jQuery itself if only one argument is passed\n\tif ( length === i ) {\n\t\ttarget = this;\n\t\t--i;\n\t}\n\n\tfor ( ; i < length; i++ ) {\n\t\t// Only deal with non-null/undefined values\n\t\tif ( (options = arguments[ i ]) != null ) {\n\t\t\t// Extend the base object\n\t\t\tfor ( name in options ) {\n\t\t\t\tsrc = target[ name ];\n\t\t\t\tcopy = options[ name ];\n\n\t\t\t\t// Prevent never-ending loop\n\t\t\t\tif ( target === copy ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Recurse if we're merging plain objects or arrays\n\t\t\t\tif ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {\n\t\t\t\t\tif ( copyIsArray ) {\n\t\t\t\t\t\tcopyIsArray = false;\n\t\t\t\t\t\tclone = src && jQuery.isArray(src) ? src : [];\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclone = src && jQuery.isPlainObject(src) ? src : {};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Never move original objects, clone them\n\t\t\t\t\ttarget[ name ] = jQuery.extend( deep, clone, copy );\n\n\t\t\t\t// Don't bring in undefined values\n\t\t\t\t} else if ( copy !== undefined ) {\n\t\t\t\t\ttarget[ name ] = copy;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Return the modified object\n\treturn target;\n};\n\njQuery.extend({\n\tnoConflict: function( deep ) {\n\t\tif ( window.$ === jQuery ) {\n\t\t\twindow.$ = _$;\n\t\t}\n\n\t\tif ( deep && window.jQuery === jQuery ) {\n\t\t\twindow.jQuery = _jQuery;\n\t\t}\n\n\t\treturn jQuery;\n\t},\n\n\t// Is the DOM ready to be used? Set to true once it occurs.\n\tisReady: false,\n\n\t// A counter to track how many items to wait for before\n\t// the ready event fires. See #6781\n\treadyWait: 1,\n\n\t// Hold (or release) the ready event\n\tholdReady: function( hold ) {\n\t\tif ( hold ) {\n\t\t\tjQuery.readyWait++;\n\t\t} else {\n\t\t\tjQuery.ready( true );\n\t\t}\n\t},\n\n\t// Handle when the DOM is ready\n\tready: function( wait ) {\n\n\t\t// Abort if there are pending holds or we're already ready\n\t\tif ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).\n\t\tif ( !document.body ) {\n\t\t\treturn setTimeout( jQuery.ready );\n\t\t}\n\n\t\t// Remember that the DOM is ready\n\t\tjQuery.isReady = true;\n\n\t\t// If a normal DOM Ready event fired, decrement, and wait if need be\n\t\tif ( wait !== true && --jQuery.readyWait > 0 ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// If there are functions bound, to execute\n\t\treadyList.resolveWith( document, [ jQuery ] );\n\n\t\t// Trigger any bound ready events\n\t\tif ( jQuery.fn.trigger ) {\n\t\t\tjQuery( document ).trigger(\"ready\").off(\"ready\");\n\t\t}\n\t},\n\n\t// See test/unit/core.js for details concerning isFunction.\n\t// Since version 1.3, DOM methods and functions like alert\n\t// aren't supported. They return false on IE (#2968).\n\tisFunction: function( obj ) {\n\t\treturn jQuery.type(obj) === \"function\";\n\t},\n\n\tisArray: Array.isArray || function( obj ) {\n\t\treturn jQuery.type(obj) === \"array\";\n\t},\n\n\tisWindow: function( obj ) {\n\t\treturn obj != null && obj == obj.window;\n\t},\n\n\tisNumeric: function( obj ) {\n\t\treturn !isNaN( parseFloat(obj) ) && isFinite( obj );\n\t},\n\n\ttype: function( obj ) {\n\t\treturn obj == null ?\n\t\t\tString( obj ) :\n\t\t\tclass2type[ core_toString.call(obj) ] || \"object\";\n\t},\n\n\tisPlainObject: function( obj ) {\n\t\t// Must be an Object.\n\t\t// Because of IE, we also have to check the presence of the constructor property.\n\t\t// Make sure that DOM nodes and window objects don't pass through, as well\n\t\tif ( !obj || jQuery.type(obj) !== \"object\" || obj.nodeType || jQuery.isWindow( obj ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\t// Not own constructor property must be Object\n\t\t\tif ( obj.constructor &&\n\t\t\t\t!core_hasOwn.call(obj, \"constructor\") &&\n\t\t\t\t!core_hasOwn.call(obj.constructor.prototype, \"isPrototypeOf\") ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} catch ( e ) {\n\t\t\t// IE8,9 Will throw exceptions on certain host objects #9897\n\t\t\treturn false;\n\t\t}\n\n\t\t// Own properties are enumerated firstly, so to speed up,\n\t\t// if last one is own, then all properties are own.\n\n\t\tvar key;\n\t\tfor ( key in obj ) {}\n\n\t\treturn key === undefined || core_hasOwn.call( obj, key );\n\t},\n\n\tisEmptyObject: function( obj ) {\n\t\tvar name;\n\t\tfor ( name in obj ) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t},\n\n\terror: function( msg ) {\n\t\tthrow new Error( msg );\n\t},\n\n\t// data: string of html\n\t// context (optional): If specified, the fragment will be created in this context, defaults to document\n\t// keepScripts (optional): If true, will include scripts passed in the html string\n\tparseHTML: function( data, context, keepScripts ) {\n\t\tif ( !data || typeof data !== \"string\" ) {\n\t\t\treturn null;\n\t\t}\n\t\tif ( typeof context === \"boolean\" ) {\n\t\t\tkeepScripts = context;\n\t\t\tcontext = false;\n\t\t}\n\t\tcontext = context || document;\n\n\t\tvar parsed = rsingleTag.exec( data ),\n\t\t\tscripts = !keepScripts && [];\n\n\t\t// Single tag\n\t\tif ( parsed ) {\n\t\t\treturn [ context.createElement( parsed[1] ) ];\n\t\t}\n\n\t\tparsed = context.createDocumentFragment();\n\t\tjQuery.clean( [ data ], context, parsed, scripts );\n\t\tif ( scripts ) {\n\t\t\tjQuery( scripts ).remove();\n\t\t}\n\t\treturn jQuery.merge( [], parsed.childNodes );\n\t},\n\n\tparseJSON: function( data ) {\n\t\t// Attempt to parse using the native JSON parser first\n\t\tif ( window.JSON && window.JSON.parse ) {\n\t\t\treturn window.JSON.parse( data );\n\t\t}\n\n\t\tif ( data === null ) {\n\t\t\treturn data;\n\t\t}\n\n\t\tif ( typeof data === \"string\" ) {\n\n\t\t\t// Make sure leading/trailing whitespace is removed (IE can't handle it)\n\t\t\tdata = jQuery.trim( data );\n\n\t\t\tif ( data ) {\n\t\t\t\t// Make sure the incoming data is actual JSON\n\t\t\t\t// Logic borrowed from http://json.org/json2.js\n\t\t\t\tif ( rvalidchars.test( data.replace( rvalidescape, \"@\" )\n\t\t\t\t\t.replace( rvalidtokens, \"]\" )\n\t\t\t\t\t.replace( rvalidbraces, \"\")) ) {\n\n\t\t\t\t\treturn ( new Function( \"return \" + data ) )();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tjQuery.error( \"Invalid JSON: \" + data );\n\t},\n\n\t// Cross-browser xml parsing\n\tparseXML: function( data ) {\n\t\tvar xml, tmp;\n\t\tif ( !data || typeof data !== \"string\" ) {\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\t\t\tif ( window.DOMParser ) { // Standard\n\t\t\t\ttmp = new DOMParser();\n\t\t\t\txml = tmp.parseFromString( data , \"text/xml\" );\n\t\t\t} else { // IE\n\t\t\t\txml = new ActiveXObject( \"Microsoft.XMLDOM\" );\n\t\t\t\txml.async = \"false\";\n\t\t\t\txml.loadXML( data );\n\t\t\t}\n\t\t} catch( e ) {\n\t\t\txml = undefined;\n\t\t}\n\t\tif ( !xml || !xml.documentElement || xml.getElementsByTagName( \"parsererror\" ).length ) {\n\t\t\tjQuery.error( \"Invalid XML: \" + data );\n\t\t}\n\t\treturn xml;\n\t},\n\n\tnoop: function() {},\n\n\t// Evaluates a script in a global context\n\t// Workarounds based on findings by Jim Driscoll\n\t// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context\n\tglobalEval: function( data ) {\n\t\tif ( data && jQuery.trim( data ) ) {\n\t\t\t// We use execScript on Internet Explorer\n\t\t\t// We use an anonymous function so that context is window\n\t\t\t// rather than jQuery in Firefox\n\t\t\t( window.execScript || function( data ) {\n\t\t\t\twindow[ \"eval\" ].call( window, data );\n\t\t\t} )( data );\n\t\t}\n\t},\n\n\t// Convert dashed to camelCase; used by the css and data modules\n\t// Microsoft forgot to hump their vendor prefix (#9572)\n\tcamelCase: function( string ) {\n\t\treturn string.replace( rmsPrefix, \"ms-\" ).replace( rdashAlpha, fcamelCase );\n\t},\n\n\tnodeName: function( elem, name ) {\n\t\treturn elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();\n\t},\n\n\t// args is for internal usage only\n\teach: function( obj, callback, args ) {\n\t\tvar value,\n\t\t\ti = 0,\n\t\t\tlength = obj.length,\n\t\t\tisArray = isArraylike( obj );\n\n\t\tif ( args ) {\n\t\t\tif ( isArray ) {\n\t\t\t\tfor ( ; i < length; i++ ) {\n\t\t\t\t\tvalue = callback.apply( obj[ i ], args );\n\n\t\t\t\t\tif ( value === false ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor ( i in obj ) {\n\t\t\t\t\tvalue = callback.apply( obj[ i ], args );\n\n\t\t\t\t\tif ( value === false ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t// A special, fast, case for the most common use of each\n\t\t} else {\n\t\t\tif ( isArray ) {\n\t\t\t\tfor ( ; i < length; i++ ) {\n\t\t\t\t\tvalue = callback.call( obj[ i ], i, obj[ i ] );\n\n\t\t\t\t\tif ( value === false ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor ( i in obj ) {\n\t\t\t\t\tvalue = callback.call( obj[ i ], i, obj[ i ] );\n\n\t\t\t\t\tif ( value === false ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn obj;\n\t},\n\n\t// Use native String.trim function wherever possible\n\ttrim: core_trim && !core_trim.call(\"\\uFEFF\\xA0\") ?\n\t\tfunction( text ) {\n\t\t\treturn text == null ?\n\t\t\t\t\"\" :\n\t\t\t\tcore_trim.call( text );\n\t\t} :\n\n\t\t// Otherwise use our own trimming functionality\n\t\tfunction( text ) {\n\t\t\treturn text == null ?\n\t\t\t\t\"\" :\n\t\t\t\t( text + \"\" ).replace( rtrim, \"\" );\n\t\t},\n\n\t// results is for internal usage only\n\tmakeArray: function( arr, results ) {\n\t\tvar ret = results || [];\n\n\t\tif ( arr != null ) {\n\t\t\tif ( isArraylike( Object(arr) ) ) {\n\t\t\t\tjQuery.merge( ret,\n\t\t\t\t\ttypeof arr === \"string\" ?\n\t\t\t\t\t[ arr ] : arr\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tcore_push.call( ret, arr );\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t},\n\n\tinArray: function( elem, arr, i ) {\n\t\tvar len;\n\n\t\tif ( arr ) {\n\t\t\tif ( core_indexOf ) {\n\t\t\t\treturn core_indexOf.call( arr, elem, i );\n\t\t\t}\n\n\t\t\tlen = arr.length;\n\t\t\ti = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;\n\n\t\t\tfor ( ; i < len; i++ ) {\n\t\t\t\t// Skip accessing in sparse arrays\n\t\t\t\tif ( i in arr && arr[ i ] === elem ) {\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn -1;\n\t},\n\n\tmerge: function( first, second ) {\n\t\tvar l = second.length,\n\t\t\ti = first.length,\n\t\t\tj = 0;\n\n\t\tif ( typeof l === \"number\" ) {\n\t\t\tfor ( ; j < l; j++ ) {\n\t\t\t\tfirst[ i++ ] = second[ j ];\n\t\t\t}\n\t\t} else {\n\t\t\twhile ( second[j] !== undefined ) {\n\t\t\t\tfirst[ i++ ] = second[ j++ ];\n\t\t\t}\n\t\t}\n\n\t\tfirst.length = i;\n\n\t\treturn first;\n\t},\n\n\tgrep: function( elems, callback, inv ) {\n\t\tvar retVal,\n\t\t\tret = [],\n\t\t\ti = 0,\n\t\t\tlength = elems.length;\n\t\tinv = !!inv;\n\n\t\t// Go through the array, only saving the items\n\t\t// that pass the validator function\n\t\tfor ( ; i < length; i++ ) {\n\t\t\tretVal = !!callback( elems[ i ], i );\n\t\t\tif ( inv !== retVal ) {\n\t\t\t\tret.push( elems[ i ] );\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t},\n\n\t// arg is for internal usage only\n\tmap: function( elems, callback, arg ) {\n\t\tvar value,\n\t\t\ti = 0,\n\t\t\tlength = elems.length,\n\t\t\tisArray = isArraylike( elems ),\n\t\t\tret = [];\n\n\t\t// Go through the array, translating each of the items to their\n\t\tif ( isArray ) {\n\t\t\tfor ( ; i < length; i++ ) {\n\t\t\t\tvalue = callback( elems[ i ], i, arg );\n\n\t\t\t\tif ( value != null ) {\n\t\t\t\t\tret[ ret.length ] = value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t// Go through every key on the object,\n\t\t} else {\n\t\t\tfor ( i in elems ) {\n\t\t\t\tvalue = callback( elems[ i ], i, arg );\n\n\t\t\t\tif ( value != null ) {\n\t\t\t\t\tret[ ret.length ] = value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Flatten any nested arrays\n\t\treturn core_concat.apply( [], ret );\n\t},\n\n\t// A global GUID counter for objects\n\tguid: 1,\n\n\t// Bind a function to a context, optionally partially applying any\n\t// arguments.\n\tproxy: function( fn, context ) {\n\t\tvar tmp, args, proxy;\n\n\t\tif ( typeof context === \"string\" ) {\n\t\t\ttmp = fn[ context ];\n\t\t\tcontext = fn;\n\t\t\tfn = tmp;\n\t\t}\n\n\t\t// Quick check to determine if target is callable, in the spec\n\t\t// this throws a TypeError, but we will just return undefined.\n\t\tif ( !jQuery.isFunction( fn ) ) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// Simulated bind\n\t\targs = core_slice.call( arguments, 2 );\n\t\tproxy = function() {\n\t\t\treturn fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );\n\t\t};\n\n\t\t// Set the guid of unique handler to the same of original handler, so it can be removed\n\t\tproxy.guid = fn.guid = fn.guid || jQuery.guid++;\n\n\t\treturn proxy;\n\t},\n\n\t// Multifunctional method to get and set values of a collection\n\t// The value/s can optionally be executed if it's a function\n\taccess: function( elems, fn, key, value, chainable, emptyGet, raw ) {\n\t\tvar i = 0,\n\t\t\tlength = elems.length,\n\t\t\tbulk = key == null;\n\n\t\t// Sets many values\n\t\tif ( jQuery.type( key ) === \"object\" ) {\n\t\t\tchainable = true;\n\t\t\tfor ( i in key ) {\n\t\t\t\tjQuery.access( elems, fn, i, key[i], true, emptyGet, raw );\n\t\t\t}\n\n\t\t// Sets one value\n\t\t} else if ( value !== undefined ) {\n\t\t\tchainable = true;\n\n\t\t\tif ( !jQuery.isFunction( value ) ) {\n\t\t\t\traw = true;\n\t\t\t}\n\n\t\t\tif ( bulk ) {\n\t\t\t\t// Bulk operations run against the entire set\n\t\t\t\tif ( raw ) {\n\t\t\t\t\tfn.call( elems, value );\n\t\t\t\t\tfn = null;\n\n\t\t\t\t// ...except when executing function values\n\t\t\t\t} else {\n\t\t\t\t\tbulk = fn;\n\t\t\t\t\tfn = function( elem, key, value ) {\n\t\t\t\t\t\treturn bulk.call( jQuery( elem ), value );\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( fn ) {\n\t\t\t\tfor ( ; i < length; i++ ) {\n\t\t\t\t\tfn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn chainable ?\n\t\t\telems :\n\n\t\t\t// Gets\n\t\t\tbulk ?\n\t\t\t\tfn.call( elems ) :\n\t\t\t\tlength ? fn( elems[0], key ) : emptyGet;\n\t},\n\n\tnow: function() {\n\t\treturn ( new Date() ).getTime();\n\t}\n});\n\njQuery.ready.promise = function( obj ) {\n\tif ( !readyList ) {\n\n\t\treadyList = jQuery.Deferred();\n\n\t\t// Catch cases where $(document).ready() is called after the browser event has already occurred.\n\t\t// we once tried to use readyState \"interactive\" here, but it caused issues like the one\n\t\t// discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15\n\t\tif ( document.readyState === \"complete\" ) {\n\t\t\t// Handle it asynchronously to allow scripts the opportunity to delay ready\n\t\t\tsetTimeout( jQuery.ready );\n\n\t\t// Standards-based browsers support DOMContentLoaded\n\t\t} else if ( document.addEventListener ) {\n\t\t\t// Use the handy event callback\n\t\t\tdocument.addEventListener( \"DOMContentLoaded\", DOMContentLoaded, false );\n\n\t\t\t// A fallback to window.onload, that will always work\n\t\t\twindow.addEventListener( \"load\", jQuery.ready, false );\n\n\t\t// If IE event model is used\n\t\t} else {\n\t\t\t// Ensure firing before onload, maybe late but safe also for iframes\n\t\t\tdocument.attachEvent( \"onreadystatechange\", DOMContentLoaded );\n\n\t\t\t// A fallback to window.onload, that will always work\n\t\t\twindow.attachEvent( \"onload\", jQuery.ready );\n\n\t\t\t// If IE and not a frame\n\t\t\t// continually check to see if the document is ready\n\t\t\tvar top = false;\n\n\t\t\ttry {\n\t\t\t\ttop = window.frameElement == null && document.documentElement;\n\t\t\t} catch(e) {}\n\n\t\t\tif ( top && top.doScroll ) {\n\t\t\t\t(function doScrollCheck() {\n\t\t\t\t\tif ( !jQuery.isReady ) {\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// Use the trick by Diego Perini\n\t\t\t\t\t\t\t// http://javascript.nwbox.com/IEContentLoaded/\n\t\t\t\t\t\t\ttop.doScroll(\"left\");\n\t\t\t\t\t\t} catch(e) {\n\t\t\t\t\t\t\treturn setTimeout( doScrollCheck, 50 );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// and execute any waiting functions\n\t\t\t\t\t\tjQuery.ready();\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t}\n\t\t}\n\t}\n\treturn readyList.promise( obj );\n};\n\n// Populate the class2type map\njQuery.each(\"Boolean Number String Function Array Date RegExp Object Error\".split(\" \"), function(i, name) {\n\tclass2type[ \"[object \" + name + \"]\" ] = name.toLowerCase();\n});\n\nfunction isArraylike( obj ) {\n\tvar length = obj.length,\n\t\ttype = jQuery.type( obj );\n\n\tif ( jQuery.isWindow( obj ) ) {\n\t\treturn false;\n\t}\n\n\tif ( obj.nodeType === 1 && length ) {\n\t\treturn true;\n\t}\n\n\treturn type === \"array\" || type !== \"function\" &&\n\t\t( length === 0 ||\n\t\ttypeof length === \"number\" && length > 0 && ( length - 1 ) in obj );\n}\n\n// All jQuery objects should point back to these\nrootjQuery = jQuery(document);\n\n", "input": "", "output": "【漏洞类型】HTML注入漏洞\n\n【严重性】高\n\n【漏洞摘要】原始代码中的`rquickExpr`正则表达式用于匹配HTML字符串,但其对HTML字符串的检测过于宽松。具体来说,漏洞出现在以下代码行:\n```javascript\nrquickExpr = /^(?:[^#<]*(<[\\w\\W]+>)[^>]*|#([\\w-]*))$/;\n```\n该正则表达式允许匹配任何不以`#`或`<`开头的字符串,只要在字符串中包含`<`和`>`即可被认为是HTML字符串。这可能导致XSS漏洞,因为攻击者可以构造特定的字符串,使其在不以`<`开头的情况下仍然包含HTML标签。\n\n根本原因在于正则表达式没有对HTML字符串的开始部分进行严格验证。`location.hash`中的内容可能会被解析为HTML,从而导致XSS攻击。\n\n【关键代码模式】\n- `rquickExpr`正则表达式\n- `jQuery.parseHTML`函数\n- `location.hash`的使用\n- 未验证的用户输入\n\n【修复方法说明】\n修复方法通过调整正则表达式,使其严格要求HTML字符串必须以`<`开头:\n```javascript\nrquickExpr = /^(?:(<[\\w\\W]+>)[^>]*|#([\\w-]*))$/;\n```\n这样可以防止攻击者通过构造特定的字符串来绕过HTML检测,从而减少XSS攻击的风险。\n\n最佳实践或安全编码技术包括使用更严格的正则表达式来验证用户输入,以及对潜在的HTML内容进行适当的转义或过滤。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nCHANGELOG.md\n### 代码内容:\n# CHANGELOG\n\nInspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)\n\n## [Unreleased]\n### Added\n- Add fix for auto expand replica validation ([#4994](https://github.com/opensearch-project/OpenSearch/pull/4994))\n- Add support for s390x architecture ([#4001](https://github.com/opensearch-project/OpenSearch/pull/4001))\n- Github workflow for changelog verification ([#4085](https://github.com/opensearch-project/OpenSearch/pull/4085))\n- Point in time rest layer changes for create and delete PIT API ([#4064](https://github.com/opensearch-project/OpenSearch/pull/4064))\n- Point in time rest layer changes for list PIT and PIT segments API ([#4388](https://github.com/opensearch-project/OpenSearch/pull/4388))\n- Added @dreamer-89 as an Opensearch maintainer ([#4342](https://github.com/opensearch-project/OpenSearch/pull/4342))\n- Added release notes for 1.3.5 ([#4343](https://github.com/opensearch-project/OpenSearch/pull/4343))\n- Added release notes for 2.2.1 ([#4344](https://github.com/opensearch-project/OpenSearch/pull/4344))\n- Label configuration for dependabot PRs ([#4348](https://github.com/opensearch-project/OpenSearch/pull/4348))\n- Support for HTTP/2 (server-side) ([#3847](https://github.com/opensearch-project/OpenSearch/pull/3847))\n- BWC version 2.2.2 ([#4383](https://github.com/opensearch-project/OpenSearch/pull/4383))\n- Support for labels on version bump PRs, skip label support for changelog verifier ([#4391](https://github.com/opensearch-project/OpenSearch/pull/4391))\n- Update previous release bwc version to 2.4.0 ([#4455](https://github.com/opensearch-project/OpenSearch/pull/4455))\n- 2.3.0 release notes ([#4457](https://github.com/opensearch-project/OpenSearch/pull/4457))\n- Added missing javadocs for `:distribution:tools` modules ([#4483](https://github.com/opensearch-project/OpenSearch/pull/4483))\n- Add BWC version 2.3.1 ([#4513](https://github.com/opensearch-project/OpenSearch/pull/4513))\n- [Segment Replication] Add snapshot and restore tests for segment replication feature ([#3993](https://github.com/opensearch-project/OpenSearch/pull/3993))\n- Added missing javadocs for `:example-plugins` modules ([#4540](https://github.com/opensearch-project/OpenSearch/pull/4540))\n- Add missing Javadoc tag descriptions in unit tests ([#4629](https://github.com/opensearch-project/OpenSearch/pull/4629))\n- Add getter for path field in NestedQueryBuilder ([#4636](https://github.com/opensearch-project/OpenSearch/pull/4636))\n- Added precommit support for windows ([#4676](https://github.com/opensearch-project/OpenSearch/pull/4676))\n- Added release notes for 1.3.6 ([#4681](https://github.com/opensearch-project/OpenSearch/pull/4681))\n- Added precommit support for MacOS ([#4682](https://github.com/opensearch-project/OpenSearch/pull/4682))\n- Recommission API changes for service layer ([#4320](https://github.com/opensearch-project/OpenSearch/pull/4320))\n- Update GeoGrid base class access modifier to support extensibility ([#4572](https://github.com/opensearch-project/OpenSearch/pull/4572))\n- Add a new node role 'search' which is dedicated to provide search capability ([#4689](https://github.com/opensearch-project/OpenSearch/pull/4689))\n- Introduce experimental searchable snapshot API ([#4680](https://github.com/opensearch-project/OpenSearch/pull/4680))\n- Implement Searchable Snapshot using OnDemandBlockIndexInput phase 1 ([#4892](https://github.com/opensearch-project/OpenSearch/pull/4892))\n- Recommissioning of zone. REST layer support. ([#4624](https://github.com/opensearch-project/OpenSearch/pull/4604))\n- Make searchable snapshot indexes read-only but allow deletion ([#4764](https://github.com/opensearch-project/OpenSearch/pull/4764))\n- Added in-flight cancellation of SearchShardTask based on resource consumption ([#4565](https://github.com/opensearch-project/OpenSearch/pull/4565))\n- Apply reproducible builds configuration for OpenSearch plugins through gradle plugin ([#4746](https://github.com/opensearch-project/OpenSearch/pull/4746))\n- Add icon for IntelliJidea toolbox ([#4882](https://github.com/opensearch-project/OpenSearch/pull/4882))\n- Add groupId value propagation tests for ZIP publication task ([#4772](https://github.com/opensearch-project/OpenSearch/pull/4772))\n- Add support for GeoJson Point type in GeoPoint field ([#4597](https://github.com/opensearch-project/OpenSearch/pull/4597))\n- Add dev guide for dealing with flaky tests ([4868](https://github.com/opensearch-project/OpenSearch/pull/4868))\n- Update pull request template ([#4851](https://github.com/opensearch-project/OpenSearch/pull/4851))\n- Added missing no-jdk distributions ([#4722](https://github.com/opensearch-project/OpenSearch/pull/4722))\n- Add dev help in gradle check CI failures ([4872](https://github.com/opensearch-project/OpenSearch/pull/4872))\n- Copy `build.sh` over from opensearch-build ([#4887](https://github.com/opensearch-project/OpenSearch/pull/4887))\n- Add project health badges to the README.md ([#4843](https://github.com/opensearch-project/OpenSearch/pull/4843))\n- Added changes for graceful node decommission ([#4586](https://github.com/opensearch-project/OpenSearch/pull/4586))\n- Build no-jdk distributions as part of release build ([#4902](https://github.com/opensearch-project/OpenSearch/pull/4902))\n- Added resource usage trackers for in-flight cancellation of SearchShardTask ([#4805](https://github.com/opensearch-project/OpenSearch/pull/4805))\n- Renamed flaky tests ([#4912](https://github.com/opensearch-project/OpenSearch/pull/4912))\n- Update previous release bwc version to 2.5.0 ([#5003](https://github.com/opensearch-project/OpenSearch/pull/5003))\n- Use getParameterCount instead of getParameterTypes ([#4821](https://github.com/opensearch-project/OpenSearch/pull/4821))\n- Remote shard balancer support for searchable snapshots ([#4870](https://github.com/opensearch-project/OpenSearch/pull/4870))\n- Added search backpressure stats API ([#4932](https://github.com/opensearch-project/OpenSearch/pull/4932))\n- [Test] Add IAE test for deprecated edgeNGram analyzer name ([#5040](https://github.com/opensearch-project/OpenSearch/pull/5040))\n- Add Cluster manager task throttling framework. Cluster manager node will throttle task submission based on throttling thresholds.\n This throttling will be at task type level. Data nodes will perform retries on these throttling exception with exponential delay. (PR: [#4986](https://github.com/opensearch-project/OpenSearch/pull/4986)) ( Issue : [#479](https://github.com/opensearch-project/OpenSearch/issues/479))\n - Throttling Exception / New Backoff policy([#3527](https://github.com/opensearch-project/OpenSearch/pull/3527))\n - Cluster Manager node side change([#3882](https://github.com/opensearch-project/OpenSearch/pull/3882))\n - Data node side change([#4204](https://github.com/opensearch-project/OpenSearch/pull/4204))\n - on-boarding of tasks([#4542](https://github.com/opensearch-project/OpenSearch/pull/4542))\n - Integs ([4588](https://github.com/opensearch-project/OpenSearch/pull/4588))\n- Integration tests for searchable snapshots ([#5048](https://github.com/opensearch-project/OpenSearch/pull/5048))\n### Dependencies\n- Bumps `log4j-core` from 2.18.0 to 2.19.0\n- Bumps `reactor-netty-http` from 1.0.18 to 1.0.23\n- Bumps `jettison` from 1.5.0 to 1.5.1\n- Bumps `azure-storage-common` from 12.18.0 to 12.18.1\n- Bumps `forbiddenapis` from 3.3 to 3.4\n- Bumps `gson` from 2.9.0 to 2.9.1\n- Bumps `protobuf-java` from 3.21.2 to 3.21.7k\n- Bumps `azure-core` from 1.31.0 to 1.33.0\n- Bumps `avro` from 1.11.0 to 1.11.1\n- Bumps `woodstox-core` from 6.3.0 to 6.3.1\n- Bumps `xmlbeans` from 5.1.0 to 5.1.1 ([#4354](https://github.com/opensearch-project/OpenSearch/pull/4354))\n- Bumps `azure-core-http-netty` from 1.12.0 to 1.12.4 ([#4160](https://github.com/opensearch-project/OpenSearch/pull/4160))\n- Bumps `azure-core` from 1.27.0 to 1.31.0 ([#4160](https://github.com/opensearch-project/OpenSearch/pull/4160))\n- Bumps `azure-storage-common` from 12.16.0 to 12.18.0 ([#4160](https://github.com/opensearch-project/OpenSearch/pull/4160))\n- Bumps `org.gradle.test-retry` from 1.4.0 to 1.4.1 ([#4411](https://github.com/opensearch-project/OpenSearch/pull/4411))\n- Bumps `reactor-netty-core` from 1.0.19 to 1.0.22 ([#4447](https://github.com/opensearch-project/OpenSearch/pull/4447))\n- Bumps `reactive-streams` from 1.0.3 to 1.0.4 ([#4488](https://github.com/opensearch-project/OpenSearch/pull/4488))\n- Bumps `com.diffplug.spotless` from 6.10.0 to 6.11.0 ([#4547](https://github.com/opensearch-project/OpenSearch/pull/4547))\n- Bumps `reactor-core` from 3.4.18 to 3.4.23 ([#4548](https://github.com/opensearch-project/OpenSearch/pull/4548))\n- Bumps `jempbox` from 1.8.16 to 1.8.17 ([#4550](https://github.com/opensearch-project/OpenSearch/pull/4550))\n- Bumps `hadoop-hdfs` from 3.3.3 to 3.3.4 ([#4644](https://github.com/opensearch-project/OpenSearch/pull/4644))\n- Update Jackson Databind to 2.13.4.2 (addressing CVE-2022-42003) ([#4779](https://github.com/opensearch-project/OpenSearch/pull/4779))\n- Bumps `tika` from 2.4.0 to 2.5.0 ([#4791](https://github.com/opensearch-project/OpenSearch/pull/4791))\n- Exclude jettison version brought in with hadoop-minicluster. ([#4787](https://github.com/opensearch-project/OpenSearch/pull/4787))\n- Bump protobuf-java to 3.21.7 in repository-gcs and repository-hdfs ([#4790](https://github.com/opensearch-project/OpenSearch/pull/4790))\n- Bump reactor-netty-http to 1.0.24 in repository-azure ([#4880](https://github.com/opensearch-project/OpenSearch/pull/4880))\n- Bumps `protobuf-java` from 3.21.7 to 3.21.8 ([#4886](https://github.com/opensearch-project/OpenSearch/pull/4886))\n- Upgrade netty to 4.1.84.Final ([#4893](https://github.com/opensearch-project/OpenSearch/pull/4893))\n- Dependency updates: asm 9.3 -> 9.4, bytebuddy 1.12.12 -> 1.12.18 ([#4889](https://github.com/opensearch-project/OpenSearch/pull/4889))\n- Update Apache Lucene to 9.4.1 ([#4922](https://github.com/opensearch-project/OpenSearch/pull/4922))\n- Bump `woodstox-core` to 6.4.0 ([#4947](https://github.com/opensearch-project/OpenSearch/pull/4947))\n- Update Apache Lucene to 9.5.0-snapshot-a4ef70f ([#4979](https://github.com/opensearch-project/OpenSearch/pull/4979))\n- Upgrade jetty-http, kotlin-stdlib and snakeyaml ([#4963](https://github.com/opensearch-project/OpenSearch/pull/4963))\n- OpenJDK Update (October 2022 Patch releases) ([#4997](https://github.com/opensearch-project/OpenSearch/pull/4997))\n- Upgrade zookeeper dependency in hdfs-fixture ([#5007](https://github.com/opensearch-project/OpenSearch/pull/5007))\n- Update Jackson to 2.14.0 ([#5105](https://github.com/opensearch-project/OpenSearch/pull/5105))\n\n### Changed\n- Dependency updates (httpcore, mockito, slf4j, httpasyncclient, commons-codec) ([#4308](https://github.com/opensearch-project/OpenSearch/pull/4308))\n- Use RemoteSegmentStoreDirectory instead of RemoteDirectory ([#4240](https://github.com/opensearch-project/OpenSearch/pull/4240))\n- Plugin ZIP publication groupId value is configurable ([#4156](https://github.com/opensearch-project/OpenSearch/pull/4156))\n- Weighted round-robin scheduling policy for shard coordination traffic ([#4241](https://github.com/opensearch-project/OpenSearch/pull/4241))\n- Add index specific setting for remote repository ([#4253](https://github.com/opensearch-project/OpenSearch/pull/4253))\n- [Segment Replication] Update replicas to commit SegmentInfos instead of relying on SIS files from primary shards. ([#4402](https://github.com/opensearch-project/OpenSearch/pull/4402))\n- [CCR] Add getHistoryOperationsFromTranslog method to fetch the history snapshot from translogs ([#3948](https://github.com/opensearch-project/OpenSearch/pull/3948))\n- [Remote Store] Change behaviour in replica recovery for remote translog enabled indices ([#4318](https://github.com/opensearch-project/OpenSearch/pull/4318))\n- PUT api for weighted shard routing ([#4272](https://github.com/opensearch-project/OpenSearch/pull/4272))\n- GET api for weighted shard routing([#4275](https://github.com/opensearch-project/OpenSearch/pull/4275/))\n- Unmute test RelocationIT.testRelocationWhileIndexingRandom ([#4580](https://github.com/opensearch-project/OpenSearch/pull/4580))\n- Add DecommissionService and helper to execute awareness attribute decommissioning ([#4084](https://github.com/opensearch-project/OpenSearch/pull/4084))\n- Delete api for weighted shard routing([#4400](https://github.com/opensearch-project/OpenSearch/pull/4400/))\n- Further simplification of the ZIP publication implementation ([#4360](https://github.com/opensearch-project/OpenSearch/pull/4360))\n- Relax visibility of the HTTP_CHANNEL_KEY and HTTP_SERVER_CHANNEL_KEY to make it possible for the plugins to access associated Netty4HttpChannel / Netty4HttpServerChannel instance ([#4638](https://github.com/opensearch-project/OpenSearch/pull/4638))\n- Load the deprecated master role in a dedicated method instead of in setAdditionalRoles() ([#4582](https://github.com/opensearch-project/OpenSearch/pull/4582))\n- Add APIs (GET/PUT) to decommission awareness attribute ([#4261](https://github.com/opensearch-project/OpenSearch/pull/4261))\n- Improve Gradle pre-commit checks to pre-empt Jenkins build ([#4660](https://github.com/opensearch-project/OpenSearch/pull/4660))\n- Update to Apache Lucene 9.4.0 ([#4661](https://github.com/opensearch-project/OpenSearch/pull/4661))\n- Controlling discovery for decommissioned nodes ([#4590](https://github.com/opensearch-project/OpenSearch/pull/4590))\n- Backport Apache Lucene version change for 2.4.0 ([#4677](https://github.com/opensearch-project/OpenSearch/pull/4677))\n- Integ Tests for Awareness Attribute Decommissioning ([#4715](https://github.com/opensearch-project/OpenSearch/pull/4715))\n- Use ReplicationFailedException instead of OpensearchException in ReplicationTarget ([#4725](https://github.com/opensearch-project/OpenSearch/pull/4725))\n- Fix weighted routing metadata deserialization error on process restart ([#4691](https://github.com/opensearch-project/OpenSearch/pull/4691))\n- Refactor Base Action class javadocs to OpenSearch.API ([#4732](https://github.com/opensearch-project/OpenSearch/pull/4732))\n- Migrate client transports to Apache HttpClient / Core 5.x ([#4459](https://github.com/opensearch-project/OpenSearch/pull/4459))\n- Refactored BalancedAllocator.Balancer to LocalShardsBalancer ([#4761](https://github.com/opensearch-project/OpenSearch/pull/4761))\n- Fail weight update when decommission ongoing and fail decommission when attribute not weighed away ([#4839](https://github.com/opensearch-project/OpenSearch/pull/4839))\n- Skip SymbolicLinkPreservingTarIT when running on Windows ([#5023](https://github.com/opensearch-project/OpenSearch/pull/5023))\n- Change the output error message back to use OpenSearchException in the cause chain. ([#5081](https://github.com/opensearch-project/OpenSearch/pull/5081))\n\n### Deprecated\n### Removed\n- Remove deprecated code to add node name into log pattern of log4j property file ([#4568](https://github.com/opensearch-project/OpenSearch/pull/4568))\n- Unused object and import within TransportClusterAllocationExplainAction ([#4639](https://github.com/opensearch-project/OpenSearch/pull/4639))\n- Remove LegacyESVersion.V_7_0_* and V_7_1_* Constants ([#2768](https://https://github.com/opensearch-project/OpenSearch/pull/2768))\n- Remove LegacyESVersion.V_7_2_ and V_7_3_ Constants ([#4702](https://github.com/opensearch-project/OpenSearch/pull/4702))\n- Always auto release the flood stage block ([#4703](https://github.com/opensearch-project/OpenSearch/pull/4703))\n- Remove LegacyESVersion.V_7_4_ and V_7_5_ Constants ([#4704](https://github.com/opensearch-project/OpenSearch/pull/4704))\n- Remove Legacy Version support from Snapshot/Restore Service ([#4728](https://github.com/opensearch-project/OpenSearch/pull/4728))\n- Remove deprecated serialization logic from pipeline aggs ([#4847](https://github.com/opensearch-project/OpenSearch/pull/4847))\n- Remove unused private methods ([#4926](https://github.com/opensearch-project/OpenSearch/pull/4926))\n- Revert PR 4656 to unblock Windows CI ([#4949](https://github.com/opensearch-project/OpenSearch/pull/4949))\n- Remove LegacyESVersion.V_7_8_ and V_7_9_ Constants ([#4855](https://github.com/opensearch-project/OpenSearch/pull/4855))\n- Remove LegacyESVersion.V_7_6_ and V_7_7_ Constants ([#4837](https://github.com/opensearch-project/OpenSearch/pull/4837))\n- Remove LegacyESVersion.V_7_10_ Constants ([#5018](https://github.com/opensearch-project/OpenSearch/pull/5018))\n- Remove Version.V_1_ Constants ([#5021](https://github.com/opensearch-project/OpenSearch/pull/5021))\n\n### Fixed\n- `opensearch-service.bat start` and `opensearch-service.bat manager` failing to run ([#4289](https://github.com/opensearch-project/OpenSearch/pull/4289))\n- PR reference to checkout code for changelog verifier ([#4296](https://github.com/opensearch-project/OpenSearch/pull/4296))\n- `opensearch.bat` and `opensearch-service.bat install` failing to run, missing logs directory ([#4305](https://github.com/opensearch-project/OpenSearch/pull/4305))\n- Restore using the class ClusterInfoRequest and ClusterInfoRequestBuilder from package 'org.opensearch.action.support.master.info' for subclasses ([#4307](https://github.com/opensearch-project/OpenSearch/pull/4307))\n- Do not fail replica shard due to primary closure ([#4133](https://github.com/opensearch-project/OpenSearch/pull/4133))\n- Add timeout on Mockito.verify to reduce flakyness in testReplicationOnDone test([#4314](https://github.com/opensearch-project/OpenSearch/pull/4314))\n- Commit workflow for dependabot changelog helper ([#4331](https://github.com/opensearch-project/OpenSearch/pull/4331))\n- Fixed cancellation of segment replication events ([#4225](https://github.com/opensearch-project/OpenSearch/pull/4225))\n- [Segment Replication] Bump segment infos counter before commit during replica promotion ([#4365](https://github.com/opensearch-project/OpenSearch/pull/4365))\n- Bugs for dependabot changelog verifier workflow ([#4364](https://github.com/opensearch-project/OpenSearch/pull/4364))\n- Fix flaky random test `NRTReplicationEngineTests.testUpdateSegments` ([#4352](https://github.com/opensearch-project/OpenSearch/pull/4352))\n- [Segment Replication] Extend FileChunkWriter to allow cancel on transport client ([#4386](https://github.com/opensearch-project/OpenSearch/pull/4386))\n- [Segment Replication] Add check to cancel ongoing replication with old primary on onNewCheckpoint on replica ([#4363](https://github.com/opensearch-project/OpenSearch/pull/4363))\n- Fix NoSuchFileExceptions with segment replication when computing primary metadata snapshots ([#4366](https://github.com/opensearch-project/OpenSearch/pull/4366))\n- [Segment Replication] Update flaky testOnNewCheckpointFromNewPrimaryCancelOngoingReplication unit test ([#4414](https://github.com/opensearch-project/OpenSearch/pull/4414))\n- Fixed the `_cat/shards/10_basic.yml` test cases fix.\n- [Segment Replication] Fix timeout issue by calculating time needed to process getSegmentFiles ([#4426](https://github.com/opensearch-project/OpenSearch/pull/4426))\n- [Bug]: gradle check failing with java heap OutOfMemoryError ([#4328](https://github.com/opensearch-project/OpenSearch/))\n- `opensearch.bat` fails to execute when install path includes spaces ([#4362](https://github.com/opensearch-project/OpenSearch/pull/4362))\n- Getting security exception due to access denied 'java.lang.RuntimePermission' 'accessDeclaredMembers' when trying to get snapshot with S3 IRSA ([#4469](https://github.com/opensearch-project/OpenSearch/pull/4469))\n- Fixed flaky test `ResourceAwareTasksTests.testTaskIdPersistsInThreadContext` ([#4484](https://github.com/opensearch-project/OpenSearch/pull/4484))\n- Fixed the ignore_malformed setting to also ignore objects ([#4494](https://github.com/opensearch-project/OpenSearch/pull/4494))\n- [Segment Replication] Ignore lock file when testing cleanupAndPreserveLatestCommitPoint ([#4544](https://github.com/opensearch-project/OpenSearch/pull/4544))\n- Updated jackson to 2.13.4 and snakeyml to 1.32 ([#4556](https://github.com/opensearch-project/OpenSearch/pull/4556))\n- Fixing PIT flaky tests ([4632](https://github.com/opensearch-project/OpenSearch/pull/4632))\n- Fixed day of year defaulting for round up parser ([#4627](https://github.com/opensearch-project/OpenSearch/pull/4627))\n- Fixed the SnapshotsInProgress error during index deletion ([#4570](https://github.com/opensearch-project/OpenSearch/pull/4570))\n- [Segment Replication] Adding check to make sure checkpoint is not processed when a shard's shard routing is primary ([#4630](https://github.com/opensearch-project/OpenSearch/pull/4630))\n- [Bug]: Fixed invalid location of JDK dependency for arm64 architecture([#4613](https://github.com/opensearch-project/OpenSearch/pull/4613))\n- [Bug]: Alias filter lost after rollover ([#4499](https://github.com/opensearch-project/OpenSearch/pull/4499))\n- Fixed the SnapshotsInProgress error during index deletion ([#4570](https://github.com/opensearch-project/OpenSearch/pull/4570))\n- [Segment Replication] Adding check to make sure checkpoint is not processed when a shard's shard routing is primary ([#4630](https://github.com/opensearch-project/OpenSearch/pull/4630))\n- [Bug]: Fixed invalid location of JDK dependency for arm64 architecture([#4613](https://github.com/opensearch-project/OpenSearch/pull/4613))\n- [Bug]: Alias filter lost after rollover ([#4499](https://github.com/opensearch-project/OpenSearch/pull/4499))\n- Fixed misunderstanding message \"No OpenSearchException found\" when detailed_error disabled ([#4669](https://github.com/opensearch-project/OpenSearch/pull/4669))\n- Attempt to fix Github workflow for Gradle Check job ([#4679](https://github.com/opensearch-project/OpenSearch/pull/4679))\n- Fix flaky DecommissionControllerTests.testTimesOut ([4683](https://github.com/opensearch-project/OpenSearch/pull/4683))\n- Fix new race condition in DecommissionControllerTests ([4688](https://github.com/opensearch-project/OpenSearch/pull/4688))\n- Fix SearchStats (de)serialization (caused by https://github.com/opensearch-project/OpenSearch/pull/4616) ([#4697](https://github.com/opensearch-project/OpenSearch/pull/4697))\n- Fixing Gradle warnings associated with publishPluginZipPublicationToXxx tasks ([#4696](https://github.com/opensearch-project/OpenSearch/pull/4696))\n- [BUG]: Remove redundant field from GetDecommissionStateResponse ([#4751](https://github.com/opensearch-project/OpenSearch/pull/4751))\n- Fixed randomly failing test ([4774](https://github.com/opensearch-project/OpenSearch/pull/4774))\n- Update version check after backport ([4786](https://github.com/opensearch-project/OpenSearch/pull/4786))\n- Fix decommission status update to non leader nodes ([4800](https://github.com/opensearch-project/OpenSearch/pull/4800))\n- Fix recovery path for searchable snapshots ([4813](https://github.com/opensearch-project/OpenSearch/pull/4813))\n- Fix bug in AwarenessAttributeDecommissionIT([4822](https://github.com/opensearch-project/OpenSearch/pull/4822))\n- Fix 'org.apache.hc.core5.http.ParseException: Invalid protocol version' under JDK 16+ ([#4827](https://github.com/opensearch-project/OpenSearch/pull/4827))\n- [BUG]: flaky test index/80_geo_point/Single point test([#4860](https://github.com/opensearch-project/OpenSearch/pull/4860))\n- Fix bug in SlicedInputStream with zero length ([#4863](https://github.com/opensearch-project/OpenSearch/pull/4863))\n- Fix a bug on handling an invalid array value for point type field ([#4900](https://github.com/opensearch-project/OpenSearch/pull/4900))\n- [Segment Replication] Fix bug of replica shard's translog not purging on index flush when segment replication is enabled ([4928](https://github.com/opensearch-project/OpenSearch/pull/4928))\n- Fix a bug on handling an invalid array value for point type field #4900([#4900](https://github.com/opensearch-project/OpenSearch/pull/4900))\n- [BUG]: Allow decommission to support delay timeout ([#4930](https://github.com/opensearch-project/OpenSearch/pull/4930))\n- Fix failing test: VerifyVersionConstantsIT ([#4946](https://github.com/opensearch-project/OpenSearch/pull/4946))\n- Fixed compression support for h2c protocol ([#4944](https://github.com/opensearch-project/OpenSearch/pull/4944))\n- Fix for failing checkExtraction, checkLicense and checkNotice tasks for windows gradle check ([#4941](https://github.com/opensearch-project/OpenSearch/pull/4941))\n- Backport failures for merge conflicts on CHANGELOG.md file ([#4977](https://github.com/opensearch-project/OpenSearch/pull/4977))\n- Remove gradle-check dependency on precommit [#5027](https://github.com/opensearch-project/OpenSearch/pull/5027)\n- Fix version check for 2.x release for awareness attribute decommission([#5034](https://github.com/opensearch-project/OpenSearch/pull/5034))\n- Fix flaky test ResourceAwareTasksTests on Windows ([#5077](https://github.com/opensearch-project/OpenSearch/pull/5077))\n- Length calculation for block based fetching ([#5055](https://github.com/opensearch-project/OpenSearch/pull/5055))\n- [Segment Replication] Fix for AlreadyClosedException for engine ([#4743](https://github.com/opensearch-project/OpenSearch/pull/4743))\n### Security\n- CVE-2022-25857 org.yaml:snakeyaml DOS vulnerability ([#4341](https://github.com/opensearch-project/OpenSearch/pull/4341))\n\n## [2.x]\n### Added\n- Github workflow for changelog verification ([#4085](https://github.com/opensearch-project/OpenSearch/pull/4085))\n- Label configuration for dependabot PRs ([#4348](https://github.com/opensearch-project/OpenSearch/pull/4348))\n- Added RestLayer Changes for PIT stats ([#4217](https://github.com/opensearch-project/OpenSearch/pull/4217))\n- Added GeoBounds aggregation on GeoShape field type.([#4266](https://github.com/opensearch-project/OpenSearch/pull/4266))\n - Addition of Doc values on the GeoShape Field\n - Addition of GeoShape ValueSource level code interfaces for accessing the DocValues.\n - Addition of Missing Value feature in the GeoShape Aggregations.\n- Install and configure Log4j JUL Adapter for Lucene 9.4 ([#4754](https://github.com/opensearch-project/OpenSearch/pull/4754))\n- Added feature to ignore indexes starting with dot during shard limit validation.([#4695](https://github.com/opensearch-project/OpenSearch/pull/4695))\n### Changed\n### Deprecated\n### Removed\n### Fixed\n- PR reference to checkout code for changelog verifier ([#4296](https://github.com/opensearch-project/OpenSearch/pull/4296))\n- Commit workflow for dependabot changelog helper ([#4331](https://github.com/opensearch-project/OpenSearch/pull/4331))\n- Better plural stemmer than minimal_english ([#4738](https://github.com/opensearch-project/OpenSearch/pull/4738))\n- Disable merge on refresh in DiskThresholdDeciderIT ([#4828](https://github.com/opensearch-project/OpenSearch/pull/4828))\n\n### Security\n[Unreleased]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...HEAD\n[2.x]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...2.x\n\n## 内容2\n### 文件路径:\nmodules/analysis-common/src/main/java/org/opensearch/analysis/common/HyphenationCompoundWordTokenFilterFactory.java\n### 代码内容:\npackage org.opensearch.analysis.common;\n\nimport org.apache.lucene.analysis.TokenStream;\nimport org.apache.lucene.analysis.compound.HyphenationCompoundWordTokenFilter;\nimport org.apache.lucene.analysis.compound.hyphenation.HyphenationTree;\nimport org.opensearch.common.settings.Settings;\nimport org.opensearch.env.Environment;\nimport org.opensearch.index.IndexSettings;\nimport org.xml.sax.InputSource;\n\nimport java.io.InputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\n/**\n * Uses the {@link org.apache.lucene.analysis.compound.HyphenationCompoundWordTokenFilter} to decompound tokens based on hyphenation rules.\n *\n * @see org.apache.lucene.analysis.compound.HyphenationCompoundWordTokenFilter\n */\npublic class HyphenationCompoundWordTokenFilterFactory extends AbstractCompoundWordTokenFilterFactory {\n\n private final HyphenationTree hyphenationTree;\n\n HyphenationCompoundWordTokenFilterFactory(IndexSettings indexSettings, Environment env, String name, Settings settings) {\n super(indexSettings, env, name, settings);\n\n String hyphenationPatternsPath = settings.get(\"hyphenation_patterns_path\", null);\n if (hyphenationPatternsPath == null) {\n throw new IllegalArgumentException(\"hyphenation_patterns_path is a required setting.\");\n }\n\n Path hyphenationPatternsFile = env.configDir().resolve(hyphenationPatternsPath);\n\n try {\n InputStream in = Files.newInputStream(hyphenationPatternsFile);\n hyphenationTree = HyphenationCompoundWordTokenFilter.getHyphenationTree(new InputSource(in));\n } catch (Exception e) {\n throw new IllegalArgumentException(\"Exception while reading hyphenation_patterns_path.\", e);\n }\n }\n\n @Override\n public TokenStream create(TokenStream tokenStream) {\n return new HyphenationCompoundWordTokenFilter(\n tokenStream,\n hyphenationTree,\n wordList,\n minWordSize,\n minSubwordSize,\n maxSubwordSize,\n onlyLongestMatch\n );\n }\n}\n\n## 内容3\n### 文件路径:\nmodules/analysis-common/src/main/java/org/opensearch/analysis/common/MappingCharFilterFactory.java\n### 代码内容:\npackage org.opensearch.analysis.common;\n\nimport org.apache.lucene.analysis.charfilter.MappingCharFilter;\nimport org.apache.lucene.analysis.charfilter.NormalizeCharMap;\nimport org.opensearch.common.settings.Settings;\nimport org.opensearch.env.Environment;\nimport org.opensearch.index.IndexSettings;\nimport org.opensearch.index.analysis.AbstractCharFilterFactory;\nimport org.opensearch.index.analysis.Analysis;\nimport org.opensearch.index.analysis.NormalizingCharFilterFactory;\n\nimport java.io.Reader;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\npublic class MappingCharFilterFactory extends AbstractCharFilterFactory implements NormalizingCharFilterFactory {\n\n private final NormalizeCharMap normMap;\n\n MappingCharFilterFactory(IndexSettings indexSettings, Environment env, String name, Settings settings) {\n super(indexSettings, name);\n\n List<String> rules = Analysis.getWordList(env, settings, \"mappings\");\n if (rules == null) {\n throw new IllegalArgumentException(\"mapping requires either `mappings` or `mappings_path` to be configured\");\n }\n\n NormalizeCharMap.Builder normMapBuilder = new NormalizeCharMap.Builder();\n parseRules(rules, normMapBuilder);\n normMap = normMapBuilder.build();\n }\n\n @Override\n public Reader create(Reader tokenStream) {\n return new MappingCharFilter(normMap, tokenStream);\n }\n\n // source => target\n private static Pattern rulePattern = Pattern.compile(\"(.*)\\\\s*=>\\\\s*(.*)\\\\s*$\");\n\n /**\n * parses a list of MappingCharFilter style rules into a normalize char map\n */\n private void parseRules(List<String> rules, NormalizeCharMap.Builder map) {\n for (String rule : rules) {\n Matcher m = rulePattern.matcher(rule);\n if (!m.find()) throw new RuntimeException(\"Invalid Mapping Rule : [\" + rule + \"]\");\n String lhs = parseString(m.group(1).trim());\n String rhs = parseString(m.group(2).trim());\n if (lhs == null || rhs == null) throw new RuntimeException(\"Invalid Mapping Rule : [\" + rule + \"]. Illegal mapping.\");\n map.add(lhs, rhs);\n }\n }\n\n char[] out = new char[256];\n\n private String parseString(String s) {\n int readPos = 0;\n int len = s.length();\n int writePos = 0;\n while (readPos < len) {\n char c = s.charAt(readPos++);\n if (c == '\\\\') {\n if (readPos >= len) throw new RuntimeException(\"Invalid escaped char in [\" + s + \"]\");\n c = s.charAt(readPos++);\n switch (c) {\n case '\\\\':\n c = '\\\\';\n break;\n case 'n':\n c = '\\n';\n break;\n case 't':\n c = '\\t';\n break;\n case 'r':\n c = '\\r';\n break;\n case 'b':\n c = '\\b';\n break;\n case 'f':\n c = '\\f';\n break;\n case 'u':\n if (readPos + 3 >= len) throw new RuntimeException(\"Invalid escaped char in [\" + s + \"]\");\n c = (char) Integer.parseInt(s.substring(readPos, readPos + 4), 16);\n readPos += 4;\n break;\n }\n }\n out[writePos++] = c;\n }\n return new String(out, 0, writePos);\n }\n\n}\n\n## 内容4\n### 文件路径:\nmodules/analysis-common/src/main/java/org/opensearch/analysis/common/StemmerOverrideTokenFilterFactory.java\n### 代码内容:\npackage org.opensearch.analysis.common;\n\nimport org.apache.lucene.analysis.TokenStream;\nimport org.apache.lucene.analysis.miscellaneous.StemmerOverrideFilter;\nimport org.apache.lucene.analysis.miscellaneous.StemmerOverrideFilter.StemmerOverrideMap;\nimport org.opensearch.common.settings.Settings;\nimport org.opensearch.env.Environment;\nimport org.opensearch.index.IndexSettings;\nimport org.opensearch.index.analysis.AbstractTokenFilterFactory;\nimport org.opensearch.index.analysis.Analysis;\n\nimport java.io.IOException;\nimport java.util.List;\n\npublic class StemmerOverrideTokenFilterFactory extends AbstractTokenFilterFactory {\n\n private final StemmerOverrideMap overrideMap;\n\n StemmerOverrideTokenFilterFactory(IndexSettings indexSettings, Environment env, String name, Settings settings) throws IOException {\n super(indexSettings, name, settings);\n\n List<String> rules = Analysis.getWordList(env, settings, \"rules\");\n if (rules == null) {\n throw new IllegalArgumentException(\"stemmer override filter requires either `rules` or `rules_path` to be configured\");\n }\n\n StemmerOverrideFilter.Builder builder = new StemmerOverrideFilter.Builder(false);\n parseRules(rules, builder, \"=>\");\n overrideMap = builder.build();\n\n }\n\n @Override\n public TokenStream create(TokenStream tokenStream) {\n return new StemmerOverrideFilter(tokenStream, overrideMap);\n }\n\n static void parseRules(List<String> rules, StemmerOverrideFilter.Builder builder, String mappingSep) {\n for (String rule : rules) {\n String[] sides = rule.split(mappingSep, -1);\n if (sides.length != 2) {\n throw new RuntimeException(\"Invalid Keyword override Rule:\" + rule);\n }\n\n String[] keys = sides[0].split(\",\", -1);\n String override = sides[1].trim();\n if (override.isEmpty() || override.indexOf(',') != -1) {\n throw new RuntimeException(\"Invalid Keyword override Rule:\" + rule);\n }\n\n for (String key : keys) {\n String trimmedKey = key.trim();\n if (trimmedKey.isEmpty()) {\n throw new RuntimeException(\"Invalid Keyword override Rule:\" + rule);\n }\n builder.add(trimmedKey, override);\n }\n }\n }\n\n}\n\n## 内容5\n### 文件路径:\nmodules/analysis-common/src/main/java/org/opensearch/analysis/common/SynonymTokenFilterFactory.java\n### 代码内容:\npackage org.opensearch.analysis.common;\n\nimport org.apache.lucene.analysis.Analyzer;\nimport org.apache.lucene.analysis.TokenStream;\nimport org.apache.lucene.analysis.synonym.SynonymFilter;\nimport org.apache.lucene.analysis.synonym.SynonymMap;\nimport org.opensearch.common.logging.DeprecationLogger;\nimport org.opensearch.common.settings.Settings;\nimport org.opensearch.env.Environment;\nimport org.opensearch.index.IndexSettings;\nimport org.opensearch.index.analysis.AbstractTokenFilterFactory;\nimport org.opensearch.index.analysis.Analysis;\nimport org.opensearch.index.analysis.AnalysisMode;\nimport org.opensearch.index.analysis.CharFilterFactory;\nimport org.opensearch.index.analysis.CustomAnalyzer;\nimport org.opensearch.index.analysis.TokenFilterFactory;\nimport org.opensearch.index.analysis.TokenizerFactory;\n\nimport java.io.Reader;\nimport java.io.StringReader;\nimport java.util.List;\nimport java.util.function.Function;\n\npublic class SynonymTokenFilterFactory extends AbstractTokenFilterFactory {\n\n private static final DeprecationLogger DEPRECATION_LOGGER = DeprecationLogger.getLogger(SynonymTokenFilterFactory.class);\n\n private final String format;\n private final boolean expand;\n private final boolean lenient;\n protected final Settings settings;\n protected final Environment environment;\n protected final AnalysisMode analysisMode;\n\n SynonymTokenFilterFactory(IndexSettings indexSettings, Environment env, String name, Settings settings) {\n super(indexSettings, name, settings);\n this.settings = settings;\n\n if (settings.get(\"ignore_case\") != null) {\n DEPRECATION_LOGGER.deprecate(\n \"synonym_ignore_case_option\",\n \"The ignore_case option on the synonym_graph filter is deprecated. \"\n + \"Instead, insert a lowercase filter in the filter chain before the synonym_graph filter.\"\n );\n }\n\n this.expand = settings.getAsBoolean(\"expand\", true);\n this.lenient = settings.getAsBoolean(\"lenient\", false);\n this.format = settings.get(\"format\", \"\");\n boolean updateable = settings.getAsBoolean(\"updateable\", false);\n this.analysisMode = updateable ? AnalysisMode.SEARCH_TIME : AnalysisMode.ALL;\n this.environment = env;\n }\n\n @Override\n public AnalysisMode getAnalysisMode() {\n return this.analysisMode;\n }\n\n @Override\n public TokenStream create(TokenStream tokenStream) {\n throw new IllegalStateException(\"Call createPerAnalyzerSynonymFactory to specialize this factory for an analysis chain first\");\n }\n\n @Override\n public TokenFilterFactory getChainAwareTokenFilterFactory(\n TokenizerFactory tokenizer,\n List<CharFilterFactory> charFilters,\n List<TokenFilterFactory> previousTokenFilters,\n Function<String, TokenFilterFactory> allFilters\n ) {\n final Analyzer analyzer = buildSynonymAnalyzer(tokenizer, charFilters, previousTokenFilters, allFilters);\n final SynonymMap synonyms = buildSynonyms(analyzer, getRulesFromSettings(environment));\n final String name = name();\n return new TokenFilterFactory() {\n @Override\n public String name() {\n return name;\n }\n\n @Override\n public TokenStream create(TokenStream tokenStream) {\n return synonyms.fst == null ? tokenStream : new SynonymFilter(tokenStream, synonyms, false);\n }\n\n @Override\n public TokenFilterFactory getSynonymFilter() {\n // In order to allow chained synonym filters, we return IDENTITY here to\n // ensure that synonyms don't get applied to the synonym map itself,\n // which doesn't support stacked input tokens\n return IDENTITY_FILTER;\n }\n\n @Override\n public AnalysisMode getAnalysisMode() {\n return analysisMode;\n }\n };\n }\n\n Analyzer buildSynonymAnalyzer(\n TokenizerFactory tokenizer,\n List<CharFilterFactory> charFilters,\n List<TokenFilterFactory> tokenFilters,\n Function<String, TokenFilterFactory> allFilters\n ) {\n return new CustomAnalyzer(\n tokenizer,\n charFilters.toArray(new CharFilterFactory[0]),\n tokenFilters.stream().map(TokenFilterFactory::getSynonymFilter).toArray(TokenFilterFactory[]::new)\n );\n }\n\n SynonymMap buildSynonyms(Analyzer analyzer, Reader rules) {\n try {\n SynonymMap.Builder parser;\n if (\"wordnet\".equalsIgnoreCase(format)) {\n parser = new OpenSearchWordnetSynonymParser(true, expand, lenient, analyzer);\n ((OpenSearchWordnetSynonymParser) parser).parse(rules);\n } else {\n parser = new OpenSearchSolrSynonymParser(true, expand, lenient, analyzer);\n ((OpenSearchSolrSynonymParser) parser).parse(rules);\n }\n return parser.build();\n } catch (Exception e) {\n throw new IllegalArgumentException(\"failed to build synonyms\", e);\n }\n }\n\n Reader getRulesFromSettings(Environment env) {\n Reader rulesReader;\n if (settings.getAsList(\"synonyms\", null) != null) {\n List<String> rulesList = Analysis.getWordList(env, settings, \"synonyms\");\n StringBuilder sb = new StringBuilder();\n for (String line : rulesList) {\n sb.append(line).append(System.lineSeparator());\n }\n rulesReader = new StringReader(sb.toString());\n } else if (settings.get(\"synonyms_path\") != null) {\n rulesReader = Analysis.getReaderFromFile(env, settings, \"synonyms_path\");\n } else {\n throw new IllegalArgumentException(\"synonym requires either `synonyms` or `synonyms_path` to be configured\");\n }\n return rulesReader;\n }\n\n}\n\n## 内容6\n### 文件路径:\nmodules/analysis-common/src/main/java/org/opensearch/analysis/common/WordDelimiterGraphTokenFilterFactory.java\n### 代码内容:\npackage org.opensearch.analysis.common;\n\nimport org.apache.lucene.analysis.CharArraySet;\nimport org.apache.lucene.analysis.TokenStream;\nimport org.apache.lucene.analysis.miscellaneous.WordDelimiterGraphFilter;\nimport org.apache.lucene.analysis.miscellaneous.WordDelimiterIterator;\nimport org.opensearch.common.settings.Settings;\nimport org.opensearch.env.Environment;\nimport org.opensearch.index.IndexSettings;\nimport org.opensearch.index.analysis.AbstractTokenFilterFactory;\nimport org.opensearch.index.analysis.Analysis;\nimport org.opensearch.index.analysis.TokenFilterFactory;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport static org.apache.lucene.analysis.miscellaneous.WordDelimiterGraphFilter.CATENATE_ALL;\nimport static org.apache.lucene.analysis.miscellaneous.WordDelimiterGraphFilter.CATENATE_NUMBERS;\nimport static org.apache.lucene.analysis.miscellaneous.WordDelimiterGraphFilter.CATENATE_WORDS;\nimport static org.apache.lucene.analysis.miscellaneous.WordDelimiterGraphFilter.GENERATE_NUMBER_PARTS;\nimport static org.apache.lucene.analysis.miscellaneous.WordDelimiterGraphFilter.GENERATE_WORD_PARTS;\nimport static org.apache.lucene.analysis.miscellaneous.WordDelimiterGraphFilter.IGNORE_KEYWORDS;\nimport static org.apache.lucene.analysis.miscellaneous.WordDelimiterGraphFilter.PRESERVE_ORIGINAL;\nimport static org.apache.lucene.analysis.miscellaneous.WordDelimiterGraphFilter.SPLIT_ON_CASE_CHANGE;\nimport static org.apache.lucene.analysis.miscellaneous.WordDelimiterGraphFilter.SPLIT_ON_NUMERICS;\nimport static org.apache.lucene.analysis.miscellaneous.WordDelimiterGraphFilter.STEM_ENGLISH_POSSESSIVE;\nimport static org.opensearch.analysis.common.WordDelimiterTokenFilterFactory.parseTypes;\n\npublic class WordDelimiterGraphTokenFilterFactory extends AbstractTokenFilterFactory {\n private final byte[] charTypeTable;\n private final int flags;\n private final CharArraySet protoWords;\n private final boolean adjustOffsets;\n\n public WordDelimiterGraphTokenFilterFactory(IndexSettings indexSettings, Environment env, String name, Settings settings) {\n super(indexSettings, name, settings);\n\n // Sample Format for the type table:\n // $ => DIGIT\n // % => DIGIT\n // . => DIGIT\n // \\u002C => DIGIT\n // \\u200D => ALPHANUM\n List<String> charTypeTableValues = Analysis.getWordList(env, settings, \"type_table\");\n if (charTypeTableValues == null) {\n this.charTypeTable = WordDelimiterIterator.DEFAULT_WORD_DELIM_TABLE;\n } else {\n this.charTypeTable = parseTypes(charTypeTableValues);\n }\n int flags = 0;\n // If set, causes parts of words to be generated: \"PowerShot\" => \"Power\" \"Shot\"\n flags |= getFlag(GENERATE_WORD_PARTS, settings, \"generate_word_parts\", true);\n // If set, causes number subwords to be generated: \"500-42\" => \"500\" \"42\"\n flags |= getFlag(GENERATE_NUMBER_PARTS, settings, \"generate_number_parts\", true);\n // 1, causes maximum runs of word parts to be catenated: \"wi-fi\" => \"wifi\"\n flags |= getFlag(CATENATE_WORDS, settings, \"catenate_words\", false);\n // If set, causes maximum runs of number parts to be catenated: \"500-42\" => \"50042\"\n flags |= getFlag(CATENATE_NUMBERS, settings, \"catenate_numbers\", false);\n // If set, causes all subword parts to be catenated: \"wi-fi-4000\" => \"wifi4000\"\n flags |= getFlag(CATENATE_ALL, settings, \"catenate_all\", false);\n // 1, causes \"PowerShot\" to be two tokens; (\"Power-Shot\" remains two parts regards)\n flags |= getFlag(SPLIT_ON_CASE_CHANGE, settings, \"split_on_case_change\", true);\n // If set, includes original words in subwords: \"500-42\" => \"500\" \"42\" \"500-42\"\n flags |= getFlag(PRESERVE_ORIGINAL, settings, \"preserve_original\", false);\n // 1, causes \"j2se\" to be three tokens; \"j\" \"2\" \"se\"\n flags |= getFlag(SPLIT_ON_NUMERICS, settings, \"split_on_numerics\", true);\n // If set, causes trailing \"'s\" to be removed for each subword: \"O'Neil's\" => \"O\", \"Neil\"\n flags |= getFlag(STEM_ENGLISH_POSSESSIVE, settings, \"stem_english_possessive\", true);\n // If not null is the set of tokens to protect from being delimited\n flags |= getFlag(IGNORE_KEYWORDS, settings, \"ignore_keywords\", false);\n // If set, suppresses processing terms with KeywordAttribute#isKeyword()=true.\n Set<?> protectedWords = Analysis.getWordSet(env, settings, \"protected_words\");\n this.protoWords = protectedWords == null ? null : CharArraySet.copy(protectedWords);\n this.flags = flags;\n this.adjustOffsets = settings.getAsBoolean(\"adjust_offsets\", true);\n }\n\n @Override\n public TokenStream create(TokenStream tokenStream) {\n return new WordDelimiterGraphFilter(tokenStream, adjustOffsets, charTypeTable, flags, protoWords);\n }\n\n @Override\n public TokenFilterFactory getSynonymFilter() {\n throw new IllegalArgumentException(\"Token filter [\" + name() + \"] cannot be used to parse synonyms\");\n }\n\n private int getFlag(int flag, Settings settings, String key, boolean defaultValue) {\n if (settings.getAsBoolean(key, defaultValue)) {\n return flag;\n }\n return 0;\n }\n}\n\n## 内容7\n### 文件路径:\nmodules/analysis-common/src/main/java/org/opensearch/analysis/common/WordDelimiterTokenFilterFactory.java\n### 代码内容:\npackage org.opensearch.analysis.common;\n\nimport org.apache.lucene.analysis.CharArraySet;\nimport org.apache.lucene.analysis.TokenStream;\nimport org.apache.lucene.analysis.miscellaneous.WordDelimiterFilter;\nimport org.apache.lucene.analysis.miscellaneous.WordDelimiterIterator;\nimport org.opensearch.common.settings.Settings;\nimport org.opensearch.env.Environment;\nimport org.opensearch.index.IndexSettings;\nimport org.opensearch.index.analysis.AbstractTokenFilterFactory;\nimport org.opensearch.index.analysis.Analysis;\nimport org.opensearch.index.analysis.TokenFilterFactory;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.SortedMap;\nimport java.util.TreeMap;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport static org.apache.lucene.analysis.miscellaneous.WordDelimiterFilter.CATENATE_ALL;\nimport static org.apache.lucene.analysis.miscellaneous.WordDelimiterFilter.CATENATE_NUMBERS;\nimport static org.apache.lucene.analysis.miscellaneous.WordDelimiterFilter.CATENATE_WORDS;\nimport static org.apache.lucene.analysis.miscellaneous.WordDelimiterFilter.GENERATE_NUMBER_PARTS;\nimport static org.apache.lucene.analysis.miscellaneous.WordDelimiterFilter.GENERATE_WORD_PARTS;\nimport static org.apache.lucene.analysis.miscellaneous.WordDelimiterFilter.PRESERVE_ORIGINAL;\nimport static org.apache.lucene.analysis.miscellaneous.WordDelimiterFilter.SPLIT_ON_CASE_CHANGE;\nimport static org.apache.lucene.analysis.miscellaneous.WordDelimiterFilter.SPLIT_ON_NUMERICS;\nimport static org.apache.lucene.analysis.miscellaneous.WordDelimiterFilter.STEM_ENGLISH_POSSESSIVE;\n\npublic class WordDelimiterTokenFilterFactory extends AbstractTokenFilterFactory {\n private final byte[] charTypeTable;\n private final int flags;\n private final CharArraySet protoWords;\n\n public WordDelimiterTokenFilterFactory(IndexSettings indexSettings, Environment env, String name, Settings settings) {\n super(indexSettings, name, settings);\n\n // Sample Format for the type table:\n // $ => DIGIT\n // % => DIGIT\n // . => DIGIT\n // \\u002C => DIGIT\n // \\u200D => ALPHANUM\n List<String> charTypeTableValues = Analysis.getWordList(env, settings, \"type_table\");\n if (charTypeTableValues == null) {\n this.charTypeTable = WordDelimiterIterator.DEFAULT_WORD_DELIM_TABLE;\n } else {\n this.charTypeTable = parseTypes(charTypeTableValues);\n }\n int flags = 0;\n // If set, causes parts of words to be generated: \"PowerShot\" => \"Power\" \"Shot\"\n flags |= getFlag(GENERATE_WORD_PARTS, settings, \"generate_word_parts\", true);\n // If set, causes number subwords to be generated: \"500-42\" => \"500\" \"42\"\n flags |= getFlag(GENERATE_NUMBER_PARTS, settings, \"generate_number_parts\", true);\n // 1, causes maximum runs of word parts to be catenated: \"wi-fi\" => \"wifi\"\n flags |= getFlag(CATENATE_WORDS, settings, \"catenate_words\", false);\n // If set, causes maximum runs of number parts to be catenated: \"500-42\" => \"50042\"\n flags |= getFlag(CATENATE_NUMBERS, settings, \"catenate_numbers\", false);\n // If set, causes all subword parts to be catenated: \"wi-fi-4000\" => \"wifi4000\"\n flags |= getFlag(CATENATE_ALL, settings, \"catenate_all\", false);\n // 1, causes \"PowerShot\" to be two tokens; (\"Power-Shot\" remains two parts regards)\n flags |= getFlag(SPLIT_ON_CASE_CHANGE, settings, \"split_on_case_change\", true);\n // If set, includes original words in subwords: \"500-42\" => \"500\" \"42\" \"500-42\"\n flags |= getFlag(PRESERVE_ORIGINAL, settings, \"preserve_original\", false);\n // 1, causes \"j2se\" to be three tokens; \"j\" \"2\" \"se\"\n flags |= getFlag(SPLIT_ON_NUMERICS, settings, \"split_on_numerics\", true);\n // If set, causes trailing \"'s\" to be removed for each subword: \"O'Neil's\" => \"O\", \"Neil\"\n flags |= getFlag(STEM_ENGLISH_POSSESSIVE, settings, \"stem_english_possessive\", true);\n // If not null is the set of tokens to protect from being delimited\n Set<?> protectedWords = Analysis.getWordSet(env, settings, \"protected_words\");\n this.protoWords = protectedWords == null ? null : CharArraySet.copy(protectedWords);\n this.flags = flags;\n }\n\n @Override\n public TokenStream create(TokenStream tokenStream) {\n return new WordDelimiterFilter(tokenStream, charTypeTable, flags, protoWords);\n }\n\n @Override\n public TokenFilterFactory getSynonymFilter() {\n throw new IllegalArgumentException(\"Token filter [\" + name() + \"] cannot be used to parse synonyms\");\n }\n\n public int getFlag(int flag, Settings settings, String key, boolean defaultValue) {\n if (settings.getAsBoolean(key, defaultValue)) {\n return flag;\n }\n return 0;\n }\n\n // source => type\n private static Pattern typePattern = Pattern.compile(\"(.*)\\\\s*=>\\\\s*(.*)\\\\s*$\");\n\n /**\n * parses a list of MappingCharFilter style rules into a custom byte[] type table\n */\n static byte[] parseTypes(Collection<String> rules) {\n SortedMap<Character, Byte> typeMap = new TreeMap<>();\n for (String rule : rules) {\n Matcher m = typePattern.matcher(rule);\n if (!m.find()) throw new RuntimeException(\"Invalid Mapping Rule : [\" + rule + \"]\");\n String lhs = parseString(m.group(1).trim());\n Byte rhs = parseType(m.group(2).trim());\n if (lhs.length() != 1) throw new RuntimeException(\"Invalid Mapping Rule : [\" + rule + \"]. Only a single character is allowed.\");\n if (rhs == null) throw new RuntimeException(\"Invalid Mapping Rule : [\" + rule + \"]. Illegal type.\");\n typeMap.put(lhs.charAt(0), rhs);\n }\n\n // ensure the table is always at least as big as DEFAULT_WORD_DELIM_TABLE for performance\n byte types[] = new byte[Math.max(typeMap.lastKey() + 1, WordDelimiterIterator.DEFAULT_WORD_DELIM_TABLE.length)];\n for (int i = 0; i < types.length; i++)\n types[i] = WordDelimiterIterator.getType(i);\n for (Map.Entry<Character, Byte> mapping : typeMap.entrySet())\n types[mapping.getKey()] = mapping.getValue();\n return types;\n }\n\n private static Byte parseType(String s) {\n if (s.equals(\"LOWER\")) return WordDelimiterFilter.LOWER;\n else if (s.equals(\"UPPER\")) return WordDelimiterFilter.UPPER;\n else if (s.equals(\"ALPHA\")) return WordDelimiterFilter.ALPHA;\n else if (s.equals(\"DIGIT\")) return WordDelimiterFilter.DIGIT;\n else if (s.equals(\"ALPHANUM\")) return WordDelimiterFilter.ALPHANUM;\n else if (s.equals(\"SUBWORD_DELIM\")) return WordDelimiterFilter.SUBWORD_DELIM;\n else return null;\n }\n\n private static String parseString(String s) {\n char[] out = new char[256];\n int readPos = 0;\n int len = s.length();\n int writePos = 0;\n while (readPos < len) {\n char c = s.charAt(readPos++);\n if (c == '\\\\') {\n if (readPos >= len) throw new RuntimeException(\"Invalid escaped char in [\" + s + \"]\");\n c = s.charAt(readPos++);\n switch (c) {\n case '\\\\':\n c = '\\\\';\n break;\n case 'n':\n c = '\\n';\n break;\n case 't':\n c = '\\t';\n break;\n case 'r':\n c = '\\r';\n break;\n case 'b':\n c = '\\b';\n break;\n case 'f':\n c = '\\f';\n break;\n case 'u':\n if (readPos + 3 >= len) throw new RuntimeException(\"Invalid escaped char in [\" + s + \"]\");\n c = (char) Integer.parseInt(s.substring(readPos, readPos + 4), 16);\n readPos += 4;\n break;\n }\n }\n out[writePos++] = c;\n }\n return new String(out, 0, writePos);\n }\n\n @Override\n public boolean breaksFastVectorHighlighter() {\n return true;\n }\n}\n\n## 内容8\n### 文件路径:\nplugins/analysis-icu/src/main/java/org/opensearch/index/analysis/IcuCollationTokenFilterFactory.java\n### 代码内容:\npackage org.opensearch.index.analysis;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.InvalidPathException;\n\nimport org.apache.lucene.analysis.TokenStream;\nimport org.opensearch.common.io.Streams;\nimport org.opensearch.common.settings.Settings;\nimport org.opensearch.env.Environment;\nimport org.opensearch.index.IndexSettings;\n\nimport com.ibm.icu.text.Collator;\nimport com.ibm.icu.text.RuleBasedCollator;\nimport com.ibm.icu.util.ULocale;\n\n/**\n * An ICU based collation token filter. There are two ways to configure collation:\n * <p>The first is simply specifying the locale (defaults to the default\n * locale). The {@code language} parameter is the lowercase two-letter\n * ISO-639 code. An additional {@code country} and {@code variant}\n * can be provided.\n * <p>The second option is to specify collation rules as defined in the\n * <a href=\"http://www.icu-project.org/userguide/Collate_Customization.html\">\n * Collation customization</a> chapter in icu docs. The {@code rules}\n * parameter can either embed the rules definition\n * in the settings or refer to an external location (preferable located under\n * the {@code config} location, relative to it).\n */\npublic class IcuCollationTokenFilterFactory extends AbstractTokenFilterFactory {\n\n private final Collator collator;\n\n public IcuCollationTokenFilterFactory(IndexSettings indexSettings, Environment environment, String name, Settings settings) {\n super(indexSettings, name, settings);\n\n Collator collator;\n String rules = settings.get(\"rules\");\n if (rules != null) {\n Exception failureToResolve = null;\n try {\n rules = Streams.copyToString(Files.newBufferedReader(environment.configDir().resolve(rules), StandardCharsets.UTF_8));\n } catch (IOException | SecurityException | InvalidPathException e) {\n failureToResolve = e;\n }\n try {\n collator = new RuleBasedCollator(rules);\n } catch (Exception e) {\n if (failureToResolve != null) {\n throw new IllegalArgumentException(\"Failed to resolve collation rules location\", failureToResolve);\n } else {\n throw new IllegalArgumentException(\"Failed to parse collation rules\", e);\n }\n }\n } else {\n String language = settings.get(\"language\");\n if (language != null) {\n ULocale locale;\n String country = settings.get(\"country\");\n if (country != null) {\n String variant = settings.get(\"variant\");\n if (variant != null) {\n locale = new ULocale(language, country, variant);\n } else {\n locale = new ULocale(language, country);\n }\n } else {\n locale = new ULocale(language);\n }\n collator = Collator.getInstance(locale);\n } else {\n collator = Collator.getInstance(ULocale.ROOT);\n }\n }\n\n // set the strength flag, otherwise it will be the default.\n String strength = settings.get(\"strength\");\n if (strength != null) {\n if (strength.equalsIgnoreCase(\"primary\")) {\n collator.setStrength(Collator.PRIMARY);\n } else if (strength.equalsIgnoreCase(\"secondary\")) {\n collator.setStrength(Collator.SECONDARY);\n } else if (strength.equalsIgnoreCase(\"tertiary\")) {\n collator.setStrength(Collator.TERTIARY);\n } else if (strength.equalsIgnoreCase(\"quaternary\")) {\n collator.setStrength(Collator.QUATERNARY);\n } else if (strength.equalsIgnoreCase(\"identical\")) {\n collator.setStrength(Collator.IDENTICAL);\n } else {\n throw new IllegalArgumentException(\"Invalid strength: \" + strength);\n }\n }\n\n // set the decomposition flag, otherwise it will be the default.\n String decomposition = settings.get(\"decomposition\");\n if (decomposition != null) {\n if (decomposition.equalsIgnoreCase(\"no\")) {\n collator.setDecomposition(Collator.NO_DECOMPOSITION);\n } else if (decomposition.equalsIgnoreCase(\"canonical\")) {\n collator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);\n } else {\n throw new IllegalArgumentException(\"Invalid decomposition: \" + decomposition);\n }\n }\n\n // expert options: concrete subclasses are always a RuleBasedCollator\n RuleBasedCollator rbc = (RuleBasedCollator) collator;\n String alternate = settings.get(\"alternate\");\n if (alternate != null) {\n if (alternate.equalsIgnoreCase(\"shifted\")) {\n rbc.setAlternateHandlingShifted(true);\n } else if (alternate.equalsIgnoreCase(\"non-ignorable\")) {\n rbc.setAlternateHandlingShifted(false);\n } else {\n throw new IllegalArgumentException(\"Invalid alternate: \" + alternate);\n }\n }\n\n Boolean caseLevel = settings.getAsBoolean(\"caseLevel\", null);\n if (caseLevel != null) {\n rbc.setCaseLevel(caseLevel);\n }\n\n String caseFirst = settings.get(\"caseFirst\");\n if (caseFirst != null) {\n if (caseFirst.equalsIgnoreCase(\"lower\")) {\n rbc.setLowerCaseFirst(true);\n } else if (caseFirst.equalsIgnoreCase(\"upper\")) {\n rbc.setUpperCaseFirst(true);\n } else {\n throw new IllegalArgumentException(\"Invalid caseFirst: \" + caseFirst);\n }\n }\n\n Boolean numeric = settings.getAsBoolean(\"numeric\", null);\n if (numeric != null) {\n rbc.setNumericCollation(numeric);\n }\n\n String variableTop = settings.get(\"variableTop\");\n if (variableTop != null) {\n rbc.setVariableTop(variableTop);\n }\n\n Boolean hiraganaQuaternaryMode = settings.getAsBoolean(\"hiraganaQuaternaryMode\", null);\n if (hiraganaQuaternaryMode != null) {\n rbc.setHiraganaQuaternary(hiraganaQuaternaryMode);\n }\n\n this.collator = collator;\n }\n\n @Override\n public TokenStream create(TokenStream tokenStream) {\n return new ICUCollationKeyFilter(tokenStream, collator);\n }\n}\n\n## 内容9\n### 文件路径:\nplugins/analysis-kuromoji/src/main/java/org/opensearch/index/analysis/KuromojiPartOfSpeechFilterFactory.java\n### 代码内容:\npackage org.opensearch.index.analysis;\n\nimport org.apache.lucene.analysis.TokenStream;\nimport org.apache.lucene.analysis.ja.JapaneseAnalyzer;\nimport org.apache.lucene.analysis.ja.JapanesePartOfSpeechStopFilter;\nimport org.opensearch.common.settings.Settings;\nimport org.opensearch.env.Environment;\nimport org.opensearch.index.IndexSettings;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\npublic class KuromojiPartOfSpeechFilterFactory extends AbstractTokenFilterFactory {\n\n private final Set<String> stopTags = new HashSet<>();\n\n public KuromojiPartOfSpeechFilterFactory(IndexSettings indexSettings, Environment env, String name, Settings settings) {\n super(indexSettings, name, settings);\n List<String> wordList = Analysis.getWordList(env, settings, \"stoptags\");\n if (wordList != null) {\n stopTags.addAll(wordList);\n } else {\n stopTags.addAll(JapaneseAnalyzer.getDefaultStopTags());\n }\n }\n\n @Override\n public TokenStream create(TokenStream tokenStream) {\n return new JapanesePartOfSpeechStopFilter(tokenStream, stopTags);\n }\n\n}\n\n## 内容10\n### 文件路径:\nplugins/analysis-kuromoji/src/main/java/org/opensearch/index/analysis/KuromojiTokenizerFactory.java\n### 代码内容:\npackage org.opensearch.index.analysis;\n\nimport org.apache.lucene.analysis.Tokenizer;\nimport org.apache.lucene.analysis.ja.JapaneseTokenizer;\nimport org.apache.lucene.analysis.ja.JapaneseTokenizer.Mode;\nimport org.apache.lucene.analysis.ja.dict.UserDictionary;\nimport org.apache.lucene.analysis.ja.util.CSVUtil;\nimport org.opensearch.OpenSearchException;\nimport org.opensearch.common.settings.Settings;\nimport org.opensearch.env.Environment;\nimport org.opensearch.index.IndexSettings;\n\nimport java.io.IOException;\nimport java.io.StringReader;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\npublic class KuromojiTokenizerFactory extends AbstractTokenizerFactory {\n\n private static final String USER_DICT_PATH_OPTION = \"user_dictionary\";\n private static final String USER_DICT_RULES_OPTION = \"user_dictionary_rules\";\n private static final String NBEST_COST = \"nbest_cost\";\n private static final String NBEST_EXAMPLES = \"nbest_examples\";\n private static final String DISCARD_COMPOUND_TOKEN = \"discard_compound_token\";\n\n private final UserDictionary userDictionary;\n private final Mode mode;\n private final String nBestExamples;\n private final int nBestCost;\n\n private boolean discardPunctuation;\n private boolean discardCompoundToken;\n\n public KuromojiTokenizerFactory(IndexSettings indexSettings, Environment env, String name, Settings settings) {\n super(indexSettings, settings, name);\n mode = getMode(settings);\n userDictionary = getUserDictionary(env, settings);\n discardPunctuation = settings.getAsBoolean(\"discard_punctuation\", true);\n nBestCost = settings.getAsInt(NBEST_COST, -1);\n nBestExamples = settings.get(NBEST_EXAMPLES);\n discardCompoundToken = settings.getAsBoolean(DISCARD_COMPOUND_TOKEN, false);\n }\n\n public static UserDictionary getUserDictionary(Environment env, Settings settings) {\n if (settings.get(USER_DICT_PATH_OPTION) != null && settings.get(USER_DICT_RULES_OPTION) != null) {\n throw new IllegalArgumentException(\n \"It is not allowed to use [\" + USER_DICT_PATH_OPTION + \"] in conjunction\" + \" with [\" + USER_DICT_RULES_OPTION + \"]\"\n );\n }\n try {\n List<String> ruleList = Analysis.getWordList(env, settings, USER_DICT_PATH_OPTION, USER_DICT_RULES_OPTION, false);\n if (ruleList == null || ruleList.isEmpty()) {\n return null;\n }\n Set<String> dup = new HashSet<>();\n int lineNum = 0;\n for (String line : ruleList) {\n // ignore comments\n if (line.startsWith(\"#\") == false) {\n String[] values = CSVUtil.parse(line);\n if (dup.add(values[0]) == false) {\n throw new IllegalArgumentException(\n \"Found duplicate term [\" + values[0] + \"] in user dictionary \" + \"at line [\" + lineNum + \"]\"\n );\n }\n }\n ++lineNum;\n }\n StringBuilder sb = new StringBuilder();\n for (String line : ruleList) {\n sb.append(line).append(System.lineSeparator());\n }\n return UserDictionary.open(new StringReader(sb.toString()));\n } catch (IOException e) {\n throw new OpenSearchException(\"failed to load kuromoji user dictionary\", e);\n }\n }\n\n public static JapaneseTokenizer.Mode getMode(Settings settings) {\n JapaneseTokenizer.Mode mode = JapaneseTokenizer.DEFAULT_MODE;\n String modeSetting = settings.get(\"mode\", null);\n if (modeSetting != null) {\n if (\"search\".equalsIgnoreCase(modeSetting)) {\n mode = JapaneseTokenizer.Mode.SEARCH;\n } else if (\"normal\".equalsIgnoreCase(modeSetting)) {\n mode = JapaneseTokenizer.Mode.NORMAL;\n } else if (\"extended\".equalsIgnoreCase(modeSetting)) {\n mode = JapaneseTokenizer.Mode.EXTENDED;\n }\n }\n return mode;\n }\n\n @Override\n public Tokenizer create() {\n JapaneseTokenizer t = new JapaneseTokenizer(userDictionary, discardPunctuation, discardCompoundToken, mode);\n int nBestCost = this.nBestCost;\n if (nBestExamples != null) {\n nBestCost = Math.max(nBestCost, t.calcNBestCost(nBestExamples));\n }\n t.setNBestCost(nBestCost);\n return t;\n }\n\n}\n\n## 内容11\n### 文件路径:\nplugins/analysis-nori/src/main/java/org/opensearch/index/analysis/NoriAnalyzerProvider.java\n### 代码内容:\npackage org.opensearch.index.analysis;\n\nimport java.util.List;\nimport java.util.Set;\nimport org.apache.lucene.analysis.ko.KoreanAnalyzer;\nimport org.apache.lucene.analysis.ko.KoreanPartOfSpeechStopFilter;\nimport org.apache.lucene.analysis.ko.KoreanTokenizer;\nimport org.apache.lucene.analysis.ko.dict.UserDictionary;\nimport org.apache.lucene.analysis.ko.POS;\nimport org.opensearch.common.settings.Settings;\nimport org.opensearch.env.Environment;\nimport org.opensearch.index.IndexSettings;\n\nimport static org.opensearch.index.analysis.NoriPartOfSpeechStopFilterFactory.resolvePOSList;\n\npublic class NoriAnalyzerProvider extends AbstractIndexAnalyzerProvider<KoreanAnalyzer> {\n private final KoreanAnalyzer analyzer;\n\n public NoriAnalyzerProvider(IndexSettings indexSettings, Environment env, String name, Settings settings) {\n super(indexSettings, name, settings);\n final KoreanTokenizer.DecompoundMode mode = NoriTokenizerFactory.getMode(settings);\n final UserDictionary userDictionary = NoriTokenizerFactory.getUserDictionary(env, settings);\n final List<String> tagList = Analysis.getWordList(env, settings, \"stoptags\");\n final Set<POS.Tag> stopTags = tagList != null ? resolvePOSList(tagList) : KoreanPartOfSpeechStopFilter.DEFAULT_STOP_TAGS;\n analyzer = new KoreanAnalyzer(userDictionary, mode, stopTags, false);\n }\n\n @Override\n public KoreanAnalyzer get() {\n return analyzer;\n }\n\n}\n\n## 内容12\n### 文件路径:\nplugins/analysis-nori/src/main/java/org/opensearch/index/analysis/NoriPartOfSpeechStopFilterFactory.java\n### 代码内容:\npackage org.opensearch.index.analysis;\n\nimport org.apache.lucene.analysis.TokenStream;\nimport org.apache.lucene.analysis.ko.KoreanPartOfSpeechStopFilter;\nimport org.apache.lucene.analysis.ko.POS;\nimport org.opensearch.common.settings.Settings;\nimport org.opensearch.env.Environment;\nimport org.opensearch.index.IndexSettings;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\npublic class NoriPartOfSpeechStopFilterFactory extends AbstractTokenFilterFactory {\n private final Set<POS.Tag> stopTags;\n\n public NoriPartOfSpeechStopFilterFactory(IndexSettings indexSettings, Environment env, String name, Settings settings) {\n super(indexSettings, name, settings);\n List<String> tagList = Analysis.getWordList(env, settings, \"stoptags\");\n this.stopTags = tagList != null ? resolvePOSList(tagList) : KoreanPartOfSpeechStopFilter.DEFAULT_STOP_TAGS;\n }\n\n @Override\n public TokenStream create(TokenStream tokenStream) {\n return new KoreanPartOfSpeechStopFilter(tokenStream, stopTags);\n }\n\n static Set<POS.Tag> resolvePOSList(List<String> tagList) {\n Set<POS.Tag> stopTags = new HashSet<>();\n for (String tag : tagList) {\n stopTags.add(POS.resolveTag(tag));\n }\n return stopTags;\n }\n}\n\n## 内容13\n### 文件路径:\nplugins/analysis-nori/src/main/java/org/opensearch/index/analysis/NoriTokenizerFactory.java\n### 代码内容:\npackage org.opensearch.index.analysis;\n\nimport org.apache.lucene.analysis.Tokenizer;\nimport org.apache.lucene.analysis.ko.KoreanTokenizer;\nimport org.apache.lucene.analysis.ko.dict.UserDictionary;\nimport org.opensearch.OpenSearchException;\nimport org.opensearch.common.settings.Settings;\nimport org.opensearch.env.Environment;\nimport org.opensearch.index.IndexSettings;\n\nimport java.io.IOException;\nimport java.io.Reader;\nimport java.io.StringReader;\nimport java.util.List;\nimport java.util.Locale;\n\npublic class NoriTokenizerFactory extends AbstractTokenizerFactory {\n private static final String USER_DICT_PATH_OPTION = \"user_dictionary\";\n private static final String USER_DICT_RULES_OPTION = \"user_dictionary_rules\";\n\n private final UserDictionary userDictionary;\n private final KoreanTokenizer.DecompoundMode decompoundMode;\n private final boolean discardPunctuation;\n\n public NoriTokenizerFactory(IndexSettings indexSettings, Environment env, String name, Settings settings) {\n super(indexSettings, settings, name);\n decompoundMode = getMode(settings);\n userDictionary = getUserDictionary(env, settings);\n discardPunctuation = settings.getAsBoolean(\"discard_punctuation\", true);\n }\n\n public static UserDictionary getUserDictionary(Environment env, Settings settings) {\n if (settings.get(USER_DICT_PATH_OPTION) != null && settings.get(USER_DICT_RULES_OPTION) != null) {\n throw new IllegalArgumentException(\n \"It is not allowed to use [\" + USER_DICT_PATH_OPTION + \"] in conjunction\" + \" with [\" + USER_DICT_RULES_OPTION + \"]\"\n );\n }\n List<String> ruleList = Analysis.getWordList(env, settings, USER_DICT_PATH_OPTION, USER_DICT_RULES_OPTION, true);\n StringBuilder sb = new StringBuilder();\n if (ruleList == null || ruleList.isEmpty()) {\n return null;\n }\n for (String line : ruleList) {\n sb.append(line).append(System.lineSeparator());\n }\n try (Reader rulesReader = new StringReader(sb.toString())) {\n return UserDictionary.open(rulesReader);\n } catch (IOException e) {\n throw new OpenSearchException(\"failed to load nori user dictionary\", e);\n }\n }\n\n public static KoreanTokenizer.DecompoundMode getMode(Settings settings) {\n KoreanTokenizer.DecompoundMode mode = KoreanTokenizer.DEFAULT_DECOMPOUND;\n String modeSetting = settings.get(\"decompound_mode\", null);\n if (modeSetting != null) {\n mode = KoreanTokenizer.DecompoundMode.valueOf(modeSetting.toUpperCase(Locale.ENGLISH));\n }\n return mode;\n }\n\n @Override\n public Tokenizer create() {\n return new KoreanTokenizer(\n KoreanTokenizer.DEFAULT_TOKEN_ATTRIBUTE_FACTORY,\n userDictionary,\n decompoundMode,\n false,\n discardPunctuation\n );\n }\n\n}\n\n## 内容14\n### 文件路径:\nserver/src/main/java/org/opensearch/index/analysis/Analysis.java\n### 代码内容:\npackage org.opensearch.index.analysis;\n\nimport org.apache.lucene.analysis.CharArraySet;\nimport org.apache.lucene.analysis.ar.ArabicAnalyzer;\nimport org.apache.lucene.analysis.bg.BulgarianAnalyzer;\nimport org.apache.lucene.analysis.bn.BengaliAnalyzer;\nimport org.apache.lucene.analysis.br.BrazilianAnalyzer;\nimport org.apache.lucene.analysis.ca.CatalanAnalyzer;\nimport org.apache.lucene.analysis.ckb.SoraniAnalyzer;\nimport org.apache.lucene.analysis.cz.CzechAnalyzer;\nimport org.apache.lucene.analysis.da.DanishAnalyzer;\nimport org.apache.lucene.analysis.de.GermanAnalyzer;\nimport org.apache.lucene.analysis.el.GreekAnalyzer;\nimport org.apache.lucene.analysis.en.EnglishAnalyzer;\nimport org.apache.lucene.analysis.es.SpanishAnalyzer;\nimport org.apache.lucene.analysis.et.EstonianAnalyzer;\nimport org.apache.lucene.analysis.eu.BasqueAnalyzer;\nimport org.apache.lucene.analysis.fa.PersianAnalyzer;\nimport org.apache.lucene.analysis.fi.FinnishAnalyzer;\nimport org.apache.lucene.analysis.fr.FrenchAnalyzer;\nimport org.apache.lucene.analysis.ga.IrishAnalyzer;\nimport org.apache.lucene.analysis.gl.GalicianAnalyzer;\nimport org.apache.lucene.analysis.hi.HindiAnalyzer;\nimport org.apache.lucene.analysis.hu.HungarianAnalyzer;\nimport org.apache.lucene.analysis.hy.ArmenianAnalyzer;\nimport org.apache.lucene.analysis.id.IndonesianAnalyzer;\nimport org.apache.lucene.analysis.it.ItalianAnalyzer;\nimport org.apache.lucene.analysis.lt.LithuanianAnalyzer;\nimport org.apache.lucene.analysis.lv.LatvianAnalyzer;\nimport org.apache.lucene.analysis.nl.DutchAnalyzer;\nimport org.apache.lucene.analysis.no.NorwegianAnalyzer;\nimport org.apache.lucene.analysis.pt.PortugueseAnalyzer;\nimport org.apache.lucene.analysis.ro.RomanianAnalyzer;\nimport org.apache.lucene.analysis.ru.RussianAnalyzer;\nimport org.apache.lucene.analysis.sv.SwedishAnalyzer;\nimport org.apache.lucene.analysis.th.ThaiAnalyzer;\nimport org.apache.lucene.analysis.tr.TurkishAnalyzer;\nimport org.opensearch.common.Strings;\nimport org.opensearch.common.settings.Settings;\nimport org.opensearch.env.Environment;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.Reader;\nimport java.nio.charset.CharacterCodingException;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static java.util.Collections.unmodifiableMap;\n\n/**\n * Core analysis class\n *\n * @opensearch.internal\n */\npublic class Analysis {\n\n public static CharArraySet parseStemExclusion(Settings settings, CharArraySet defaultStemExclusion) {\n String value = settings.get(\"stem_exclusion\");\n if (\"_none_\".equals(value)) {\n return CharArraySet.EMPTY_SET;\n }\n List<String> stemExclusion = settings.getAsList(\"stem_exclusion\", null);\n if (stemExclusion != null) {\n // LUCENE 4 UPGRADE: Should be settings.getAsBoolean(\"stem_exclusion_case\", false)?\n return new CharArraySet(stemExclusion, false);\n } else {\n return defaultStemExclusion;\n }\n }\n\n public static final Map<String, Set<?>> NAMED_STOP_WORDS;\n static {\n Map<String, Set<?>> namedStopWords = new HashMap<>();\n namedStopWords.put(\"_arabic_\", ArabicAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_armenian_\", ArmenianAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_basque_\", BasqueAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_bengali_\", BengaliAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_brazilian_\", BrazilianAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_bulgarian_\", BulgarianAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_catalan_\", CatalanAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_czech_\", CzechAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_danish_\", DanishAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_dutch_\", DutchAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_english_\", EnglishAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_estonian_\", EstonianAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_finnish_\", FinnishAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_french_\", FrenchAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_galician_\", GalicianAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_german_\", GermanAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_greek_\", GreekAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_hindi_\", HindiAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_hungarian_\", HungarianAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_indonesian_\", IndonesianAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_irish_\", IrishAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_italian_\", ItalianAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_latvian_\", LatvianAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_lithuanian_\", LithuanianAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_norwegian_\", NorwegianAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_persian_\", PersianAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_portuguese_\", PortugueseAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_romanian_\", RomanianAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_russian_\", RussianAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_sorani_\", SoraniAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_spanish_\", SpanishAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_swedish_\", SwedishAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_thai_\", ThaiAnalyzer.getDefaultStopSet());\n namedStopWords.put(\"_turkish_\", TurkishAnalyzer.getDefaultStopSet());\n\n NAMED_STOP_WORDS = unmodifiableMap(namedStopWords);\n }\n\n public static CharArraySet parseWords(\n Environment env,\n Settings settings,\n String name,\n CharArraySet defaultWords,\n Map<String, Set<?>> namedWords,\n boolean ignoreCase\n ) {\n String value = settings.get(name);\n if (value != null) {\n if (\"_none_\".equals(value)) {\n return CharArraySet.EMPTY_SET;\n } else {\n return resolveNamedWords(settings.getAsList(name), namedWords, ignoreCase);\n }\n }\n List<String> pathLoadedWords = getWordList(env, settings, name);\n if (pathLoadedWords != null) {\n return resolveNamedWords(pathLoadedWords, namedWords, ignoreCase);\n }\n return defaultWords;\n }\n\n public static CharArraySet parseCommonWords(Environment env, Settings settings, CharArraySet defaultCommonWords, boolean ignoreCase) {\n return parseWords(env, settings, \"common_words\", defaultCommonWords, NAMED_STOP_WORDS, ignoreCase);\n }\n\n public static CharArraySet parseArticles(Environment env, Settings settings) {\n boolean articlesCase = settings.getAsBoolean(\"articles_case\", false);\n return parseWords(env, settings, \"articles\", null, null, articlesCase);\n }\n\n public static CharArraySet parseStopWords(Environment env, Settings settings, CharArraySet defaultStopWords) {\n boolean stopwordsCase = settings.getAsBoolean(\"stopwords_case\", false);\n return parseStopWords(env, settings, defaultStopWords, stopwordsCase);\n }\n\n public static CharArraySet parseStopWords(Environment env, Settings settings, CharArraySet defaultStopWords, boolean ignoreCase) {\n return parseWords(env, settings, \"stopwords\", defaultStopWords, NAMED_STOP_WORDS, ignoreCase);\n }\n\n private static CharArraySet resolveNamedWords(Collection<String> words, Map<String, Set<?>> namedWords, boolean ignoreCase) {\n if (namedWords == null) {\n return new CharArraySet(words, ignoreCase);\n }\n CharArraySet setWords = new CharArraySet(words.size(), ignoreCase);\n for (String word : words) {\n if (namedWords.containsKey(word)) {\n setWords.addAll(namedWords.get(word));\n } else {\n setWords.add(word);\n }\n }\n return setWords;\n }\n\n public static CharArraySet getWordSet(Environment env, Settings settings, String settingsPrefix) {\n List<String> wordList = getWordList(env, settings, settingsPrefix);\n if (wordList == null) {\n return null;\n }\n boolean ignoreCase = settings.getAsBoolean(settingsPrefix + \"_case\", false);\n return new CharArraySet(wordList, ignoreCase);\n }\n\n /**\n * Fetches a list of words from the specified settings file. The list should either be available at the key\n * specified by settingsPrefix or in a file specified by settingsPrefix + _path.\n *\n * @throws IllegalArgumentException\n * If the word list cannot be found at either key.\n */\n public static List<String> getWordList(Environment env, Settings settings, String settingPrefix) {\n return getWordList(env, settings, settingPrefix + \"_path\", settingPrefix, true);\n }\n\n /**\n * Fetches a list of words from the specified settings file. The list should either be available at the key\n * specified by <code>settingList</code> or in a file specified by <code>settingPath</code>.\n *\n * @throws IllegalArgumentException\n * If the word list cannot be found at either key.\n */\n public static List<String> getWordList(\n Environment env,\n Settings settings,\n String settingPath,\n String settingList,\n boolean removeComments\n ) {\n String wordListPath = settings.get(settingPath, null);\n\n if (wordListPath == null) {\n List<String> explicitWordList = settings.getAsList(settingList, null);\n if (explicitWordList == null) {\n return null;\n } else {\n return explicitWordList;\n }\n }\n\n final Path path = env.configDir().resolve(wordListPath);\n\n try {\n return loadWordList(path, removeComments);\n } catch (CharacterCodingException ex) {\n String message = String.format(\n Locale.ROOT,\n \"Unsupported character encoding detected while reading %s: %s - files must be UTF-8 encoded\",\n settingPath,\n path.toString()\n );\n throw new IllegalArgumentException(message, ex);\n } catch (IOException ioe) {\n String message = String.format(Locale.ROOT, \"IOException while reading %s: %s\", settingPath, path.toString());\n throw new IllegalArgumentException(message, ioe);\n }\n }\n\n private static List<String> loadWordList(Path path, boolean removeComments) throws IOException {\n final List<String> result = new ArrayList<>();\n try (BufferedReader br = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {\n String word;\n while ((word = br.readLine()) != null) {\n if (Strings.hasText(word) == false) {\n continue;\n }\n if (removeComments == false || word.startsWith(\"#\") == false) {\n result.add(word.trim());\n }\n }\n }\n return result;\n }\n\n /**\n * @return null If no settings set for \"settingsPrefix\" then return <code>null</code>.\n * @throws IllegalArgumentException\n * If the Reader can not be instantiated.\n */\n public static Reader getReaderFromFile(Environment env, Settings settings, String settingPrefix) {\n String filePath = settings.get(settingPrefix, null);\n\n if (filePath == null) {\n return null;\n }\n final Path path = env.configDir().resolve(filePath);\n try {\n return Files.newBufferedReader(path, StandardCharsets.UTF_8);\n } catch (CharacterCodingException ex) {\n String message = String.format(\n Locale.ROOT,\n \"Unsupported character encoding detected while reading %s_path: %s files must be UTF-8 encoded\",\n settingPrefix,\n path.toString()\n );\n throw new IllegalArgumentException(message, ex);\n } catch (IOException ioe) {\n String message = String.format(Locale.ROOT, \"IOException while reading %s_path: %s\", settingPrefix, path.toString());\n throw new IllegalArgumentException(message, ioe);\n }\n }\n\n}\n\n## 内容15\n### 文件路径:\nserver/src/main/java/org/opensearch/indices/analysis/HunspellService.java\n### 代码内容:\npackage org.opensearch.indices.analysis;\n\nimport org.apache.logging.log4j.LogManager;\nimport org.apache.logging.log4j.Logger;\nimport org.apache.logging.log4j.message.ParameterizedMessage;\nimport org.apache.lucene.analysis.hunspell.Dictionary;\nimport org.apache.lucene.store.Directory;\nimport org.apache.lucene.store.NIOFSDirectory;\nimport org.opensearch.OpenSearchException;\nimport org.opensearch.core.internal.io.IOUtils;\nimport org.opensearch.common.io.FileSystemUtils;\nimport org.opensearch.common.settings.Setting;\nimport org.opensearch.common.settings.Setting.Property;\nimport org.opensearch.common.settings.Settings;\nimport org.opensearch.env.Environment;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.file.DirectoryStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\n\n/**\n * Serves as a node level registry for hunspell dictionaries. This services expects all dictionaries to be located under\n * the {@code <path.conf>/hunspell} directory, where each locale has its dedicated sub-directory which holds the dictionary\n * files. For example, the dictionary files for {@code en_US} locale must be placed under {@code <path.conf>/hunspell/en_US}\n * directory.\n * <p>\n * The following settings can be set for each dictionary:\n * <ul>\n * <li>{@code ignore_case} - If true, dictionary matching will be case insensitive (defaults to {@code false})</li>\n * <li>{@code strict_affix_parsing} - Determines whether errors while reading a affix rules file will cause exception or simple be ignored\n * (defaults to {@code true})</li>\n * </ul>\n * <p>\n * These settings can either be configured as node level configuration, such as:\n * <br><br>\n * <pre><code>\n * indices.analysis.hunspell.dictionary.en_US.ignore_case: true\n * indices.analysis.hunspell.dictionary.en_US.strict_affix_parsing: false\n * </code></pre>\n * <p>\n * or, as dedicated configuration per dictionary, placed in a {@code settings.yml} file under the dictionary directory. For\n * example, the following can be the content of the {@code <path.config>/hunspell/en_US/settings.yml} file:\n * <br><br>\n * <pre><code>\n * ignore_case: true\n * strict_affix_parsing: false\n * </code></pre>\n *\n * @see org.opensearch.index.analysis.HunspellTokenFilterFactory\n *\n * @opensearch.internal\n */\npublic class HunspellService {\n\n private static final Logger logger = LogManager.getLogger(HunspellService.class);\n\n public static final Setting<Boolean> HUNSPELL_LAZY_LOAD = Setting.boolSetting(\n \"indices.analysis.hunspell.dictionary.lazy\",\n Boolean.FALSE,\n Property.NodeScope\n );\n public static final Setting<Boolean> HUNSPELL_IGNORE_CASE = Setting.boolSetting(\n \"indices.analysis.hunspell.dictionary.ignore_case\",\n Boolean.FALSE,\n Property.NodeScope\n );\n public static final Setting<Settings> HUNSPELL_DICTIONARY_OPTIONS = Setting.groupSetting(\n \"indices.analysis.hunspell.dictionary.\",\n Property.NodeScope\n );\n private final ConcurrentHashMap<String, Dictionary> dictionaries = new ConcurrentHashMap<>();\n private final Map<String, Dictionary> knownDictionaries;\n private final boolean defaultIgnoreCase;\n private final Path hunspellDir;\n private final Function<String, Dictionary> loadingFunction;\n\n public HunspellService(final Settings settings, final Environment env, final Map<String, Dictionary> knownDictionaries)\n throws IOException {\n this.knownDictionaries = Collections.unmodifiableMap(knownDictionaries);\n this.hunspellDir = resolveHunspellDirectory(env);\n this.defaultIgnoreCase = HUNSPELL_IGNORE_CASE.get(settings);\n this.loadingFunction = (locale) -> {\n try {\n return loadDictionary(locale, settings, env);\n } catch (Exception e) {\n throw new IllegalStateException(\"failed to load hunspell dictionary for locale: \" + locale, e);\n }\n };\n if (!HUNSPELL_LAZY_LOAD.get(settings)) {\n scanAndLoadDictionaries();\n }\n\n }\n\n /**\n * Returns the hunspell dictionary for the given locale.\n *\n * @param locale The name of the locale\n */\n public Dictionary getDictionary(String locale) {\n Dictionary dictionary = knownDictionaries.get(locale);\n if (dictionary == null) {\n dictionary = dictionaries.computeIfAbsent(locale, loadingFunction);\n }\n return dictionary;\n }\n\n private Path resolveHunspellDirectory(Environment env) {\n return env.configDir().resolve(\"hunspell\");\n }\n\n /**\n * Scans the hunspell directory and loads all found dictionaries\n */\n private void scanAndLoadDictionaries() throws IOException {\n if (Files.isDirectory(hunspellDir)) {\n try (DirectoryStream<Path> stream = Files.newDirectoryStream(hunspellDir)) {\n for (Path file : stream) {\n if (Files.isDirectory(file)) {\n try (DirectoryStream<Path> inner = Files.newDirectoryStream(hunspellDir.resolve(file), \"*.dic\")) {\n if (inner.iterator().hasNext()) { // just making sure it's indeed a dictionary dir\n try {\n getDictionary(file.getFileName().toString());\n } catch (Exception e) {\n // The cache loader throws unchecked exception (see #loadDictionary()),\n // here we simply report the exception and continue loading the dictionaries\n logger.error(\n () -> new ParameterizedMessage(\"exception while loading dictionary {}\", file.getFileName()),\n e\n );\n }\n }\n }\n }\n }\n }\n }\n }\n\n /**\n * Loads the hunspell dictionary for the given local.\n *\n * @param locale The locale of the hunspell dictionary to be loaded.\n * @param nodeSettings The node level settings\n * @param env The node environment (from which the conf path will be resolved)\n * @return The loaded Hunspell dictionary\n * @throws Exception when loading fails (due to IO errors or malformed dictionary files)\n */\n private Dictionary loadDictionary(String locale, Settings nodeSettings, Environment env) throws Exception {\n if (logger.isDebugEnabled()) {\n logger.debug(\"Loading hunspell dictionary [{}]...\", locale);\n }\n Path dicDir = hunspellDir.resolve(locale);\n if (FileSystemUtils.isAccessibleDirectory(dicDir, logger) == false) {\n throw new OpenSearchException(String.format(Locale.ROOT, \"Could not find hunspell dictionary [%s]\", locale));\n }\n\n // merging node settings with hunspell dictionary specific settings\n Settings dictSettings = HUNSPELL_DICTIONARY_OPTIONS.get(nodeSettings);\n nodeSettings = loadDictionarySettings(dicDir, dictSettings.getByPrefix(locale + \".\"));\n\n boolean ignoreCase = nodeSettings.getAsBoolean(\"ignore_case\", defaultIgnoreCase);\n\n Path[] affixFiles = FileSystemUtils.files(dicDir, \"*.aff\");\n if (affixFiles.length == 0) {\n throw new OpenSearchException(String.format(Locale.ROOT, \"Missing affix file for hunspell dictionary [%s]\", locale));\n }\n if (affixFiles.length != 1) {\n throw new OpenSearchException(String.format(Locale.ROOT, \"Too many affix files exist for hunspell dictionary [%s]\", locale));\n }\n InputStream affixStream = null;\n\n Path[] dicFiles = FileSystemUtils.files(dicDir, \"*.dic\");\n List<InputStream> dicStreams = new ArrayList<>(dicFiles.length);\n try {\n\n for (int i = 0; i < dicFiles.length; i++) {\n dicStreams.add(Files.newInputStream(dicFiles[i]));\n }\n\n affixStream = Files.newInputStream(affixFiles[0]);\n\n try (Directory tmp = new NIOFSDirectory(env.tmpDir())) {\n return new Dictionary(tmp, \"hunspell\", affixStream, dicStreams, ignoreCase);\n }\n\n } catch (Exception e) {\n logger.error(() -> new ParameterizedMessage(\"Could not load hunspell dictionary [{}]\", locale), e);\n throw e;\n } finally {\n IOUtils.close(affixStream);\n IOUtils.close(dicStreams);\n }\n }\n\n /**\n * Each hunspell dictionary directory may contain a {@code settings.yml} which holds dictionary specific settings. Default\n * values for these settings are defined in the given default settings.\n *\n * @param dir The directory of the dictionary\n * @param defaults The default settings for this dictionary\n * @return The resolved settings.\n */\n private static Settings loadDictionarySettings(Path dir, Settings defaults) throws IOException {\n Path file = dir.resolve(\"settings.yml\");\n if (Files.exists(file)) {\n return Settings.builder().loadFromPath(file).put(defaults).build();\n }\n\n file = dir.resolve(\"settings.json\");\n if (Files.exists(file)) {\n return Settings.builder().loadFromPath(file).put(defaults).build();\n }\n\n return defaults;\n }\n}\n\n", "input": "", "output": "【漏洞类型】错误处理不当\n【严重性】Low\n【漏洞摘要】代码在读取分析器映射规则时没有足够的错误处理机制,可能导致异常在处理过程中被忽略或未记录,从而影响系统的稳定性和安全性。具体表现为`getWordList`方法在处理错误时没有记录错误信息,仅抛出异常。\n【关键代码模式】\n- `Analysis.getWordList`\n- `Analysis.parseWordList`\n- `IllegalArgumentException`\n- `RuntimeException`\n【修复方法说明】\n- 添加了新的`parseWordList`方法,该方法接受一个解析器作为参数,能够读取自定义规则、解析和处理错误。\n- 对于位于配置目录之外的规则文件,减少了错误消息的冗余性。\n- 通过记录错误信息到日志,增强了错误处理的可见性。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\ndatahub-frontend/app/auth/sso/oidc/custom/CustomOidcAuthenticator.java\n### 代码内容:\npackage auth.sso.oidc.custom;\n\nimport com.nimbusds.oauth2.sdk.AuthorizationGrant;\nimport com.nimbusds.oauth2.sdk.AuthorizationCode;\nimport com.nimbusds.oauth2.sdk.AuthorizationCodeGrant;\nimport com.nimbusds.oauth2.sdk.ParseException;\nimport com.nimbusds.oauth2.sdk.TokenErrorResponse;\nimport com.nimbusds.oauth2.sdk.TokenRequest;\nimport com.nimbusds.oauth2.sdk.TokenResponse;\nimport com.nimbusds.oauth2.sdk.auth.ClientAuthentication;\nimport com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod;\nimport com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;\nimport com.nimbusds.oauth2.sdk.auth.ClientSecretPost;\nimport com.nimbusds.oauth2.sdk.auth.Secret;\nimport com.nimbusds.oauth2.sdk.http.HTTPRequest;\nimport com.nimbusds.oauth2.sdk.http.HTTPResponse;\nimport com.nimbusds.oauth2.sdk.id.ClientID;\nimport com.nimbusds.openid.connect.sdk.OIDCTokenResponse;\nimport com.nimbusds.openid.connect.sdk.OIDCTokenResponseParser;\nimport com.nimbusds.openid.connect.sdk.token.OIDCTokens;\nimport java.io.IOException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Optional;\nimport org.pac4j.core.context.WebContext;\nimport org.pac4j.core.credentials.authenticator.Authenticator;\nimport org.pac4j.core.exception.TechnicalException;\nimport org.pac4j.core.util.CommonHelper;\nimport org.pac4j.oidc.client.OidcClient;\nimport org.pac4j.oidc.config.OidcConfiguration;\nimport org.pac4j.oidc.credentials.OidcCredentials;\nimport org.pac4j.oidc.credentials.authenticator.OidcAuthenticator;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class CustomOidcAuthenticator implements Authenticator<OidcCredentials> {\n\n private static final Logger logger = LoggerFactory.getLogger(OidcAuthenticator.class);\n\n private static final Collection<ClientAuthenticationMethod> SUPPORTED_METHODS =\n Arrays.asList(\n ClientAuthenticationMethod.CLIENT_SECRET_POST,\n ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n ClientAuthenticationMethod.NONE);\n\n protected OidcConfiguration configuration;\n\n protected OidcClient<OidcConfiguration> client;\n\n private final ClientAuthentication clientAuthentication;\n\n public CustomOidcAuthenticator(final OidcClient<OidcConfiguration> client) {\n CommonHelper.assertNotNull(\"configuration\", client.getConfiguration());\n CommonHelper.assertNotNull(\"client\", client);\n this.configuration = client.getConfiguration();\n this.client = client;\n\n // check authentication methods\n final List<ClientAuthenticationMethod> metadataMethods = configuration.findProviderMetadata().getTokenEndpointAuthMethods();\n\n final ClientAuthenticationMethod preferredMethod = getPreferredAuthenticationMethod(configuration);\n\n final ClientAuthenticationMethod chosenMethod;\n if (CommonHelper.isNotEmpty(metadataMethods)) {\n if (preferredMethod != null) {\n if (ClientAuthenticationMethod.NONE.equals(preferredMethod) || metadataMethods.contains(preferredMethod)) {\n chosenMethod = preferredMethod;\n } else {\n throw new TechnicalException(\n \"Preferred authentication method (\"\n + preferredMethod\n + \") not supported \"\n + \"by provider according to provider metadata (\"\n + metadataMethods\n + \").\");\n }\n } else {\n chosenMethod = firstSupportedMethod(metadataMethods);\n }\n } else {\n chosenMethod = preferredMethod != null ? preferredMethod : ClientAuthenticationMethod.getDefault();\n logger.info(\"Provider metadata does not provide Token endpoint authentication methods. Using: {}\",\n chosenMethod);\n }\n\n final ClientID clientID = new ClientID(configuration.getClientId());\n if (ClientAuthenticationMethod.CLIENT_SECRET_POST.equals(chosenMethod)) {\n final Secret secret = new Secret(configuration.getSecret());\n clientAuthentication = new ClientSecretPost(clientID, secret);\n } else if (ClientAuthenticationMethod.CLIENT_SECRET_BASIC.equals(chosenMethod)) {\n final Secret secret = new Secret(configuration.getSecret());\n clientAuthentication = new ClientSecretBasic(clientID, secret);\n } else if (ClientAuthenticationMethod.NONE.equals(chosenMethod)) {\n clientAuthentication = null; // No client authentication in none mode\n } else {\n throw new TechnicalException(\"Unsupported client authentication method: \" + chosenMethod);\n }\n }\n\n /**\n * The preferred {@link ClientAuthenticationMethod} specified in the given\n * {@link OidcConfiguration}, or <code>null</code> meaning that the a\n * provider-supported method should be chosen.\n */\n private static ClientAuthenticationMethod getPreferredAuthenticationMethod(OidcConfiguration config) {\n final ClientAuthenticationMethod configurationMethod = config.getClientAuthenticationMethod();\n if (configurationMethod == null) {\n return null;\n }\n\n if (!SUPPORTED_METHODS.contains(configurationMethod)) {\n throw new TechnicalException(\"Configured authentication method (\" + configurationMethod + \") is not supported.\");\n }\n\n return configurationMethod;\n }\n\n /**\n * The first {@link ClientAuthenticationMethod} from the given list of\n * methods that is supported by this implementation.\n *\n * @throws TechnicalException\n * if none of the provider-supported methods is supported.\n */\n private static ClientAuthenticationMethod firstSupportedMethod(final List<ClientAuthenticationMethod> metadataMethods) {\n Optional<ClientAuthenticationMethod> firstSupported =\n metadataMethods.stream().filter((m) -> SUPPORTED_METHODS.contains(m)).findFirst();\n if (firstSupported.isPresent()) {\n return firstSupported.get();\n } else {\n throw new TechnicalException(\"None of the Token endpoint provider metadata authentication methods are supported: \"\n + metadataMethods);\n }\n }\n\n @Override\n public void validate(final OidcCredentials credentials, final WebContext context) {\n final AuthorizationCode code = credentials.getCode();\n // if we have a code\n if (code != null) {\n try {\n final String computedCallbackUrl = client.computeFinalCallbackUrl(context);\n // Token request\n final TokenRequest request = createTokenRequest(new AuthorizationCodeGrant(code, new URI(computedCallbackUrl)));\n HTTPRequest tokenHttpRequest = request.toHTTPRequest();\n tokenHttpRequest.setConnectTimeout(configuration.getConnectTimeout());\n tokenHttpRequest.setReadTimeout(configuration.getReadTimeout());\n\n final HTTPResponse httpResponse = tokenHttpRequest.send();\n logger.debug(\"Token response: status={}, content={}\", httpResponse.getStatusCode(),\n httpResponse.getContent());\n\n final TokenResponse response = OIDCTokenResponseParser.parse(httpResponse);\n if (response instanceof TokenErrorResponse) {\n throw new TechnicalException(\"Bad token response, error=\" + ((TokenErrorResponse) response).getErrorObject());\n }\n logger.debug(\"Token response successful\");\n final OIDCTokenResponse tokenSuccessResponse = (OIDCTokenResponse) response;\n\n // save tokens in credentials\n final OIDCTokens oidcTokens = tokenSuccessResponse.getOIDCTokens();\n credentials.setAccessToken(oidcTokens.getAccessToken());\n credentials.setRefreshToken(oidcTokens.getRefreshToken());\n credentials.setIdToken(oidcTokens.getIDToken());\n\n } catch (final URISyntaxException | IOException | ParseException e) {\n throw new TechnicalException(e);\n }\n }\n }\n\n private TokenRequest createTokenRequest(final AuthorizationGrant grant) {\n if (clientAuthentication != null) {\n return new TokenRequest(configuration.findProviderMetadata().getTokenEndpointURI(),\n this.clientAuthentication, grant);\n } else {\n return new TokenRequest(configuration.findProviderMetadata().getTokenEndpointURI(),\n new ClientID(configuration.getClientId()), grant);\n }\n }\n}\n\n", "input": "", "output": "【漏洞类型】认证漏洞\n【严重性】High\n【漏洞摘要】原始代码在处理OpenID Connect认证时,由于未正确验证或处理Authorization Code Grant中的code_verifier参数,可能允许攻击者通过重放攻击或其他方式获得未经授权的访问。具体的漏洞出现在`validate`方法中,当创建`TokenRequest`时,没有生成或验证`code_verifier`参数,这在使用Proof Key for Code Exchange (PKCE)的场景下是一个严重问题。\n【关键代码模式】AuthorizationCodeGrant, TokenRequest, clientAuthentication, createTokenRequest, validate\n【修复方法说明】修复方法通过从配置中检索`code_verifier`参数并将其添加到`AuthorizationCodeGrant`中,以确保在PKCE流程中正确验证code。这遵循了OpenID Connect的最佳实践,加强了认证的安全性。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nopc-ua-sdk/sdk-server/src/main/java/org/eclipse/milo/opcua/sdk/server/api/config/OpcUaServerConfigLimits.java\n### 代码内容:\npackage org.eclipse.milo.opcua.sdk.server.api.config;\n\nimport java.util.concurrent.TimeUnit;\n\nimport org.eclipse.milo.opcua.stack.core.channel.EncodingLimits;\nimport org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;\nimport org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UShort;\n\nimport static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;\nimport static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ushort;\n\npublic interface OpcUaServerConfigLimits {\n\n /**\n * Get the maximum number of sessions that can be open at any given time.\n *\n * @return the maximum number of sessions that can be open at any given time.\n */\n default UInteger getMaxSessionCount() {\n return uint(100);\n }\n\n /**\n * Get the maximum session timeout, in milliseconds, allowed by the server.\n * <p>\n * Requests by clients for larger timeouts will be revised down to this limit.\n *\n * @return the maximum session timeout, in milliseconds, allowed by the server.\n */\n default Double getMaxSessionTimeout() {\n return 2d * 60_000d;\n }\n\n /**\n * Get the minimum allowed publishing interval.\n *\n * @return the minimum allowed publishing interval.\n */\n default Double getMinPublishingInterval() {\n return 10.0;\n }\n\n /**\n * Get the maximum allowed publishing interval.\n *\n * @return the maximum allowed publishing interval.\n */\n default Double getMaxPublishingInterval() {\n return (double) TimeUnit.MILLISECONDS.convert(8, TimeUnit.HOURS);\n }\n\n /**\n * Get the default publishing interval, used when the requested interval is either invalid or below the minimum.\n *\n * @return the default publishing interval.\n */\n default Double getDefaultPublishingInterval() {\n return 250.0;\n }\n\n /**\n * Get the minimum subscription lifetime, in milliseconds.\n * <p>\n * This value should be larger than the configured minimum publishing interval.\n *\n * @return the minimum subscription lifetime, in milliseconds.\n */\n default Double getMinSubscriptionLifetime() {\n return 10_000.0;\n }\n\n /**\n * Get the maximum subscription lifetime, in milliseconds.\n * <p>\n * This value should be larger than the configured maximum publishing interval.\n *\n * @return the maximum subscription lifetime, in milliseconds.\n */\n default Double getMaxSubscriptionLifetime() {\n return (double) TimeUnit.MILLISECONDS.convert(24, TimeUnit.HOURS);\n }\n\n default Double getMaxSupportedSampleRate() {\n return (double) TimeUnit.MILLISECONDS.convert(24, TimeUnit.HOURS);\n }\n\n /**\n * Get the maximum allowed password length.\n *\n * @return the maximum allowed password length.\n */\n default UInteger getMaxPasswordLength() {\n return uint(1024);\n }\n\n /**\n * Get the maximum number of MonitoredItems, across all Sessions, that can be created.\n *\n * @return the maximum number of MonitoredItems, across all Sessions, that can be created.\n */\n default UInteger getMaxMonitoredItems() {\n return uint(Integer.MAX_VALUE);\n }\n\n //region ServerCapabilities\n\n default Double getMinSupportedSampleRate() {\n return 0.0;\n }\n\n default UInteger getMaxArrayLength() {\n return uint(EncodingLimits.DEFAULT_MAX_MESSAGE_SIZE / 2);\n }\n\n default UInteger getMaxStringLength() {\n return uint(EncodingLimits.DEFAULT_MAX_MESSAGE_SIZE / 2);\n }\n\n default UInteger getMaxByteStringLength() {\n return uint(EncodingLimits.DEFAULT_MAX_MESSAGE_SIZE / 2);\n }\n\n default UShort getMaxBrowseContinuationPoints() {\n return ushort(250);\n }\n\n default UShort getMaxQueryContinuationPoints() {\n return ushort(250);\n }\n\n default UShort getMaxHistoryContinuationPoints() {\n return ushort(250);\n }\n\n //endregion\n\n //region OperationLimits\n\n default UInteger getMaxNodesPerRead() {\n return uint(10_000);\n }\n\n default UInteger getMaxNodesPerWrite() {\n return uint(10_000);\n }\n\n default UInteger getMaxMonitoredItemsPerCall() {\n return uint(10_000);\n }\n\n default UInteger getMaxNodesPerRegisterNodes() {\n return uint(250);\n }\n\n default UInteger getMaxNodesPerHistoryReadData() {\n return uint(250);\n }\n\n default UInteger getMaxNodesPerHistoryReadEvents() {\n return uint(250);\n }\n\n default UInteger getMaxNodesPerHistoryUpdateData() {\n return uint(250);\n }\n\n default UInteger getMaxNodesPerHistoryUpdateEvents() {\n return uint(250);\n }\n\n default UInteger getMaxNodesPerMethodCall() {\n return uint(250);\n }\n\n default UInteger getMaxNodesPerBrowse() {\n return uint(250);\n }\n\n default UInteger getMaxNodesPerTranslateBrowsePathsToNodeIds() {\n return uint(250);\n }\n\n default UInteger getMaxNodesPerNodeManagement() {\n return uint(250);\n }\n\n //endregion\n\n}\n\n## 内容2\n### 文件路径:\nopc-ua-sdk/sdk-server/src/main/java/org/eclipse/milo/opcua/sdk/server/subscriptions/SubscriptionManager.java\n### 代码内容:\npackage org.eclipse.milo.opcua.sdk.server.subscriptions;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.stream.Stream;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport io.netty.util.AttributeKey;\nimport org.eclipse.milo.opcua.sdk.core.AccessLevel;\nimport org.eclipse.milo.opcua.sdk.core.NumericRange;\nimport org.eclipse.milo.opcua.sdk.core.Reference;\nimport org.eclipse.milo.opcua.sdk.server.OpcUaServer;\nimport org.eclipse.milo.opcua.sdk.server.Session;\nimport org.eclipse.milo.opcua.sdk.server.api.DataItem;\nimport org.eclipse.milo.opcua.sdk.server.api.EventItem;\nimport org.eclipse.milo.opcua.sdk.server.api.MonitoredItem;\nimport org.eclipse.milo.opcua.sdk.server.api.services.AttributeServices.ReadContext;\nimport org.eclipse.milo.opcua.sdk.server.items.BaseMonitoredItem;\nimport org.eclipse.milo.opcua.sdk.server.items.MonitoredDataItem;\nimport org.eclipse.milo.opcua.sdk.server.items.MonitoredEventItem;\nimport org.eclipse.milo.opcua.sdk.server.nodes.UaNode;\nimport org.eclipse.milo.opcua.sdk.server.subscriptions.Subscription.State;\nimport org.eclipse.milo.opcua.stack.core.AttributeId;\nimport org.eclipse.milo.opcua.stack.core.Identifiers;\nimport org.eclipse.milo.opcua.stack.core.StatusCodes;\nimport org.eclipse.milo.opcua.stack.core.UaException;\nimport org.eclipse.milo.opcua.stack.core.UaSerializationException;\nimport org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;\nimport org.eclipse.milo.opcua.stack.core.types.builtin.DiagnosticInfo;\nimport org.eclipse.milo.opcua.stack.core.types.builtin.ExtensionObject;\nimport org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;\nimport org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName;\nimport org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode;\nimport org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UByte;\nimport org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;\nimport org.eclipse.milo.opcua.stack.core.types.enumerated.DeadbandType;\nimport org.eclipse.milo.opcua.stack.core.types.enumerated.MonitoringMode;\nimport org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;\nimport org.eclipse.milo.opcua.stack.core.types.structured.CreateMonitoredItemsRequest;\nimport org.eclipse.milo.opcua.stack.core.types.structured.CreateMonitoredItemsResponse;\nimport org.eclipse.milo.opcua.stack.core.types.structured.CreateSubscriptionRequest;\nimport org.eclipse.milo.opcua.stack.core.types.structured.CreateSubscriptionResponse;\nimport org.eclipse.milo.opcua.stack.core.types.structured.DataChangeFilter;\nimport org.eclipse.milo.opcua.stack.core.types.structured.DeleteMonitoredItemsRequest;\nimport org.eclipse.milo.opcua.stack.core.types.structured.DeleteMonitoredItemsResponse;\nimport org.eclipse.milo.opcua.stack.core.types.structured.DeleteSubscriptionsRequest;\nimport org.eclipse.milo.opcua.stack.core.types.structured.DeleteSubscriptionsResponse;\nimport org.eclipse.milo.opcua.stack.core.types.structured.EventFilter;\nimport org.eclipse.milo.opcua.stack.core.types.structured.ModifyMonitoredItemsRequest;\nimport org.eclipse.milo.opcua.stack.core.types.structured.ModifyMonitoredItemsResponse;\nimport org.eclipse.milo.opcua.stack.core.types.structured.ModifySubscriptionRequest;\nimport org.eclipse.milo.opcua.stack.core.types.structured.ModifySubscriptionResponse;\nimport org.eclipse.milo.opcua.stack.core.types.structured.MonitoredItemCreateRequest;\nimport org.eclipse.milo.opcua.stack.core.types.structured.MonitoredItemCreateResult;\nimport org.eclipse.milo.opcua.stack.core.types.structured.MonitoredItemModifyRequest;\nimport org.eclipse.milo.opcua.stack.core.types.structured.MonitoredItemModifyResult;\nimport org.eclipse.milo.opcua.stack.core.types.structured.MonitoringFilter;\nimport org.eclipse.milo.opcua.stack.core.types.structured.MonitoringParameters;\nimport org.eclipse.milo.opcua.stack.core.types.structured.NotificationMessage;\nimport org.eclipse.milo.opcua.stack.core.types.structured.PublishRequest;\nimport org.eclipse.milo.opcua.stack.core.types.structured.ReadValueId;\nimport org.eclipse.milo.opcua.stack.core.types.structured.RepublishRequest;\nimport org.eclipse.milo.opcua.stack.core.types.structured.RepublishResponse;\nimport org.eclipse.milo.opcua.stack.core.types.structured.ResponseHeader;\nimport org.eclipse.milo.opcua.stack.core.types.structured.SetMonitoringModeRequest;\nimport org.eclipse.milo.opcua.stack.core.types.structured.SetMonitoringModeResponse;\nimport org.eclipse.milo.opcua.stack.core.types.structured.SetPublishingModeRequest;\nimport org.eclipse.milo.opcua.stack.core.types.structured.SetPublishingModeResponse;\nimport org.eclipse.milo.opcua.stack.core.types.structured.SetTriggeringRequest;\nimport org.eclipse.milo.opcua.stack.core.types.structured.SetTriggeringResponse;\nimport org.eclipse.milo.opcua.stack.core.types.structured.SubscriptionAcknowledgement;\nimport org.eclipse.milo.opcua.stack.server.services.ServiceRequest;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport static com.google.common.collect.Lists.newArrayListWithCapacity;\nimport static java.util.stream.Collectors.toList;\nimport static org.eclipse.milo.opcua.sdk.core.util.StreamUtil.opt2stream;\nimport static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ubyte;\nimport static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;\nimport static org.eclipse.milo.opcua.stack.core.util.ConversionUtil.l;\n\npublic class SubscriptionManager {\n\n static final AttributeKey<StatusCode[]> KEY_ACK_RESULTS = AttributeKey.valueOf(\"ackResults\");\n\n private static final QualifiedName DEFAULT_BINARY_ENCODING = new QualifiedName(0, \"DefaultBinary\");\n private static final QualifiedName DEFAULT_XML_ENCODING = new QualifiedName(0, \"DefaultXML\");\n\n private static final AtomicLong SUBSCRIPTION_IDS = new AtomicLong(0L);\n\n private static UInteger nextSubscriptionId() {\n return uint(SUBSCRIPTION_IDS.incrementAndGet());\n }\n\n private final Logger logger = LoggerFactory.getLogger(getClass());\n\n private final PublishQueue publishQueue = new PublishQueue();\n\n private final Map<UInteger, Subscription> subscriptions = Maps.newConcurrentMap();\n private final List<Subscription> transferred = Lists.newCopyOnWriteArrayList();\n\n private final Session session;\n private final OpcUaServer server;\n\n public SubscriptionManager(Session session, OpcUaServer server) {\n this.session = session;\n this.server = server;\n }\n\n public Session getSession() {\n return session;\n }\n\n public PublishQueue getPublishQueue() {\n return publishQueue;\n }\n\n public OpcUaServer getServer() {\n return server;\n }\n\n @Nullable\n public Subscription getSubscription(UInteger subscriptionId) {\n return subscriptions.get(subscriptionId);\n }\n\n public List<Subscription> getSubscriptions() {\n return new ArrayList<>(subscriptions.values());\n }\n\n public void createSubscription(ServiceRequest service) {\n CreateSubscriptionRequest request = (CreateSubscriptionRequest) service.getRequest();\n\n UInteger subscriptionId = nextSubscriptionId();\n\n Subscription subscription = new Subscription(\n this,\n subscriptionId,\n request.getRequestedPublishingInterval(),\n request.getRequestedMaxKeepAliveCount().longValue(),\n request.getRequestedLifetimeCount().longValue(),\n request.getMaxNotificationsPerPublish().longValue(),\n request.getPublishingEnabled(),\n request.getPriority().intValue()\n );\n\n subscriptions.put(subscriptionId, subscription);\n server.getSubscriptions().put(subscriptionId, subscription);\n server.getDiagnosticsSummary().getCumulatedSubscriptionCount().increment();\n server.getEventBus().post(new SubscriptionCreatedEvent(subscription));\n\n subscription.setStateListener((s, ps, cs) -> {\n if (cs == State.Closing) {\n subscriptions.remove(s.getId());\n server.getSubscriptions().remove(s.getId());\n server.getEventBus().post(new SubscriptionDeletedEvent(s));\n\n /*\n * Notify AddressSpaces the items for this subscription are deleted.\n */\n\n Map<UInteger, BaseMonitoredItem<?>> monitoredItems = s.getMonitoredItems();\n\n byMonitoredItemType(\n monitoredItems.values(),\n dataItems -> server.getAddressSpaceManager().onDataItemsDeleted(dataItems),\n eventItems -> server.getAddressSpaceManager().onEventItemsDeleted(eventItems)\n );\n\n server.getMonitoredItemCount().getAndUpdate(count -> count - monitoredItems.size());\n\n monitoredItems.clear();\n }\n });\n\n subscription.startPublishingTimer();\n\n ResponseHeader header = service.createResponseHeader();\n\n CreateSubscriptionResponse response = new CreateSubscriptionResponse(\n header, subscriptionId,\n subscription.getPublishingInterval(),\n uint(subscription.getLifetimeCount()),\n uint(subscription.getMaxKeepAliveCount())\n );\n\n service.setResponse(response);\n }\n\n public void modifySubscription(ServiceRequest service) throws UaException {\n ModifySubscriptionRequest request = (ModifySubscriptionRequest) service.getRequest();\n\n UInteger subscriptionId = request.getSubscriptionId();\n Subscription subscription = subscriptions.get(subscriptionId);\n\n if (subscription == null) {\n throw new UaException(StatusCodes.Bad_SubscriptionIdInvalid);\n }\n\n subscription.modifySubscription(request);\n\n ResponseHeader header = service.createResponseHeader();\n\n ModifySubscriptionResponse response = new ModifySubscriptionResponse(\n header,\n subscription.getPublishingInterval(),\n uint(subscription.getLifetimeCount()),\n uint(subscription.getMaxKeepAliveCount())\n );\n\n service.setResponse(response);\n }\n\n public void deleteSubscription(ServiceRequest service) throws UaException {\n DeleteSubscriptionsRequest request = (DeleteSubscriptionsRequest) service.getRequest();\n\n List<UInteger> subscriptionIds = l(request.getSubscriptionIds());\n\n if (subscriptionIds.isEmpty()) {\n throw new UaException(StatusCodes.Bad_NothingToDo);\n }\n\n StatusCode[] results = new StatusCode[subscriptionIds.size()];\n\n for (int i = 0; i < subscriptionIds.size(); i++) {\n UInteger subscriptionId = subscriptionIds.get(i);\n Subscription subscription = subscriptions.remove(subscriptionId);\n\n if (subscription != null) {\n server.getSubscriptions().remove(subscription.getId());\n server.getEventBus().post(new SubscriptionDeletedEvent(subscription));\n\n List<BaseMonitoredItem<?>> deletedItems = subscription.deleteSubscription();\n\n /*\n * Notify AddressSpaces of the items we just deleted.\n */\n\n byMonitoredItemType(\n deletedItems,\n dataItems -> server.getAddressSpaceManager().onDataItemsDeleted(dataItems),\n eventItems -> server.getAddressSpaceManager().onEventItemsDeleted(eventItems)\n );\n\n results[i] = StatusCode.GOOD;\n\n server.getMonitoredItemCount().getAndUpdate(count -> count - deletedItems.size());\n } else {\n results[i] = new StatusCode(StatusCodes.Bad_SubscriptionIdInvalid);\n }\n }\n\n ResponseHeader header = service.createResponseHeader();\n\n DeleteSubscriptionsResponse response = new DeleteSubscriptionsResponse(\n header,\n results,\n new DiagnosticInfo[0]\n );\n\n service.setResponse(response);\n\n while (subscriptions.isEmpty() && publishQueue.isNotEmpty()) {\n ServiceRequest publishService = publishQueue.poll();\n if (publishService != null) {\n publishService.setServiceFault(StatusCodes.Bad_NoSubscription);\n }\n }\n }\n\n public void setPublishingMode(ServiceRequest service) {\n SetPublishingModeRequest request = (SetPublishingModeRequest) service.getRequest();\n List<UInteger> subscriptionIds = l(request.getSubscriptionIds());\n\n StatusCode[] results = new StatusCode[subscriptionIds.size()];\n\n for (int i = 0; i < subscriptionIds.size(); i++) {\n Subscription subscription = subscriptions.get(subscriptionIds.get(i));\n if (subscription == null) {\n results[i] = new StatusCode(StatusCodes.Bad_SubscriptionIdInvalid);\n } else {\n subscription.setPublishingMode(request);\n results[i] = StatusCode.GOOD;\n }\n }\n\n ResponseHeader header = service.createResponseHeader();\n\n SetPublishingModeResponse response = new SetPublishingModeResponse(\n header,\n results,\n new DiagnosticInfo[0]\n );\n\n service.setResponse(response);\n }\n\n public void createMonitoredItems(ServiceRequest service) throws UaException {\n CreateMonitoredItemsRequest request = (CreateMonitoredItemsRequest) service.getRequest();\n\n UInteger subscriptionId = request.getSubscriptionId();\n Subscription subscription = subscriptions.get(subscriptionId);\n TimestampsToReturn timestamps = request.getTimestampsToReturn();\n List<MonitoredItemCreateRequest> itemsToCreate = l(request.getItemsToCreate());\n\n if (subscription == null) {\n throw new UaException(StatusCodes.Bad_SubscriptionIdInvalid);\n }\n if (timestamps == null) {\n throw new UaException(StatusCodes.Bad_TimestampsToReturnInvalid);\n }\n if (itemsToCreate.isEmpty()) {\n throw new UaException(StatusCodes.Bad_NothingToDo);\n }\n\n List<NodeId> distinctNodeIds = itemsToCreate.stream()\n .map(item -> item.getItemToMonitor().getNodeId())\n .distinct()\n .collect(toList());\n\n CompletableFuture<Map<NodeId, AttributeGroup>> attributesFuture = readMonitoringAttributes(distinctNodeIds);\n\n attributesFuture.thenAccept(attributeGroups -> {\n MonitoredItemCreateResult[] createResults = new MonitoredItemCreateResult[itemsToCreate.size()];\n\n List<BaseMonitoredItem<?>> monitoredItems = new ArrayList<>();\n\n for (int i = 0; i < itemsToCreate.size(); i++) {\n MonitoredItemCreateRequest createRequest = itemsToCreate.get(i);\n\n try {\n long maxMonitoredItems = server.getConfig().getLimits().getMaxMonitoredItems().longValue();\n\n if (server.getMonitoredItemCount().incrementAndGet() <= maxMonitoredItems) {\n BaseMonitoredItem<?> monitoredItem = createMonitoredItem(\n createRequest,\n subscription,\n timestamps,\n attributeGroups\n );\n\n monitoredItems.add(monitoredItem);\n\n createResults[i] = new MonitoredItemCreateResult(\n StatusCode.GOOD,\n monitoredItem.getId(),\n monitoredItem.getSamplingInterval(),\n uint(monitoredItem.getQueueSize()),\n monitoredItem.getFilterResult()\n );\n } else {\n throw new UaException(StatusCodes.Bad_TooManyMonitoredItems);\n }\n } catch (UaException e) {\n server.getMonitoredItemCount().decrementAndGet();\n\n createResults[i] = new MonitoredItemCreateResult(\n e.getStatusCode(),\n UInteger.MIN,\n 0.0,\n UInteger.MIN,\n null\n );\n }\n }\n\n subscription.addMonitoredItems(monitoredItems);\n\n // Notify AddressSpaces of the items we just created.\n\n byMonitoredItemType(\n monitoredItems,\n dataItems -> server.getAddressSpaceManager().onDataItemsCreated(dataItems),\n eventItems -> server.getAddressSpaceManager().onEventItemsCreated(eventItems)\n );\n\n ResponseHeader header = service.createResponseHeader();\n\n CreateMonitoredItemsResponse response = new CreateMonitoredItemsResponse(\n header,\n createResults,\n new DiagnosticInfo[0]\n );\n\n service.setResponse(response);\n });\n }\n\n private BaseMonitoredItem<?> createMonitoredItem(\n MonitoredItemCreateRequest request,\n Subscription subscription,\n TimestampsToReturn timestamps,\n Map<NodeId, AttributeGroup> attributeGroups\n ) throws UaException {\n\n NodeId nodeId = request.getItemToMonitor().getNodeId();\n UInteger attributeId = request.getItemToMonitor().getAttributeId();\n QualifiedName dataEncoding = request.getItemToMonitor().getDataEncoding();\n\n if (!AttributeId.isValid(attributeId)) {\n throw new UaException(StatusCodes.Bad_AttributeIdInvalid);\n }\n\n if (dataEncoding.isNotNull()) {\n if (!AttributeId.Value.isEqual(attributeId)) {\n throw new UaException(StatusCodes.Bad_DataEncodingInvalid);\n }\n\n if (!dataEncoding.equals(DEFAULT_BINARY_ENCODING) &&\n !dataEncoding.equals(DEFAULT_XML_ENCODING)) {\n\n throw new UaException(StatusCodes.Bad_DataEncodingUnsupported);\n }\n }\n\n AttributeGroup attributeGroup = attributeGroups.get(nodeId);\n\n if (attributeId.equals(AttributeId.EventNotifier.uid())) {\n UByte eventNotifier = attributeGroup.getEventNotifier();\n\n // Verify that the SubscribeToEvents bit is set\n if (eventNotifier == null || (eventNotifier.intValue() & 1) == 0) {\n throw new UaException(StatusCodes.Bad_AttributeIdInvalid);\n }\n\n Object filterObject = request.getRequestedParameters()\n .getFilter()\n .decode(server.getSerializationContext());\n\n MonitoringFilter filter = validateEventItemFilter(filterObject, attributeGroup);\n\n UInteger requestedQueueSize = request.getRequestedParameters().getQueueSize();\n AtomicReference<UInteger> revisedQueueSize = new AtomicReference<>(requestedQueueSize);\n\n try {\n server.getAddressSpaceManager().onCreateEventItem(\n request.getItemToMonitor(),\n requestedQueueSize,\n revisedQueueSize::set\n );\n } catch (Throwable t) {\n throw new UaException(StatusCodes.Bad_InternalError, t);\n }\n\n MonitoredEventItem monitoredEventItem = new MonitoredEventItem(\n server,\n session,\n uint(subscription.nextItemId()),\n subscription.getId(),\n request.getItemToMonitor(),\n request.getMonitoringMode(),\n timestamps,\n request.getRequestedParameters().getClientHandle(),\n 0.0,\n revisedQueueSize.get(),\n request.getRequestedParameters().getDiscardOldest()\n );\n\n monitoredEventItem.installFilter(filter);\n\n return monitoredEventItem;\n } else {\n if (attributeId.equals(AttributeId.Value.uid())) {\n UByte accessLevel = attributeGroup.getAccessLevel();\n if (accessLevel == null) accessLevel = ubyte(0);\n\n UByte userAccessLevel = attributeGroup.getUserAccessLevel();\n if (userAccessLevel == null) userAccessLevel = ubyte(0);\n\n EnumSet<AccessLevel> accessLevels = AccessLevel.fromValue(accessLevel);\n EnumSet<AccessLevel> userAccessLevels = AccessLevel.fromValue(userAccessLevel);\n\n if (!accessLevels.contains(AccessLevel.CurrentRead)) {\n throw new UaException(StatusCodes.Bad_NotReadable);\n }\n if (!userAccessLevels.contains(AccessLevel.CurrentRead)) {\n throw new UaException(StatusCodes.Bad_UserAccessDenied);\n }\n }\n\n // Validate the requested index range by parsing it.\n String indexRange = request.getItemToMonitor().getIndexRange();\n if (indexRange != null) NumericRange.parse(indexRange);\n\n Double minimumSamplingInterval = -1.0;\n try {\n minimumSamplingInterval = attributeGroup.getMinimumSamplingInterval();\n if (minimumSamplingInterval == null) {\n minimumSamplingInterval = server.getConfig().getLimits().getMinSupportedSampleRate();\n }\n } catch (UaException e) {\n if (e.getStatusCode().getValue() != StatusCodes.Bad_AttributeIdInvalid) {\n throw e;\n }\n }\n\n MonitoringFilter filter = MonitoredDataItem.DEFAULT_FILTER;\n\n try {\n ExtensionObject filterXo = request.getRequestedParameters().getFilter();\n\n if (filterXo != null && !filterXo.isNull()) {\n Object filterObject = filterXo\n .decode(server.getSerializationContext());\n\n filter = validateDataItemFilter(filterObject, attributeId, attributeGroup);\n }\n } catch (UaSerializationException e) {\n logger.debug(\"error decoding MonitoringFilter\", e);\n\n throw new UaException(StatusCodes.Bad_MonitoredItemFilterInvalid, e);\n }\n\n double requestedSamplingInterval = getSamplingInterval(\n subscription,\n minimumSamplingInterval,\n request.getRequestedParameters().getSamplingInterval()\n );\n\n UInteger requestedQueueSize = request.getRequestedParameters().getQueueSize();\n\n AtomicReference<Double> revisedSamplingInterval = new AtomicReference<>(requestedSamplingInterval);\n AtomicReference<UInteger> revisedQueueSize = new AtomicReference<>(requestedQueueSize);\n\n try {\n server.getAddressSpaceManager().onCreateDataItem(\n request.getItemToMonitor(),\n requestedSamplingInterval,\n requestedQueueSize,\n (rsi, rqs) -> {\n revisedSamplingInterval.set(rsi);\n revisedQueueSize.set(rqs);\n }\n );\n } catch (Throwable t) {\n throw new UaException(StatusCodes.Bad_InternalError, t);\n }\n\n MonitoredDataItem monitoredDataItem = new MonitoredDataItem(\n server,\n session,\n uint(subscription.nextItemId()),\n subscription.getId(),\n request.getItemToMonitor(),\n request.getMonitoringMode(),\n timestamps,\n request.getRequestedParameters().getClientHandle(),\n revisedSamplingInterval.get(),\n revisedQueueSize.get(),\n request.getRequestedParameters().getDiscardOldest()\n );\n\n monitoredDataItem.installFilter(filter);\n\n return monitoredDataItem;\n }\n }\n\n private MonitoringFilter validateDataItemFilter(\n Object filterObject,\n UInteger attributeId,\n AttributeGroup attributeGroup\n ) throws UaException {\n\n if (filterObject instanceof MonitoringFilter) {\n if (filterObject instanceof DataChangeFilter) {\n DataChangeFilter filter = (DataChangeFilter) filterObject;\n\n DeadbandType deadbandType = DeadbandType.from(filter.getDeadbandType().intValue());\n\n if (deadbandType == null) {\n throw new UaException(StatusCodes.Bad_DeadbandFilterInvalid);\n }\n\n if (deadbandType == DeadbandType.Percent) {\n // Percent deadband is not currently implemented\n throw new UaException(StatusCodes.Bad_MonitoredItemFilterUnsupported);\n }\n\n if (deadbandType == DeadbandType.Absolute &&\n !AttributeId.Value.isEqual(attributeId)) {\n\n // Absolute deadband is only allowed for Value attributes\n throw new UaException(StatusCodes.Bad_FilterNotAllowed);\n }\n\n if (deadbandType != DeadbandType.None) {\n NodeId dataTypeId = null;\n try {\n dataTypeId = attributeGroup.getDataType();\n } catch (UaException ignored) {\n // noop\n }\n if (dataTypeId == null) {\n dataTypeId = NodeId.NULL_VALUE;\n }\n\n if (!Identifiers.Number.equals(dataTypeId) && !subtypeOf(server, dataTypeId, Identifiers.Number)) {\n throw new UaException(StatusCodes.Bad_FilterNotAllowed);\n }\n }\n\n return filter;\n } else if (filterObject instanceof EventFilter) {\n throw new UaException(StatusCodes.Bad_FilterNotAllowed);\n } else {\n // AggregateFilter or some future unimplemented filter\n throw new UaException(StatusCodes.Bad_MonitoredItemFilterUnsupported);\n }\n } else {\n throw new UaException(StatusCodes.Bad_MonitoredItemFilterInvalid);\n }\n }\n\n private MonitoringFilter validateEventItemFilter(\n Object filterObject,\n AttributeGroup attributeGroup\n ) throws UaException {\n\n if (filterObject instanceof MonitoringFilter) {\n if (!(filterObject instanceof EventFilter)) {\n throw new UaException(StatusCodes.Bad_FilterNotAllowed);\n }\n return (EventFilter) filterObject;\n } else {\n throw new UaException(StatusCodes.Bad_MonitoredItemFilterInvalid);\n }\n }\n\n public void modifyMonitoredItems(ServiceRequest service) throws UaException {\n ModifyMonitoredItemsRequest request = (ModifyMonitoredItemsRequest) service.getRequest();\n\n UInteger subscriptionId = request.getSubscriptionId();\n Subscription subscription = subscriptions.get(subscriptionId);\n TimestampsToReturn timestamps = request.getTimestampsToReturn();\n List<MonitoredItemModifyRequest> itemsToModify = l(request.getItemsToModify());\n\n if (subscription == null) {\n throw new UaException(StatusCodes.Bad_SubscriptionIdInvalid);\n }\n if (timestamps == null) {\n throw new UaException(StatusCodes.Bad_TimestampsToReturnInvalid);\n }\n if (itemsToModify.isEmpty()) {\n throw new UaException(StatusCodes.Bad_NothingToDo);\n }\n\n List<NodeId> distinctNodeIds = itemsToModify.stream()\n .map(item -> {\n UInteger itemId = item.getMonitoredItemId();\n BaseMonitoredItem<?> monitoredItem = subscription.getMonitoredItems().get(itemId);\n return monitoredItem != null ? monitoredItem.getReadValueId().getNodeId() : NodeId.NULL_VALUE;\n })\n .filter(NodeId::isNotNull)\n .distinct()\n .collect(toList());\n\n CompletableFuture<Map<NodeId, AttributeGroup>> attributesFuture = readMonitoringAttributes(distinctNodeIds);\n\n attributesFuture.thenAccept(attributeGroups -> {\n MonitoredItemModifyResult[] modifyResults = new MonitoredItemModifyResult[itemsToModify.size()];\n\n List<BaseMonitoredItem<?>> monitoredItems = new ArrayList<>();\n\n for (int i = 0; i < itemsToModify.size(); i++) {\n MonitoredItemModifyRequest modifyRequest = itemsToModify.get(i);\n\n try {\n BaseMonitoredItem<?> monitoredItem = modifyMonitoredItem(\n modifyRequest,\n timestamps,\n subscription,\n attributeGroups\n );\n\n monitoredItems.add(monitoredItem);\n\n modifyResults[i] = new MonitoredItemModifyResult(\n StatusCode.GOOD,\n monitoredItem.getSamplingInterval(),\n uint(monitoredItem.getQueueSize()),\n monitoredItem.getFilterResult()\n );\n } catch (UaException e) {\n modifyResults[i] = new MonitoredItemModifyResult(\n e.getStatusCode(),\n 0.0,\n UInteger.MIN,\n null\n );\n }\n }\n\n subscription.resetLifetimeCounter();\n\n /*\n * Notify AddressSpaces of the items we just modified.\n */\n\n byMonitoredItemType(\n monitoredItems,\n dataItems -> server.getAddressSpaceManager().onDataItemsModified(dataItems),\n eventItems -> server.getAddressSpaceManager().onEventItemsModified(eventItems)\n );\n\n /*\n * AddressSpaces have been notified; send response.\n */\n\n ResponseHeader header = service.createResponseHeader();\n\n ModifyMonitoredItemsResponse response = new ModifyMonitoredItemsResponse(\n header,\n modifyResults,\n new DiagnosticInfo[0]\n );\n\n service.setResponse(response);\n });\n }\n\n private BaseMonitoredItem<?> modifyMonitoredItem(\n MonitoredItemModifyRequest request,\n TimestampsToReturn timestamps,\n Subscription subscription,\n Map<NodeId, AttributeGroup> attributeGroups\n ) throws UaException {\n\n UInteger itemId = request.getMonitoredItemId();\n MonitoringParameters parameters = request.getRequestedParameters();\n\n BaseMonitoredItem<?> monitoredItem = subscription.getMonitoredItems().get(itemId);\n\n if (monitoredItem == null) {\n throw new UaException(StatusCodes.Bad_MonitoredItemIdInvalid);\n }\n\n NodeId nodeId = monitoredItem.getReadValueId().getNodeId();\n UInteger attributeId = monitoredItem.getReadValueId().getAttributeId();\n AttributeGroup attributeGroup = attributeGroups.get(nodeId);\n\n if (attributeId.equals(AttributeId.EventNotifier.uid())) {\n Object filterObject = request.getRequestedParameters()\n .getFilter()\n .decode(server.getSerializationContext());\n\n MonitoringFilter filter = validateEventItemFilter(filterObject, attributeGroup);\n\n UInteger requestedQueueSize = parameters.getQueueSize();\n AtomicReference<UInteger> revisedQueueSize = new AtomicReference<>(requestedQueueSize);\n\n try {\n server.getAddressSpaceManager().onModifyEventItem(\n monitoredItem.getReadValueId(),\n requestedQueueSize,\n revisedQueueSize::set\n );\n } catch (Throwable t) {\n throw new UaException(StatusCodes.Bad_InternalError, t);\n }\n\n monitoredItem.modify(\n timestamps,\n parameters.getClientHandle(),\n monitoredItem.getSamplingInterval(),\n filter,\n revisedQueueSize.get(),\n parameters.getDiscardOldest()\n );\n } else {\n MonitoringFilter filter = MonitoredDataItem.DEFAULT_FILTER;\n\n try {\n ExtensionObject filterXo = request.getRequestedParameters().getFilter();\n\n if (filterXo != null && !filterXo.isNull()) {\n Object filterObject = filterXo\n .decode(server.getSerializationContext());\n\n filter = validateDataItemFilter(filterObject, attributeId, attributeGroup);\n }\n } catch (UaSerializationException e) {\n logger.debug(\"error decoding MonitoringFilter\", e);\n\n throw new UaException(StatusCodes.Bad_MonitoredItemFilterInvalid, e);\n }\n\n Double minimumSamplingInterval = -1.0;\n try {\n minimumSamplingInterval = attributeGroup.getMinimumSamplingInterval();\n if (minimumSamplingInterval == null) {\n minimumSamplingInterval = server.getConfig().getLimits().getMinSupportedSampleRate();\n }\n } catch (UaException e) {\n long statusCodeValue = e.getStatusCode().getValue();\n\n if (statusCodeValue != StatusCodes.Bad_AttributeIdInvalid &&\n statusCodeValue != StatusCodes.Bad_NodeIdUnknown) {\n\n throw e;\n }\n }\n\n double requestedSamplingInterval = getSamplingInterval(\n subscription,\n minimumSamplingInterval,\n request.getRequestedParameters().getSamplingInterval()\n );\n\n UInteger requestedQueueSize = parameters.getQueueSize();\n\n AtomicReference<Double> revisedSamplingInterval = new AtomicReference<>(requestedSamplingInterval);\n AtomicReference<UInteger> revisedQueueSize = new AtomicReference<>(requestedQueueSize);\n\n try {\n server.getAddressSpaceManager().onModifyDataItem(\n monitoredItem.getReadValueId(),\n requestedSamplingInterval,\n requestedQueueSize,\n (rsi, rqs) -> {\n revisedSamplingInterval.set(rsi);\n revisedQueueSize.set(rqs);\n }\n );\n } catch (Throwable t) {\n throw new UaException(StatusCodes.Bad_InternalError, t);\n }\n\n monitoredItem.modify(\n timestamps,\n parameters.getClientHandle(),\n revisedSamplingInterval.get(),\n filter,\n revisedQueueSize.get(),\n parameters.getDiscardOldest()\n );\n }\n\n return monitoredItem;\n }\n\n private double getSamplingInterval(\n Subscription subscription,\n Double minimumSamplingInterval,\n Double requestedSamplingInterval\n ) {\n\n double samplingInterval = requestedSamplingInterval;\n\n if (requestedSamplingInterval < 0) {\n samplingInterval = subscription.getPublishingInterval();\n } else if (requestedSamplingInterval == 0) {\n if (minimumSamplingInterval < 0) {\n // Node has no opinion on sampling interval (indeterminate)\n samplingInterval = subscription.getPublishingInterval();\n } else if (minimumSamplingInterval == 0) {\n // Node allows report-by-exception\n samplingInterval = minimumSamplingInterval;\n } else if (minimumSamplingInterval > 0) {\n // Node has a defined minimum sampling interval, use that\n // because requested rate of 0 means \"fastest practical rate\"\n samplingInterval = minimumSamplingInterval;\n }\n } else {\n if (requestedSamplingInterval < minimumSamplingInterval) {\n samplingInterval = minimumSamplingInterval;\n }\n }\n\n double minSupportedSampleRate = server.getConfig().getLimits().getMinSupportedSampleRate();\n double maxSupportedSampleRate = server.getConfig().getLimits().getMaxSupportedSampleRate();\n\n if (samplingInterval < minSupportedSampleRate) {\n samplingInterval = minSupportedSampleRate;\n }\n if (samplingInterval > maxSupportedSampleRate) {\n samplingInterval = maxSupportedSampleRate;\n }\n\n return samplingInterval;\n }\n\n private CompletableFuture<Map<NodeId, AttributeGroup>> readMonitoringAttributes(List<NodeId> nodeIds) {\n List<ReadValueId> attributesToRead = nodeIds.stream()\n .flatMap(nodeId -> {\n Function<AttributeId, ReadValueId> f = id -> new ReadValueId(\n nodeId,\n id.uid(),\n null,\n QualifiedName.NULL_VALUE\n );\n\n return Stream.of(\n f.apply(AttributeId.AccessLevel),\n f.apply(AttributeId.UserAccessLevel),\n f.apply(AttributeId.EventNotifier),\n f.apply(AttributeId.MinimumSamplingInterval),\n f.apply(AttributeId.DataType)\n );\n })\n .collect(toList());\n\n ReadContext context = new ReadContext(server, session);\n\n server.getAddressSpaceManager().read(\n context,\n 0.0,\n TimestampsToReturn.Neither,\n attributesToRead\n );\n\n return context.getFuture().thenApply(attributeValues -> {\n Map<NodeId, AttributeGroup> monitoringAttributes = new HashMap<>();\n\n for (int nodeIdx = 0, attrIdx = 0; nodeIdx < nodeIds.size(); nodeIdx++, attrIdx += 5) {\n monitoringAttributes.put(nodeIds.get(nodeIdx), new AttributeGroup(\n attributeValues.get(attrIdx),\n attributeValues.get(attrIdx + 1),\n attributeValues.get(attrIdx + 2),\n attributeValues.get(attrIdx + 3),\n attributeValues.get(attrIdx + 4)\n ));\n }\n\n return monitoringAttributes;\n });\n }\n\n public void deleteMonitoredItems(ServiceRequest service) throws UaException {\n DeleteMonitoredItemsRequest request = (DeleteMonitoredItemsRequest) service.getRequest();\n\n UInteger subscriptionId = request.getSubscriptionId();\n Subscription subscription = subscriptions.get(subscriptionId);\n List<UInteger> itemsToDelete = l(request.getMonitoredItemIds());\n\n if (subscription == null) {\n throw new UaException(StatusCodes.Bad_SubscriptionIdInvalid);\n }\n if (itemsToDelete.isEmpty()) {\n throw new UaException(StatusCodes.Bad_NothingToDo);\n }\n\n StatusCode[] deleteResults = new StatusCode[itemsToDelete.size()];\n List<BaseMonitoredItem<?>> deletedItems = newArrayListWithCapacity(itemsToDelete.size());\n\n synchronized (subscription) {\n for (int i = 0; i < itemsToDelete.size(); i++) {\n UInteger itemId = itemsToDelete.get(i);\n BaseMonitoredItem<?> item = subscription.getMonitoredItems().get(itemId);\n\n if (item == null) {\n deleteResults[i] = new StatusCode(StatusCodes.Bad_MonitoredItemIdInvalid);\n } else {\n deletedItems.add(item);\n\n deleteResults[i] = StatusCode.GOOD;\n\n server.getMonitoredItemCount().decrementAndGet();\n }\n }\n\n subscription.removeMonitoredItems(deletedItems);\n }\n\n /*\n * Notify AddressSpaces of the items that have been deleted.\n */\n\n byMonitoredItemType(\n deletedItems,\n dataItems -> server.getAddressSpaceManager().onDataItemsDeleted(dataItems),\n eventItems -> server.getAddressSpaceManager().onEventItemsDeleted(eventItems)\n );\n\n /*\n * Build and return results.\n */\n ResponseHeader header = service.createResponseHeader();\n\n DeleteMonitoredItemsResponse response = new DeleteMonitoredItemsResponse(\n header,\n deleteResults,\n new DiagnosticInfo[0]\n );\n\n service.setResponse(response);\n }\n\n public void setMonitoringMode(ServiceRequest service) {\n SetMonitoringModeRequest request = (SetMonitoringModeRequest) service.getRequest();\n UInteger subscriptionId = request.getSubscriptionId();\n\n try {\n Subscription subscription = subscriptions.get(subscriptionId);\n List<UInteger> itemsToModify = l(request.getMonitoredItemIds());\n\n if (subscription == null) {\n throw new UaException(StatusCodes.Bad_SubscriptionIdInvalid);\n }\n if (itemsToModify.isEmpty()) {\n throw new UaException(StatusCodes.Bad_NothingToDo);\n }\n\n /*\n * Set MonitoringMode on each monitored item, if it exists.\n */\n\n MonitoringMode monitoringMode = request.getMonitoringMode();\n StatusCode[] results = new StatusCode[itemsToModify.size()];\n List<MonitoredItem> modified = newArrayListWithCapacity(itemsToModify.size());\n\n for (int i = 0; i < itemsToModify.size(); i++) {\n UInteger itemId = itemsToModify.get(i);\n BaseMonitoredItem<?> item = subscription.getMonitoredItems().get(itemId);\n\n if (item != null) {\n item.setMonitoringMode(monitoringMode);\n\n modified.add(item);\n\n results[i] = StatusCode.GOOD;\n } else {\n results[i] = new StatusCode(StatusCodes.Bad_MonitoredItemIdInvalid);\n }\n }\n\n /*\n * Notify AddressSpace of the items whose MonitoringMode has been modified.\n */\n\n server.getAddressSpaceManager().onMonitoringModeChanged(modified);\n\n /*\n * Build and return results.\n */\n\n ResponseHeader header = service.createResponseHeader();\n SetMonitoringModeResponse response = new SetMonitoringModeResponse(\n header,\n results,\n new DiagnosticInfo[0]\n );\n\n service.setResponse(response);\n } catch (UaException e) {\n service.setServiceFault(e);\n }\n }\n\n public void publish(ServiceRequest service) {\n PublishRequest request = (PublishRequest) service.getRequest();\n\n SubscriptionAcknowledgement[] acknowledgements = request.getSubscriptionAcknowledgements();\n\n if (acknowledgements != null) {\n StatusCode[] results = new StatusCode[acknowledgements.length];\n\n for (int i = 0; i < acknowledgements.length; i++) {\n SubscriptionAcknowledgement acknowledgement = acknowledgements[i];\n\n UInteger sequenceNumber = acknowledgement.getSequenceNumber();\n UInteger subscriptionId = acknowledgement.getSubscriptionId();\n\n Subscription subscription = subscriptions.get(subscriptionId);\n\n if (subscription == null) {\n logger.debug(\n \"Can't acknowledge sequenceNumber={} on subscriptionId={}; id not valid for this session\",\n sequenceNumber,\n subscriptionId\n );\n results[i] = new StatusCode(StatusCodes.Bad_SubscriptionIdInvalid);\n } else {\n logger.debug(\"Acknowledging sequenceNumber={} on subscriptionId={}\",\n sequenceNumber,\n subscriptionId\n );\n results[i] = subscription.acknowledge(sequenceNumber);\n }\n }\n\n service.attr(KEY_ACK_RESULTS).set(results);\n }\n\n if (!transferred.isEmpty()) {\n Subscription subscription = transferred.remove(0);\n subscription.returnStatusChangeNotification(\n service,\n new StatusCode(StatusCodes.Good_SubscriptionTransferred)\n );\n return;\n }\n\n // waitList must also be empty because the last remaining subscription could have\n // expired, which removes it from bookkeeping, but leaves it in the PublishQueue\n // waitList if there were no available requests to send Bad_Timeout.\n if (subscriptions.isEmpty() && publishQueue.isWaitListEmpty()) {\n service.setServiceFault(StatusCodes.Bad_NoSubscription);\n return;\n }\n\n publishQueue.addRequest(service);\n }\n\n public void republish(ServiceRequest service) {\n RepublishRequest request = (RepublishRequest) service.getRequest();\n\n if (subscriptions.isEmpty()) {\n service.setServiceFault(StatusCodes.Bad_SubscriptionIdInvalid);\n return;\n }\n\n UInteger subscriptionId = request.getSubscriptionId();\n Subscription subscription = subscriptions.get(subscriptionId);\n\n if (subscription == null) {\n service.setServiceFault(StatusCodes.Bad_SubscriptionIdInvalid);\n return;\n }\n\n UInteger sequenceNumber = request.getRetransmitSequenceNumber();\n NotificationMessage notificationMessage = subscription.republish(sequenceNumber);\n\n if (notificationMessage == null) {\n service.setServiceFault(StatusCodes.Bad_MessageNotAvailable);\n return;\n }\n\n ResponseHeader header = service.createResponseHeader();\n RepublishResponse response = new RepublishResponse(header, notificationMessage);\n\n service.setResponse(response);\n }\n\n public void setTriggering(ServiceRequest service) {\n SetTriggeringRequest request = (SetTriggeringRequest) service.getRequest();\n\n UInteger subscriptionId = request.getSubscriptionId();\n Subscription subscription = subscriptions.get(subscriptionId);\n\n if (subscription == null) {\n service.setServiceFault(StatusCodes.Bad_SubscriptionIdInvalid);\n return;\n }\n\n UInteger triggerId = request.getTriggeringItemId();\n List<UInteger> linksToAdd = l(request.getLinksToAdd());\n List<UInteger> linksToRemove = l(request.getLinksToRemove());\n\n if (linksToAdd.isEmpty() && linksToRemove.isEmpty()) {\n service.setServiceFault(StatusCodes.Bad_NothingToDo);\n return;\n }\n\n StatusCode[] addResults;\n StatusCode[] removeResults;\n\n synchronized (subscription) {\n Map<UInteger, BaseMonitoredItem<?>> itemsById = subscription.getMonitoredItems();\n\n BaseMonitoredItem<?> triggerItem = itemsById.get(triggerId);\n if (triggerItem == null) {\n service.setServiceFault(StatusCodes.Bad_MonitoredItemIdInvalid);\n return;\n }\n\n removeResults = linksToRemove.stream()\n .map(linkedItemId -> {\n BaseMonitoredItem<?> item = itemsById.get(linkedItemId);\n if (item != null) {\n if (triggerItem.getTriggeredItems().remove(linkedItemId) != null) {\n return StatusCode.GOOD;\n } else {\n return new StatusCode(StatusCodes.Bad_MonitoredItemIdInvalid);\n }\n } else {\n return new StatusCode(StatusCodes.Bad_MonitoredItemIdInvalid);\n }\n })\n .toArray(StatusCode[]::new);\n\n addResults = linksToAdd.stream()\n .map(linkedItemId -> {\n BaseMonitoredItem<?> linkedItem = itemsById.get(linkedItemId);\n if (linkedItem != null) {\n triggerItem.getTriggeredItems().put(linkedItemId, linkedItem);\n return StatusCode.GOOD;\n } else {\n return new StatusCode(StatusCodes.Bad_MonitoredItemIdInvalid);\n }\n })\n .toArray(StatusCode[]::new);\n }\n\n SetTriggeringResponse response = new SetTriggeringResponse(\n service.createResponseHeader(),\n addResults,\n new DiagnosticInfo[0],\n removeResults,\n new DiagnosticInfo[0]\n );\n\n service.setResponse(response);\n }\n\n public void sessionClosed(boolean deleteSubscriptions) {\n Iterator<Subscription> iterator = subscriptions.values().iterator();\n\n while (iterator.hasNext()) {\n Subscription s = iterator.next();\n s.setStateListener(null);\n\n if (deleteSubscriptions) {\n server.getSubscriptions().remove(s.getId());\n server.getEventBus().post(new SubscriptionDeletedEvent(s));\n\n List<BaseMonitoredItem<?>> deletedItems = s.deleteSubscription();\n\n /*\n * Notify AddressSpaces the items for this subscription are deleted.\n */\n\n byMonitoredItemType(\n deletedItems,\n dataItems -> server.getAddressSpaceManager().onDataItemsDeleted(dataItems),\n eventItems -> server.getAddressSpaceManager().onEventItemsDeleted(eventItems)\n );\n\n server.getMonitoredItemCount().getAndUpdate(count -> count - deletedItems.size());\n }\n\n iterator.remove();\n }\n\n if (deleteSubscriptions) {\n while (publishQueue.isNotEmpty()) {\n ServiceRequest publishService = publishQueue.poll();\n if (publishService != null) {\n publishService.setServiceFault(StatusCodes.Bad_SessionClosed);\n }\n }\n }\n }\n\n /**\n * Add (transfer) {@code subscription} to this {@link SubscriptionManager}.\n *\n * @param subscription the {@link Subscription} to add.\n */\n public void addSubscription(Subscription subscription) {\n subscriptions.put(subscription.getId(), subscription);\n server.getEventBus().post(new SubscriptionCreatedEvent(subscription));\n\n subscription.setStateListener((s, ps, cs) -> {\n if (cs == State.Closing) {\n subscriptions.remove(s.getId());\n server.getSubscriptions().remove(s.getId());\n server.getEventBus().post(new SubscriptionDeletedEvent(s));\n\n /*\n * Notify AddressSpaces the items for this subscription are deleted.\n */\n\n Map<UInteger, BaseMonitoredItem<?>> monitoredItems = s.getMonitoredItems();\n\n byMonitoredItemType(\n monitoredItems.values(),\n dataItems -> server.getAddressSpaceManager().onDataItemsDeleted(dataItems),\n eventItems -> server.getAddressSpaceManager().onEventItemsDeleted(eventItems)\n );\n\n server.getMonitoredItemCount().getAndUpdate(count -> count - monitoredItems.size());\n\n monitoredItems.clear();\n }\n });\n }\n\n /**\n * Remove (transfer from) the Subscription by {@code subscriptionId} from this {@link SubscriptionManager}.\n *\n * @param subscriptionId the id of the {@link Subscription} to remove.\n * @return the removed {@link Subscription}.\n */\n public Subscription removeSubscription(UInteger subscriptionId) {\n Subscription subscription = subscriptions.remove(subscriptionId);\n server.getEventBus().post(new SubscriptionDeletedEvent(subscription));\n\n if (subscription != null) {\n subscription.setStateListener(null);\n }\n\n return subscription;\n }\n\n public void sendStatusChangeNotification(Subscription subscription, StatusCode status) {\n ServiceRequest service = publishQueue.poll();\n\n if (service != null) {\n subscription.returnStatusChangeNotification(service, status);\n } else {\n transferred.add(subscription);\n }\n }\n\n /**\n * Split {@code monitoredItems} into a list of {@link DataItem}s and a list of {@link EventItem}s and invoke the\n * corresponding {@link Consumer} for each list if non-empty.\n *\n * @param monitoredItems the list of MonitoredItems to group.\n * @param dataItemConsumer a {@link Consumer} that accepts a non-empty list of {@link DataItem}s.\n * @param eventItemConsumer a {@link Consumer} that accepts a non-empty list of {@link EventItem}s.\n */\n private static void byMonitoredItemType(\n Collection<BaseMonitoredItem<?>> monitoredItems,\n Consumer<List<DataItem>> dataItemConsumer,\n Consumer<List<EventItem>> eventItemConsumer\n ) {\n\n List<DataItem> dataItems = Lists.newArrayList();\n List<EventItem> eventItems = Lists.newArrayList();\n\n for (BaseMonitoredItem<?> item : monitoredItems) {\n if (item instanceof MonitoredDataItem) {\n dataItems.add((DataItem) item);\n } else if (item instanceof MonitoredEventItem) {\n eventItems.add((EventItem) item);\n }\n }\n\n try {\n if (!dataItems.isEmpty()) {\n dataItemConsumer.accept(dataItems);\n }\n } catch (Throwable t) {\n LoggerFactory.getLogger(SubscriptionManager.class)\n .error(\"Uncaught Throwable in dataItemConsumer\", t);\n }\n\n try {\n if (!eventItems.isEmpty()) {\n eventItemConsumer.accept(eventItems);\n }\n } catch (Throwable t) {\n LoggerFactory.getLogger(SubscriptionManager.class)\n .error(\"Uncaught Throwable in eventItemConsumer\", t);\n }\n }\n\n /**\n * @return {@code true} if {@code dataTypeId} is a subtype of {@code potentialSuperTypeId}.\n */\n private static boolean subtypeOf(OpcUaServer server, NodeId dataTypeId, NodeId potentialSuperTypeId) {\n UaNode dataTypeNode = server.getAddressSpaceManager()\n .getManagedNode(dataTypeId)\n .orElse(null);\n\n if (dataTypeNode != null) {\n NodeId superTypeId = getSuperTypeId(server, dataTypeId);\n\n if (superTypeId != null) {\n return superTypeId.equals(potentialSuperTypeId) ||\n subtypeOf(server, superTypeId, potentialSuperTypeId);\n } else {\n return false;\n }\n } else {\n return false;\n }\n }\n\n @Nullable\n private static NodeId getSuperTypeId(OpcUaServer server, NodeId dataTypeId) {\n UaNode dataTypeNode = server.getAddressSpaceManager()\n .getManagedNode(dataTypeId)\n .orElse(null);\n\n if (dataTypeNode != null) {\n return dataTypeNode.getReferences()\n .stream()\n .filter(Reference.SUBTYPE_OF)\n .flatMap(r -> opt2stream(r.getTargetNodeId().toNodeId(server.getNamespaceTable())))\n .findFirst()\n .orElse(null);\n } else {\n return null;\n }\n }\n\n private static class AttributeGroup {\n final DataValue accessLevelValue;\n final DataValue userAccessLevelValue;\n final DataValue eventNotifierValue;\n final DataValue minimumSamplingIntervalValue;\n final DataValue dataType;\n\n AttributeGroup(\n DataValue accessLevelValue,\n DataValue userAccessLevelValue,\n DataValue eventNotifierValue,\n DataValue minimumSamplingIntervalValue,\n DataValue dataType\n ) {\n\n this.accessLevelValue = accessLevelValue;\n this.userAccessLevelValue = userAccessLevelValue;\n this.eventNotifierValue = eventNotifierValue;\n this.minimumSamplingIntervalValue = minimumSamplingIntervalValue;\n this.dataType = dataType;\n }\n\n @Nullable\n UByte getAccessLevel() throws UaException {\n Object value = getValue(accessLevelValue);\n\n if (value instanceof UByte) {\n return (UByte) value;\n } else {\n return null;\n }\n }\n\n @Nullable\n UByte getUserAccessLevel() throws UaException {\n Object value = getValue(userAccessLevelValue);\n\n if (value instanceof UByte) {\n return (UByte) value;\n } else {\n return null;\n }\n }\n\n @Nullable\n UByte getEventNotifier() throws UaException {\n Object value = getValue(eventNotifierValue);\n\n if (value instanceof UByte) {\n return (UByte) value;\n } else {\n return null;\n }\n }\n\n @Nullable\n Double getMinimumSamplingInterval() throws UaException {\n Object value = getValue(minimumSamplingIntervalValue);\n\n if (value instanceof Double) {\n return (Double) value;\n } else {\n return null;\n }\n }\n\n @Nullable\n NodeId getDataType() throws UaException {\n Object value = getValue(dataType);\n\n if (value instanceof NodeId) {\n return (NodeId) value;\n } else {\n return null;\n }\n }\n\n private Object getValue(DataValue dataValue) throws UaException {\n StatusCode statusCode = dataValue.getStatusCode();\n\n if (statusCode == null) {\n throw new UaException(StatusCode.BAD);\n }\n\n if (statusCode.isBad()) {\n throw new UaException(statusCode);\n }\n\n return dataValue.getValue().getValue();\n }\n }\n\n}\n\n\n", "input": "", "output": "【漏洞类型】业务逻辑安全漏洞 - 不合理的资源限制配置\n【严重性】High\n【漏洞摘要】原始代码中`getMaxMonitoredItems()`方法返回的是`Integer.MAX_VALUE`,这意味着服务器理论上可以创建无限数量的MonitoredItems,这可能会导致资源耗尽(如内存、CPU)和潜在的安全问题,如拒绝服务攻击(DoS)。根本原因在于,代码中对每个会话的MonitoredItems数量没有进行合理的限制。\n【关键代码模式】\n- `getMaxMonitoredItems()`方法\n- `SubscriptionManager`类中的`createMonitoredItems()`方法\n- `AtomicLong monitoredItemCount`变量\n- `server.getMonitoredItemCount()`方法\n【修复方法说明】通过在`OpcUaServerConfigLimits`接口中添加`getMaxMonitoredItemsPerSession()`方法,并在`SubscriptionManager`中使用这个新的配置来限制每个会话的MonitoredItems数量,修复了不合理的资源限制配置的问题。这样可以确保每个会话创建的MonitoredItems数量不会超过预设的最大值,从而防止资源耗尽和潜在的DoS攻击。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\njt-jiffle/jt-jiffle-language/src/main/java/it/geosolutions/jaiext/jiffle/Jiffle.java\n### 代码内容:\npackage it.geosolutions.jaiext.jiffle;\n\nimport org.antlr.v4.runtime.CharStream;\nimport org.antlr.v4.runtime.CharStreams;\nimport org.antlr.v4.runtime.CommonTokenStream;\nimport org.antlr.v4.runtime.TokenStream;\nimport org.antlr.v4.runtime.tree.ParseTree;\nimport org.codehaus.janino.SimpleCompiler;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\nimport it.geosolutions.jaiext.jiffle.parser.ExpressionWorker;\nimport it.geosolutions.jaiext.jiffle.parser.ImagesBlockWorker;\nimport it.geosolutions.jaiext.jiffle.parser.InitBlockWorker;\nimport it.geosolutions.jaiext.jiffle.parser.JiffleLexer;\nimport it.geosolutions.jaiext.jiffle.parser.JiffleParser;\nimport it.geosolutions.jaiext.jiffle.parser.JiffleParserErrorListener;\nimport it.geosolutions.jaiext.jiffle.parser.Messages;\nimport it.geosolutions.jaiext.jiffle.parser.OptionsBlockWorker;\nimport it.geosolutions.jaiext.jiffle.parser.RuntimeModelWorker;\nimport it.geosolutions.jaiext.jiffle.parser.SourcePositionsWorker;\nimport it.geosolutions.jaiext.jiffle.parser.VarWorker;\nimport it.geosolutions.jaiext.jiffle.parser.node.GetSourceValue;\nimport it.geosolutions.jaiext.jiffle.parser.node.Script;\nimport it.geosolutions.jaiext.jiffle.parser.node.SourceWriter;\nimport it.geosolutions.jaiext.jiffle.runtime.JiffleDirectRuntime;\nimport it.geosolutions.jaiext.jiffle.runtime.JiffleIndirectRuntime;\nimport it.geosolutions.jaiext.jiffle.runtime.JiffleRuntime;\n\n/**\n * Compiles scripts and generates Java sources and executable bytecode for\n * runtime classes.\n * <p>\n * Example of use:\n * <pre><code>\n * // A script to write sequential values to image pixels\n * String script = \"images { dest=write; } dest = x() + y() * width();\" ;\n *\n * Jiffle jiffle = new Jiffle();\n * jiffle.setScript(script);\n * jifle.compile();\n *\n * // Now we get the runtime object from the compiled Jiffle object\n * JiffleDirectRuntime runtime = jiffle.getRuntimeInstance();\n *\n * // Create an image to hold the results of the script\n * final int width = 10;\n * WritableRenderedImage destImg = ImageUtils.createConstantImage(width, width, 0.0d);\n * \n * // Associate this image with the variable name used in the script\n * runtime.setDestinationImage(\"dest\", destImg);\n *\n * // Evaluate the script for all destination image pixels\n * runtime.evaluateAll();\n * </code></pre>\n * For further examples of how to create and run Jiffle scripts see the\n * {@code it.geosolutions.jaiext.demo.jiffle} package in the JAI-tools demo module.\n *\n * <h4>Implementation note</h4>\n * The Jiffle compiler is actually a Jiffle to Java translator.\n * When a client requests a runtime object, the script is translated into \n * Java source for a runtime class. This source code is then passed to an \n * embedded Janino compiler which produces the runtime object.\n *\n * @see JiffleBuilder\n * @see it.geosolutions.jaiext.jiffle.runtime.JiffleExecutor\n * \n * @author Michael Bedward\n * @since 0.1\n * @version $Id$\n */\npublic class Jiffle {\n \n public static final Logger LOGGER = Logger.getLogger(Jiffle.class.getName());\n \n private static Pattern BLOCK_COMMENT_STRIPPER = Pattern.compile(\"(?:/\\\\*(?:[^*]|(?:\\\\*+[^*/]))*\\\\*+/)\");\n\n /** \n * Constants for runtime model. Jiffle supports two runtime models:\n * <ol type=\"1\">\n * <li>\n * <b>Direct</b> - where the runtime class {@code evaluate} method directly \n * sets values in the destination image(s)\n * </li>\n * <li>\n * <b>Indirect</b> - where there is a single destination image and the runtime\n * class {@code evaluate} method returns the destination value, leaving\n * it up to the caller to set the value in the image.\n * object \n * </li>\n * </ol>\n * The indirect model is designed for use in an image operator or similar\n * context where further processing of destination values might be required\n * before writing to an image.\n */\n public static enum RuntimeModel {\n /** The runtime class implements {@link JiffleDirectRuntime} */\n DIRECT(JiffleDirectRuntime.class),\n \n /** The runtime class implements {@link JiffleIndirectRuntime} */\n INDIRECT(JiffleIndirectRuntime.class);\n \n private Class<? extends JiffleRuntime> runtimeClass;\n \n private RuntimeModel(Class<? extends JiffleRuntime> clazz) {\n this.runtimeClass = clazz;\n }\n\n /**\n * Gets the runtime interface.\n *\n * @return the runtime interface\n */\n public Class<? extends JiffleRuntime> getRuntimeClass() {\n return runtimeClass;\n }\n \n /**\n * Gets the matching constant for the given runtime class.\n *\n * @param clazz a runtime class\n *\n * @return the contant or {@code null} if the class does not derive\n * from a supported base class\n */\n public static Jiffle.RuntimeModel get(Class<? extends JiffleRuntime> clazz) {\n for (Jiffle.RuntimeModel t : Jiffle.RuntimeModel.values()) {\n if (t.runtimeClass.isAssignableFrom(clazz)) {\n return t;\n }\n }\n \n return null;\n }\n }\n\n \n /** Number of Jiffle instances */\n private static int refCount = 0;\n \n\n /**\n * Used to specify the roles of images referenced in\n * a Jiffle script. An image may be either read-only\n * ({@link Jiffle.ImageRole#SOURCE}) or write-only\n * ({@link Jiffle.ImageRole#DEST}) but not both.\n */\n public static enum ImageRole {\n /** Indicates an image is used for input (read-only) */\n SOURCE,\n \n /** Indicates an image is used for output (write-only) */\n DEST;\n }\n\n /** A name: either a default or one set by the client */\n private String name;\n\n private String theScript;\n private Script scriptModel;\n private Map<String, Jiffle.ImageRole> imageParams;\n private Map<String, Integer> destinationBands;\n \n /**\n * Creates a new instance.\n */\n public Jiffle() {\n init();\n }\n \n /**\n * Creates a new instance by compiling the provided script. Using this\n * constructor is equivalent to:\n * <pre><code>\n * Jiffle jiffle = new Jiffle();\n * jiffle.setScript(script);\n * jiffle.setImageParams(params);\n * jiffle.compile();\n * </code></pre>\n * \n * @param script Jiffle source code to compile\n * \n * @param params defines the names and roles of image variables\n * referred to in the script.\n * @throws JiffleException on compilation errors\n * \n */\n public Jiffle(String script, Map<String, Jiffle.ImageRole> params)\n throws it.geosolutions.jaiext.jiffle.JiffleException {\n init();\n setScript(script);\n setImageParams(params);\n compile();\n }\n\n /**\n * Creates a new instance by compiling the script read from {@code scriptFile}. \n * Using this constructor is equivalent to:\n * <pre><code>\n * Jiffle jiffle = new Jiffle();\n * jiffle.setScript(scriptFile);\n * jiffle.setImageParams(params);\n * jiffle.compile();\n * </code></pre>\n * \n * @param scriptFile file containing the Jiffle script\n * \n * @param params defines the names and roles of image variables\n * referred to in the script.\n * \n * @throws JiffleException on compilation errors\n */\n public Jiffle(File scriptFile, Map<String, Jiffle.ImageRole> params)\n throws it.geosolutions.jaiext.jiffle.JiffleException {\n init();\n setScript(scriptFile);\n setImageParams(params);\n compile();\n }\n \n /**\n * Sets the script. Calling this method will clear any previous script\n * and runtime objects.\n * \n * @param script a Jiffle script\n * @throws JiffleException if the script is empty or {@code null}\n */\n public final void setScript(String script) throws\n it.geosolutions.jaiext.jiffle.JiffleException {\n if (script == null || script.trim().length() == 0) {\n throw new it.geosolutions.jaiext.jiffle.JiffleException(\"script is empty !\");\n }\n \n clearCompiledObjects();\n \n // add extra new line just in case last statement hits EOF\n theScript = script + \"\\n\";\n }\n\n /**\n * Sets the script. Calling this method will clear any previous script\n * and runtime objects.\n * \n * @param scriptFile a file containing a Jiffle script\n * @throws JiffleException if the script is empty or {@code null}\n */\n public final void setScript(File scriptFile) throws\n it.geosolutions.jaiext.jiffle.JiffleException {\n BufferedReader reader = null;\n try {\n reader = new BufferedReader(new FileReader(scriptFile));\n StringBuilder sb = new StringBuilder();\n String line;\n while ((line = reader.readLine()) != null) {\n line = line.trim();\n if (line.length() > 0) {\n sb.append(line);\n sb.append('\\n'); // put the newline back on for the parser\n }\n }\n \n setScript(sb.toString());\n \n } catch (IOException ex) {\n throw new it.geosolutions.jaiext.jiffle.JiffleException(\"Could not read the script file\", ex);\n\n } finally {\n if (reader != null) {\n try {\n reader.close();\n } catch (IOException ignored) {\n }\n }\n }\n }\n \n /**\n * Gets the Jiffle script.\n * \n * @return the script or an empty {@code String} if none\n * has been set\n */\n public String getScript() {\n return theScript == null ? \"\" : theScript;\n }\n \n /**\n * Sets the image parameters. These define which variables in\n * the script refer to images and their types (source or destination).\n * <p>\n * This may be called before or after setting the script. No check is\n * made between script and parameters until the script is compiled.\n * \n * @param params the image parameters\n */\n public final void setImageParams(Map<String, Jiffle.ImageRole> params) {\n imageParams.clear();\n imageParams.putAll(params);\n }\n \n /**\n * Gets the current image parameters. The parameters are returned\n * as an unmodifiable map.\n * \n * @return image parameters or an empty {@code Map} if none\n * are set\n */\n public Map<String, Jiffle.ImageRole> getImageParams() {\n return Collections.unmodifiableMap(imageParams);\n }\n\n /**\n * Replaces the default name set for this object with a user-supplied name.\n * The name is solely for use by client code. No checks are made for \n * duplication between current instances.\n * \n * @param name the name to assign\n */\n public void setName(String name) {\n this.name = name;\n }\n\n /**\n * Gets the name assigned to this object. This will either be the\n * default name or one assigned by the client via {@link #setName(String)}\n *\n * @return the name\n */\n public String getName() {\n return name;\n }\n \n /**\n * Compiles the script into Java source for the runtime class.\n * \n * @throws JiffleException on compilation errors\n */\n public final void compile() throws it.geosolutions.jaiext.jiffle.JiffleException {\n if (theScript == null) {\n throw new it.geosolutions.jaiext.jiffle.JiffleException(\"No script has been set\");\n }\n \n Jiffle.Result<ParseTree> parseResult = parseScript(theScript);\n if (parseResult.messages.isError()) {\n reportMessages(parseResult);\n return;\n }\n \n /*\n * If image var parameters were provided by the caller we\n * ignore any in the script. Otherwise, we look for an \n * images block in the script.\n */\n ParseTree tree = parseResult.result;\n if (imageParams.isEmpty()) {\n Jiffle.Result<Map<String, Jiffle.ImageRole>> r = getScriptImageParams(\n tree);\n if (r.messages.isError()) {\n reportMessages(r);\n return;\n }\n if (r.result.isEmpty()) {\n throw new it.geosolutions.jaiext.jiffle.JiffleException(\n \"No image parameters provided and none found in script\");\n }\n \n setImageParams(r.result);\n }\n\n // run the other preparation and check related workers\n OptionsBlockWorker optionsWorker = new OptionsBlockWorker(tree);\n reportMessages(optionsWorker.messages);\n InitBlockWorker initWorker = new InitBlockWorker(tree);\n reportMessages(initWorker.messages);\n VarWorker vw = new VarWorker(tree, imageParams);\n ExpressionWorker expressionWorker = new ExpressionWorker(tree, vw);\n reportMessages(expressionWorker.messages);\n\n // \n RuntimeModelWorker worker = new RuntimeModelWorker(tree, optionsWorker.options, expressionWorker.getProperties(), expressionWorker.getScopes());\n this.scriptModel = worker.getScriptNode();\n this.destinationBands = worker.getDestinationBands();\n }\n \n /**\n * Tests whether the script has been compiled successfully.\n *\n * @return {@code true} if the script has been compiled;\n * {@code false} otherwise\n */\n public boolean isCompiled() {\n return scriptModel != null;\n }\n \n /**\n * Creates an instance of the default runtime class. \n * <p>\n * The default runtime class implements {@link JiffleDirectRuntime} and\n * extends an abstract base class provided by the Jiffle compiler. Objects\n * of this class evaluate the Jiffle script and write results directly to\n * the destination image(s). Client code can call either of the methods:\n * <ul>\n * <li>{@code evaluate(int x, int y)}\n * <li>{@code evaluateAll(JiffleProgressListener listener}\n * </ul>\n * The {@code Jiffle} object must be compiled before calling this method.\n * \n * @return the runtime object\n * @throws JiffleException if the script has not been compiled or if errors\n * occur in creating the runtime instance\n */\n public JiffleDirectRuntime getRuntimeInstance() throws\n it.geosolutions.jaiext.jiffle.JiffleException {\n return (JiffleDirectRuntime) getRuntimeInstance(RuntimeModel.DIRECT);\n }\n \n /**\n * Creates a runtime object based using the class specified by {@code model}.\n * <p>\n * The {@code Jiffle} object must be compiled before calling this method.\n * \n * @param model the {@link Jiffle.RuntimeModel}\n * @return the runtime object\n * @throws JiffleException if the script has not been compiled or if errors\n * occur in creating the runtime instance\n */\n public JiffleRuntime getRuntimeInstance(Jiffle.RuntimeModel model) throws\n it.geosolutions.jaiext.jiffle.JiffleException {\n return createRuntimeInstance(model, getRuntimeBaseClass(model), false);\n }\n\n /**\n * Gets the runtime object for this script. \n * <p>\n * The runtime object is an instance of {@link JiffleRuntime}. By default\n * it extends an abstract base class supplied JAI-tools: \n * {@link it.geosolutions.jaiext.jiffle.runtime.AbstractDirectRuntime}\n * when using the direct runtiem model or \n * {@link it.geosolutions.jaiext.jiffle.runtime.AbstractIndirectRuntime}\n * when using the indirect model. This method allows you to\n * specify a custom base class. The custom class must implement either \n * {@link JiffleDirectRuntime} or {@link JiffleIndirectRuntime}.\n *\n * @param <T> the runtime base class type\n * @param baseClass the runtime base class\n *\n * @return the runtime object\n * @throws JiffleException if the script has not been compiled or if errors\n * occur in creating the runtime instance\n */\n\n public <T extends JiffleRuntime> T getRuntimeInstance(Class<T> baseClass) throws\n it.geosolutions.jaiext.jiffle.JiffleException {\n RuntimeModel model = RuntimeModel.get(baseClass);\n if (model == null) {\n throw new it.geosolutions.jaiext.jiffle.JiffleException(baseClass.getName() +\n \" does not implement a required Jiffle runtime interface\");\n }\n\n return (T) createRuntimeInstance(model, baseClass, false);\n }\n\n private JiffleRuntime createRuntimeInstance(RuntimeModel model, Class<? extends JiffleRuntime> runtimeClass, boolean scriptInDocs) throws\n \n it.geosolutions.jaiext.jiffle.JiffleException {\n if (!isCompiled()) {\n throw new it.geosolutions.jaiext.jiffle.JiffleException(\"The script has not been compiled\");\n }\n\n String runtimeSource = createRuntimeSource(model, runtimeClass.getName(), scriptInDocs);\n if (LOGGER.isLoggable(Level.FINE)) {\n LOGGER.log(Level.FINE, \"Jiffle script compiled to:\\n\\n\" + runtimeSource); \n }\n try {\n SimpleCompiler compiler = new SimpleCompiler();\n compiler.cook(runtimeSource);\n\n StringBuilder sb = new StringBuilder();\n sb.append(it.geosolutions.jaiext.jiffle.JiffleProperties.get(\n it.geosolutions.jaiext.jiffle.JiffleProperties.RUNTIME_PACKAGE_KEY)).append(\".\");\n\n switch (model) {\n case DIRECT:\n sb.append(it.geosolutions.jaiext.jiffle.JiffleProperties.get(\n it.geosolutions.jaiext.jiffle.JiffleProperties.DIRECT_CLASS_KEY));\n break;\n\n case INDIRECT:\n sb.append(it.geosolutions.jaiext.jiffle.JiffleProperties.get(\n it.geosolutions.jaiext.jiffle.JiffleProperties.INDIRECT_CLASS_KEY));\n break;\n\n default:\n throw new IllegalArgumentException(\"Internal compiler error\");\n }\n\n Class<?> clazz = compiler.getClassLoader().loadClass(sb.toString());\n JiffleRuntime runtime = (JiffleRuntime) clazz.newInstance();\n runtime.setImageParams(imageParams);\n if (runtime instanceof JiffleIndirectRuntime) {\n ((JiffleIndirectRuntime) runtime).setDestinationBands(destinationBands);\n }\n return runtime;\n\n } catch (Exception ex) {\n throw new it.geosolutions.jaiext.jiffle.JiffleException(\"Runtime source error for source: \" + runtimeSource, ex);\n }\n }\n\n /**\n * Gets a copy of the Java source for the runtime class. The \n * script must have been compiled before calling this method.\n * \n * @param scriptInDocs whether to include the original Jiffle script\n * in the class javadocs\n * \n * @return source for the runtime class\n * \n * @throws JiffleException if the script has not been compiled or if errors\n * occur in creating the runtime source code\n */\n public String getRuntimeSource(boolean scriptInDocs)\n throws it.geosolutions.jaiext.jiffle.JiffleException {\n\n return getRuntimeSource(RuntimeModel.DIRECT, scriptInDocs);\n }\n \n /**\n * Gets a copy of the Java source for the runtime class. The \n * script must have been compiled before calling this method.\n * \n * @param model the {@link Jiffle.RuntimeModel}\n * @param scriptInDocs whether to include the original Jiffle script\n * in the class javadocs\n * \n * @return source for the runtime class\n * @throws JiffleException if the script has not been compiled or if errors\n * occur in creating the runtime source code\n * \n */\n public String getRuntimeSource(Jiffle.RuntimeModel model, boolean scriptInDocs)\n throws it.geosolutions.jaiext.jiffle.JiffleException {\n return createRuntimeSource(model, getRuntimeBaseClass(model).getName(), scriptInDocs);\n }\n\n private Class<? extends JiffleRuntime> getRuntimeBaseClass(RuntimeModel model) {\n Class<? extends JiffleRuntime> baseClass = null;\n switch (model) {\n case DIRECT:\n baseClass = it.geosolutions.jaiext.jiffle.JiffleProperties.DEFAULT_DIRECT_BASE_CLASS;\n break;\n\n case INDIRECT:\n baseClass = it.geosolutions.jaiext.jiffle.JiffleProperties.DEFAULT_INDIRECT_BASE_CLASS;\n break;\n }\n \n return baseClass;\n }\n\n private String createRuntimeSource(RuntimeModel model, String baseClassName, boolean scriptInDocs) {\n if (scriptInDocs) {\n throw new RuntimeException(\"Do no know how to clean the block comments yet\");\n }\n\n SourceWriter writer = new SourceWriter(model);\n writer.setScript(stripComments(theScript));\n writer.setBaseClassName(baseClassName);\n scriptModel.write(writer);\n return writer.getSource();\n }\n\n private String stripComments(String theScript) {\n return BLOCK_COMMENT_STRIPPER.matcher(theScript).replaceAll(\"\");\n }\n\n /**\n * Initializes this object's name and runtime base class.\n */\n private void init() {\n Jiffle.refCount++ ;\n \n // TODO - CollectionFactory\n imageParams = new HashMap<String, Jiffle.ImageRole>();\n }\n \n /**\n * Clears all compiler and runtime objects.\n */\n private void clearCompiledObjects() {\n // TODO if necessary\n }\n \n /**\n * Builds the parse tree from the script.\n * @param script\n */\n private static Jiffle.Result<ParseTree> parseScript(String script) {\n CharStream input = CharStreams.fromString(script);\n \n JiffleLexer lexer = new JiffleLexer(input);\n TokenStream tokens = new CommonTokenStream(lexer);\n \n JiffleParser parser = new JiffleParser(tokens);\n parser.removeErrorListeners();\n \n JiffleParserErrorListener errListener = new JiffleParserErrorListener();\n parser.addErrorListener(errListener);\n\n ParseTree tree = parser.script();\n return new Jiffle.Result(tree, errListener.messages);\n }\n \n private static Jiffle.Result<Map<String, Jiffle.ImageRole>> getScriptImageParams(ParseTree tree) {\n ImagesBlockWorker reader = new ImagesBlockWorker(tree);\n return new Jiffle.Result(reader.imageVars, reader.messages);\n }\n\n private static void reportMessages(Jiffle.Result result) throws\n it.geosolutions.jaiext.jiffle.JiffleException {\n reportMessages(result.messages);\n }\n\n private static void reportMessages(Messages messages) throws\n it.geosolutions.jaiext.jiffle.JiffleException {\n if (messages.isError()) {\n String expectionMessage = messages.toString();\n throw new it.geosolutions.jaiext.jiffle.JiffleException(expectionMessage);\n }\n }\n \n private static class Result<T> {\n final T result;\n final Messages messages;\n\n public Result(T result, Messages messages) {\n this.result = result;\n this.messages = messages;\n }\n }\n\n /**\n * A utility method returning the source positions used in a given script\n *\n * @param script\n * @return\n */\n public static Set<GetSourceValue> getReadPositions(String script, List<String> sourceImageNames)\n throws JiffleException {\n Jiffle.Result<ParseTree> parseResult = parseScript(script);\n if (parseResult.messages.isError()) {\n reportMessages(parseResult);\n return Collections.emptySet();\n }\n\n // If image var parameters were provided by the caller we ignore any in the script.\n // Otherwise, we look for an images block in the script.\n ParseTree tree = parseResult.result;\n if (sourceImageNames == null) {\n Jiffle.Result<Map<String, Jiffle.ImageRole>> r = getScriptImageParams(tree);\n sourceImageNames =\n r.result\n .entrySet()\n .stream()\n .filter(k -> k.getValue() == ImageRole.SOURCE)\n .map(k -> k.getKey())\n .collect(Collectors.toList());\n }\n\n if (sourceImageNames.isEmpty()) {\n return Collections.emptySet();\n }\n\n SourcePositionsWorker worker = new SourcePositionsWorker(tree, sourceImageNames);\n Set<GetSourceValue> positions = worker.getPositions();\n\n return positions;\n }\n}\n\n## 内容2\n### 文件路径:\njt-jiffle/jt-jiffle-language/src/main/java/it/geosolutions/jaiext/jiffle/parser/node/Script.java\n### 代码内容:\npackage it.geosolutions.jaiext.jiffle.parser.node;\n\nimport static java.lang.String.format;\n\nimport it.geosolutions.jaiext.jiffle.Jiffle;\nimport it.geosolutions.jaiext.jiffle.parser.JiffleParserException;\nimport it.geosolutions.jaiext.jiffle.parser.OptionLookup;\nimport it.geosolutions.jaiext.jiffle.parser.RepeatedReadOptimizer;\nimport it.geosolutions.jaiext.jiffle.parser.UndefinedOptionException;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\n/** @author michael */\npublic class Script implements Node {\n private final StatementList stmts;\n private final RepeatedReadOptimizer readOptimizer;\n private Map<String, String> options;\n private Set<String> sourceImages;\n private Set<String> destImages;\n private final GlobalVars globals;\n\n public Script(\n Map<String, String> options,\n Set<String> sourceImages,\n Set<String> destImages,\n GlobalVars globals,\n StatementList stmts,\n RepeatedReadOptimizer readOptimizer) {\n this.options = options;\n this.sourceImages = sourceImages;\n this.destImages = destImages;\n this.globals = globals;\n this.stmts = stmts;\n this.readOptimizer = readOptimizer;\n }\n\n public void write(SourceWriter w) {\n // class header\n String packageName = \"it.geosolutions.jaiext.jiffle.runtime\";\n w.line(\"package \" + packageName + \";\");\n w.newLine();\n w.line(\"import java.util.List;\");\n w.line(\"import java.util.ArrayList;\");\n w.line(\"import java.util.Arrays;\");\n w.newLine();\n\n // add the script source, if available\n String script = w.getScript();\n if (script != null) {\n String[] lines = script.split(\"\\n\");\n w.line(\"/**\");\n w.line(\" * Java runtime class generated from the following Jiffle script: \");\n w.line(\" *<code>\");\n for (String line : lines) {\n w.append(\" * \").append(line).newLine();\n }\n w.line(\" *</code>\");\n w.line(\" */\");\n }\n\n // class declaration\n String template = \"public class %s extends %s {\";\n String className;\n Jiffle.RuntimeModel model = w.getRuntimeModel();\n if (model == Jiffle.RuntimeModel.DIRECT) {\n className = \"JiffleDirectRuntimeImpl\";\n } else {\n className = \"JiffleIndirectRuntimeImpl\";\n }\n w.line(format(template, className, w.getBaseClassName()));\n\n // writing class fields\n w.inc();\n // ... if we are using a internal class, dodge map lookups while working on pixels\n if (w.isInternalBaseClass()) {\n for (String sourceImage : sourceImages) {\n w.indent().append(\"SourceImage s_\").append(sourceImage).append(\";\").newLine();\n }\n if (model == Jiffle.RuntimeModel.DIRECT) {\n for (String destImage : destImages) {\n w.indent()\n .append(\"DestinationImage d_\")\n .append(destImage)\n .append(\";\")\n .newLine();\n }\n }\n }\n globals.writeFields(w);\n w.newLine();\n\n // adding the constructor\n w.indent().append(\"public \").append(className).append(\"() {\").newLine();\n w.inc();\n w.indent().append(\"super(new String[] {\");\n globals.listNames(w);\n w.append(\"});\").newLine();\n w.dec();\n w.line(\"}\");\n w.newLine();\n\n // add the options init, if required\n if (options != null && !options.isEmpty()) {\n w.line(\"protected void initOptionVars() {\");\n w.inc();\n for (Map.Entry<String, String> entry : options.entrySet()) {\n String name = entry.getKey();\n String value = entry.getValue();\n try {\n String activeExpr = OptionLookup.getActiveRuntimExpr(name, value);\n w.line(activeExpr);\n } catch (UndefinedOptionException e) {\n throw new JiffleParserException(e);\n }\n }\n w.dec();\n w.line(\"}\");\n }\n\n // and field initializer method\n w.line(\"protected void initImageScopeVars() {\");\n w.inc();\n if (w.isInternalBaseClass()) {\n for (String sourceImage : sourceImages) {\n w.indent()\n .append(\"s_\")\n .append(sourceImage)\n .append(\" = (SourceImage) _images.get(\\\"\")\n .append(sourceImage)\n .append(\"\\\");\")\n .newLine();\n }\n if (model == Jiffle.RuntimeModel.DIRECT) {\n for (String destImage : destImages) {\n w.indent()\n .append(\"d_\")\n .append(destImage)\n .append(\"= (DestinationImage) _destImages.get(\\\"\")\n .append(destImage)\n .append(\"\\\");\")\n .newLine();\n }\n }\n }\n globals.write(w);\n w.line(\"_imageScopeVarsInitialized = true;\");\n w.dec();\n w.line(\"}\");\n w.newLine();\n\n // the evaluate method\n if (model == Jiffle.RuntimeModel.DIRECT) {\n w.line(\"public void evaluate(double _x, double _y) {\");\n } else {\n w.line(\"public void evaluate(double _x, double _y, double[] result) {\");\n }\n w.inc();\n\n // basic checks at the beginning of pixel evaluation\n w.line(\"if (!isWorldSet()) {\");\n w.inc();\n w.line(\"setDefaultBounds();\");\n w.dec();\n w.line(\"}\");\n w.line(\"if (!_imageScopeVarsInitialized) {\");\n w.inc();\n w.line(\"initImageScopeVars();\");\n w.dec();\n w.line(\"}\");\n w.line(\"_stk.clear();\");\n\n // centralize the source reads to avoid repeated reads\n readOptimizer.declareRepeatedReads(w);\n\n // the actual script\n w.newLine();\n stmts.write(w);\n\n w.dec();\n w.line(\"}\");\n\n // closing class\n w.dec();\n w.line(\"}\");\n }\n\n @Override\n public boolean equals(Object o) {\n if (this == o) return true;\n if (o == null || getClass() != o.getClass()) return false;\n Script script = (Script) o;\n return Objects.equals(stmts, script.stmts) &&\n Objects.equals(options, script.options) &&\n Objects.equals(sourceImages, script.sourceImages) &&\n Objects.equals(destImages, script.destImages) &&\n Objects.equals(globals, script.globals);\n }\n\n @Override\n public int hashCode() {\n return Objects.hash(stmts, options, sourceImages, destImages, globals);\n }\n\n public StatementList getStmts() {\n return stmts;\n }\n\n public Map<String, String> getOptions() {\n return Collections.unmodifiableMap(options);\n }\n\n public Set<String> getSourceImages() {\n return Collections.unmodifiableSet(sourceImages);\n }\n\n public Set<String> getDestImages() {\n return Collections.unmodifiableSet(destImages);\n }\n\n public GlobalVars getGlobals() {\n return globals;\n }\n}\n\n", "input": "", "output": "【漏洞类型】未验证的用户输入\n【严重性】Medium\n【漏洞摘要】在Jiffle类的`setScript`方法中,用户提供的脚本未经验证就被直接编译和执行。如果用户输入恶意脚本,可能会导致远程代码执行(RCE)漏洞。具体问题在于,`setScript`方法直接将用户输入的脚本传递给`compile`方法,而`compile`方法中使用Janino编译器将脚本转换为Java源代码并执行。这使得攻击者可以通过构造恶意脚本来执行任意代码。\n【关键代码模式】setScript方法、compile方法、SimpleCompiler类、getRuntimeInstance方法\n【修复方法说明】修复方法通过在`setScript`和`getRuntimeInstance`方法中添加`includeScript`标志,并在`createRuntimeSource`方法中使用该标志来决定是否包含原始脚本。同时,修复代码在`Script`类中添加了对变量名的正则表达式验证,以确保它们符合Java标识符的语法。这有助于防止恶意脚本的执行。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nsrc/main/java/org/codehaus/plexus/archiver/AbstractUnArchiver.java\n### 代码内容:\npackage org.codehaus.plexus.archiver;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport org.codehaus.plexus.archiver.util.ArchiveEntryUtils;\nimport org.codehaus.plexus.components.io.attributes.SymlinkUtils;\nimport org.codehaus.plexus.components.io.fileselectors.FileSelector;\nimport org.codehaus.plexus.components.io.resources.PlexusIoResource;\nimport org.codehaus.plexus.logging.AbstractLogEnabled;\nimport org.codehaus.plexus.util.FileUtils;\nimport org.codehaus.plexus.util.IOUtil;\nimport org.codehaus.plexus.util.StringUtils;\n\n/**\n * @author <a href=\"mailto:evenisse@codehaus.org\">Emmanuel Venisse</a>\n * @todo there should really be constructors which take the source file.\n */\npublic abstract class AbstractUnArchiver\n extends AbstractLogEnabled\n implements UnArchiver, FinalizerEnabled\n{\n\n private File destDirectory;\n\n private File destFile;\n\n private File sourceFile;\n\n private boolean overwrite = true;\n\n private List finalizers;\n\n private FileSelector[] fileSelectors;\n\n /**\n * since 2.3 is on by default\n *\n * @since 1.1\n */\n private boolean useJvmChmod = true;\n\n /**\n * @since 1.1\n */\n private boolean ignorePermissions = false;\n\n public AbstractUnArchiver()\n {\n // no op\n }\n\n public AbstractUnArchiver( final File sourceFile )\n {\n this.sourceFile = sourceFile;\n }\n\n @Override\n public File getDestDirectory()\n {\n return destDirectory;\n }\n\n @Override\n public void setDestDirectory( final File destDirectory )\n {\n this.destDirectory = destDirectory;\n }\n\n @Override\n public File getDestFile()\n {\n return destFile;\n }\n\n @Override\n public void setDestFile( final File destFile )\n {\n this.destFile = destFile;\n }\n\n @Override\n public File getSourceFile()\n {\n return sourceFile;\n }\n\n @Override\n public void setSourceFile( final File sourceFile )\n {\n this.sourceFile = sourceFile;\n }\n\n @Override\n public boolean isOverwrite()\n {\n return overwrite;\n }\n\n @Override\n public void setOverwrite( final boolean b )\n {\n overwrite = b;\n }\n\n @Override\n public final void extract()\n throws ArchiverException\n {\n validate();\n execute();\n runArchiveFinalizers();\n }\n\n @Override\n public final void extract( final String path, final File outputDirectory )\n throws ArchiverException\n {\n validate( path, outputDirectory );\n execute( path, outputDirectory );\n runArchiveFinalizers();\n }\n\n @Override\n public void addArchiveFinalizer( final ArchiveFinalizer finalizer )\n {\n if ( finalizers == null )\n {\n finalizers = new ArrayList();\n }\n\n finalizers.add( finalizer );\n }\n\n @Override\n public void setArchiveFinalizers( final List archiveFinalizers )\n {\n finalizers = archiveFinalizers;\n }\n\n private void runArchiveFinalizers()\n throws ArchiverException\n {\n if ( finalizers != null )\n {\n for ( Object finalizer1 : finalizers )\n {\n final ArchiveFinalizer finalizer = (ArchiveFinalizer) finalizer1;\n\n finalizer.finalizeArchiveExtraction( this );\n }\n }\n }\n\n protected void validate( final String path, final File outputDirectory )\n {\n }\n\n protected void validate()\n throws ArchiverException\n {\n if ( sourceFile == null )\n {\n throw new ArchiverException( \"The source file isn't defined.\" );\n }\n\n if ( sourceFile.isDirectory() )\n {\n throw new ArchiverException( \"The source must not be a directory.\" );\n }\n\n if ( !sourceFile.exists() )\n {\n throw new ArchiverException( \"The source file \" + sourceFile + \" doesn't exist.\" );\n }\n\n if ( destDirectory == null && destFile == null )\n {\n throw new ArchiverException( \"The destination isn't defined.\" );\n }\n\n if ( destDirectory != null && destFile != null )\n {\n throw new ArchiverException( \"You must choose between a destination directory and a destination file.\" );\n }\n\n if ( destDirectory != null && !destDirectory.isDirectory() )\n {\n destFile = destDirectory;\n destDirectory = null;\n }\n\n if ( destFile != null && destFile.isDirectory() )\n {\n destDirectory = destFile;\n destFile = null;\n }\n }\n\n @Override\n public void setFileSelectors( final FileSelector[] fileSelectors )\n {\n this.fileSelectors = fileSelectors;\n }\n\n @Override\n public FileSelector[] getFileSelectors()\n {\n return fileSelectors;\n }\n\n protected boolean isSelected( final String fileName, final PlexusIoResource fileInfo )\n throws ArchiverException\n {\n if ( fileSelectors != null )\n {\n for ( FileSelector fileSelector : fileSelectors )\n {\n try\n {\n\n if ( !fileSelector.isSelected( fileInfo ) )\n {\n return false;\n }\n }\n catch ( final IOException e )\n {\n throw new ArchiverException(\n \"Failed to check, whether \" + fileInfo.getName() + \" is selected: \" + e.getMessage(), e );\n }\n }\n }\n return true;\n }\n\n protected abstract void execute()\n throws ArchiverException;\n\n protected abstract void execute( String path, File outputDirectory )\n throws ArchiverException;\n\n /**\n * @since 1.1\n */\n @Override\n public boolean isUseJvmChmod()\n {\n return useJvmChmod;\n }\n\n /**\n * <b>jvm chmod won't set group level permissions !</b>\n *\n * @since 1.1\n */\n @Override\n public void setUseJvmChmod( final boolean useJvmChmod )\n {\n this.useJvmChmod = useJvmChmod;\n }\n\n /**\n * @since 1.1\n */\n @Override\n public boolean isIgnorePermissions()\n {\n return ignorePermissions;\n }\n\n /**\n * @since 1.1\n */\n @Override\n public void setIgnorePermissions( final boolean ignorePermissions )\n {\n this.ignorePermissions = ignorePermissions;\n }\n\n protected void extractFile( final File srcF, final File dir, final InputStream compressedInputStream,\n final String entryName, final Date entryDate, final boolean isDirectory,\n final Integer mode, String symlinkDestination )\n throws IOException, ArchiverException\n {\n // Hmm. Symlinks re-evaluate back to the original file here. Unsure if this is a good thing...\n final File f = FileUtils.resolveFile( dir, entryName );\n\n // Make sure that the resolved path of the extracted file doesn't escape the destination directory\n String canonicalDirPath = dir.getCanonicalPath();\n String canonicalDestPath = f.getCanonicalPath();\n\n if ( !canonicalDestPath.startsWith( canonicalDirPath ) )\n {\n throw new ArchiverException( \"Entry is outside of the target directory (\" + entryName + \")\" );\n }\n\n try\n {\n if ( !isOverwrite() && f.exists() && ( f.lastModified() >= entryDate.getTime() ) )\n {\n return;\n }\n\n // create intermediary directories - sometimes zip don't add them\n final File dirF = f.getParentFile();\n if ( dirF != null )\n {\n dirF.mkdirs();\n }\n\n if ( !StringUtils.isEmpty( symlinkDestination ) )\n {\n SymlinkUtils.createSymbolicLink( f, new File( symlinkDestination ) );\n }\n else if ( isDirectory )\n {\n f.mkdirs();\n }\n else\n {\n OutputStream out = null;\n try\n {\n out = new FileOutputStream( f );\n\n IOUtil.copy( compressedInputStream, out );\n out.close();\n out = null;\n }\n finally\n {\n IOUtil.close( out );\n }\n }\n\n f.setLastModified( entryDate.getTime() );\n\n if ( !isIgnorePermissions() && mode != null && !isDirectory )\n {\n ArchiveEntryUtils.chmod( f, mode );\n }\n }\n catch ( final FileNotFoundException ex )\n {\n getLogger().warn( \"Unable to expand to file \" + f.getPath() );\n }\n }\n\n}\n\n", "input": "", "output": "【漏洞类型】路径遍历漏洞\n【严重性】High\n【漏洞摘要】这是一个路径遍历漏洞。代码在`extractFile`函数中,通过`FileUtils.resolveFile(dir, entryName)`解析文件路径,但未进一步验证`entryName`是否会导致文件路径在目标目录之外。攻击者可以利用此漏洞通过精心构造的`entryName`来写入任意位置的文件,从而可能覆盖关键系统文件或进行其他恶意操作。\n【关键代码模式】\n- `FileUtils.resolveFile(dir, entryName)`\n- 未验证的`entryName`是否会导致文件路径在目标目录之外\n- `File f = FileUtils.resolveFile(dir, entryName)`\n【修复方法说明】修复方法通过获取`dir`和`f`的绝对路径,并检查`canonicalDestPath`是否以`canonicalDirPath`开头,来确保解压的文件路径在目标目录之内。这解决了路径遍历漏洞的问题,增加了对文件路径的验证。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nxwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/objects/classes/TextAreaClass.java\n### 代码内容:\n/*\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n */\npackage com.xpn.xwiki.objects.classes;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.xwiki.edit.EditException;\nimport org.xwiki.edit.Editor;\nimport org.xwiki.edit.EditorManager;\nimport org.xwiki.rendering.syntax.Syntax;\nimport org.xwiki.rendering.syntax.SyntaxContent;\nimport org.xwiki.stability.Unstable;\n\nimport com.xpn.xwiki.XWikiContext;\nimport com.xpn.xwiki.doc.XWikiDocument;\nimport com.xpn.xwiki.internal.velocity.VelocityEvaluator;\nimport com.xpn.xwiki.objects.BaseCollection;\nimport com.xpn.xwiki.objects.BaseProperty;\nimport com.xpn.xwiki.objects.LargeStringProperty;\nimport com.xpn.xwiki.objects.meta.PropertyMetaClass;\nimport com.xpn.xwiki.web.Utils;\n\npublic class TextAreaClass extends StringClass\n{\n /**\n * Possible values for the editor meta property.\n * <p>\n * Indicates which editor should be used to manipulate the content of the property.\n */\n public enum EditorType\n {\n /**\n * Plain text without any known syntax.\n */\n PURE_TEXT(\"PureText\"),\n\n /**\n * Edit wiki syntax using a text editor.\n */\n TEXT(\"Text\"),\n\n /**\n * Edit wiki syntax using a visual editor.\n */\n WYSIWYG(\"Wysiwyg\");\n\n private static final Map<String, EditorType> editorTypeMap =\n Arrays.stream(EditorType.values()).collect(Collectors.toMap(e -> e.value.toLowerCase(), e -> e));\n\n private final String value;\n\n /**\n * Retreive the {@link EditorType} based on its value.\n * <p>\n * The search is case insensitive.\n *\n * @param value the value of the editor type\n * @return the editor type matching the value or null\n * @since 10.7RC1\n */\n public static EditorType getByValue(String value)\n {\n return value != null ? editorTypeMap.get(value.toLowerCase()) : null;\n }\n\n private EditorType(String value)\n {\n this.value = value;\n }\n\n @Override\n public String toString()\n {\n return this.value;\n }\n }\n\n /**\n * Possible values for the contenttype meta property.\n * <p>\n * Indicates what kind of content this field contains (wiki, plain text, etc.).\n * \n * @since 8.3\n */\n public enum ContentType\n {\n /**\n * Plain text without any known syntax.\n */\n PURE_TEXT(\"PureText\"),\n\n /**\n * Wiki content.\n */\n WIKI_TEXT(\"FullyRenderedText\"),\n\n /**\n * Velocity content.\n */\n VELOCITY_CODE(\"VelocityCode\"),\n\n /**\n * Velocity content producing wiki content.\n * \n * @since 13.0\n */\n VELOCITYWIKI(\"VelocityWiki\");\n\n private static final Map<String, ContentType> contentTypeMap =\n Arrays.stream(ContentType.values()).collect(Collectors.toMap(c -> c.value.toLowerCase(), c -> c));\n\n private final String value;\n\n /**\n * Retreive the {@link ContentType} based on its value.\n * <p>\n * The search is case insensitive.\n *\n * @param value the value of the content type\n * @return the content type matching the value or null\n * @since 10.7RC1\n */\n public static ContentType getByValue(String value)\n {\n return value != null ? contentTypeMap.get(value.toLowerCase()) : null;\n }\n\n private ContentType(String value)\n {\n this.value = value;\n }\n\n @Override\n public String toString()\n {\n return this.value;\n }\n }\n\n private static final long serialVersionUID = 1L;\n\n private static final String XCLASSNAME = \"textarea\";\n\n private static final Logger LOGGER = LoggerFactory.getLogger(TextAreaClass.class);\n\n public TextAreaClass(PropertyMetaClass wclass)\n {\n super(XCLASSNAME, \"Text Area\", wclass);\n\n setSize(40);\n setRows(5);\n }\n\n public TextAreaClass()\n {\n this(null);\n }\n\n /**\n * @param contentType the content type\n * @return the editor type compatible with the passed content type, null if several are compatible\n */\n public static EditorType getEditorType(ContentType contentType, EditorType def)\n {\n if (contentType != null && contentType != ContentType.WIKI_TEXT) {\n return EditorType.PURE_TEXT;\n }\n\n return def;\n }\n\n /**\n * @param contentType the content type value\n * @param def the current editor type\n * @return the editor type compatible with the passed content type, def if several are compatible\n * @since 10.7RC1\n */\n public static EditorType getEditorType(String contentType, EditorType def)\n {\n return getEditorType(ContentType.getByValue(contentType), def);\n }\n\n /**\n * @param editorType the editor type\n * @return the content type compatible with the passed editor type, null if several are compatible\n */\n public static ContentType getContentType(EditorType editorType, ContentType def)\n {\n if (editorType != EditorType.PURE_TEXT) {\n return ContentType.WIKI_TEXT;\n }\n\n return def;\n }\n\n @Override\n public BaseProperty newProperty()\n {\n BaseProperty property = new LargeStringProperty();\n property.setName(getName());\n\n return property;\n }\n\n public int getRows()\n {\n return getIntValue(\"rows\");\n }\n\n public void setRows(int rows)\n {\n setIntValue(\"rows\", rows);\n }\n\n public String getEditor()\n {\n String editor = getStringValue(\"editor\").toLowerCase();\n if (EditorType.getByValue(editor) == null) {\n EditorType compatibleEditor = getEditorType(getContentType(), null);\n if (compatibleEditor != null) {\n return compatibleEditor.value.toLowerCase();\n }\n }\n\n return editor;\n }\n\n /**\n * Sets the editor meta property.\n * \n * @param editor the editor type\n * @since 8.2RC1\n */\n public void setEditor(String editor)\n {\n setStringValue(\"editor\", editor);\n }\n\n /**\n * Sets the editor meta property.\n * \n * @param editorType the editor type\n * @since 8.3\n */\n public void setEditor(EditorType editorType)\n {\n setEditor(editorType.toString());\n\n // Make sure the content type is compatible\n ContentType compatible = getContentType(editorType, null);\n if (compatible != null) {\n setContentType(compatible);\n }\n }\n\n public String getContentType()\n {\n String result = getStringValue(\"contenttype\").toLowerCase();\n if (result.isEmpty()) {\n result = ContentType.WIKI_TEXT.toString().toLowerCase();\n }\n\n return result;\n }\n\n public void setContentType(String contentType)\n {\n setStringValue(\"contenttype\", contentType);\n }\n\n /**\n * @param contentType the content type\n * @since 8.3\n */\n public void setContentType(ContentType contentType)\n {\n setContentType(contentType.toString());\n\n // Make sure the editor type is compatible\n EditorType compatible = getEditorType(contentType, null);\n if (compatible != null) {\n setEditor(compatible);\n }\n }\n\n public boolean isWysiwyg(XWikiContext context)\n {\n return \"wysiwyg\".equals(getEditorType(context));\n }\n\n private String getEditorType(XWikiContext context)\n {\n String editorType = null;\n if (context != null && context.getRequest() != null) {\n editorType = context.getRequest().get(\"xeditmode\");\n }\n if (isEmptyValue(editorType)) {\n editorType = getEditor();\n if (isEmptyValue(editorType) && context != null && context.getWiki() != null) {\n editorType = context.getWiki().getEditorPreference(context);\n }\n }\n return isEmptyValue(editorType) ? null : editorType.toLowerCase();\n }\n\n private boolean isEmptyValue(String value)\n {\n // See XWIKI-10853: Some static lists in XWiki.XWikiPreferences have the \"---\" value\n return StringUtils.isEmpty(value) || \"---\".equals(value);\n }\n\n /**\n * @return true if the content of this text area is not a wiki syntax content\n */\n public boolean isWikiContent()\n {\n String contentType = getContentType();\n\n if (contentType != null && !contentType.equals(\"puretext\") && !contentType.equals(\"velocitycode\")) {\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * Indicate if the content of this property should be executed in a restricted content (provided the type indicate\n * that this content should be executed).\n * \n * @return true if the content of this property should be executed in a restricted content, false otherwise\n * @since 14.10\n * @since 14.4.7\n * @since 13.10.11\n */\n @Unstable\n public boolean isRestricted()\n {\n return getIntValue(\"restricted\", 0) == 1;\n }\n\n /**\n * Indicate if the content of this property should be executed in a restricted content (provided the type indicate\n * that this content should be executed).\n * \n * @param restricted true if the content of this property should be executed in a restricted content, false\n * otherwise\n * @since 14.10\n * @since 14.4.7\n * @since 13.10.11\n */\n @Unstable\n public void setRestricted(boolean restricted)\n {\n setIntValue(\"restricted\", restricted ? 1 : 0);\n }\n\n @Override\n public void displayEdit(StringBuffer buffer, String name, String prefix, BaseCollection object,\n XWikiContext context)\n {\n String editorType = getEditorType(context);\n EditorManager editorManager = Utils.getComponent(EditorManager.class);\n Editor<SyntaxContent> editor = editorManager.getDefaultEditor(SyntaxContent.class, editorType);\n XWikiDocument ownerDocument = getObjectDocument(object, context);\n Map<String, Object> parameters = new HashMap<>();\n String fieldName = prefix + name;\n parameters.put(\"id\", fieldName);\n parameters.put(\"name\", fieldName);\n parameters.put(\"cols\", getSize());\n parameters.put(\"rows\", getRows());\n parameters.put(\"disabled\", isDisabled());\n parameters.put(\"restricted\", isRestricted() || (ownerDocument != null && ownerDocument.isRestricted()));\n parameters.put(\"sourceDocumentReference\", object.getDocumentReference());\n Syntax syntax = null;\n String contentType = getContentType();\n\n // We set the syntax by first checking the content type: if it's pure text or velocity code\n // the syntax is necessarily plain syntax.\n // Else we check if the wanted editor is puretext: in such case we also consider that the syntax is plain/text\n // finally we fallback on actual document syntax.\n // FIXME: if the content type is WIKI_TEXT we should probably force the syntax to Wiki syntax, but which one:\n // 2.0, 2.1?\n if (StringUtils.equalsIgnoreCase(ContentType.PURE_TEXT.toString(), contentType)\n || StringUtils.equalsIgnoreCase(ContentType.VELOCITY_CODE.toString(), contentType))\n {\n syntax = Syntax.PLAIN_1_0;\n } else {\n syntax = \"puretext\".equals(editorType) ? Syntax.PLAIN_1_0 : getObjectDocumentSyntax(object, context);\n }\n SyntaxContent syntaxContent = new SyntaxContent(object.getStringValue(name), syntax);\n try {\n buffer.append(editor.render(syntaxContent, parameters));\n } catch (EditException e) {\n LOGGER.error(\"Failed to display the text area property.\", e);\n }\n }\n\n @Override\n public void displayView(StringBuffer buffer, String name, String prefix, BaseCollection object,\n XWikiContext context)\n {\n displayView(buffer, name, prefix, object, true, context);\n }\n\n @Override\n public void displayView(StringBuffer buffer, String name, String prefix, BaseCollection object, boolean isolated,\n XWikiContext context)\n {\n String contentTypeString = getContentType();\n ContentType contentType = ContentType.getByValue(contentTypeString);\n\n if (contentType == ContentType.PURE_TEXT) {\n super.displayView(buffer, name, prefix, object, context);\n } else if (contentType == ContentType.VELOCITY_CODE) {\n StringBuffer result = new StringBuffer();\n super.displayView(result, name, prefix, object, context);\n if (getObjectDocumentSyntax(object, context).equals(Syntax.XWIKI_1_0)) {\n buffer.append(context.getWiki().parseContent(result.toString(), context));\n } else {\n // Don't do anything since this mode is deprecated and not supported in the new rendering.\n buffer.append(result);\n }\n } else {\n BaseProperty property = (BaseProperty) object.safeget(name);\n if (property != null) {\n String content = property.toText();\n XWikiDocument sdoc = getObjectDocument(object, context);\n\n if (sdoc != null) {\n if (contentType == ContentType.VELOCITYWIKI) {\n // Start with a pass of Velocity\n // TODO: maybe make velocity+wiki a syntax so that getRenderedContent can directly take care\n // of that\n VelocityEvaluator velocityEvaluator = Utils.getComponent(VelocityEvaluator.class);\n content = velocityEvaluator.evaluateVelocityNoException(content,\n isolated ? sdoc.getDocumentReference() : null);\n }\n\n // Make sure the right author is used to execute the textarea\n // Clone the document to void messaging with the cache\n if (!Objects.equals(sdoc.getAuthors().getEffectiveMetadataAuthor(),\n sdoc.getAuthors().getContentAuthor())) {\n sdoc = sdoc.clone();\n sdoc.getAuthors().setContentAuthor(sdoc.getAuthors().getEffectiveMetadataAuthor());\n }\n\n buffer.append(context.getDoc().getRenderedContent(content, sdoc.getSyntax(), isRestricted(), sdoc,\n isolated, context));\n } else {\n buffer.append(content);\n }\n }\n }\n }\n\n private XWikiDocument getObjectDocument(BaseCollection object, XWikiContext context)\n {\n try {\n XWikiDocument doc = object.getOwnerDocument();\n if (doc == null) {\n doc = context.getWiki().getDocument(object.getDocumentReference(), context);\n }\n\n return doc;\n } catch (Exception e) {\n // Used to convert a Document Reference to string (compact form without the wiki part if it matches\n // the current wiki).\n LOGGER.warn(\n \"Error while getting the syntax corresponding to object [{}]. \"\n + \"Defaulting to using XWiki 1.0 syntax. Internal error [{}]\",\n object.getReference(), ExceptionUtils.getRootCauseMessage(e));\n }\n\n return null;\n }\n\n private Syntax getObjectDocumentSyntax(BaseCollection object, XWikiContext context)\n {\n XWikiDocument doc = getObjectDocument(object, context);\n\n return doc != null && doc.getSyntax() != null ? doc.getSyntax() : Syntax.XWIKI_1_0;\n }\n}\n\n", "input": "", "output": "【漏洞类型】潜在的代码注入漏洞\n【严重性】高\n【漏洞摘要】此代码在处理Velocity内容时,存在潜在的代码注入漏洞。具体表现为在`displayView`方法中,当`contentType`为`VELOCITY_CODE`时,直接调用`velocityEvaluator.evaluateVelocityNoException`方法来执行Velocity代码,而没有进行足够的权限检查。此外,当`contentType`为`VELOCITYWIKI`时,虽然尝试了执行Velocity代码,但仍然缺乏对当前用户的权限检查,可能导致非授权用户执行潜在的恶意代码。\n【关键代码模式】`VelocityEvaluator.evaluateVelocityNoException`、`isDocumentAuthorAllowedToEvaluateScript`、`AuthorExecutor`、`Right.SCRIPT`、`isRestricted`。\n【修复方法说明】修复方法通过引入`AuthorExecutor`来确保在执行Velocity代码时,上下文中的作者信息是正确的,并通过`isDocumentAuthorAllowedToEvaluateScript`方法检查当前用户的权限,以防止非授权用户执行潜在的恶意代码。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\nxwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/CreateAction.java\n### 代码内容:\n/*\n * See the NOTICE file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This is free software; you can redistribute it and/or modify it\n * under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * This software is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this software; if not, write to the Free\n * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n */\npackage com.xpn.xwiki.web;\n\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport javax.inject.Named;\nimport javax.inject.Provider;\nimport javax.inject.Singleton;\nimport javax.script.ScriptContext;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.xwiki.component.annotation.Component;\nimport org.xwiki.csrf.CSRFToken;\nimport org.xwiki.model.reference.DocumentReference;\nimport org.xwiki.model.reference.EntityReferenceSerializer;\nimport org.xwiki.model.reference.SpaceReference;\nimport org.xwiki.security.authorization.ContextualAuthorizationManager;\nimport org.xwiki.security.authorization.Right;\n\nimport com.xpn.xwiki.XWiki;\nimport com.xpn.xwiki.XWikiContext;\nimport com.xpn.xwiki.XWikiException;\nimport com.xpn.xwiki.doc.XWikiDocument;\nimport com.xpn.xwiki.objects.BaseObject;\nimport com.xpn.xwiki.util.Util;\n\n/**\n * Create document action.\n *\n * @version $Id$\n * @since 2.4M2\n */\n@Component\n@Named(\"create\")\n@Singleton\npublic class CreateAction extends XWikiAction\n{\n /**\n * The name of the create.vm template to render.\n */\n private static final String CREATE_TEMPLATE = \"create\";\n\n /**\n * The name of the parent parameter.\n */\n private static final String PARENT = \"parent\";\n\n /**\n * The name of the space reference parameter.\n */\n private static final String SPACE_REFERENCE = \"spaceReference\";\n\n /**\n * The name parameter.\n */\n private static final String NAME = \"name\";\n\n /**\n * The name of the template field inside the template provider, or the template parameter which can be sent\n * directly, without passing through the template provider.\n */\n private static final String TEMPLATE = \"template\";\n\n /**\n * Internal name for a flag determining if we are creating a Nested Space or a terminal document.\n */\n private static final String IS_SPACE = \"isSpace\";\n\n /**\n * Space homepage document name.\n */\n private static final String WEBHOME = \"WebHome\";\n\n /**\n * Local entity reference serializer hint.\n */\n private static final String LOCAL_SERIALIZER_HINT = \"local\";\n\n /**\n * The action to perform when creating a new page from a template.\n *\n * @version $Id$\n */\n private enum ActionOnCreate\n {\n /**\n * Go to edit mode without saving.\n */\n EDIT(\"edit\"),\n\n /**\n * Save and then go to edit mode.\n */\n SAVE_AND_EDIT(\"saveandedit\"),\n\n /**\n * Save and then go to view mode.\n */\n SAVE_AND_VIEW(\"saveandview\");\n\n private static final Map<String, ActionOnCreate> BY_ACTION = new HashMap<>();\n\n static {\n for (ActionOnCreate actionOnCreate : values()) {\n BY_ACTION.put(actionOnCreate.action, actionOnCreate);\n }\n }\n\n private final String action;\n\n ActionOnCreate(String action)\n {\n this.action = action;\n }\n\n public static ActionOnCreate valueOfAction(String action)\n {\n return BY_ACTION.get(action);\n }\n }\n\n /**\n * Default constructor.\n */\n public CreateAction()\n {\n this.waitForXWikiInitialization = false;\n }\n\n @Override\n public String render(XWikiContext context) throws XWikiException\n {\n CreateActionRequestHandler handler = new CreateActionRequestHandler(context);\n\n // Read the request and extract the passed information.\n handler.processRequest();\n\n // Save the determined values so we have them available in the action template.\n ScriptContext scontext = getCurrentScriptContext();\n scontext.setAttribute(SPACE_REFERENCE, handler.getSpaceReference(), ScriptContext.ENGINE_SCOPE);\n scontext.setAttribute(NAME, handler.getName(), ScriptContext.ENGINE_SCOPE);\n scontext.setAttribute(IS_SPACE, handler.isSpace(), ScriptContext.ENGINE_SCOPE);\n // put the available templates on the context, for the .vm to not compute them again\n scontext.setAttribute(\"availableTemplateProviders\", handler.getAvailableTemplateProviders(),\n ScriptContext.ENGINE_SCOPE);\n scontext.setAttribute(\"recommendedTemplateProviders\", handler.getRecommendedTemplateProviders(),\n ScriptContext.ENGINE_SCOPE);\n\n DocumentReference newDocumentReference = handler.getNewDocumentReference();\n if (newDocumentReference == null) {\n // There is information still missing, go back to the template and fill it.\n return CREATE_TEMPLATE;\n }\n\n // Check if the creation in the spaceReference is allowed.\n if (!handler.isTemplateProviderAllowedToCreateInCurrentSpace()) {\n // The selected template provider is not usable in the selected location. Go back to the template and pick\n // something else.\n return CREATE_TEMPLATE;\n }\n\n // Checking the rights to create the new document.\n // Note: Note checking the logical spaceReference, but the space of the final actual document reference, since\n // that is where we are creating the new document.\n checkRights(newDocumentReference.getLastSpaceReference(), context);\n\n // Check if the document to create already exists and if it respects the name strategy\n XWikiDocument newDocument = context.getWiki().getDocument(newDocumentReference, context);\n if (handler.isDocumentAlreadyExisting(newDocument) || handler.isDocumentPathTooLong(newDocumentReference)\n || !this.isEntityReferenceNameValid(newDocumentReference)) {\n return CREATE_TEMPLATE;\n }\n\n // Verify if the \"type\" of document to create has been set, even if we currently do not use it in the action.\n // The goal is let the user be able to chose it, which have some consequences in the UI (thanks to javascript).\n // See: https://jira.xwiki.org/browse/XWIKI-12580\n // Note: we do not need the \"type\" if we have a template provider: the type of the new document will be the type\n // of the template.\n // TODO: handle this type in doCreate() that we call above (see: https://jira.xwiki.org/browse/XWIKI-12585).\n if (StringUtils.isBlank(handler.getType()) && !handler.hasTemplate()) {\n return CREATE_TEMPLATE;\n }\n\n // create is finally valid, can be executed\n doCreate(context, newDocument, handler.isSpace(), handler.getTemplateProvider());\n\n return null;\n }\n\n /**\n * @param context the XWiki context\n * @param spaceReference the reference of the space where the new document will be created\n * @throws XWikiException in case the permission to create a new document in the specified space is denied\n */\n private void checkRights(SpaceReference spaceReference, XWikiContext context) throws XWikiException\n {\n ContextualAuthorizationManager authManager = Utils.getComponent(ContextualAuthorizationManager.class);\n if (!authManager.hasAccess(Right.EDIT, spaceReference)) {\n Object[] args = { spaceReference.toString(), context.getUser() };\n throw new XWikiException(XWikiException.MODULE_XWIKI_ACCESS, XWikiException.ERROR_XWIKI_ACCESS_DENIED,\n \"The creation of a document into the space {0} has been denied to user {1}\", null, args);\n }\n }\n\n /**\n * Actually executes the create, after all preconditions have been verified.\n *\n * @param context the context of this action\n * @param newDocument the document to be created\n * @param isSpace whether the document is a space webhome or a page\n * @param templateProvider the template provider to create from\n * @throws XWikiException in case anything goes wrong accessing xwiki documents\n */\n private void doCreate(XWikiContext context, XWikiDocument newDocument, boolean isSpace, BaseObject templateProvider)\n throws XWikiException\n {\n XWikiRequest request = context.getRequest();\n XWikiDocument doc = context.getDoc();\n\n String parent = getParent(request, doc, isSpace, context);\n\n // get the title of the page to create, as specified in the parameters\n String title = getTitle(request, newDocument, isSpace);\n\n // get the template from the template parameter, to allow creation directly from template, without\n // forcing to create a template provider for each template creation\n String template = getTemplate(templateProvider, request);\n\n // Read from the template provide the action to perform when creating the page.\n ActionOnCreate actionOnCreate = getActionOnCreate(templateProvider);\n\n String action = null;\n if (actionOnCreate == ActionOnCreate.SAVE_AND_EDIT) {\n initAndSaveDocument(context, newDocument, title, template, parent);\n action = newDocument.getDefaultEditMode(context);\n } else {\n action = actionOnCreate == ActionOnCreate.SAVE_AND_VIEW ? \"save\" : getEditMode(template, context);\n }\n\n // Perform a redirection to the selected action of the document to create.\n String redirectParams = getRedirectParameters(parent, title, template, actionOnCreate);\n String redirectURL = newDocument.getURL(action, redirectParams, context);\n redirectURL = context.getResponse().encodeRedirectURL(redirectURL);\n if (context.getRequest().getParameterMap().containsKey(\"ajax\")) {\n // If this template is displayed from a modal popup, send a header in the response notifying that a\n // redirect must be performed in the calling page.\n context.getResponse().setHeader(\"redirect\", redirectURL);\n } else {\n // Perform the redirect\n sendRedirect(context.getResponse(), redirectURL);\n }\n }\n\n /**\n * Initialize and save the new document before editing it. Follow the steps done by the Save action.\n * \n * @param context the XWiki context\n * @param newDocument the document being created\n * @param title the document title\n * @param template the template to copy\n * @param parent the parent document\n * @throws XWikiException if copying the template or saving the document fails\n */\n private void initAndSaveDocument(XWikiContext context, XWikiDocument newDocument, String title, String template,\n String parent) throws XWikiException\n {\n XWiki xwiki = context.getWiki();\n\n // Set the locale and default locale, considering that we're creating the original version of the document\n // (not a translation).\n newDocument.setLocale(Locale.ROOT);\n if (newDocument.getDefaultLocale() == Locale.ROOT) {\n newDocument.setDefaultLocale(xwiki.getLocalePreference(context));\n }\n\n // Copy the template.\n readFromTemplate(newDocument, template, context);\n\n // Set the parent field.\n if (!StringUtils.isEmpty(parent)) {\n DocumentReference parentReference = getCurrentMixedDocumentReferenceResolver().resolve(parent);\n newDocument.setParentReference(parentReference);\n }\n\n // Set the document title\n if (title != null) {\n newDocument.setTitle(title);\n }\n\n // Set the author and creator.\n DocumentReference currentUserReference = context.getUserReference();\n newDocument.setAuthorReference(currentUserReference);\n newDocument.setCreatorReference(currentUserReference);\n\n // Make sure the user is allowed to make this modification\n xwiki.checkSavingDocument(currentUserReference, newDocument, context);\n\n xwiki.saveDocument(newDocument, context);\n }\n\n private String getRedirectParameters(String parent, String title, String template, ActionOnCreate actionOnCreate)\n {\n if (actionOnCreate == ActionOnCreate.SAVE_AND_EDIT) {\n // We don't need to pass any parameters because the document is saved before the redirect using the\n // parameter values.\n return null;\n }\n\n String redirectParams = \"template=\" + Util.encodeURI(template, null);\n if (parent != null) {\n redirectParams += \"&parent=\" + Util.encodeURI(parent, null);\n }\n if (title != null) {\n redirectParams += \"&title=\" + Util.encodeURI(title, null);\n }\n // Both the save and the edit action might require a CSRF token\n CSRFToken csrf = Utils.getComponent(CSRFToken.class);\n redirectParams += \"&form_token=\" + Util.encodeURI(csrf.getToken(), null);\n\n return redirectParams;\n }\n\n /**\n * @param templateProvider the set template provider, if any\n * @param request the request on which to fallback\n * @return the string reference of the document to use as template or {@code \"\"} if none set\n */\n private String getTemplate(BaseObject templateProvider, XWikiRequest request)\n {\n String result = \"\";\n\n if (templateProvider != null) {\n result = templateProvider.getStringValue(TEMPLATE);\n } else if (request.getParameter(TEMPLATE) != null) {\n result = request.getParameter(TEMPLATE);\n }\n\n return result;\n }\n\n /**\n * @param request the current request for which this action is executed\n * @param doc the current document\n * @param isSpace {@code true} if the request is to create a space, {@code false} if a page should be created\n * @param context the XWiki context\n * @return the serialized reference of the parent to create the document for\n */\n private String getParent(XWikiRequest request, XWikiDocument doc, boolean isSpace, XWikiContext context)\n {\n // This template can be passed a parent document reference in parameter (using the \"parent\" parameter).\n // If a parent parameter is passed, use it to set the parent when creating the new Page or Space.\n // If no parent parameter was passed:\n // * use the current document\n // ** if we're creating a new page and if the current document exists or\n // * use the Main space's WebHome\n // ** if we're creating a new page and the current document does not exist.\n String parent = request.getParameter(PARENT);\n if (StringUtils.isEmpty(parent)) {\n EntityReferenceSerializer<String> localSerializer =\n Utils.getComponent(EntityReferenceSerializer.TYPE_STRING, LOCAL_SERIALIZER_HINT);\n\n if (doc.isNew()) {\n // Use the Main space's WebHome.\n Provider<DocumentReference> defaultDocumentReferenceProvider =\n Utils.getComponent(DocumentReference.TYPE_PROVIDER);\n\n DocumentReference parentRef =\n defaultDocumentReferenceProvider.get().setWikiReference(context.getWikiReference());\n\n parent = localSerializer.serialize(parentRef);\n } else {\n // Use the current document.\n DocumentReference parentRef = doc.getDocumentReference();\n\n parent = localSerializer.serialize(parentRef);\n }\n }\n\n return parent;\n }\n\n /**\n * @param request the current request for which this action is executed\n * @param newDocument the document to be created\n * @param isSpace {@code true} if the request is to create a space, {@code false} if a page should be created\n * @return the title of the page to be created. If no request parameter is set, the page name is returned for a new\n * page and the space name is returned for a new space\n */\n private String getTitle(XWikiRequest request, XWikiDocument newDocument, boolean isSpace)\n {\n String title = request.getParameter(\"title\");\n if (StringUtils.isEmpty(title)) {\n if (isSpace) {\n title = newDocument.getDocumentReference().getLastSpaceReference().getName();\n } else {\n title = newDocument.getDocumentReference().getName();\n // Avoid WebHome titles for pages that are really space homepages.\n if (WEBHOME.equals(title)) {\n title = newDocument.getDocumentReference().getLastSpaceReference().getName();\n }\n }\n }\n\n return title;\n }\n\n /**\n * @param templateProvider the template provider for this creation\n * @return {@code true} if the created document should be saved on create, before editing, {@code false} otherwise\n */\n private ActionOnCreate getActionOnCreate(BaseObject templateProvider)\n {\n if (templateProvider != null) {\n String action = templateProvider.getStringValue(\"action\");\n ActionOnCreate actionOnCreate = ActionOnCreate.valueOfAction(action);\n if (actionOnCreate != null) {\n return actionOnCreate;\n }\n }\n\n // Default action when creating a page from a template.\n return ActionOnCreate.EDIT;\n }\n\n /**\n * @param template the template to create document from\n * @param context the context of the current request\n * @return the default edit mode for a document created from the passed template\n * @throws XWikiException in case something goes wrong accessing template document\n */\n private String getEditMode(String template, XWikiContext context)\n throws XWikiException\n {\n // Determine the edit action (edit/inline) for the newly created document, if a template is passed it is\n // used to determine the action. Default is 'edit'.\n String editAction = ActionOnCreate.EDIT.name().toLowerCase();\n XWiki xwiki = context.getWiki();\n if (!StringUtils.isEmpty(template)) {\n DocumentReference templateReference = getCurrentMixedDocumentReferenceResolver().resolve(template);\n if (xwiki.exists(templateReference, context)) {\n editAction = xwiki.getDocument(templateReference, context).getDefaultEditMode(context);\n }\n }\n\n return editAction;\n }\n}\n\n## 内容2\n### 文件路径:\nxwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/createinline.vm\n### 代码内容:\n## ---------------------------------------------------------------------------\n## See the NOTICE file distributed with this work for additional\n## information regarding copyright ownership.\n##\n## This is free software; you can redistribute it and/or modify it\n## under the terms of the GNU Lesser General Public License as\n## published by the Free Software Foundation; either version 2.1 of\n## the License, or (at your option) any later version.\n##\n## This software is distributed in the hope that it will be useful,\n## but WITHOUT ANY WARRANTY; without even the implied warranty of\n## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n## Lesser General Public License for more details.\n##\n## You should have received a copy of the GNU Lesser General Public\n## License along with this software; if not, write to the Free\n## Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n## 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n## ---------------------------------------------------------------------------\n## ---------------------------------------------------------------------------------------------------------\n## This template is called by the create action which binds some context variables as it executes, based on\n## the request and the current document. These variables can be used in this template:\n## * $spaceReference - the space reference of the new document, read from the \"spaceReference\" parameter\n## * $name - the name to use for the new document, read from the \"name\" parameter\n## * $isSpace - true if we are creating a non-terminal document ($name is the space name), false if we are\n## creating a terminal document ($name is the document name). Determined by the create action\n## * $availableTemplateProviders - list of template provider documents available from the current space\n##\n## Note: Make sure to use these bindings instead of directly using the request parameters, as the bindings\n## also consider backwards compatibility and convert older \"space\" and \"page\" parameters if they are passed.\n## ---------------------------------------------------------------------------------------------------------\n\n## ---------------------------------------------------------------------------------------------------------\n## This template can be passed a parent document reference in parameter (using the \"parent\" parameter).\n## If a parent parameter is passed, use it to set the parent when creating the new page.\n## If no parent parameter was passed, the create action will determine it to be the current document if it\n## exists or the Main space's webhome if the current document does not exist.\n## ---------------------------------------------------------------------------------------------------------\n#set ($parent = \"$!request.getParameter('parent')\")\n\n## ---------------------------------------------------------------------------------------------------------\n## This parent can be passed a title for the document to create (using the \"title\" parameter).\n## The title is used to automatically compute a page name, which can then be customized by the user.\n## If no title is given, the page name (if set) will be used as title.\n## ---------------------------------------------------------------------------------------------------------\n#if (\"$!request.title\" != '')\n #set ($title = \"$!request.title\")\n#else\n #set ($title = \"$!{escapetool.velocity($name)}\")\n#end\n\n## ---------------------------------------------------------------------------------------------------------\n## Get the passed template provider, if any\n## ---------------------------------------------------------------------------------------------------------\n#set($templateProvider = \"$!request.templateprovider\")\n## Get the template as well, since in case template provider is missing, template should be used, \n## to allow creation from template directly, without necessarily going through template provider.\n#set($template = \"$!request.template\")\n\n## ---------------------------------------------------------------------------------------------------------\n## If there was an exception creating the page because of invalid space, notify. \n## Should set the other variables only if there is no exception. Otherwise, if there's an exception other than the \n## 11016 one, nothing will be displayed, depending on the other passed parameters.\n## ---------------------------------------------------------------------------------------------------------\n#set($exception = $createException)\n## check if the exception is about the non-allowed spaces for template\n#if(\"$!exception\" != '' && $exception.code == 11016)\n <div class=\"box errormessage\">\n #set($allowedSpaces = $createAllowedSpaces)\n #if ($allowedSpaces.size() == 1)\n $services.localization.render('core.create.template.allowedspace.inline', [$templateProvider, $allowedSpaces.get(0)])\n #else\n $services.localization.render('core.create.template.allowedspaces.inline', [$templateProvider, $allowedSpaces.toString()])\n #end\n </div>\n#end\n## check if the exception is about the document which already exists\n#if(\"$!exception\" != '' && $exception.code == 11002)\n <div class='box errormessage'>\n ## Use the 'existingDocumentReference' context binding set by the create action for this case.\n $services.localization.render('core.create.page.error.docalreadyexists',\n [\"${existingDocumentReference}\",\n $xwiki.getURL($existingDocumentReference, 'view', ''),\n $xwiki.getURL($existingDocumentReference, 'edit', '')\n ]\n )\n </div>\n#end\n## check if the exception is about the document name being invalid\n#if(\"$!exception\" != '' && $exception.code == 11018)\n<div class='box errormessage'>\n ## Use the 'invalidNameReference' context binding set by the create action for this case.\n $services.localization.render('entitynamevalidation.create.invalidname', [$invalidNameReference])\n</div>\n#end\n\n## check if the exception is about the document having a path too long\n#template('hierarchy_macros.vm')\n#if(\"$!exception\" != '' && $exception.code == 11017)\n #set ($options = {})\n #set ($documentReference = $services.model.resolveDocument($tooLongPath))\n <div class='box errormessage'>\n $services.localization.render('core.create.page.error.docpathtoolong',\n [\"<p>#hierarchy($documentReference, $options)</p>\",\n $doc.getLocalReferenceMaxLength(),\n $tooLongPath.length()\n ]\n )\n </div>\n#end\n\n## ---------------------------------------------------------------------------------------------------------\n## Determine if we need to do extra checks for a deprecated, pre-NestedSpaces request to create a space.\n## ---------------------------------------------------------------------------------------------------------\n#set ($deprecatedSpaceCreate = \"$!request.tocreate\" == 'space')\n\n## ---------------------------------------------------------------------------------------------------------\n## Note: The list of available template providers is computed in the action and set on the context under\n## the \"availableTemplateProviders\" binding.\n## ---------------------------------------------------------------------------------------------------------\n\n## ---------------------------------------------------------------------------------------------------------\n## Macro for displaying the available types and templates and changing target space rules accordingly\n## ---------------------------------------------------------------------------------------------------------\n#macro (displayTypes)\n #if ($template == '')\n #set ($blankTerminalState = !$isSpace)\n #set ($options = [\n { \n 'name': $services.localization.render('core.create.type.default'),\n 'options': [\n {\n 'name': $services.localization.render('core.create.type.blank'),\n 'value': 'blank',\n 'description': $services.localization.render('core.create.type.blank.description'),\n 'icon': 'file-white',\n 'data': {\n 'type': 'blank',\n 'terminal' : \"$blankTerminalState\"\n }\n }\n ]\n }])\n #if ($xwiki.exists($services.model.createDocumentReference('', 'XWiki', 'OfficeImporter')) && $services.officemanager.isConnected())\n ## TODO: handle this use case with an extension point\n #set ($discard = $options[0].options.add({\n 'name': $services.localization.render('office.createForm.type.name'),\n 'value': 'office',\n 'description': $services.localization.render('office.createForm.type.description'),\n 'icon': 'file-word',\n 'data': {\n 'type':'office'\n }\n }))\n #end\n #if (!$availableTemplateProviders.isEmpty())\n #set ($templateOptions = [])\n #set ($discard = $options.add({'name': $services.localization.render('core.create.type.templates'), 'options': $templateOptions}))\n #foreach($tpdoc in $availableTemplateProviders)\n #set ($tpobj = $tpdoc.getObject('XWiki.TemplateProviderClass'))\n #set ($tpdocRef = $services.model.serialize($tpdoc.documentReference, 'local'))\n ##\n ## Extract and save in data-* attributes list of allowed spaces to be used in the JavaScript validation of the spaceReference input.\n ##\n #set($jsAllowedSpacesArray = '[]')## Reset any previous value\n #set($allowedSpaces = $tpobj.getValue('creationRestrictions'))\n #if($allowedSpaces.size() == 0)\n ## Backwards compatibility for Template Providers created before 8.3M2.\n #set($allowedSpaces = $tpobj.getValue('spaces'))\n #end\n ## Reset to an empty list when no restrictions are set.\n #if (\"$!allowedSpaces\" == '')\n #set ($allowedSpaces = [])\n #end\n #if($allowedSpaces.size() > 0)\n #set($jsAllowedSpacesArray = $jsontool.serialize($allowedSpaces))\n #end\n ## Compute the error message.\n #set ($templateProviderName = $services.localization.render($tpobj.getValue('name')))\n #if($allowedSpaces.isEmpty())\n #set($message = '')\n #elseif($allowedSpaces.size() == 1)\n #set($message = $services.localization.render('core.create.template.allowedspace.inline', [$templateProviderName, $jsAllowedSpacesArray]))\n #else\n #set($message = $services.localization.render('core.create.template.allowedspaces.inline', [$templateProviderName, $jsAllowedSpacesArray]))\n #end\n ##\n ## Determine if the creation restrictions are just suggestions\n ##\n #set ($restrictionsAreSuggestions = ($tpobj.getValue('creationRestrictionsAreSuggestions') == 1))\n ##\n ## Determine if it's a terminal page template\n ##\n #set ($isTerminalProvider = $tpobj.getValue('terminal'))\n ## Check if this is an old template provider that has not been migrated (the 'type' property has not been\n ## replaced by the 'terminal' property).\n #set ($type = $tpobj.getValue('type'))\n #if (\"$!isTerminalProvider\" == '' && (\"$!type\" != '' || $type == ''))\n ## This must be an old template provider because the 'terminal' property is not set (or it was set to '---'\n ## from the UI) and the 'type' property is still present.\n #if ($type == 'space')\n #set ($isTerminalProvider = false)\n #else\n ## 'page' or '' both resolve to true, for backwards compatibility reasons.\n #set ($isTerminalProvider = true)\n #end\n #else\n #set ($isTerminalProvider = ($isTerminalProvider == 1))\n #end\n ##\n ## Display the template provider field option in the form.\n ##\n #set ($inputId = \"xwiki-create-form-${foreach.count}\")\n #set ($icon = $tpobj.getValue('icon'))\n #if (\"$!icon\" == '')\n #set($icon = 'file-white')\n #end\n #set ($discard = $templateOptions.add({\n 'name': $services.localization.render($tpobj.getValue('name')),\n 'value': \"$tpdocRef\",\n 'description': $services.localization.render($tpobj.getValue('description')),\n 'icon': $icon,\n 'data': {\n 'allowed-spaces': $jsAllowedSpacesArray,\n 'allowed-spaces-message': $message,\n 'restrictions-are-suggestions': $restrictionsAreSuggestions,\n 'type': 'template',\n 'terminal' : $isTerminalProvider\n }\n }))\n #end\n #end\n <dt>\n <label>$services.localization.render('core.create.type')</label>\n <span class=\"xHint\">$escapetool.xml($services.localization.render('core.create.type.hint'))</span>\n </dt>\n #set ($defaultValue = $request.type)\n #if (\"$!defaultValue\" == '')\n #if ($recommendedTemplateProviders.size() > 0)\n #set ($defaultValue = $services.model.serialize($recommendedTemplateProviders[0].documentReference, 'local'))\n #else\n #set ($defaultValue = 'blank')\n #end\n #end\n <dd>#xwikiSelect('type', $options, $defaultValue, false, 'xwiki-select-adaptable-medium page-type', '', true)</dd>\n #else\n <input type=\"hidden\" name=\"template\" value=\"$escapetool.xml($template)\" />\n #end\n#end\n#set ($discard = $xwiki.jsfx.use('js/xwiki/create.js', {'forceSkinAction': true}))\n\n## ---------------------------------------------------------------------------------------------------------\n## Macro used to extract a wiki's homepage reference.\n## ---------------------------------------------------------------------------------------------------------\n#macro (getWikiHomePageReference $wiki $return)\n #set ($_wikiHomePageReference = $services.wiki.getById($wiki).mainPageReference)\n #if (!$_wikiHomePageReference)\n #set ($wikiReference = $services.model.createWikiReference($wiki))\n #set ($_wikiHomePageReference = $services.model.resolveDocument('', 'default', $wikiReference))\n #end\n #set ($return = $NULL)\n #setVariable(\"$return\" $_wikiHomePageReference)\n#end\n\n## ---------------------------------------------------------------------------------------------------------\n## Display the form.\n## ---------------------------------------------------------------------------------------------------------\n## FIXME: Using the 'container' class when displayed in an ajax call by clicking on a Wanted Link so that it looks good (proper margins) in the popup.\n<form action=\"$doc.getURL('create')\" method=\"post\" id=\"create\" class=\"xform #if($request.ajax)container#end\">\n <fieldset>\n\n ## Deprecated: Pass along any received page parent.\n <input type='hidden' name='parent' value=\"$!{escapetool.xml($parent)}\"/>\n\n ## The \"templateprovider\" field is needed by the CreateAction, even empty.\n ## By the past, it was \"xwiki/create.js\" which created this field, but it was causing problems when the user sent the\n ## form before the JavaScript code was executed.\n <input type=\"hidden\" name=\"templateprovider\" id=\"templateprovider\" value=\"\" />\n\n <div class='row'>\n ## Hide the first column when displayed in an AJAX call by clicking on a Wanted Link (because we know the target\n ## location from the link reference) or when the current document is new (because the create action uses the location\n ## of the current document as target in this case).\n #if (!$request.ajax && !$doc.isNew())\n ## Determine the parent reference for the new document.\n #set ($parentReference = $spaceReference)\n #if (!$parentReference)\n ## No parent reference specified.\n ## We keep this code although we tested above that the current document is not new because in the future we may\n ## want to support changing the target location even if the current document is new.\n #if ($doc.isNew())\n ## Encourage the user to create the current document.\n #set ($parentReference = $doc.documentReference.parent)\n #if ($doc.documentReference.name == 'WebHome')\n #set ($parentReference = $parentReference.parent)\n #end\n #else\n #getWikiHomePageReference($doc.wiki $homeReference)\n #if ($doc.documentReference.equals($homeReference) || $deprecatedSpaceCreate)\n ## Encourage the user to create top level documents from the home page.\n #set ($parentReference = $doc.documentReference.wikiReference)\n #else\n ## Encourage the user to create a child or sibling of the current document.\n #set ($parentReference = $doc.documentReference.lastSpaceReference)\n #end\n #end\n #end\n\n ## Display the location picker.\n <div class='col-xs-12 col-lg-6'>\n #template('locationPicker_macros.vm')\n #locationPicker({\n 'id': 'target',\n 'title': {\n 'label': 'core.create.title',\n 'hint': 'core.create.title.hint',\n 'name': 'title',\n 'value': $title,\n 'placeholder': 'core.create.name.placeholder'\n },\n 'preview': {\n 'label': 'core.create.locationPreview.label',\n 'hint': 'core.create.locationPreview.hint'\n },\n 'parent': {\n 'label': 'core.create.spaceReference.label',\n 'hint': 'core.create.spaceReference.hint',\n 'name': 'spaceReference',\n 'reference': $parentReference,\n 'placeholder': 'core.create.spaceReference.placeholder'\n },\n 'name': {\n 'label': 'core.create.name.label',\n 'hint': 'core.create.name.hint',\n 'name': 'name',\n 'value': $name,\n 'placeholder': 'core.create.name.placeholder'\n }\n })\n </div>\n #else\n ## First column is hidden.\n ## Pass along the known values as hidden inputs.\n ##\n <input type='hidden' name='title' value=\"$!{escapetool.xml($title)}\"/>\n <input type='hidden' id='name' name='name' value=\"$!{escapetool.xml($name)}\" />\n #set ($spaceReferenceLocalString = $services.model.serialize($spaceReference, 'local'))\n <input type='hidden' id='spaceReference' name='spaceReference' value=\"$!{escapetool.xml($spaceReferenceLocalString)}\" />\n #end\n ## FIXME: When displayed in an ajax call by clicking on a Wanted Link, the responsive classes consider the calling document (large screen)\n ## as the viewport and not the popup (small screen), so we can not use them since they create problems instead of fixing them.\n <div class='#if(!$request.ajax)col-xs-12 col-lg-6#end'>\n <dl>\n ## ---------------------------------------------------------------------------------------------------------\n ## Page type\n ## ---------------------------------------------------------------------------------------------------------\n #displayTypes()\n ## ---------------------------------------------------------------------------------------------------------\n ## Terminal page - Advanced users\n ## ---------------------------------------------------------------------------------------------------------\n #set ($hidden = '')\n #if (!($isAdvancedUser || $isSuperAdmin) || $deprecatedSpaceCreate)\n #set ($hidden = 'hidden')\n #end\n <dt class=\"$hidden\">\n <input type=\"checkbox\" id=\"terminal\" name=\"tocreate\" value=\"terminal\" #if(!$isSpace)checked=\"checked\"#end/>\n <label for=\"terminal\">$services.localization.render('core.create.terminal.label')</label>\n ## Hidden input used to ensure that an unchecked terminal checkbox means non-terminal and that the default is not used by mistake.\n <input type=\"hidden\" name=\"tocreate\" value=\"nonterminal\" />\n </dt>\n <dd class=\"$hidden\">\n <span class=\"xHint\">$services.localization.render('core.create.terminal.hint')</span>\n </dd>\n </dl>\n </div>\n </div>\n\n <div class='buttons'>\n <span class='buttonwrapper'>\n <input type='submit' class='button' value=\"$services.localization.render('create')\"/>\n </span>\n <span class=\"buttonwrapper\">\n <a class=\"secondary button\" href=\"$doc.getURL()\">$services.localization.render('cancel')</a>\n </span>\n </div>\n\n </fieldset>\n</form>\n\n", "input": "", "output": "【漏洞类型】跨站请求伪造(CSRF)漏洞\n【严重性】High\n【漏洞摘要】原始代码中的 `CreateAction` 类在执行创建文档的操作时,没有验证 CSRF 令牌。这意味着攻击者可以诱导已登录用户执行未经授权的操作,如创建恶意文档。具体来说,`render` 方法在检查文档是否存在、路径是否过长以及名称是否有效后,直接调用了 `doCreate` 方法,而没有验证传入的 CSRF 令牌。此外,在 `createinline.vm` 模板中,虽然生成了 CSRF 令牌,但没有将其包含在表单的隐藏字段中,导致客户端无法提交有效的 CSRF 令牌。\n【关键代码模式】`render` 方法中的 `checkRights` 和 `doCreate` 调用,`createinline.vm` 模板中的 CSRF 令牌生成和表单字段。\n【修复方法说明】修复方法通过在 `render` 方法中添加 CSRF 令牌的验证逻辑,并在 `createinline.vm` 模板中添加 CSRF 令牌的隐藏字段,确保每个创建请求都包含有效的 CSRF 令牌,从而防止 CSRF 攻击。", "category": "security_cve", "source": "modelscope_cve_dataset"}
- {"instruction": "作为 Java 安全专家,检测此代码中的安全漏洞:\n## 内容1\n### 文件路径:\ncore/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java\n### 代码内容:\npackage org.bouncycastle.crypto.generators;\n\nimport java.math.BigInteger;\n\nimport org.bouncycastle.crypto.AsymmetricCipherKeyPair;\nimport org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;\nimport org.bouncycastle.crypto.KeyGenerationParameters;\nimport org.bouncycastle.crypto.params.RSAKeyGenerationParameters;\nimport org.bouncycastle.crypto.params.RSAKeyParameters;\nimport org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;\nimport org.bouncycastle.math.Primes;\nimport org.bouncycastle.math.ec.WNafUtil;\n\n/**\n * an RSA key pair generator.\n */\npublic class RSAKeyPairGenerator\n implements AsymmetricCipherKeyPairGenerator\n{\n private static final BigInteger ONE = BigInteger.valueOf(1);\n\n private RSAKeyGenerationParameters param;\n private int iterations;\n\n public void init(KeyGenerationParameters param)\n {\n this.param = (RSAKeyGenerationParameters)param;\n this.iterations = getNumberOfIterations(this.param.getStrength(), this.param.getCertainty());\n }\n\n public AsymmetricCipherKeyPair generateKeyPair()\n {\n AsymmetricCipherKeyPair result = null;\n boolean done = false;\n\n //\n // p and q values should have a length of half the strength in bits\n //\n int strength = param.getStrength();\n int pbitlength = (strength + 1) / 2;\n int qbitlength = strength - pbitlength;\n int mindiffbits = (strength / 2) - 100;\n\n if (mindiffbits < strength / 3)\n {\n mindiffbits = strength / 3;\n }\n\n int minWeight = strength >> 2;\n\n // d lower bound is 2^(strength / 2)\n BigInteger dLowerBound = BigInteger.valueOf(2).pow(strength / 2);\n // squared bound (sqrt(2)*2^(nlen/2-1))^2\n BigInteger squaredBound = ONE.shiftLeft(strength - 1);\n // 2^(nlen/2 - 100)\n BigInteger minDiff = ONE.shiftLeft(mindiffbits);\n\n while (!done)\n {\n BigInteger p, q, n, d, e, pSub1, qSub1, gcd, lcm;\n\n e = param.getPublicExponent();\n\n p = chooseRandomPrime(pbitlength, e, squaredBound);\n\n //\n // generate a modulus of the required length\n //\n for (; ; )\n {\n q = chooseRandomPrime(qbitlength, e, squaredBound);\n\n // p and q should not be too close together (or equal!)\n BigInteger diff = q.subtract(p).abs();\n if (diff.bitLength() < mindiffbits || diff.compareTo(minDiff) <= 0)\n {\n continue;\n }\n\n //\n // calculate the modulus\n //\n n = p.multiply(q);\n\n if (n.bitLength() != strength)\n {\n //\n // if we get here our primes aren't big enough, make the largest\n // of the two p and try again\n //\n p = p.max(q);\n continue;\n }\n\n\t /*\n * Require a minimum weight of the NAF representation, since low-weight composites may\n\t * be weak against a version of the number-field-sieve for factoring.\n\t *\n\t * See \"The number field sieve for integers of low weight\", Oliver Schirokauer.\n\t */\n if (WNafUtil.getNafWeight(n) < minWeight)\n {\n p = chooseRandomPrime(pbitlength, e, squaredBound);\n continue;\n }\n\n break;\n }\n\n if (p.compareTo(q) < 0)\n {\n gcd = p;\n p = q;\n q = gcd;\n }\n\n pSub1 = p.subtract(ONE);\n qSub1 = q.subtract(ONE);\n gcd = pSub1.gcd(qSub1);\n lcm = pSub1.divide(gcd).multiply(qSub1);\n\n //\n // calculate the private exponent\n //\n d = e.modInverse(lcm);\n\n if (d.compareTo(dLowerBound) <= 0)\n {\n continue;\n }\n else\n {\n done = true;\n }\n\n //\n // calculate the CRT factors\n //\n BigInteger dP, dQ, qInv;\n\n dP = d.remainder(pSub1);\n dQ = d.remainder(qSub1);\n qInv = q.modInverse(p);\n\n result = new AsymmetricCipherKeyPair(\n new RSAKeyParameters(false, n, e),\n new RSAPrivateCrtKeyParameters(n, e, d, p, q, dP, dQ, qInv));\n }\n\n return result;\n }\n\n /**\n * Choose a random prime value for use with RSA\n *\n * @param bitlength the bit-length of the returned prime\n * @param e the RSA public exponent\n * @return A prime p, with (p-1) relatively prime to e\n */\n protected BigInteger chooseRandomPrime(int bitlength, BigInteger e, BigInteger sqrdBound)\n {\n for (int i = 0; i != 5 * bitlength; i++)\n {\n BigInteger p = new BigInteger(bitlength, 1, param.getRandom());\n\n if (p.mod(e).equals(ONE))\n {\n continue;\n }\n\n if (p.multiply(p).compareTo(sqrdBound) < 0)\n {\n continue;\n }\n\n if (!isProbablePrime(p))\n {\n continue;\n }\n\n if (!e.gcd(p.subtract(ONE)).equals(ONE))\n {\n continue;\n }\n\n return p;\n }\n\n throw new IllegalStateException(\"unable to generate prime number for RSA key\");\n }\n\n protected boolean isProbablePrime(BigInteger x)\n {\n /*\n * Primes class for FIPS 186-4 C.3 primality checking\n */\n return !Primes.hasAnySmallFactors(x) && Primes.isMRProbablePrime(x, param.getRandom(), iterations);\n }\n\n private static int getNumberOfIterations(int bits, int certainty)\n {\n /*\n * NOTE: We enforce a minimum 'certainty' of 100 for bits >= 1024 (else 80). Where the\n * certainty is higher than the FIPS 186-4 tables (C.2/C.3) cater to, extra iterations\n * are added at the \"worst case rate\" for the excess.\n */\n if (bits >= 1536)\n {\n return certainty <= 100 ? 3\n : certainty <= 128 ? 4\n : 4 + (certainty - 128 + 1) / 2;\n }\n else if (bits >= 1024)\n {\n return certainty <= 100 ? 4\n : certainty <= 112 ? 5\n : 5 + (certainty - 112 + 1) / 2;\n }\n else if (bits >= 512)\n {\n return certainty <= 80 ? 5\n : certainty <= 100 ? 7\n : 7 + (certainty - 100 + 1) / 2;\n }\n else\n {\n return certainty <= 80 ? 40\n : 40 + (certainty - 80 + 1) / 2;\n }\n }\n}\n\n", "input": "", "output": "【漏洞类型】未正确计算迭代次数\n【严重性】Medium\n【漏洞摘要】由于`iterations`变量在`init()`方法中被定义为类的成员变量,并在`generateKeyPair()`方法中被调用,但修复的代码移除了成员变量,直接在`chooseRandomPrime()`方法中计算`iterations`,导致原始的`iterations`值可能未被正确更新。这可能导致在生成RSA密钥对时,不正确地执行米勒-拉宾素性测试的迭代次数,从而影响密钥的强度和安全性。\n【关键代码模式】`iterations`变量在`init()`方法中被定义为成员变量,并在`generateKeyPair()`方法中被调用。修复的代码移除了成员变量,直接在`chooseRandomPrime()`方法中计算`iterations`。\n【修复方法说明】修复方法通过在`chooseRandomPrime()`方法中直接计算`iterations`,确保每次生成随机素数时,迭代次数都能正确更新,从而提高密钥生成的安全性。", "category": "security_cve", "source": "modelscope_cve_dataset"}
|