Skip to content

Commit

Permalink
feat: disallow remote links in toc/landmarks/page-list nav
Browse files Browse the repository at this point in the history
A new message `NAV_010` (`ERROR`) is reported when a `toc`, `landmarks`,
or `page-list` nav contains links to remote resources.

Fix #890
  • Loading branch information
rdeltour committed Mar 12, 2019
1 parent 50b29f1 commit dd0805f
Show file tree
Hide file tree
Showing 29 changed files with 320 additions and 296 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ private void initialize()
severities.put(MessageId.NAV_007, Severity.USAGE);
severities.put(MessageId.NAV_008, Severity.USAGE);
severities.put(MessageId.NAV_009, Severity.ERROR);
severities.put(MessageId.NAV_010, Severity.ERROR);

// NCX
severities.put(MessageId.NCX_001, Severity.ERROR);
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/adobe/epubcheck/messages/MessageId.java
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ public enum MessageId implements Comparable<MessageId>
NAV_007("NAV-007"),
NAV_008("NAV-008"),
NAV_009("NAV-009"),
NAV_010("NAV-010"),

// Epub2 based table of content messages
NCX_001("NCX-001"),
Expand Down
120 changes: 91 additions & 29 deletions src/main/java/com/adobe/epubcheck/nav/NavHandler.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,44 @@
package com.adobe.epubcheck.nav;

import java.util.EnumSet;
import java.util.Set;

import com.adobe.epubcheck.api.EPUBLocation;
import com.adobe.epubcheck.messages.MessageId;
import com.adobe.epubcheck.opf.ValidationContext;
import com.adobe.epubcheck.ops.OPSHandler30;
import com.adobe.epubcheck.util.EpubConstants;
import com.adobe.epubcheck.util.FeatureEnum;
import com.adobe.epubcheck.util.PathUtil;
import com.adobe.epubcheck.vocab.StructureVocab.EPUB_TYPES;
import com.adobe.epubcheck.xml.XMLElement;
import com.adobe.epubcheck.xml.XMLParser;
import com.google.common.base.CaseFormat;
import com.google.common.base.Converter;

