/******************************************************************************* * Copyright (c) 2011 Subgraph. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Subgraph - initial API and implementation ******************************************************************************/ package com.subgraph.vega.impl.scanner.handlers; import org.apache.http.client.methods.HttpUriRequest; import com.subgraph.vega.api.http.requests.IHttpResponse; import com.subgraph.vega.api.http.requests.IPageFingerprint; import com.subgraph.vega.api.model.web.IWebPath.PathType; import com.subgraph.vega.api.scanner.IInjectionModuleContext; import com.subgraph.vega.api.scanner.IPathState; public class Dir404Tests extends CrawlerModule { private static final String PAGE_DOES_NOT_EXIST = "nosuchpage123"; private final DirParentCheck dirParentCheck = new DirParentCheck(); private final CaseSensitivityCheck caseSensitivityCheck = new CaseSensitivityCheck(); @Override public void initialize(IPathState ps) { final IInjectionModuleContext ctx = ps.createModuleContext(); ctx.submitAlteredRequest(this, "/"+ PAGE_DOES_NOT_EXIST); } @Override public void runModule(HttpUriRequest request, IHttpResponse response, IInjectionModuleContext ctx) { final IPathState ps = ctx.getPathState(); final boolean isFirstResponse = ctx.getCurrentIndex() == 0; boolean failed = false; if(ps.hasFailed404Detection()) failed = true; if(response.isFetchFail()) { ctx.error(request, response, "during 404 response checks"); failed = true; } if(!failed) { processResponseFingerprint(ctx, request, response, isFirstResponse); if(isFirstResponse) finishProcessingFirstResponse(ctx, ps); } finishProcessingResponse(ctx, ps); } private void finishProcessingResponse(IInjectionModuleContext ctx, IPathState ps) { ctx.incrementResponseCount(); if(!ctx.allResponsesReceived()) return; if(!ps.has404Fingerprints() || ps.hasFailed404Detection()) handleFailed404Detection(ctx, ps); dirParentCheck.initialize(ps); } private void finishProcessingFirstResponse(IInjectionModuleContext ctx, IPathState ps) { if(!ps.has404Fingerprints()) { ctx.debug("First 404 probe failed to produce a signature"); } else { ctx.pivotChecks(ps.createRequest(), ps.getResponse()); caseSensitivityCheck.initialize(ps); scheduleProbes(ctx); } } private void processResponseFingerprint(IInjectionModuleContext ctx, HttpUriRequest req, IHttpResponse res, boolean isFirstResponse) { final IPageFingerprint fp = res.getPageFingerprint(); final IPathState ps = ctx.getPathState(); if(isFirstResponse && !ps.isSureDirectory() && !ps.isRootPath() && ps.matchesPathFingerprint(fp)) { ctx.debug("First 404 probe identical to parent page"); return; } if(!ps.add404Fingerprint(fp)) { ctx.debug("Failed 404 detection, too many unique 404 signatures received"); ps.setFailed404Detection(); return; } final IPathState parent404 = ps.get404Parent(); if((parent404 != null) && !parent404.has404FingerprintMatching(fp)) { ctx.debug("New 404 signature detected that was not detected on parent"); ctx.responseChecks(req, res); } } private void scheduleProbes(IInjectionModuleContext ctx) { for(String ext: ctx.getFileExtensionList()) ctx.submitAlteredRequest(this, PAGE_DOES_NOT_EXIST + "."+ ext, 1); ctx.submitAlteredRequest(this, "lpt9", 1); ctx.submitAlteredRequest(this, "~"+PAGE_DOES_NOT_EXIST, 1); ctx.submitAlteredRequest(this, PAGE_DOES_NOT_EXIST, 1); } private void handleFailed404Detection(IInjectionModuleContext ctx, IPathState ps) { final int code = (ps.getResponse() == null) ? (0) : (ps.getResponse().getResponseCode()); if(code == 404) { ps.setPageMissing(); } else if(code > 400) { logFailureMessage(ctx, code); } else if(!ps.isRootPath()) { ps.getPath().setPathType(PathType.PATH_PATHINFO); } else { ctx.debug("No distinctive 404 signatures detected"); } ps.clear404Fingerprints(); if(ps.getParentState() == null || ps.getParentState().getResponse() == null) { ctx.pivotChecks(ps.createRequest(), ps.getResponse()); return; } if(!ps.getParentState().matchesPathFingerprint(ps.getPathFingerprint())) { IPathState parent404 = ps.get404Parent(); if(parent404 == null || !parent404.has404FingerprintMatching(ps.getPathFingerprint())) { ctx.pivotChecks(ps.createRequest(), ps.getResponse()); } } } private void logFailureMessage(IInjectionModuleContext ctx, int httpCode) { final StringBuilder sb = new StringBuilder(); sb.append("Directory resource is not accessible: HTTP response code ("); sb.append(httpCode); sb.append(")"); if(httpCode == 401) sb.append(" [HTTP Auth required"); if(httpCode >= 500) sb.append(" [Server error]"); ctx.debug(sb.toString()); } }