mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 09:17:43 +03:00
e766f91338
* Add new security categories * Use test-security framework for OAuth2 middleware * Regenerate `security-evidence.md` * Explicitely exit the test-evidence generator * Use test-security framework for HTTP JSON * Regenerate `security-evidence.md` * Add //ledger-service/http-json:integration-tests-lib to evidence generator * Skip maven artefacts on the classpath The checker framework artefact (`checker-2.5.4.jar`) causes an `IllegalAccessError` when included in the runpath: ``` Exception in thread "main" java.lang.IllegalAccessError: class com.sun.tools.javac.code.Scope$ImportScope$ImportEntry cannot access its superclass com.sun.tools.javac.code.Scope$Entry (com.sun.tools.javac.code.Scope$ImportScope$ImportEntry is in unnamed module of loader java.net.URLClassLoader @31000e60; com.sun.tools.javac.code.Scope$Entry is in module jdk.compiler of loader app) at java.base/java.lang.ClassLoader.defineClass1(Native Method) at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1017) at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174) at java.base/java.net.URLClassLoader.defineClass(URLClassLoader.java:550) at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:458) at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:452) at java.base/java.security.AccessController.doPrivileged(Native Method) at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:451) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522) at org.scalatest.tools.SuiteDiscoveryHelper$.isDiscoverableSuite(SuiteDiscoveryHelper.scala:204) at org.scalatest.tools.SuiteDiscoveryHelper$.processClassName(SuiteDiscoveryHelper.scala:243) at org.scalatest.tools.SuiteDiscoveryHelper$.$anonfun$processFileNames$1(SuiteDiscoveryHelper.scala:279) at scala.collection.Iterator$$anon$9.next(Iterator.scala:577) at scala.collection.Iterator$$anon$6.hasNext(Iterator.scala:474) at scala.collection.Iterator$$anon$9.hasNext(Iterator.scala:576) at scala.collection.immutable.List.prependedAll(List.scala:152) at scala.collection.immutable.List$.from(List.scala:684) at scala.collection.immutable.List$.from(List.scala:681) at scala.collection.IterableFactory$Delegate.from(Factory.scala:288) at scala.collection.immutable.Iterable$.from(Iterable.scala:35) at scala.collection.immutable.Iterable$.from(Iterable.scala:32) at scala.collection.IterableFactory$Delegate.from(Factory.scala:288) at scala.collection.IterableOnceExtensionMethods$.toIterable$extension(IterableOnce.scala:178) at org.scalatest.tools.SuiteDiscoveryHelper$.processFileNames(SuiteDiscoveryHelper.scala:285) at org.scalatest.tools.SuiteDiscoveryHelper$.$anonfun$discoverSuiteNames$1(SuiteDiscoveryHelper.scala:132) at scala.collection.immutable.List.map(List.scala:250) ``` * Regenerate `security-evidence.md` * Convert remaining TEST_EVIDENCE stanzas of HTTP JSON * Regenerate `security-evidence.md` * Use test-security framework for HTTP JSON * Regenerate `security-evidence.md` * Print warning when a test suite could not be loaded * Fix typo * Use test-security framework for HTTP JSON * Read files in tests lazily The test-evidence generator tool needs to instantiate scalatest test suites in order to access the tagged tests and collect relavant test entries. * Use test-security framework for HTTP JSON * Regenerate `security-evidence.md` * Regenerate `security-evidence.md` * Use test-security framework for HTTP JSON * Regenerate `security-evidence.md` * Use test-security framework for HTTP JSON * Regenerate `security-evidence.md` * Use test-security framework for HTTP JSON * Regenerate `security-evidence.md` * Use test-security framework for trigger service * Use structural type to call `in` and `ignore` for different classes * Remove Authentication category from EvidenceSecurity tool There are no TEST_EVIDENCE annotations anymore. * Add required trigger-runner-lib * Import `scala.language.reflectiveCalls` where it is needed * Remove left-over comments * Add `test_evidence_binary` scala binary rule This rule is a customized `scala_binary` rule which also accepts a `tests` attr and generates a runpath file which is later consumed by scalatest to detect the relevant scalatest test suites. * Process test suites, add transitive deps * Support nested tests_suites in `test_evidence_binary` * Remove debug print's * Add missing dependencies to test-evidence:generator * Abort if test suites cannot be loaded * Cleanup * Reinstate scalacopts in http-json * Reword the test description to not drop information * Fix typo * Explicitly exit the JVM on exceptions This is required since non-daemon threads also prevent JVM shutdown when an exception was thrown. * Format test-evidence/BUILD.bazel * Resolve file paths lazily This avoids a `NullPointerException` on Windows where Runfiles.rlocation returns `null`. * Document new Security properties * Print target directory and file name * Clarify test descriptions * Replace duplicate Security properties Co-authored-by: Stephen Compall <stephen.compall@daml.com>Co-authored-by: Stephen Compall <stephen.compall@daml.com>
151 lines
4.6 KiB
Haskell
151 lines
4.6 KiB
Haskell
-- Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
|
-- SPDX-License-Identifier: Apache-2.0
|
|
|
|
module Main (main) where
|
|
|
|
import Control.Monad (when,void)
|
|
import Data.List ((\\),sortOn)
|
|
import Data.List.Extra (groupOn,foldl')
|
|
import Data.Map (Map)
|
|
import Data.Text (Text)
|
|
import Data.Void (Void)
|
|
import System.Exit (exitWith,ExitCode(ExitFailure))
|
|
import System.FilePath (splitPath)
|
|
import System.IO.Extra (hPutStrLn,stderr)
|
|
import Text.Megaparsec (Parsec,runParser,errorBundlePretty,eof,takeWhileP,single,label,satisfy,noneOf,chunk,(<|>),some)
|
|
import qualified Text.Megaparsec.Char (space)
|
|
import qualified Data.Char as Char (isDigit,digitToInt)
|
|
import qualified Data.Map as Map (fromList,toList)
|
|
import qualified Data.Text as T (pack,unpack)
|
|
import qualified Data.Text.IO as T (getContents)
|
|
|
|
{-
|
|
Generate _security evidence_ by documenting _security_ test cases.
|
|
|
|
Security tests may be found anywhere in the Daml repository, and written in any language
|
|
(scala, haskell, shell, etc). They are marked by the *magic comment*: "TEST_EVIDENCE"
|
|
followed by a ":".
|
|
|
|
Following the marker, the remaining text on the line is split on the next ":" to give:
|
|
Category : Free text description of the test case.
|
|
|
|
There are a fixed set of categories, listed in the enum below. There expect at least one
|
|
testcase for every category.
|
|
|
|
The generated evidence is a markdown file, listing each testcase, grouped by Category. For
|
|
each testcase we note the free-text with a link to the line in the original file.
|
|
|
|
This program is expected to be run with stdin generated by a git grep command, and stdout
|
|
redirected to the name of the generated file:
|
|
|
|
```
|
|
git grep --line-number TEST_EVIDENCE\: | bazel run security:evidence-security > security-evidence.md
|
|
```
|
|
-}
|
|
|
|
main :: IO ()
|
|
main = do
|
|
text <- T.getContents
|
|
lines <- parseLines text
|
|
let missingCats = [minBound..maxBound] \\ [ cat | Line{cat} <- lines ]
|
|
when (not $ null missingCats) $ do
|
|
messageAndExitFail ("No tests for categories: " ++ show missingCats)
|
|
putStrLn (ppCollated (collateLines lines))
|
|
|
|
type Parser = Parsec Void Text
|
|
|
|
parseLines :: Text -> IO [Line]
|
|
parseLines text = do
|
|
case runParser theParser "<stdin>" text of
|
|
Right xs -> pure xs
|
|
Left e -> messageAndExitFail $ errorBundlePretty e
|
|
|
|
messageAndExitFail :: String -> IO a
|
|
messageAndExitFail message = do
|
|
hPutStrLn stderr "** EvidenceSecurity: generation failed:"
|
|
hPutStrLn stderr message
|
|
exitWith $ ExitFailure 1
|
|
|
|
theParser :: Parser [Line]
|
|
theParser = some line <* eof
|
|
where
|
|
line = do
|
|
filename <- some notColonOrNewline
|
|
colon
|
|
lineno <- number
|
|
colon
|
|
marker
|
|
colon
|
|
optWhiteSpace
|
|
cat <- parseCategory
|
|
colon
|
|
optWhiteSpace
|
|
freeText <- takeWhileP (Just "freetext") (/= '\n')
|
|
void $ single '\n'
|
|
pure Line {cat, desc = Description{filename,lineno,freeText}}
|
|
|
|
number = foldl' (\acc d -> 10*acc+d) 0 <$> some digit
|
|
digit = label "digit" $ Char.digitToInt <$> satisfy Char.isDigit
|
|
|
|
marker =
|
|
(void $ chunk "TEST_EVIDENCE")
|
|
<|> do void notColonOrNewline; marker
|
|
|
|
optWhiteSpace = Text.Megaparsec.Char.space
|
|
|
|
parseCategory = do
|
|
foldl1 (<|>)
|
|
[ do void $ chunk $ T.pack $ ppCategory cat; pure cat
|
|
| cat <- [minBound..maxBound]
|
|
]
|
|
|
|
colon = void $ single ':'
|
|
|
|
notColonOrNewline = noneOf [':','\n']
|
|
|
|
|
|
data Category
|
|
= Authorization
|
|
| Availability
|
|
| Confidentiality
|
|
| Integrity
|
|
deriving (Eq,Ord,Bounded,Enum,Show)
|
|
|
|
data Description = Description
|
|
{ filename:: FilePath
|
|
, lineno:: Int
|
|
, freeText:: Text
|
|
}
|
|
|
|
data Line = Line { cat :: Category, desc :: Description }
|
|
|
|
newtype Collated = Collated (Map Category [Description])
|
|
|
|
collateLines :: [Line] -> Collated
|
|
collateLines lines =
|
|
Collated $ Map.fromList
|
|
[ (cat, [ desc | Line{desc} <- group ])
|
|
| group@(Line{cat}:_) <- groupOn cat (sortOn cat lines)
|
|
]
|
|
|
|
ppCollated :: Collated -> String
|
|
ppCollated (Collated m) =
|
|
unlines (["# Security tests, by category",""] ++
|
|
[ unlines (("## " ++ ppCategory cat ++ ":") : map ppDescription (sortOn freeText descs))
|
|
| (cat,descs) <- sortOn fst (Map.toList m)
|
|
])
|
|
|
|
ppDescription :: Description -> String
|
|
ppDescription Description{filename,lineno,freeText} =
|
|
"- " ++ T.unpack freeText ++ ": [" ++ basename filename ++ "](" ++ filename ++ "#L" ++ show lineno ++ ")"
|
|
where
|
|
basename :: FilePath -> FilePath
|
|
basename p = case reverse (splitPath p) of [] -> ""; x:_ -> x
|
|
|
|
ppCategory :: Category -> String
|
|
ppCategory = \case
|
|
Authorization -> "Authorization"
|
|
Availability -> "Availability"
|
|
Confidentiality -> "Confidentiality"
|
|
Integrity -> "Integrity"
|