Skip to content

Commit

Permalink
[#138] Report links that escape repo directory
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Sorokin-Anton committed Oct 3, 2022
1 parent 0fd2b8a commit 11eacca
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 1 deletion.
23 changes: 22 additions & 1 deletion src/Xrefcheck/Verify.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env bats

# SPDX-FileCopyrightText: 2021 Serokell <https://serokell.io>
#
# 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."
}
23 changes: 23 additions & 0 deletions tests/golden/check-local-references-outside-repo/expected.gold
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!--
- SPDX-FileCopyrightText: 2021 Serokell <https://serokell.io>
-
- SPDX-License-Identifier: MPL-2.0
-->

[Without anchor](../link-target.md)
[With anchor](../link-target.md#link-target-anchor)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!--
- SPDX-FileCopyrightText: 2022 Serokell <https://serokell.io>
-
- SPDX-License-Identifier: MPL-2.0
-->

# Link target anchor

0 comments on commit 11eacca

Please sign in to comment.