val path = toIdeaPath(decodedPath, offset)
if (path == null) {
- HttpResponseStatus.BAD_REQUEST.send(context.channel(), request)
+ LOG.warn("$decodedPath is not valid")
+ HttpResponseStatus.NOT_FOUND.send(context.channel(), request)
return true
}
import com.intellij.openapi.util.io.endsWithName
import com.intellij.openapi.util.io.endsWithSlash
import com.intellij.openapi.util.io.getParentPath
+import com.intellij.openapi.vfs.VFileProperty
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.PathUtilRt
+import com.intellij.util.isDirectory
+import io.netty.channel.Channel
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.http.FullHttpRequest
+import io.netty.handler.codec.http.HttpRequest
import io.netty.handler.codec.http.HttpResponseStatus
import org.jetbrains.io.send
import java.nio.file.Path
+import java.nio.file.Paths
private class DefaultWebServerPathHandler : WebServerPathHandler() {
override fun process(path: String,
}
}
+ if (!checkAccess(pathInfo, channel, request)) {
+ return true
+ }
+
val canonicalPath = if (indexUsed) "$path/${pathInfo.name}" else path
for (fileHandler in WebServerFileHandler.EP_NAME.extensions) {
LOG.catchAndLog {
}
return false
}
+}
+
+private fun checkAccess(pathInfo: PathInfo, channel: Channel, request: HttpRequest): Boolean {
+ if (pathInfo.ioFile != null || pathInfo.file!!.isInLocalFileSystem) {
+ val file = pathInfo.ioFile ?: Paths.get(pathInfo.file!!.path)
+ if (file.isDirectory()) {
+ HttpResponseStatus.NOT_FOUND.send(channel, request)
+ return false
+ }
+ else if (!checkAccess(channel, file, request, Paths.get(pathInfo.root.path))) {
+ return false
+ }
+ }
+ else if (pathInfo.file!!.`is`(VFileProperty.HIDDEN)) {
+ HttpResponseStatus.NOT_FOUND.send(channel, request)
+ return false
+ }
+
+ return true
}
\ No newline at end of file
package org.jetbrains.builtInWebServer
import com.intellij.openapi.project.Project
-import com.intellij.openapi.vfs.VFileProperty
import com.intellij.util.PathUtilRt
-import com.intellij.util.isDirectory
import io.netty.buffer.ByteBufUtf8Writer
import io.netty.channel.Channel
import io.netty.channel.ChannelFutureListener
import org.jetbrains.builtInWebServer.ssi.SsiProcessor
import org.jetbrains.io.FileResponses
import org.jetbrains.io.addKeepAliveIfNeed
+import org.jetbrains.io.okInSafeMode
import org.jetbrains.io.send
import java.nio.file.Files
import java.nio.file.Path
return true
}
- sendIoFile(channel, ioFile, Paths.get(pathInfo.root.path), request)
+ FileResponses.sendFile(request, channel, ioFile)
}
else {
val file = pathInfo.file!!
- if (file.`is`(VFileProperty.HIDDEN)) {
- HttpResponseStatus.FORBIDDEN.send(channel, request)
- return true
- }
-
val response = FileResponses.prepareSend(request, channel, file.timeStamp, file.name) ?: return true
val keepAlive = response.addKeepAliveIfNeed(request)
}
}
-private fun sendIoFile(channel: Channel, file: Path, root: Path, request: HttpRequest) {
- if (file.isDirectory()) {
- HttpResponseStatus.FORBIDDEN.send(channel, request)
- }
- else if (checkAccess(channel, file, request, root)) {
- FileResponses.sendFile(request, channel, file)
- }
-}
-
-fun checkAccess(channel: Channel, file: Path, request: HttpRequest, root: Path = file.root, doNotExposeStatus: Boolean = false): Boolean {
+internal fun checkAccess(channel: Channel, file: Path, request: HttpRequest, root: Path = file.root): Boolean {
var parent = file
do {
if (!hasAccess(parent)) {
- (if (doNotExposeStatus) HttpResponseStatus.OK else HttpResponseStatus.FORBIDDEN).send(channel, request)
+ HttpResponseStatus.FORBIDDEN.okInSafeMode().send(channel, request)
return false
}
parent = parent.parent ?: break
return true
}
-// deny access to .htaccess files
-private fun hasAccess(result: Path) = Files.isReadable(result) && !(Files.isHidden(result) || result.fileName.toString().startsWith(".ht"))
\ No newline at end of file
+// deny access to any dot prefixed file
+private fun hasAccess(result: Path) = Files.isReadable(result) && !(Files.isHidden(result) || result.fileName.toString().startsWith('.'))
\ No newline at end of file
import org.jetbrains.concurrency.Promise
import org.jetbrains.concurrency.catchError
import org.jetbrains.concurrency.rejectedPromise
+import org.jetbrains.io.okInSafeMode
import java.nio.file.Path
import java.nio.file.Paths
import java.util.concurrent.ConcurrentLinkedQueue
.rejected {
if (it === NOT_FOUND) {
// don't expose file status
- sendStatus(HttpResponseStatus.OK, keepAlive, channel)
+ sendStatus(HttpResponseStatus.NOT_FOUND.okInSafeMode(), keepAlive, channel)
LOG.warn("File ${apiRequest.file} not found")
}
else {
if (!file.exists()) {
return rejectedPromise(NOT_FOUND)
}
- return if (context == null || checkAccess(context.channel(), file, httpRequest!!, doNotExposeStatus = true)) openAbsolutePath(file, request) else null
+ return if (context == null || checkAccess(context.channel(), file, httpRequest!!)) openAbsolutePath(file, request) else null
}
// we don't want to call refresh for each attempt on findFileByRelativePath call, so, we do what ourSaveAndSyncHandlerImpl does on frame activation
headers().set("X-Frame-Options", "SameOrigin")
}
headers().set("X-Content-Type-Options", "nosniff")
+ headers().set("x-xss-protection", "1; mode=block")
}
fun HttpResponse.send(channel: Channel, close: Boolean) {
createStatusResponse(this, request, description).send(channel, request)
}
+fun HttpResponseStatus.okInSafeMode() = if (ApplicationManager.getApplication()?.isUnitTestMode ?: false) this else HttpResponseStatus.OK
+
private fun createStatusResponse(responseStatus: HttpResponseStatus, request: HttpRequest?, description: String?): HttpResponse {
if (request != null && request.method() === HttpMethod.HEAD) {
return responseStatus.response()