From 11eacca81ff7f6331934b6af9194aac6627fd571 Mon Sep 17 00:00:00 2001 From: Anton Sorokin Date: Mon, 19 Sep 2022 12:40:37 +0300 Subject: [PATCH] [#138] Report links that escape repo directory Problem: as in #138, when we see a local link, we are checking only existance of referred file, not checking that this file is a part of repo and link will compatible with Github's renderer Solution: manually count "nesting levels" of all local links, checking that number of `".."`'s is always less then number of real directories --- src/Xrefcheck/Verify.hs | 23 +++++++++++++++- .../check-local-references-outside-repo.bats | 27 +++++++++++++++++++ .../expected.gold | 23 ++++++++++++++++ .../check-local-references-outside-repo.md | 8 ++++++ .../link-target.md | 7 +++++ 5 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 tests/golden/check-local-references-outside-repo/check-local-references-outside-repo.bats create mode 100644 tests/golden/check-local-references-outside-repo/expected.gold create mode 100644 tests/golden/check-local-references-outside-repo/inner-directory/check-local-references-outside-repo.md create mode 100644 tests/golden/check-local-references-outside-repo/link-target.md diff --git a/src/Xrefcheck/Verify.hs b/src/Xrefcheck/Verify.hs index d928f725..2102dbeb 100644 --- a/src/Xrefcheck/Verify.hs +++ b/src/Xrefcheck/Verify.hs @@ -58,7 +58,7 @@ import Network.HTTP.Types.Header (hRetryAfter) import Network.HTTP.Types.Status (Status, statusCode, statusMessage) import System.Console.Pretty (Style (..), style) import System.Directory (doesDirectoryExist, doesFileExist) -import System.FilePath (normalise, takeDirectory, ()) +import System.FilePath (takeDirectory, (), normalise, splitDirectories, makeRelative) import Text.ParserCombinators.ReadPrec qualified as ReadPrec (lift) import Text.Regex.TDFA.Text (Regex, regexec) import Text.URI (Authority (..), ParseExceptionBs, URI (..), mkURIBs) @@ -119,6 +119,7 @@ instance Buildable a => Buildable (WithReferenceLoc a) where data VerifyError = LocalFileDoesNotExist FilePath + | LocalFileOutsideRepo FilePath | AnchorDoesNotExist Text [Anchor] | AmbiguousAnchorRef FilePath Text (NonEmpty Anchor) | ExternalResourceInvalidUri URIBS.URIParseError @@ -138,6 +139,9 @@ instance Buildable VerifyError where LocalFileDoesNotExist file -> "⛀ File does not exist:\n " +| file |+ "\n" + LocalFileOutsideRepo file -> + "⛀ Link targets a local file outside repository:\n " +| file |+ "\n" + AnchorDoesNotExist anchor similar -> "⛀ Anchor '" +| anchor |+ "' is not present" +| anchorHints similar @@ -407,12 +411,29 @@ verifyReference _ -> Nothing checkRef mAnchor referredFile = verifying $ do + checkReferredFileIsInsideRepo referredFile checkReferredFileExists referredFile case M.lookup referredFile repoInfo of Nothing -> pass -- no support for such file, can do nothing Just referredFileInfo -> whenJust mAnchor $ checkAnchor referredFile (_fiAnchors referredFileInfo) + checkReferredFileIsInsideRepo file = unless + (noNegativeNesting $ makeRelative root file) + $ throwError (LocalFileOutsideRepo file) + where + -- | checks that relative filepath fully belongs to current directory + -- noNegativeNesting "a/../b" = True + -- noNegativeNesting "a/../../b" = False + noNegativeNesting path = all (>= 0) $ scanl + (\n dir -> n + nestingChange dir) + (0 :: Integer) + $ splitDirectories path + + nestingChange ".." = -1 + nestingChange "." = 0 + nestingChange _ = 1 + checkReferredFileExists file = do let fileExists = readingSystem $ doesFileExist file let dirExists = readingSystem $ doesDirectoryExist file diff --git a/tests/golden/check-local-references-outside-repo/check-local-references-outside-repo.bats b/tests/golden/check-local-references-outside-repo/check-local-references-outside-repo.bats new file mode 100644 index 00000000..fb9dc6e3 --- /dev/null +++ b/tests/golden/check-local-references-outside-repo/check-local-references-outside-repo.bats @@ -0,0 +1,27 @@ +#!/usr/bin/env bats + +# SPDX-FileCopyrightText: 2021 Serokell +# +# SPDX-License-Identifier: MPL-2.0 + +load '../helpers/bats-support/load' +load '../helpers/bats-assert/load' +load '../helpers' + +@test "We report all links that target files outside root" { + xrefcheck -r inner-directory\ + | prepare > /tmp/check-notScanned.test || true + + diff /tmp/check-notScanned.test expected.gold \ + --ignore-space-change \ + --ignore-blank-lines \ + --new-file # treat absent files as empty + + rm /tmp/check-notScanned.test +} + +@test "When target is inside repo root, everything is good" { + run xrefcheck -r .\ + + assert_output --partial "All repository links are valid." +} diff --git a/tests/golden/check-local-references-outside-repo/expected.gold b/tests/golden/check-local-references-outside-repo/expected.gold new file mode 100644 index 00000000..62783804 --- /dev/null +++ b/tests/golden/check-local-references-outside-repo/expected.gold @@ -0,0 +1,23 @@ +=== Invalid references found === + + ➥ In file inner-directory/check-local-references-outside-repo.md + bad reference (relative) at src:7:1-35: + - text: "Without anchor" + - link: ../link-target.md + - anchor: - + + ⛀ Link targets a local file outside repository: + inner-directory/../link-target.md + + + ➥ In file inner-directory/check-local-references-outside-repo.md + bad reference (relative) at src:8:1-51: + - text: "With anchor" + - link: ../link-target.md + - anchor: link-target-anchor + + ⛀ Link targets a local file outside repository: + inner-directory/../link-target.md + + +Invalid references dumped, 2 in total. diff --git a/tests/golden/check-local-references-outside-repo/inner-directory/check-local-references-outside-repo.md b/tests/golden/check-local-references-outside-repo/inner-directory/check-local-references-outside-repo.md new file mode 100644 index 00000000..9cbb9a68 --- /dev/null +++ b/tests/golden/check-local-references-outside-repo/inner-directory/check-local-references-outside-repo.md @@ -0,0 +1,8 @@ + + +[Without anchor](../link-target.md) +[With anchor](../link-target.md#link-target-anchor) diff --git a/tests/golden/check-local-references-outside-repo/link-target.md b/tests/golden/check-local-references-outside-repo/link-target.md new file mode 100644 index 00000000..a30a59d8 --- /dev/null +++ b/tests/golden/check-local-references-outside-repo/link-target.md @@ -0,0 +1,7 @@ + + +# Link target anchor