public class NavHandler extends OPSHandler30
{

private boolean inToc = false;
private NavType currentNavType = NavType.NONE;
private boolean isNavTypes = false;

private static enum NavType
{
NONE,
TOC,
LANDMARKS,
PAGE_LIST,
OTHER;

private Converter<String, String> formatter = CaseFormat.UPPER_UNDERSCORE
.converterTo(CaseFormat.LOWER_HYPHEN);

@Override
public String toString()
{
return formatter.convert(this.name());
}
}

NavHandler(ValidationContext context, XMLParser parser)
{
Expand All @@ -24,10 +50,29 @@ public void startElement()
{
super.startElement();
XMLElement e = parser.getCurrentElement();
String name = e.getName();
if (inToc && "a".equals(name))
if (EpubConstants.HtmlNamespaceUri.equals(e.getNamespace()) && e.getName().equals("a"))
{
context.featureReport.report(FeatureEnum.TOC_LINKS, parser.getLocation());
String href = e.getAttribute("href");
if (href != null)
{
// Feature reporting
if (currentNavType == NavType.TOC)
{
context.featureReport.report(FeatureEnum.TOC_LINKS, parser.getLocation());
}

// For 'toc', 'landmarks', and 'page-list' nav:
// the link MUST resolve to a Top-level Content Document
// Note: links to out-of-spine in-container items are already reported
// (RSC_011), so we only need to report links to remote resources
if (EnumSet.of(NavType.TOC, NavType.LANDMARKS, NavType.PAGE_LIST).contains(currentNavType)
&& PathUtil.isRemote(href))
{
report.message(MessageId.NAV_010,
EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber()),
currentNavType, href);
}
}
}
}

Expand All @@ -36,39 +81,56 @@ public void endElement()
{
super.endElement();
XMLElement e = parser.getCurrentElement();
String name = e.getName();
if (inToc && "nav".equals(name))
if (EpubConstants.HtmlNamespaceUri.equals(e.getNamespace()) && e.getName().equals("nav"))
{
inToc = false;
currentNavType = NavType.NONE;
}
}

@Override
protected void checkType(XMLElement e, String type)
{
isNavTypes = (EpubConstants.HtmlNamespaceUri.equals(e.getNamespace())
&& e.getName().equals("nav"));
super.checkType(e, type);
isNavTypes = false;
}

@Override
protected void checkTypes(Set<EPUB_TYPES> types)
{
super.checkTypes(types);
if (types.contains(EPUB_TYPES.TOC))
{
inToc = true;
}
if (types.contains(EPUB_TYPES.PAGE_LIST))
{
context.featureReport.report(FeatureEnum.PAGE_LIST, parser.getLocation());
}
if (types.contains(EPUB_TYPES.LOI))
{
context.featureReport.report(FeatureEnum.LOI, parser.getLocation());
}
if (types.contains(EPUB_TYPES.LOT))
{
context.featureReport.report(FeatureEnum.LOT, parser.getLocation());
}
if (types.contains(EPUB_TYPES.LOA))
{
context.featureReport.report(FeatureEnum.LOA, parser.getLocation());
}
if (types.contains(EPUB_TYPES.LOV))
if (isNavTypes)
{
context.featureReport.report(FeatureEnum.LOV, parser.getLocation());
if (types.contains(EPUB_TYPES.TOC))
{
currentNavType = NavType.TOC;
}
if (types.contains(EPUB_TYPES.PAGE_LIST))
{
currentNavType = NavType.PAGE_LIST;
context.featureReport.report(FeatureEnum.PAGE_LIST, parser.getLocation());
}
if (types.contains(EPUB_TYPES.LANDMARKS))
{
currentNavType = NavType.LANDMARKS;
}
if (types.contains(EPUB_TYPES.LOI))
{
context.featureReport.report(FeatureEnum.LOI, parser.getLocation());
}
if (types.contains(EPUB_TYPES.LOT))
{
context.featureReport.report(FeatureEnum.LOT, parser.getLocation());
}
if (types.contains(EPUB_TYPES.LOA))
{
context.featureReport.report(FeatureEnum.LOA, parser.getLocation());
}
if (types.contains(EPUB_TYPES.LOV))
{
context.featureReport.report(FeatureEnum.LOV, parser.getLocation());
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ NAV_006=Content documents contain 'figure' elements but the Navigation Document
NAV_007=Content documents contain 'table' elements but the Navigation Document does not have a listing of tables (epub:type="lot").
NAV_008=Content documents contain 'video' elements but the Navigation Document does not have a listing of video clips (epub:type="lov").
NAV_009=Region-based navigation links must point to Fixed-Layout Documents.
NAV_010='%1$s' nav must not link to remote resources; found link to '%2$s'.

#NCX EPUB v2 Table of Contents
NCX_001=NCX identifier ('%1$s') does not match OPF identifier ('%2$s').
Expand Down
18 changes: 16 additions & 2 deletions src/test/java/com/adobe/epubcheck/api/Epub30CheckExpandedTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,24 @@ public void testValidateEPUBPInvalidLoremForeign()
}

@Test
public void testValidateEPUB30_navInvalid()
public void testValidateNav_TocMissing()
{
Collections.addAll(expectedErrors, MessageId.RSC_005);
testValidateDocument("invalid/nav-invalid/");
testValidateDocument("invalid/nav-toc-missing/");
}

@Test
public void testValidateNav_LinksOutOfSpine()
{
expectedErrors.add(MessageId.RSC_011);
testValidateDocument("invalid/nav-links-out-of-spine/");
}

@Test
public void testValidateNav_LinksRemote()
{
expectedErrors.addAll(Collections.nCopies(3, MessageId.NAV_010));
testValidateDocument("invalid/nav-links-remote/");
}

@Test
Expand Down

This file was deleted.

127 changes: 0 additions & 127 deletions src/test/resources/30/expanded/invalid/nav-invalid/OEBPS/4.xhtml

This file was deleted.

Loading

0 comments on commit dd0805f

Please sign in to comment.