From 82fcdbf44df1f3bb96e70362b20e48a3b6943fbe Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Thu, 7 Sep 2023 12:46:41 +0200 Subject: [PATCH 1/2] Add: feed transpile command Adds a new feature called transpile to feed of nasl-cli. With it you can manipulate a feed, currently it is able to rename, remove, add, push parameter or functions within a feed. To demonstrate it there is an example within `examples/nasl-cli/transpile.toml` to show case - rename service `www` to `word-wide-web` in register_product - `register_host_detail` to `add_host_detail` to execute it call: ``` nasl-cli -v feed transpile -p /tmp/feed\ -r examples/nasl-cli/transpile.toml ``` Additionally to line and colum number byte ranges got added. This allows us to lookup complete statements without having to create line no lookup tables or reading twice. Usually the statements get discarded after execution so the additional memory should not be a big issue. --- rust/Cargo.lock | 472 +++---- rust/examples/nasl-cli/transpile.toml | 63 + rust/feed/Cargo.toml | 5 + rust/feed/src/lib.rs | 2 + rust/feed/src/oid/mod.rs | 6 +- rust/feed/src/transpile/mod.rs | 1158 +++++++++++++++++ rust/feed/src/verify/mod.rs | 88 ++ rust/models/src/lib.rs | 3 +- rust/nasl-builtin-std/src/lib.rs | 2 +- rust/nasl-c-lib/libgcrypt-sys/build.rs | 2 +- .../libgcrypt-sys/install-gcrypt.sh | 2 +- rust/nasl-cli/Cargo.toml | 2 + rust/nasl-cli/README.md | 8 +- rust/nasl-cli/src/error.rs | 10 +- rust/nasl-cli/src/interpret/mod.rs | 2 +- rust/nasl-cli/src/main.rs | 96 ++ rust/nasl-interpreter/README.md | 12 +- rust/nasl-interpreter/src/assign.rs | 4 +- rust/nasl-interpreter/src/declare.rs | 20 +- rust/nasl-interpreter/src/error.rs | 2 +- rust/nasl-interpreter/src/include.rs | 4 +- rust/nasl-interpreter/src/interpreter.rs | 44 +- rust/nasl-interpreter/tests/description.rs | 6 +- rust/nasl-syntax/src/cursor.rs | 2 + rust/nasl-syntax/src/error.rs | 3 + rust/nasl-syntax/src/grouping_extension.rs | 95 +- rust/nasl-syntax/src/infix_extension.rs | 146 ++- rust/nasl-syntax/src/keyword_extension.rs | 506 ++++--- rust/nasl-syntax/src/lexer.rs | 24 +- rust/nasl-syntax/src/lib.rs | 29 +- rust/nasl-syntax/src/loader.rs | 2 + rust/nasl-syntax/src/naslvalue.rs | 2 +- rust/nasl-syntax/src/postfix_extension.rs | 24 +- rust/nasl-syntax/src/prefix_extension.rs | 58 +- rust/nasl-syntax/src/statement.rs | 424 +++++- rust/nasl-syntax/src/token.rs | 351 ++--- rust/nasl-syntax/src/variable_extension.rs | 205 +-- rust/openvasd/src/controller/mod.rs | 2 +- rust/openvasd/src/tls.rs | 2 +- rust/osp/src/response.rs | 2 +- rust/smoketest/src/config.rs | 10 +- rust/smoketest/src/lib.rs | 7 +- rust/smoketest/tests/tests.rs | 21 +- 43 files changed, 2649 insertions(+), 1279 deletions(-) create mode 100644 rust/examples/nasl-cli/transpile.toml create mode 100644 rust/feed/src/transpile/mod.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 35d6c71a1..5f2620117 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -153,7 +153,7 @@ checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -270,9 +270,9 @@ dependencies = [ [[package]] name = "buffered-reader" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d3bea5bcc3ecc38fe5388e6bc35e6fe7bd665eb3ae9a44283e15b91ad3867d" +checksum = "2b9b0a25eb06e83579bc985d836e1e3b957a7201301b48538764d2b2e78090d4" dependencies = [ "lazy_static", "libc", @@ -298,9 +298,9 @@ checksum = "2dca085c2c7d9d65ad749d450b19b551efaa8e3476a439bdca07aca8533097f3" [[package]] name = "capnp-futures" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9821801cc6f199a9d9c3c793504e800c797b536d2befddaffb15144e40a6e63a" +checksum = "06d7115c9c31cc85c6d21aea9417059f9d6ee87762a36693a2afcfb8ba23009b" dependencies = [ "capnp", "futures", @@ -423,9 +423,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.7" +version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" dependencies = [ "clap_builder", "clap_derive", @@ -433,9 +433,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.7" +version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" dependencies = [ "anstream", "anstyle", @@ -452,7 +452,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -469,7 +469,7 @@ checksum = "8543454e3c3f5126effff9cd44d562af4e31fb8ce1cc0d3dcd8f084515dbc1aa" dependencies = [ "cipher", "dbl", - "digest 0.10.7", + "digest", ] [[package]] @@ -512,9 +512,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fbc60abd742b35f2492f808e1abbb83d45f72db402e14c55057edc9c7b1e9e4" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] @@ -620,7 +620,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array 0.14.7", - "rand_core 0.6.4", + "rand_core", "typenum", ] @@ -677,15 +677,6 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.7", -] - [[package]] name = "digest" version = "0.10.7" @@ -740,9 +731,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd" +checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" [[package]] name = "either" @@ -787,9 +778,9 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e" dependencies = [ "libc", "windows-sys 0.48.0", @@ -816,13 +807,16 @@ name = "feed" version = "0.1.0" dependencies = [ "anyhow", + "glob", "hex", "nasl-interpreter", "nasl-syntax", "sequoia-ipc", "sequoia-openpgp", + "serde", "sha2", "storage", + "toml 0.8.8", "tracing", ] @@ -892,9 +886,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" dependencies = [ "futures-channel", "futures-core", @@ -907,9 +901,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", "futures-sink", @@ -917,15 +911,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" dependencies = [ "futures-core", "futures-task", @@ -934,38 +928,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-channel", "futures-core", @@ -1000,27 +994,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.16" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.9.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] @@ -1107,14 +1088,14 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.7", + "digest", ] [[package]] name = "http" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ "bytes", "fnv", @@ -1170,9 +1151,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http", @@ -1207,16 +1188,6 @@ dependencies = [ "cc", ] -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.4.0" @@ -1239,9 +1210,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", "hashbrown 0.14.2", @@ -1255,7 +1226,7 @@ dependencies = [ "chacha20", "criterion", "pbkdf2", - "rand 0.8.5", + "rand", "serde", "sha2", "uuid", @@ -1314,9 +1285,9 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" dependencies = [ "wasm-bindgen", ] @@ -1329,28 +1300,6 @@ dependencies = [ "storage", ] -[[package]] -name = "lalrpop" -version = "0.19.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1cbf952127589f2851ab2046af368fd20645491bb4b376f04b7f94d7a9837b" -dependencies = [ - "ascii-canvas", - "bit-set", - "diff", - "ena", - "is-terminal", - "itertools", - "lalrpop-util 0.19.12", - "petgraph", - "regex", - "regex-syntax 0.6.29", - "string_cache", - "term", - "tiny-keccak", - "unicode-xid", -] - [[package]] name = "lalrpop" version = "0.20.0" @@ -1363,7 +1312,7 @@ dependencies = [ "ena", "is-terminal", "itertools", - "lalrpop-util 0.20.0", + "lalrpop-util", "petgraph", "regex", "regex-syntax 0.7.5", @@ -1373,12 +1322,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "lalrpop-util" -version = "0.19.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3c48237b9604c5a4702de6b824e02006c3214327564636aef27c1028a8fa0ed" - [[package]] name = "lalrpop-util" version = "0.20.0" @@ -1393,9 +1336,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.149" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libgcrypt-sys" @@ -1411,11 +1354,22 @@ dependencies = [ "winapi", ] +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall", +] + [[package]] name = "libssh-rs" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fce13093a2ce71e1f6813a6a1dde7e373acd6f458f9ea6f3047b5f63982aacc6" +checksum = "eb3fe324fb06b71d28abb81382ac547f25b4895e853a9968482dc5002fb3db08" dependencies = [ "bitflags 1.3.2", "libssh-rs-sys", @@ -1425,9 +1379,9 @@ dependencies = [ [[package]] name = "libssh-rs-sys" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777fd4deb4b6aab4e05a20d0a9c77580e50c9ea32b5e9618fb8c5e2e4323809b" +checksum = "3af07827858d82a7b74d6f935ad4201ff764fb1de8efcc26aeaa33e5f9c89ca2" dependencies = [ "cc", "libz-sys", @@ -1449,9 +1403,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" +checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" [[package]] name = "lock_api" @@ -1485,7 +1439,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ "cfg-if", - "digest 0.10.7", + "digest", ] [[package]] @@ -1494,7 +1448,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f4f0f3ed25ff4f8d8d102288d92f900efc202661c884cf67dfe4f0d07c43d1f" dependencies = [ - "digest 0.10.7", + "digest", ] [[package]] @@ -1540,7 +1494,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.48.0", ] @@ -1563,7 +1517,7 @@ dependencies = [ "ccm", "cmac", "ctr", - "digest 0.10.7", + "digest", "hex", "hmac", "md-5", @@ -1707,8 +1661,10 @@ dependencies = [ "nasl-syntax", "redis-storage", "scanconfig", + "serde", "serde_json", "storage", + "toml 0.8.8", "tracing", "tracing-subscriber", "walkdir", @@ -1803,9 +1759,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.57" +version = "0.10.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" +checksum = "7a257ad03cd8fb16ad4172fedf8094451e1af1c4b70097636ef2eac9a5f0cc33" dependencies = [ "bitflags 2.4.1", "cfg-if", @@ -1824,7 +1780,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -1844,9 +1800,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.93" +version = "0.9.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" +checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9" dependencies = [ "cc", "libc", @@ -1876,7 +1832,7 @@ dependencies = [ "nasl-interpreter", "osp", "pbkdf2", - "rand 0.8.5", + "rand", "rustls", "rustls-pemfile", "serde", @@ -1885,7 +1841,7 @@ dependencies = [ "storage", "tokio", "tokio-rustls", - "toml", + "toml 0.7.8", "tracing", "tracing-subscriber", "uuid", @@ -1926,7 +1882,7 @@ checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", + "redox_syscall", "smallvec", "windows-targets", ] @@ -1938,7 +1894,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" dependencies = [ "base64ct", - "rand_core 0.6.4", + "rand_core", "subtle", ] @@ -1948,7 +1904,7 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "digest 0.10.7", + "digest", "hmac", "password-hash", ] @@ -1981,7 +1937,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.0.2", + "indexmap 2.1.0", ] [[package]] @@ -2197,19 +2153,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - [[package]] name = "rand" version = "0.8.5" @@ -2217,18 +2160,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_chacha", + "rand_core", ] [[package]] @@ -2238,16 +2171,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core", ] [[package]] @@ -2256,16 +2180,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.10", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", + "getrandom", ] [[package]] @@ -2310,24 +2225,6 @@ dependencies = [ "storage", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -2339,12 +2236,12 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ - "getrandom 0.2.10", - "redox_syscall 0.2.16", + "getrandom", + "libredox", "thiserror", ] @@ -2445,7 +2342,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" dependencies = [ "cc", - "getrandom 0.2.10", + "getrandom", "libc", "spin", "untrusted", @@ -2458,7 +2355,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ - "digest 0.10.7", + "digest", ] [[package]] @@ -2469,12 +2366,12 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.20" +version = "0.38.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0" +checksum = "9ad981d6c340a49cdc40a1028d9c6084ec7e9fa33fcb839cab656a267071e234" dependencies = [ "bitflags 2.4.1", - "errno 0.3.5", + "errno 0.3.6", "libc", "linux-raw-sys", "windows-sys 0.48.0", @@ -2506,9 +2403,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ "base64", ] @@ -2619,12 +2516,12 @@ dependencies = [ "dirs", "fs2", "futures", - "lalrpop 0.20.0", - "lalrpop-util 0.20.0", + "lalrpop", + "lalrpop-util", "lazy_static", "libc", "memsec", - "rand 0.8.5", + "rand", "sequoia-openpgp", "socket2 0.4.10", "tempfile", @@ -2636,28 +2533,28 @@ dependencies = [ [[package]] name = "sequoia-openpgp" -version = "1.16.1" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a16854c0f6297de6db4df195e28324dfbc2429802f0e48cd04007db8e3049709" +checksum = "2ea026cf8a70d331c742e3ad7e68fd405d0743ff86630fb4334a1bf8d0e194c7" dependencies = [ "anyhow", "base64", "buffered-reader", "chrono", "dyn-clone", - "getrandom 0.2.10", - "idna 0.3.0", - "lalrpop 0.19.12", - "lalrpop-util 0.19.12", + "getrandom", + "idna", + "lalrpop", + "lalrpop-util", "lazy_static", "libc", "memsec", "once_cell", "openssl", "openssl-sys", - "rand 0.7.3", + "rand", "regex", - "regex-syntax 0.6.29", + "regex-syntax 0.8.2", "sha1collisiondetection", "thiserror", "xxhash-rust", @@ -2665,29 +2562,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.189" +version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.189" +version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -2723,7 +2620,7 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.7", + "digest", ] [[package]] @@ -2734,11 +2631,11 @@ checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" [[package]] name = "sha1collisiondetection" -version = "0.2.7" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b20793cf8330b2c7da4c438116660fed24e380bcb8a1bcfff2581b5593a0b38e" +checksum = "31c0b86a052106b16741199985c9ec2bf501f619f70c48fa479b44b093ad9a68" dependencies = [ - "digest 0.9.0", + "digest", "generic-array 0.14.7", ] @@ -2750,7 +2647,7 @@ checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.7", + "digest", ] [[package]] @@ -2788,9 +2685,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "smoketest" @@ -2879,9 +2776,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.38" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -2911,13 +2808,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.8.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.3.5", + "redox_syscall", "rustix", "windows-sys 0.48.0", ] @@ -2950,7 +2847,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -3027,9 +2924,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.33.0" +version = "1.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" +checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" dependencies = [ "backtrace", "bytes", @@ -3046,13 +2943,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -3089,7 +2986,19 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.19.15", +] + +[[package]] +name = "toml" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.21.0", ] [[package]] @@ -3107,7 +3016,20 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.0.2", + "indexmap 2.1.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap 2.1.0", "serde", "serde_spanned", "toml_datetime", @@ -3139,7 +3061,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -3154,9 +3076,9 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.4" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ "log", "once_cell", @@ -3165,9 +3087,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", "nu-ansi-term", @@ -3243,7 +3165,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", - "idna 0.4.0", + "idna", "percent-encoding", ] @@ -3265,8 +3187,8 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" dependencies = [ - "getrandom 0.2.10", - "rand 0.8.5", + "getrandom", + "rand", "serde", ] @@ -3313,12 +3235,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3327,9 +3243,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3337,24 +3253,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" dependencies = [ "cfg-if", "js-sys", @@ -3364,9 +3280,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3374,28 +3290,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" dependencies = [ "js-sys", "wasm-bindgen", @@ -3558,9 +3474,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.5.17" +version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c" +checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" dependencies = [ "memchr", ] diff --git a/rust/examples/nasl-cli/transpile.toml b/rust/examples/nasl-cli/transpile.toml new file mode 100644 index 000000000..af5b7bbde --- /dev/null +++ b/rust/examples/nasl-cli/transpile.toml @@ -0,0 +1,63 @@ +# This demo shows how to use a configuration to rename: +# `www` to `world-wide-web` when in register_product the parameter cpe, location, port are set and when service is set to `www` +# `register_host_detail` to `add_host_detail` +# To test it get a community feed and run: +# ``` +# nasl-cli -v feed transpile -p /tmp/feed -r example/replace.toml +# ``` + +[[cmds]] + +[cmds.find] +FunctionByNameAndParameter = [ + "register_product", + [ + { Name = "cpe" }, + { Name = "location" }, + { Name = "port" }, + { NameValue = [ + "service", + "\"www\"", +] }, +], +] + +[cmds.with.Parameter.Push] +Named = [ + "service_to_be", + "\"world-wide-web\"", +] + +[[cmds]] + +[cmds.find] +FunctionByNameAndParameter = [ + "register_product", + [ + { Name = "cpe" }, + { Name = "location" }, + { Name = "port" }, + { Name = "service" }, + { Name = "service_to_be" }, +], +] + +[cmds.with.Parameter] +RemoveNamed = "service" + +[[cmds]] + +[cmds.find] +FunctionByName = "register_product" + +[cmds.with.Parameter.Rename] +previous = "service_to_be" +new = "service" + +[[cmds]] + +[cmds.find] +FunctionByName = "register_host_detail" + +[cmds.with] +Name = "add_host_detail" diff --git a/rust/feed/Cargo.toml b/rust/feed/Cargo.toml index 2742c4a10..df29161d5 100644 --- a/rust/feed/Cargo.toml +++ b/rust/feed/Cargo.toml @@ -16,3 +16,8 @@ sequoia-ipc = "0.30.1" sequoia-openpgp = { version ="1.16.1", default-features = false, features = ["crypto-openssl"] } anyhow = "1.0.75" tracing = "0.1.37" +glob = "0.3.1" +serde = { version = "1.0", features = ["derive"]} + +[dev-dependencies] +toml = "0.8.8" diff --git a/rust/feed/src/lib.rs b/rust/feed/src/lib.rs index 29fe16679..ef9269e7d 100644 --- a/rust/feed/src/lib.rs +++ b/rust/feed/src/lib.rs @@ -5,6 +5,7 @@ #![doc = include_str!("../README.md")] #![warn(missing_docs)] mod oid; +pub mod transpile; mod update; mod verify; @@ -17,3 +18,4 @@ pub use verify::Error as VerifyError; pub use verify::FileNameLoader; pub use verify::HashSumNameLoader; pub use verify::Hasher; +pub use verify::NaslFileFinder; diff --git a/rust/feed/src/oid/mod.rs b/rust/feed/src/oid/mod.rs index 21fbf03c7..d9cff65e2 100644 --- a/rust/feed/src/oid/mod.rs +++ b/rust/feed/src/oid/mod.rs @@ -37,7 +37,7 @@ where fn script_oid(stmt: &Statement) -> Option { match stmt { - Statement::Call(t, stms) => match t.category() { + Statement::Call(t, stms, _) => match t.category() { TokenCategory::Identifier(IdentifierType::Undefined(s)) => match s as &str { "script_oid" => stms.first().map(|x| x.to_string()), _ => None, @@ -52,8 +52,8 @@ where fn single(&self, key: String) -> Result { let code = self.loader.load(key.as_ref())?; for stmt in nasl_syntax::parse(&code) { - if let Statement::If(_, stmts, _) = stmt? { - if let Statement::Block(x) = &*stmts { + if let Statement::If(_, _, stmts, _, _) = stmt? { + if let Statement::Block(_, x, _) = &*stmts { for stmt in x { if let Some(oid) = Self::script_oid(stmt) { return Ok(oid); diff --git a/rust/feed/src/transpile/mod.rs b/rust/feed/src/transpile/mod.rs new file mode 100644 index 000000000..744b7f24b --- /dev/null +++ b/rust/feed/src/transpile/mod.rs @@ -0,0 +1,1158 @@ +//! Replaces the function calls within a feed. + +use std::error::Error; + +use nasl_syntax::Statement; + +use crate::{verify, NaslFileFinder}; + +/// Is used to find parameter by either name or index within a ReplaceCommand +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub enum FindParameter { + /// Find a parameter by name + Name(String), + /// Find a parameter by name + NameValue(String, String), + /// Find a parameter by index + Index(usize), +} + +/// Is used within Replacer to find a specific statement to operator on. +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub enum Find { + /// Finds a function by name. + /// + /// Uses the given string to identify functions by that name. + FunctionByName(String), + /// Finds a function by parameter. + FunctionByParameter(Vec), + /// Finds a function by name and parameter. + FunctionByNameAndParameter(String, Vec), +} + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +/// Describes parameter +pub enum Parameter { + /// Named parameter (e.g.: a: 1) + Named(String, String), + /// Parameter without a name + Anon(String), +} + +impl std::fmt::Display for Parameter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Parameter::Named(k, v) => write!(f, "NamedParameter({k}, {v})"), + Parameter::Anon(v) => write!(f, "Parameter({v})"), + } + } +} + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +/// Describes how to manipulate parameter +pub enum ParameterOperation { + /// Pushes a parameter to the end + Push(Parameter), + /// Adds parameter to the given index + Add(usize, Parameter), + /// Removes a parameter found by name + RemoveNamed(String), + /// Removes a parameter found by index + Remove(usize), + /// Removes a parameter found by index + RemoveAll, + /// Renames a parameter + Rename { + /// The value to be replaced + previous: String, + /// The new value + new: String, + }, +} +impl ParameterOperation { + /// Creates a rename operation + pub fn rename(previous: S, new: S) -> Self + where + S: Into, + { + Self::Rename { + previous: previous.into(), + new: new.into(), + } + } + /// Creates a remove by name operation + pub fn remove_named(name: S) -> Self + where + S: Into, + { + Self::RemoveNamed(name.into()) + } +} + +impl std::fmt::Display for ParameterOperation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ParameterOperation::Push(p) => write!(f, "Push {p}"), + ParameterOperation::Add(i, p) => write!(f, "Add {p} to index {i}"), + ParameterOperation::RemoveNamed(s) => write!(f, "Remove {s}"), + ParameterOperation::Remove(i) => write!(f, "Remove {i}"), + ParameterOperation::Rename { previous, new } => write!(f, "Rename {previous} to {new}"), + ParameterOperation::RemoveAll => write!(f, "Remove all parameter."), + } + } +} + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +/// Replace function operation +pub enum Replace { + /// Replaces name of a function + Name(String), + /// Replaces name of a function + Remove, + /// Replace parameter + Parameter(ParameterOperation), +} + +impl std::fmt::Display for Replace { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Replace::Name(name) => write!(f, "Replace: {name}"), + Replace::Parameter(p) => { + write!(f, "Replace parameter: {}", p) + } + Replace::Remove => write!(f, "Remove found statement"), + } + } +} + +trait Matcher { + fn matches(&self, s: &Statement) -> bool; +} + +#[derive(Clone, Debug)] +struct CallMatcher {} +impl Matcher for CallMatcher { + fn matches(&self, s: &Statement) -> bool { + // Although Exit and Include are handled differently they share the call nature and hence + // are treated equially. + matches!( + s, + &Statement::FunctionDeclaration(..) + | &Statement::Call(..) + | &Statement::Exit(..) + | &Statement::Include(..) + ) + } +} + +#[derive(Clone, Debug)] +struct FunctionNameMatcher<'a> { + name: Option<&'a str>, + parameter: Option<&'a [FindParameter]>, +} + +impl<'a> Matcher for FunctionNameMatcher<'a> { + fn matches(&self, s: &Statement) -> bool { + if !match s { + Statement::Exit(t, ..) + | Statement::Include(t, ..) + | Statement::Call(t, ..) + | Statement::FunctionDeclaration(_, t, ..) => match t.category() { + nasl_syntax::TokenCategory::Identifier(nasl_syntax::IdentifierType::Undefined( + aname, + )) => self.name.as_ref().map(|name| name == aname).unwrap_or(true), + nasl_syntax::TokenCategory::Identifier(nasl_syntax::IdentifierType::Exit) => { + self.name.map(|name| name == "exit").unwrap_or(true) + } + nasl_syntax::TokenCategory::Identifier(nasl_syntax::IdentifierType::Include) => { + self.name.map(|name| name == "include").unwrap_or(true) + } + + _ => false, + }, + _ => false, + } { + return false; + } + if self.parameter.is_none() { + return true; + } + let wanted = unsafe { self.parameter.unwrap_unchecked() }; + + let (named, anon) = match s { + Statement::Include(..) | Statement::Exit(..) => (vec![], 1), + Statement::Call(_, p, ..) => { + let named = p + .iter() + .filter_map(|p| match p { + Statement::NamedParameter(name, value) => { + Some((name.category().to_string(), value.to_string())) + } + _ => None, + }) + .collect(); + let anon = p.iter().filter(|p| p.is_returnable()).count(); + (named, anon) + } + Statement::FunctionDeclaration(_, _, p, _, _block) => { + let anon = { + // we don't know how many anon parameter an declared method is using. + // Theoretically we could guess by checking _block for _FC_ANON_ARGS and return + // the given indices number when available + // + // let fcta = _block.find(&|x| { + // use nasl_syntax::{IdentifierType as IT, Token, TokenCategory as TC}; + // matches!( + // x, + // Statement::Array( + // Token { + // category: TC::Identifier(IT::FCTAnonArgs), + // line_column: _, + // position: _ + // }, + // .. + // ) + // ) + // }); + // if fcta.len() > 0 { + // self.parameter.iter().flat_map(|x|x).find_map(|x|x.idx).unwrap_or_default() + // } else { + // 0 + // } + // However I think it is better to skip each search parameter for + // anon args when finding a function declaration as this limitation is more obvious + // than wrongly changed anon parameter. + 0 + }; + let named = p + .iter() + .filter_map(|p| match p { + Statement::Variable(name) => Some(name.category().to_string()), + _ => None, + }) + .map(|x| (x, "".to_owned())) + .collect(); + + (named, anon) + } + + _ => unreachable!("Should be validated before"), + }; + if wanted.len() != named.len() + anon { + return false; + } + for w in wanted { + let result = match w { + FindParameter::Name(name) => !named.iter().any(|n| &n.0 == name), + FindParameter::Index(x) => x != &anon, + FindParameter::NameValue(n, v) => !named.iter().any(|(k, ov)| k == n && ov == v), + }; + if result { + return false; + } + } + true + } +} + +impl Find { + /// Checks if statement matches the wanted search operation + pub fn matches(&self, s: &Statement) -> bool { + // FunctionNameMatcher { name, parameter }.matches(s) + let (name, parameter) = match self { + Find::FunctionByName(name) => (Some(name as &str), None), + Find::FunctionByParameter(x) => (None, Some(x as &[_])), + Find::FunctionByNameAndParameter(x, y) => (Some(x as &str), Some(y as &[_])), + }; + + FunctionNameMatcher { name, parameter }.matches(s) + } +} + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +/// Describes what should be replaces +pub struct ReplaceCommand { + /// The identifier to find + pub find: Find, + /// The replacement for found identifier + pub with: Replace, +} + +#[derive(Debug)] +/// Error cases on a replace operation +pub enum ReplaceError { + /// The replace operation is invalid on statement + Unsupported(Replace, Statement), +} +impl std::fmt::Display for ReplaceError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ReplaceError::Unsupported(op, s) => { + write!(f, "Operation {} not allowed on {}.", op, s) + } + } + } +} + +impl Error for ReplaceError {} + +/// Handles the inplace replacements +pub struct CodeReplacer { + // since the first position we need to add offset + offsets: Vec<(usize, i64)>, + code: String, + changed: bool, +} + +impl CodeReplacer { + fn range_with_offset(&self, r: &(usize, usize)) -> (usize, usize) { + let offset: i64 = self + .offsets + .iter() + .filter_map(|(pos, offset)| if pos < &r.0 { Some(offset) } else { None }) + .sum(); + let start = (r.0 as i64 + offset) as usize; + let end = (r.1 as i64 + offset) as usize; + (start, end) + } + + fn find_named_parameter<'a>(s: &'a Statement, wanted: &str) -> Option<&'a Statement> { + match s { + Statement::FunctionDeclaration(_, _, stmts, ..) | Statement::Call(_, stmts, ..) => { + use nasl_syntax::IdentifierType::Undefined; + use nasl_syntax::TokenCategory::Identifier; + for s in stmts { + match s { + Statement::Variable(t) | Statement::NamedParameter(t, _) => { + if let nasl_syntax::Token { + category: Identifier(Undefined(name)), + .. + } = t + { + if name == wanted { + return Some(s); + } + } + } + _ => {} + } + } + } + _ => {} + } + None + } + + fn replace_range_with_offset(&mut self, new: &str, position: &(usize, usize)) { + let new_pos = self.range_with_offset(position); + self.replace_range(&new_pos, new, position) + } + + fn replace_range( + &mut self, + (start, end): &(usize, usize), + new: &str, + (previous_start, previous_end): &(usize, usize), + ) { + self.code.replace_range(start..end, new); + self.changed = true; + let offset = new.len() as i64 - (previous_end - previous_start) as i64; + match offset.cmp(&0) { + std::cmp::Ordering::Less => { + self.offsets.push((*start, offset)); + } + std::cmp::Ordering::Equal => {} + std::cmp::Ordering::Greater => { + self.offsets.push((*previous_start, offset)); + } + } + } + fn replace_as_string(&mut self, s: &Statement, r: &Replace) -> Result<(), ReplaceError> { + match r { + Replace::Remove => { + self.replace_range_with_offset("", &s.position()); + Ok(()) + } + Replace::Name(name) => match s { + Statement::FunctionDeclaration(_, n, ..) + | Statement::Call(n, ..) + | Statement::Exit(n, ..) + | Statement::Include(n, ..) => { + self.replace_range_with_offset(name, &n.position); + Ok(()) + } + _ => Err(ReplaceError::Unsupported(r.clone(), s.clone())), + }, + Replace::Parameter(params) => { + let range = match s { + Statement::FunctionDeclaration(_, _, stmts, ..) + | Statement::Call(_, stmts, ..) => match &stmts[..] { + &[] => None, + [first, ref tail @ ..] => { + let first = first.range(); + let end = tail.last().map(|x| x.range().end).unwrap_or(first.end); + let start = first.start; + Some((start, end)) + } + }, + Statement::Exit(_, stmt, ..) | Statement::Include(_, stmt, ..) => { + Some(stmt.position()) + } + _ => return Err(ReplaceError::Unsupported(r.clone(), s.clone())), + }; + + match params { + ParameterOperation::Push(p) => self.push_parameter(s, p), + ParameterOperation::Add(i, p) => self.add_parameter(s, *i, p), + + ParameterOperation::Remove(i) => self.remove_indexed_parameter(s, *i), + ParameterOperation::RemoveNamed(wanted) => { + self.remove_named_parameter(s, wanted) + } + ParameterOperation::Rename { previous, new } => { + self.rename_parameter(s, previous, new) + } + ParameterOperation::RemoveAll => { + if let Some(range) = range { + self.replace_range_with_offset("", &range); + } + } + }; + + Ok(()) + } + } + } + + /// Replaces findings based on given replace within code and returns the result as String + /// + /// Spawns a Replacer that contains a copy of the source code and manipulates it iteratively + /// based on the order of the given commands. + pub fn replace(code: &str, replace: &[ReplaceCommand]) -> Result> { + let mut code = code.to_string(); + let mut cached_stmts = Vec::new(); + // We need to be aware of parameter changes otherwise it can bug out + // with the ordering of new parameter. + for r in replace { + let mut replacer = CodeReplacer { + offsets: Vec::with_capacity(replace.len()), + code: code.clone(), + changed: false, + }; + if cached_stmts.is_empty() { + cached_stmts = nasl_syntax::parse(&code).filter_map(|x| x.ok()).collect(); + } + + for s in cached_stmts.iter() { + let results = s.find(&|s| r.find.matches(s)); + for s in results { + replacer.replace_as_string(s, &r.with)?; + } + } + if replacer.changed { + cached_stmts.clear(); + code = replacer.code; + } + } + + Ok(code) + } + + fn push_parameter(&mut self, s: &Statement, p: &Parameter) { + fn calculate_fn_decl(p: &Parameter, is_only: bool) -> Option { + if let Parameter::Named(n, _) = p { + Some(if is_only { + n.to_owned() + } else { + format!(", {n}") + }) + } else { + None + } + } + + fn calculate_call(p: &Parameter, is_only: bool) -> Option { + Some(match (is_only, p) { + (true, Parameter::Named(n, v)) => { + format!("{n}: {v}") + } + (true, Parameter::Anon(s)) => s.to_string(), + (false, Parameter::Named(n, v)) => { + format!(", {n}: {v}") + } + (false, Parameter::Anon(s)) => { + format!(", {s}") + } + }) + } + + if let Some((pos, np)) = match s { + Statement::FunctionDeclaration(_, _, args, rp, _) => { + calculate_fn_decl(p, args.is_empty()).map(|x| (rp.position, x)) + } + Statement::Call(_, args, rp) => { + calculate_call(p, args.is_empty()).map(|x| (rp.position, x)) + } + _ => None, + } { + let npos = self.range_with_offset(&pos); + let before = &self.code[npos.0..npos.1]; + let param = format!("{np}{before}"); + self.replace_range(&npos, ¶m, &pos) + } + } + + fn add_parameter(&mut self, s: &Statement, i: usize, p: &Parameter) { + fn calculate_known_index(s: &Statement, p: &Parameter) -> Option { + match (matches!(s, Statement::Call(..)), p) { + (true, Parameter::Named(n, v)) => Some(format!("{n}: {v}, ")), + (true, Parameter::Anon(s)) => Some(format!("{s}, ")), + (false, Parameter::Named(n, _)) => Some(format!("{n}, ")), + (false, Parameter::Anon(_)) => None, + } + } + fn calculate_unknown_index( + s: &Statement, + p: &Parameter, + params: &[Statement], + ) -> Option { + let np = match (matches!(s, Statement::Call(..)), params.is_empty(), p) { + (true, true, Parameter::Named(n, v)) => { + format!("{n}: {v}") + } + + (true, false, Parameter::Named(n, v)) => { + format!(", {n}: {v}") + } + (true, false, Parameter::Anon(s)) => { + format!(", {s}") + } + (true, true, Parameter::Anon(s)) => s.to_owned(), + (false, true, Parameter::Named(n, _)) => n.to_owned(), + + (false, false, Parameter::Named(n, _)) => format!(", {n}"), + (false, _, Parameter::Anon(_)) => return None, + }; + Some(np) + } + match s { + Statement::FunctionDeclaration(_, _, params, end, _) + | Statement::Call(_, params, end) + if i <= params.len() || i == 0 => + { + let index_exits = params + .get(i) + .iter() + .flat_map(|s| s.as_token()) + .map(|t| t.position) + .next(); + let np = if index_exits.is_some() { + calculate_known_index(s, p) + } else { + calculate_unknown_index(s, p, params) + }; + + if let Some(s) = np { + let position = index_exits.unwrap_or(end.position); + let new_position = self.range_with_offset(&position); + let before = &self.code[new_position.0..new_position.1]; + self.replace_range(&new_position, &format!("{s}{before}"), &position); + } + } + _ => {} + } + } + + fn remove_parameter(&mut self, s: &Statement) { + let position = s.position(); + let (start, end) = self.range_with_offset(&position); + let new_position = { + let (count, last) = self + .code + .chars() + .skip(end) + .take_while(|x| x.is_whitespace() || x == &',' || x == &')') + .fold((0, '0'), |a, b| (a.0 + 1, b)); + // unless it is the last parameter + if last == ')' { + let (count, last) = self.code[0..start] + .chars() + .rev() + .take_while(|c| c.is_whitespace() || c == &',' || c == &'(') + .fold((0, '0'), |a, b| (a.0 + 1, b)); + let is_only_parameter = last == '('; + if is_only_parameter { + (start, end) + } else { + (start - count, end) + } + } else { + (start, end + count) + } + }; + + self.replace_range(&new_position, "", &new_position); + } + fn remove_indexed_parameter(&mut self, s: &Statement, i: usize) { + match s { + Statement::FunctionDeclaration(_, _, stmts, ..) | Statement::Call(_, stmts, ..) => { + if let Some(x) = stmts.get(i) { + self.remove_parameter(x); + } + } + _ => {} + } + } + + fn remove_named_parameter(&mut self, s: &Statement, wanted: &str) { + Self::find_named_parameter(s, wanted).iter().for_each(|s| { + self.remove_parameter(s); + }) + } + + fn rename_parameter(&mut self, s: &Statement, previous: &str, new: &str) { + Self::find_named_parameter(s, previous) + .iter() + .for_each(|s| { + let pos = s.as_token().map(|x| x.position).unwrap_or_default(); + self.replace_range_with_offset(new, &pos) + }) + } +} + +/// Finds all nasl and inc files of feed and executes given replace commands +pub struct FeedReplacer<'a> { + finder: NaslFileFinder, + replace: &'a [ReplaceCommand], +} + +impl<'a> FeedReplacer<'a> { + /// Creates a new FeedReplacer + pub fn new(root: S, replace: &'a [ReplaceCommand]) -> FeedReplacer<'_> + where + S: AsRef, + { + let finder = crate::NaslFileFinder::new(&root, false); + FeedReplacer { finder, replace } + } + fn replace( + &mut self, + path: Result, + ) -> Result, Box> { + let name = path?; + let code = nasl_syntax::load_non_utf8_path(&name)?; + let new_code = CodeReplacer::replace(&code, self.replace)?; + // otherwise we will transform the whole feed to utf-8 + if code != new_code { + Ok(Some((name, new_code))) + } else { + Ok(None) + } + } +} + +impl<'a> Iterator for FeedReplacer<'a> { + type Item = Result, Box>; + + fn next(&mut self) -> Option { + let path = self.finder.next()?; + Some(self.replace(path)) + } +} + +#[cfg(test)] +mod parsing { + use crate::transpile::FindParameter; + + use super::ReplaceCommand; + + pub fn generate_replace_commands() -> Vec { + // register_product(cpe:cpe, location:"/", port:port, service:"www"); + // register_product(location:"/", port:port, service:"world-wide-web") + vec![ + ReplaceCommand { + find: crate::transpile::Find::FunctionByNameAndParameter( + "register_product".into(), + vec![ + FindParameter::Name("cpe".into()), + FindParameter::Name("location".into()), + FindParameter::Name("port".into()), + FindParameter::NameValue("service".into(), "\"www\"".into()), + ], + ), + with: crate::transpile::Replace::Parameter( + crate::transpile::ParameterOperation::Push(crate::transpile::Parameter::Named( + "service_to_be".into(), + "\"world-wide-shop\"".into(), + )), + ), + }, + ReplaceCommand { + find: crate::transpile::Find::FunctionByNameAndParameter( + "register_product".into(), + vec![ + FindParameter::Name("cpe".into()), + FindParameter::Name("location".into()), + FindParameter::Name("port".into()), + FindParameter::Name("service".into()), + FindParameter::Name("service_to_be".into()), + ], + ), + with: crate::transpile::Replace::Parameter( + crate::transpile::ParameterOperation::RemoveNamed("service".into()), + ), + }, + ReplaceCommand { + find: crate::transpile::Find::FunctionByName("register_product".into()), + with: crate::transpile::Replace::Parameter( + crate::transpile::ParameterOperation::Rename { + previous: "service_to_be".to_string(), + new: "service".to_string(), + }, + ), + }, + ReplaceCommand { + find: crate::transpile::Find::FunctionByName("register_product".into()), + with: crate::transpile::Replace::Parameter( + crate::transpile::ParameterOperation::Rename { + previous: "cpe".into(), + new: "runtime_information".into(), + }, + ), + }, + ReplaceCommand { + find: crate::transpile::Find::FunctionByName("register_host_detail".into()), + with: crate::transpile::Replace::Name("hokus_pokus".into()), + }, + ReplaceCommand { + find: crate::transpile::Find::FunctionByName("script_xref".into()), + with: crate::transpile::Replace::Remove, + }, + ] + } + #[test] + fn to_toml() { + #[derive(serde::Deserialize, serde::Serialize)] + struct Wrapper { + cmds: Vec, + } + let options = generate_replace_commands(); + let w = Wrapper { cmds: options }; + let _config = toml::to_string_pretty(&w).unwrap(); + // use std::io::Write; + // write!( + // std::fs::File::create("/tmp/rename_example.toml").unwrap(), + // "{_config}" + // ) + // .unwrap(); + } +} +#[cfg(test)] +mod functions { + use super::*; + + macro_rules! parameter_check { + ($name:expr, $code:expr, $params:expr, $expected:expr) => {{ + let name = $name.to_string(); + let replaces = [ReplaceCommand { + find: Find::FunctionByName(name), + with: Replace::Parameter($params), + }]; + let result = CodeReplacer::replace($code, &replaces).unwrap(); + + assert_eq!(&result, $expected); + }}; + ($code:expr, $params:expr, $expected:expr) => {{ + if let Some((name, _)) = $code.rsplit_once("(") { + let name = name.replace("function ", ""); + parameter_check!(name, $code, $params, $expected) + } else { + panic!( + "expected {} to contain `(` so that it can be used as a function name", + $code + ); + } + }}; + } + + #[test] + fn parameter_test() { + parameter_check!( + "my_call", + "function my_call(a){};my_call();", + ParameterOperation::Add(0, Parameter::Named("test".into(), "test".into())), + "function my_call(test, a){};my_call(test: test);" + ); + } + + #[test] + fn add_parameter_on_fn_dclr() { + parameter_check!( + "function my_call(a, b){};", + ParameterOperation::Add(1, Parameter::Named("test".into(), "test".into())), + "function my_call(a, test, b){};" + ); + parameter_check!( + "function my_call(a){};", + ParameterOperation::Add(1, Parameter::Named("test".into(), "test".into())), + "function my_call(a, test){};" + ); + parameter_check!( + "function my_call(a){};", + ParameterOperation::Add(0, Parameter::Named("test".into(), "test".into())), + "function my_call(test, a){};" + ); + + // should not add when there insufficient previous parameter + parameter_check!( + "function my_call(a){};", + ParameterOperation::Add(2, Parameter::Named("test".into(), "test".into())), + "function my_call(a){};" + ); + // but should push on first parameter even when there were none + parameter_check!( + "function my_call(){};", + ParameterOperation::Add(0, Parameter::Named("test".into(), "test".into())), + "function my_call(test){};" + ); + } + + #[test] + fn push_parameter_side_effects() { + let code = r#" +if (admin_ports = get_kb_list("sophos/xg_firewall/http-admin/port")) { + foreach port (admin_ports) { + register_product(cpe: os_cpe1, location: location, port: port, service: "www"); + register_product(cpe: os_cpe2, location: location, port: port, service: "www"); + register_product(cpe: hw_cpe, location: location, port: port, service: "www"); + } +} + +if (user_ports = get_kb_list("sophos/xg_firewall/http-user/port")) { + foreach port (user_ports) { + register_product(cpe: os_cpe1, location: location, port: port, service: "www"); + register_product(cpe: os_cpe2, location: location, port: port, service: "www"); + register_product(cpe: hw_cpe, location: location, port: port, service: "www"); + } +} + "#; + + let expected = r#" +if (admin_ports = get_kb_list("sophos/xg_firewall/http-admin/port")) { + foreach port (admin_ports) { + register_product(runtime_information: os_cpe1, location: location, port: port, service: "world-wide-shop"); + register_product(runtime_information: os_cpe2, location: location, port: port, service: "world-wide-shop"); + register_product(runtime_information: hw_cpe, location: location, port: port, service: "world-wide-shop"); + } +} + +if (user_ports = get_kb_list("sophos/xg_firewall/http-user/port")) { + foreach port (user_ports) { + register_product(runtime_information: os_cpe1, location: location, port: port, service: "world-wide-shop"); + register_product(runtime_information: os_cpe2, location: location, port: port, service: "world-wide-shop"); + register_product(runtime_information: hw_cpe, location: location, port: port, service: "world-wide-shop"); + } +} + "#; + + let replaces = parsing::generate_replace_commands(); + let result = CodeReplacer::replace(code, &replaces).unwrap(); + assert_eq!(&result, expected); + } + + #[test] + fn remove_parameter_side_effects() { + let code = r#" + if(vers == "unknown") { + register_host_detail(name:"App", value:string("cpe:/a:aeromail:aeromail"), desc:SCRIPT_DESC); + } else { + register_host_detail(name:"App", value:string("cpe:/a:aeromail:aeromail:",vers), desc:SCRIPT_DESC2); + } + + register_host_detail(name:"App", value:string("cpe:/a:aeromail:aeromail:",vers), desc:SCRIPT_DESC2); + register_host_detail(name:"App", value:string("cpe:/a:aeromail:aeromail:",vers), desc:SCRIPT_DESC2); + function my_call(a){};my_call(); + info = string("AeroMail Version '");"#; + + let expected = r#" + if(vers == "unknown") { + register_host_detail(name:"App", value:string("cpe:/a:aeromail:aeromail")); + } else { + register_host_detail(name:"App", value:string("cpe:/a:aeromail:aeromail:",vers)); + } + + register_host_detail(name:"App", value:string("cpe:/a:aeromail:aeromail:",vers)); + register_host_detail(name:"App", value:string("cpe:/a:aeromail:aeromail:",vers)); + function my_call(test, a, aha){};my_call(test: test, aha: "soso"); + info = string("AeroMail Version '");"#; + + let replaces = [ + ReplaceCommand { + find: Find::FunctionByName("register_host_detail".to_string()), + with: Replace::Parameter(ParameterOperation::remove_named("desc")), + }, + ReplaceCommand { + find: Find::FunctionByName("my_call".to_string()), + with: Replace::Parameter(ParameterOperation::Add( + 0, + Parameter::Named("test".into(), "test".into()), + )), + }, + ReplaceCommand { + find: Find::FunctionByName("my_call".to_string()), + with: Replace::Parameter(ParameterOperation::Push(Parameter::Named( + "aha".into(), + "\"soso\"".into(), + ))), + }, + ]; + let result = CodeReplacer::replace(code, &replaces).unwrap(); + + assert_eq!(&result, expected); + } + + #[test] + fn remove_parameter_on_fn_dclr() { + parameter_check!( + "function my_call(a, b, c){};", + ParameterOperation::remove_named("a"), + "function my_call(b, c){};" + ); + parameter_check!( + "function my_call(a, b, c){};", + ParameterOperation::remove_named("c"), + "function my_call(a, b){};" + ); + parameter_check!( + "function my_call(a, b, c){};", + ParameterOperation::Remove(1), + "function my_call(a, c){};" + ); + } + + #[test] + fn remove_all_parameter_on_fn_dclr() { + parameter_check!( + "function my_call(a){};", + ParameterOperation::RemoveAll, + "function my_call(){};" + ); + } + + #[test] + fn rename_parameter_on_fn_dclr() { + parameter_check!( + "function my_call(a){};", + ParameterOperation::rename("a", "b"), + "function my_call(b){};" + ); + } + + #[test] + fn push_parameter_on_fn_declaration() { + parameter_check!( + "function my_call(){};", + ParameterOperation::Push(Parameter::Named("x".to_owned(), "'moep'".to_owned())), + "function my_call(x){};" + ); + parameter_check!( + "function my_call(a){};", + ParameterOperation::Push(Parameter::Named("x".to_owned(), "'moep'".to_owned())), + "function my_call(a, x){};" + ); + } + + #[test] + fn remove_all_parameter_on_call() { + parameter_check!("my_call(1);", ParameterOperation::RemoveAll, "my_call();"); + parameter_check!( + "my_call(1, 2, 4);", + ParameterOperation::RemoveAll, + "my_call();" + ); + parameter_check!( + "my_call(a: 1, 2, 4);", + ParameterOperation::RemoveAll, + "my_call();" + ); + } + + #[test] + fn rename_parameter_on_call() { + parameter_check!( + "my_call(a: 1, 2, 4);", + ParameterOperation::rename("a", "b"), + "my_call(b: 1, 2, 4);" + ); + } + + #[test] + fn remove_parameter_on_call() { + parameter_check!( + "my_call(a: 1, 2, 4);", + ParameterOperation::remove_named("a"), + "my_call(2, 4);" + ); + parameter_check!( + "my_call(a: 1, 2, 4);", + ParameterOperation::Remove(1), + "my_call(a: 1, 4);" + ); + } + + #[test] + fn push_parameter_on_call() { + parameter_check!( + "my_call();", + ParameterOperation::Push(Parameter::Named("x".to_owned(), "'moep'".to_owned())), + "my_call(x: 'moep');" + ); + parameter_check!( + "my_call(a: 1);", + ParameterOperation::Push(Parameter::Named("x".to_owned(), "'moep'".to_owned())), + "my_call(a: 1, x: 'moep');" + ); + } + + #[test] + fn add_parameter_on_call() { + parameter_check!( + "my_call(a: 1, 2, 4);", + ParameterOperation::Add(1, Parameter::Anon("test".into())), + "my_call(a: 1, test, 2, 4);" + ); + parameter_check!( + "my_call(a: 1);", + ParameterOperation::Add(1, Parameter::Anon("test".into())), + "my_call(a: 1, test);" + ); + parameter_check!( + "my_call(a: 1);", + ParameterOperation::Add(0, Parameter::Anon("test".into())), + "my_call(test, a: 1);" + ); + + // should not add when there insufficient previous parameter + parameter_check!( + "my_call(a: 1);", + ParameterOperation::Add(2, Parameter::Anon("test".into())), + "my_call(a: 1);" + ); + // but should push on first parameter even when there were none + parameter_check!( + "my_call();", + ParameterOperation::Add(0, Parameter::Anon("test".into())), + "my_call(test);" + ); + } + + #[test] + fn find_parameter() { + let code = r#" + function funker() { # Sometimes I think it is too much, because + return aha(_FCT_ANON_ARGS[0]); # my little secret is memory inefficiency. + } + + function funker(a, b) { # Sometimes I think it is too much, because + return funker(a: a + b); # my little secret is memory inefficiency. + } + function funker(a) { # Sometimes I think it is too much, because + return funker(a); # my little secret is memory inefficiency. + } + funker(a: 42); + funker(a: 42, b: 3); + aha(b: "lol"); + aha(b: 42); + "#; + let expected = r#" + function funker() { # Sometimes I think it is too much, because + return aha(_FCT_ANON_ARGS[0]); # my little secret is memory inefficiency. + } + + function funker(a, b) { # Sometimes I think it is too much, because + return funkerino(a: a + b); # my little secret is memory inefficiency. + } + function funkerino(a) { # Sometimes I think it is too much, because + return internal_funker(a); # my little secret is memory inefficiency. + } + funkerino(a: 42); + funker(a: 42, b: 3); + ; + aha(b: 42); + "#; + + let replaces = [ + ReplaceCommand { + find: Find::FunctionByNameAndParameter( + "funker".to_string(), + vec![FindParameter::Name("a".into())], + ), + with: Replace::Name("funkerino".to_string()), + }, + ReplaceCommand { + find: Find::FunctionByNameAndParameter( + "funker".to_string(), + vec![FindParameter::Index(1_usize)], + ), + with: Replace::Name("internal_funker".to_string()), + }, + ReplaceCommand { + find: Find::FunctionByNameAndParameter( + "aha".to_string(), + vec![FindParameter::NameValue("b".into(), "\"lol\"".into())], + ), + with: Replace::Remove, + }, + ]; + let result = CodeReplacer::replace(code, &replaces).unwrap(); + assert_eq!(result, expected.to_owned(),); + } + + #[test] + fn replace_name() { + let code = r#" + include("aha.inc"); + function test(a, b) { # Sometimes I think it is too much, because + return funker(a + b); # my little secret is memory inefficiency. + } + a = funker(1); + while (funker(1) == 1) { + if (funker(2) == 2) { + return funker(2); + } else { + for ( i = funker(3); i < funker(5) + funker(5); i + funker(1)) + exit(funker(10)); + } + } + b = test(a: 1, b: 2); + exit(42); + "#; + let replaces = [ + ReplaceCommand { + find: Find::FunctionByName("funker".to_string()), + with: Replace::Name("funkerino".to_string()), + }, + ReplaceCommand { + find: Find::FunctionByName("test".to_string()), + with: Replace::Name("tee".to_string()), + }, + ReplaceCommand { + find: Find::FunctionByName("include".to_string()), + with: Replace::Name("inklusion".to_string()), + }, + ReplaceCommand { + find: Find::FunctionByName("exit".to_string()), + with: Replace::Name("ausgang".to_string()), + }, + ]; + let result = CodeReplacer::replace(code, &replaces).unwrap(); + assert_eq!( + result, + //code.replace("funker", "funkerino") + code.replace("funker", "funkerino") + .replace("test", "tee") + .replace("include", "inklusion") + .replace("exit", "ausgang") + ); + } +} diff --git a/rust/feed/src/verify/mod.rs b/rust/feed/src/verify/mod.rs index 7de8f61f1..596474888 100644 --- a/rust/feed/src/verify/mod.rs +++ b/rust/feed/src/verify/mod.rs @@ -18,6 +18,7 @@ use std::{ use hex::encode; use nasl_interpreter::{AsBufReader, LoadError}; +use nasl_syntax::Loader; use sha2::{Digest, Sha256}; use openpgp::{ @@ -77,6 +78,7 @@ impl Display for Error { } } } +impl std::error::Error for Error {} struct VHelper { keyring: String, @@ -262,6 +264,7 @@ impl Hasher { /// Loads a given hashsums file and lazily verifies the loaded filename key of the sums file and verifies /// the hash within the sums file with an calculated hash of the found content. +// pub struct HashSumNameLoader<'a, R> { reader: &'a dyn AsBufReader, hasher: Hasher, @@ -342,3 +345,88 @@ where self.next_filename() } } + +/// Finds .nasl and .inc files within a given path. +/// +/// If the base is set it returns the relative path otherwise the absolute path. +/// When the relative path is returned it behaves exactly like a `HashSumNameLoader`. +pub struct NaslFileFinder { + base: Option, + paths: glob::Paths, +} + +impl NaslFileFinder { + /// Initializes NaslFileFinder based on a base path. + pub fn new

(base: P, relative: bool) -> Self + where + P: AsRef, + { + let paths = glob::glob(&format!("{}/**/*", base.as_ref())).expect("valid glob pattern"); + Self { + base: if relative { + Some(base.as_ref().to_string()) + } else { + None + }, + paths, + } + } +} + +impl Loader for NaslFileFinder { + fn load(&self, key: &str) -> Result { + let path = if let Some(base) = &self.base { + let path: std::path::PathBuf = base.into(); + path.join(key) + } else { + key.into() + }; + + // unfortunately nasl is still in iso-8859-1 + nasl_syntax::load_non_utf8_path(path.as_path()) + } + + fn root_path(&self) -> Result { + self.base.clone().ok_or_else(|| { + LoadError::Dirty("NaslFileFinder is not initialized with a base path".to_string()) + }) + } +} + +impl Iterator for NaslFileFinder { + type Item = Result; + + fn next(&mut self) -> Option { + loop { + match self.paths.next() { + Some(result) => { + let result = result.map_err(|e| { + Error::LoadError(LoadError::Dirty(format!("Not a valid file: {}", e,))) + }); + + match result { + Ok(f) => { + let filename = f.display().to_string(); + if filename.ends_with(".nasl") | filename.ends_with(".inc") { + let result = if let Some(base) = &self.base { + filename.trim_start_matches(base).trim_start_matches('/') + } else { + &filename + }; + return Some(Ok(result.to_owned())); + } + } + Err(e) => return Some(Err(e)), + } + } + None => return None, + } + } + } +} + +impl FileNameLoader for NaslFileFinder { + fn next_filename(&mut self) -> Option> { + self.next() + } +} diff --git a/rust/models/src/lib.rs b/rust/models/src/lib.rs index 01f8a4529..e663e70b3 100644 --- a/rust/models/src/lib.rs +++ b/rust/models/src/lib.rs @@ -38,7 +38,6 @@ where #[cfg(test)] //#[cfg(feature = "serde_support")] mod tests { - use crate::Target; use super::scan::Scan; @@ -177,7 +176,7 @@ mod tests { // tests that it doesn't panic when parsing the json let s: Scan = serde_json::from_str(json_str).unwrap(); - let b = bincode::encode_to_vec(&s, bincode::config::standard()).unwrap(); + let _b = bincode::encode_to_vec(s, bincode::config::standard()).unwrap(); //let _: Target = bincode::deserialize(&b).unwrap(); } } diff --git a/rust/nasl-builtin-std/src/lib.rs b/rust/nasl-builtin-std/src/lib.rs index 57ed70bc4..034aa451a 100644 --- a/rust/nasl-builtin-std/src/lib.rs +++ b/rust/nasl-builtin-std/src/lib.rs @@ -12,7 +12,7 @@ mod array; /// It contains all functions that are defined as a standard library function within NASL. /// /// It does not contain user defined functions, as they created on runtime while executing a nasl -/// script. This is handled within the [nasl_interpreter::Interpreter]. +/// script. This is handled within the `nasl_interpreter::Interpreter`. pub struct Std; impl> nasl_builtin_utils::NaslFunctionExecuter for Std { diff --git a/rust/nasl-c-lib/libgcrypt-sys/build.rs b/rust/nasl-c-lib/libgcrypt-sys/build.rs index 98d1149bd..9f0f016a5 100644 --- a/rust/nasl-c-lib/libgcrypt-sys/build.rs +++ b/rust/nasl-c-lib/libgcrypt-sys/build.rs @@ -1,4 +1,4 @@ -use std::{process::Command, env}; +use std::{env, process::Command}; fn main() { println!("cargo:rerun-if-changed=install-gcrypt.sh"); diff --git a/rust/nasl-c-lib/libgcrypt-sys/install-gcrypt.sh b/rust/nasl-c-lib/libgcrypt-sys/install-gcrypt.sh index e69f1c7b6..731b156e3 100755 --- a/rust/nasl-c-lib/libgcrypt-sys/install-gcrypt.sh +++ b/rust/nasl-c-lib/libgcrypt-sys/install-gcrypt.sh @@ -19,7 +19,7 @@ install_gnu() { [ ! -f "$NAME-$VERSION.tar.bz2" ] && curl --fail -O https://gnupg.org/ftp/gcrypt/$NAME/$NAME-$VERSION.tar.bz2 [ ! -d "$NAME-$VERSION" ] && tar -xf $NAME-$VERSION.tar.bz2 cd $NAME-$VERSION - ./configure --prefix $PREFIX --enable-static --disable-shared $HOST + ./configure --prefix $PREFIX --enable-static --with-pic --disable-shared $HOST make install } diff --git a/rust/nasl-cli/Cargo.toml b/rust/nasl-cli/Cargo.toml index 43ba3e10b..d1919f0e5 100644 --- a/rust/nasl-cli/Cargo.toml +++ b/rust/nasl-cli/Cargo.toml @@ -25,6 +25,8 @@ json-storage = {path = "../json-storage"} tracing = "0.1.37" tracing-subscriber = { version = "0.3.17" } serde_json = "1.0.96" +toml = "0.8.6" +serde = "1.0.190" [features] diff --git a/rust/nasl-cli/README.md b/rust/nasl-cli/README.md index b5698dc47..469b82182 100644 --- a/rust/nasl-cli/README.md +++ b/rust/nasl-cli/README.md @@ -23,12 +23,12 @@ The optional `--target, -t` option allows to set a host target to run the script When `-v` is set it is printing the statements to be executed as well as the returned NaslValue. As examples executing: `nasl-cli execute examples/hello.nasl` returns: -``` +```text Hello, world! ``` while executing `nasl-cli -v execute examples/hello.nasl` returns: -``` +```text > if (description == 1) {{ ... }} => Null > display(Hello, world!) @@ -214,7 +214,7 @@ As an example we assume that the data-objects feed is in `~/src/greenbone/data-o For that we need to execute: -``` +```text echo '{ "target": { "hosts": ["localhost"], "ports": [] }, "vts": [] }'| \ nasl-cli scan-config -i -p ~/src/greenbone/vulnerability-tests/nasl/common \ -l ~/src/greenbone/data-objects/content/22.04/port-lists/openvas-default-c7e03b6c-3bbe-11e1-a057-406186ea4fc5.xml \ @@ -226,7 +226,7 @@ Be aware that each call does a description run of the defined feed to gather the #### Usage -``` +```text Transforms a scan-config xml to a scan json for openvasd. When piping a scan json it is enriched with the scan-config xml and may the portlist otherwise it will print a scan json without target or credentials. diff --git a/rust/nasl-cli/src/error.rs b/rust/nasl-cli/src/error.rs index 79d1bdd64..5cf9d7e79 100644 --- a/rust/nasl-cli/src/error.rs +++ b/rust/nasl-cli/src/error.rs @@ -88,7 +88,9 @@ impl From for CliError { actual: _, key, } => key, - VerifyError::MissingKeyring => "Signature check enabled but missing keyring. Set GNUPGHOME environment variable.", + VerifyError::MissingKeyring => { + "Signature check enabled but missing keyring. Set GNUPGHOME environment variable." + } VerifyError::BadSignature(_) => "Bad signature", }; Self { @@ -98,6 +100,12 @@ impl From for CliError { } } +impl From for CliErrorKind { + fn from(value: VerifyError) -> Self { + Self::Corrupt(value.to_string()) + } +} + impl From for CliErrorKind { fn from(value: LoadError) -> Self { Self::LoadError(value) diff --git a/rust/nasl-cli/src/interpret/mod.rs b/rust/nasl-cli/src/interpret/mod.rs index a375b1ee4..e76cc588d 100644 --- a/rust/nasl-cli/src/interpret/mod.rs +++ b/rust/nasl-cli/src/interpret/mod.rs @@ -62,7 +62,7 @@ impl Run { } } } - Err(_) => todo!(), + Err(e) => Err(e.into()), } } None => Err(LoadError::NotFound(script.to_string()).into()), diff --git a/rust/nasl-cli/src/main.rs b/rust/nasl-cli/src/main.rs index 5ee09c873..da07ddeea 100644 --- a/rust/nasl-cli/src/main.rs +++ b/rust/nasl-cli/src/main.rs @@ -85,6 +85,29 @@ enum FeedAction { /// When it is skipped it will be obtained via `openvas -s` path: Option, }, + /// Transpiles the feed based on a given ruleset. + /// + Transpile { + /// The path to the NASL plugins. + /// + path: PathBuf, + /// Describes the rules for changing the rules. + /// + /// The rules describe how to find a certain element and how to replace it. + /// Currently only toml in the following format is supported: + /// ```text + /// [[cmds]] + /// + /// [cmds.find] + /// FunctionByName = "register_host_detail" + /// + /// [cmds.with] + /// Name = "add_host_detail" + /// ``` + rules: PathBuf, + /// Prints the changed file names + verbose: bool, + }, } trait RunAction { @@ -136,6 +159,53 @@ impl RunAction<()> for FeedAction { Err(e) => Err(e), } } + FeedAction::Transpile { + path, + rules, + verbose, + } => { + #[derive(serde::Deserialize, serde::Serialize)] + struct Wrapper { + cmds: Vec, + } + + // TODO add from impl + let rules = std::fs::read_to_string(rules).unwrap(); + let rules: Wrapper = toml::from_str(&rules).unwrap(); + let rules = rules.cmds; + let base = path.to_str().unwrap_or_default(); + for r in feed::transpile::FeedReplacer::new(base, &rules) { + let name = r.unwrap(); + if let Some((name, content)) = name { + use std::io::Write; + let mut f = std::fs::OpenOptions::new() + .write(true) + .truncate(true) + .open(&name) + .map_err(|e| { + let kind = + CliErrorKind::Corrupt(format!("unable to open {name}: {e}")); + CliError { + filename: name.clone(), + kind, + } + })?; + f.write_all(content.as_bytes()).map_err(|e| { + let kind = + CliErrorKind::Corrupt(format!("unable to write {name}: {e}")); + CliError { + filename: name.clone(), + kind, + } + })?; + + if *verbose { + eprintln!("changed {name}"); + } + } + } + Ok(()) + } } } } @@ -312,6 +382,13 @@ fn main() { .arg(arg!(-p --path "Path to the feed.") .required(false) .value_parser(value_parser!(PathBuf))) ) + .subcommand(Command::new("transpile") + .about("Transforms each nasl script and inc file based on the given rules.") + .arg(arg!(-p --path "Path to the feed.") .required(false) + .value_parser(value_parser!(PathBuf))) + .arg(arg!(-r --rules "Path to transpiler rules.").required(true) + .value_parser(value_parser!(PathBuf))) + ) ) .subcommand( Command::new("syntax") @@ -376,6 +453,25 @@ When piping a scan json it is enriched with the scan-config xml and may the port action: FeedAction::Transform { path }, } } + + Some(("transpile", args)) => { + let path = match args.get_one("path").cloned() { + Some(x) => x, + None => unreachable!("path is set to required"), + }; + let rules = match args.get_one("rules").cloned() { + Some(x) => x, + None => unreachable!("rules is set to required"), + }; + Commands::Feed { + action: FeedAction::Transpile { + path, + rules, + verbose: verbose > 0, + }, + } + // ah + } _ => unreachable!("subcommand_required prevents None"), }, Some(("syntax", args)) => { diff --git a/rust/nasl-interpreter/README.md b/rust/nasl-interpreter/README.md index cafb86e15..0ee4627b5 100644 --- a/rust/nasl-interpreter/README.md +++ b/rust/nasl-interpreter/README.md @@ -9,12 +9,12 @@ Each resolve call will result in a [NaslValue](./src/naslvalue.rs) or an [Interp An interpreter requires: -- register: &'a mut Register - to hold all the available data like functions or variables -- context: &'a Context - to hold all configuration regarding to the current context: - - key: &str - is used to identify the key-value store. It is usually either an OID or a filename (on description runs). - - storage: &dyn storage - the storage implementation to be used, - - loader: &'a dyn Loader - is used to load script dependencies on `include`, - - logger: Box - the default logger +- `register: &'a mut Register` - to hold all the available data like functions or variables +- `context: &'a Context` - to hold all configuration regarding to the current context: + - `key: &str` - is used to identify the key-value store. It is usually either an OID or a filename (on description runs). + - `storage: &dyn storage` - the storage implementation to be used, + - `loader: &'a dyn Loader` - is used to load script dependencies on `include`, + - `logger: Box` - the default logger ## Example diff --git a/rust/nasl-interpreter/src/assign.rs b/rust/nasl-interpreter/src/assign.rs index 0fdd1e1cb..12cdc2e82 100644 --- a/rust/nasl-interpreter/src/assign.rs +++ b/rust/nasl-interpreter/src/assign.rs @@ -200,10 +200,10 @@ where let (key, lookup) = { match left { Variable(ref token) => (Self::identifier(token)?, None), - Array(ref token, Some(stmt)) => { + Array(ref token, Some(stmt), _) => { (Self::identifier(token)?, Some(self.resolve(stmt)?)) } - Array(ref token, None) => (Self::identifier(token)?, None), + Array(ref token, None, _) => (Self::identifier(token)?, None), _ => return Err(InterpretError::unsupported(left, "Array or Variable")), } }; diff --git a/rust/nasl-interpreter/src/declare.rs b/rust/nasl-interpreter/src/declare.rs index 244dd6a29..561480523 100644 --- a/rust/nasl-interpreter/src/declare.rs +++ b/rust/nasl-interpreter/src/declare.rs @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: GPL-2.0-or-later -use nasl_syntax::{DeclareScope, Statement, Token, TokenCategory}; +use nasl_syntax::{Statement, Token, TokenCategory}; use crate::{error::InterpretError, interpreter::InterpretResult, Interpreter}; use nasl_builtin_utils::ContextType; @@ -46,16 +46,24 @@ where } pub(crate) trait DeclareVariableExtension { - fn declare_variable(&mut self, scope: &DeclareScope, stmts: &[Statement]) -> InterpretResult; + fn declare_variable(&mut self, scope: &Token, stmts: &[Statement]) -> InterpretResult; } impl<'a, K> DeclareVariableExtension for Interpreter<'a, K> { - fn declare_variable(&mut self, scope: &DeclareScope, stmts: &[Statement]) -> InterpretResult { + fn declare_variable(&mut self, scope: &Token, stmts: &[Statement]) -> InterpretResult { let mut add = |key: &str| { let value = ContextType::Value(NaslValue::Null); - match scope { - DeclareScope::Global => self.registrat.add_global(key, value), - DeclareScope::Local => self.registrat.add_local(key, value), + match scope.category() { + TokenCategory::Identifier(nasl_syntax::IdentifierType::GlobalVar) => { + self.registrat.add_global(key, value) + } + TokenCategory::Identifier(nasl_syntax::IdentifierType::LocalVar) => { + self.registrat.add_local(key, value) + } + _ => unreachable!( + "{} should not be identified as an declare statement", + scope.category() + ), } }; diff --git a/rust/nasl-interpreter/src/error.rs b/rust/nasl-interpreter/src/error.rs index cd643a521..85e98b5de 100644 --- a/rust/nasl-interpreter/src/error.rs +++ b/rust/nasl-interpreter/src/error.rs @@ -173,7 +173,7 @@ impl InterpretError { self.origin .as_ref() .and_then(|stmt| stmt.as_token()) - .map(|x| x.position) + .map(|x| x.line_column) .unwrap_or_default() } diff --git a/rust/nasl-interpreter/src/include.rs b/rust/nasl-interpreter/src/include.rs index 35f5e6213..e8797098e 100644 --- a/rust/nasl-interpreter/src/include.rs +++ b/rust/nasl-interpreter/src/include.rs @@ -55,7 +55,9 @@ mod tests { .cloned() .ok_or_else(|| LoadError::NotFound(String::default())) } - fn root_path(&self) -> Result { todo!() } + fn root_path(&self) -> Result { + Ok(String::default()) + } } #[test] diff --git a/rust/nasl-interpreter/src/interpreter.rs b/rust/nasl-interpreter/src/interpreter.rs index 5c4374a7b..947cddce1 100644 --- a/rust/nasl-interpreter/src/interpreter.rs +++ b/rust/nasl-interpreter/src/interpreter.rs @@ -84,7 +84,7 @@ where /// Interprets a Statement pub fn resolve(&mut self, statement: &Statement) -> InterpretResult { match statement { - Array(name, position) => { + Array(name, position, _) => { let name = Self::identifier(name)?; let val = self .registrat @@ -112,26 +112,28 @@ where } } } - Exit(stmt) => { + Exit(_, stmt, _) => { let rc = self.resolve(stmt)?; match rc { NaslValue::Number(rc) => Ok(NaslValue::Exit(rc)), _ => Err(InterpretError::unsupported(stmt, "numeric")), } } - Return(stmt) => { + Return(_, stmt) => { let rc = self.resolve(stmt)?; Ok(NaslValue::Return(Box::new(rc))) } - Include(inc) => self.include(inc), - NamedParameter(_, _) => todo!(), - For(assignment, condition, update, body) => { + Include(_, inc, _) => self.include(inc), + NamedParameter(..) => { + unreachable!("named parameter should not be an executable statement.") + } + For(_, assignment, condition, update, body) => { self.for_loop(assignment, condition, update, body) } - While(condition, body) => self.while_loop(condition, body), - Repeat(body, condition) => self.repeat_loop(body, condition), - ForEach(variable, iterable, body) => self.for_each_loop(variable, iterable, body), - FunctionDeclaration(name, args, exec) => self.declare_function(name, args, exec), + While(_, condition, body) => self.while_loop(condition, body), + Repeat(_, body, condition) => self.repeat_loop(body, condition), + ForEach(_, variable, iterable, body) => self.for_each_loop(variable, iterable, body), + FunctionDeclaration(_, name, args, _, exec) => self.declare_function(name, args, exec), Primitive(token) => TryFrom::try_from(token).map_err(|e: TokenCategory| e.into()), Variable(token) => { let name: NaslValue = TryFrom::try_from(token)?; @@ -143,7 +145,7 @@ where } } } - Call(name, arguments) => self.call(name, arguments), + Call(name, arguments, _) => self.call(name, arguments), Declare(scope, stmts) => self.declare_variable(scope, stmts), // array creation Parameter(x) => { @@ -156,7 +158,7 @@ where } Assign(cat, order, left, right) => self.assign(cat, order, left, right), Operator(sign, stmts) => self.operator(sign, stmts), - If(condition, if_block, else_block) => match self.resolve(condition) { + If(_, condition, if_block, _, else_block) => match self.resolve(condition) { Ok(value) => { if bool::from(value) { return self.resolve(if_block); @@ -167,7 +169,7 @@ where } Err(err) => Err(err), }, - Block(blocks) => { + Block(_, blocks, _) => { self.registrat.create_child(HashMap::default()); for stmt in blocks { match self.resolve(stmt) { @@ -191,10 +193,18 @@ where Ok(NaslValue::Null) } NoOp(_) => Ok(NaslValue::Null), - EoF => todo!(), - AttackCategory(cat) => Ok(NaslValue::AttackCategory(*cat)), - Continue => Ok(NaslValue::Continue), - Break => Ok(NaslValue::Break), + EoF => Ok(NaslValue::Null), + AttackCategory(t) => { + match t.category() { + TokenCategory::Identifier(IdentifierType::ACT(cat)) => Ok(NaslValue::AttackCategory(*cat)), + _ => unreachable!("AttackCategory must have ACT token but got {t:?}, this is an bug within the lexer.") + + } + + + }, + Continue(_) => Ok(NaslValue::Continue), + Break(_) => Ok(NaslValue::Break), } .map_err(|e| { if e.origin.is_none() { diff --git a/rust/nasl-interpreter/tests/description.rs b/rust/nasl-interpreter/tests/description.rs index 13a4d64a2..a608569d3 100644 --- a/rust/nasl-interpreter/tests/description.rs +++ b/rust/nasl-interpreter/tests/description.rs @@ -12,8 +12,10 @@ impl Loader for NoOpLoader { fn load(&self, _: &str) -> Result { Ok(String::default()) } - - fn root_path(&self) -> Result { todo!() } + + fn root_path(&self) -> Result { + Ok(String::default()) + } } #[cfg(test)] diff --git a/rust/nasl-syntax/src/cursor.rs b/rust/nasl-syntax/src/cursor.rs index 6e6613410..4ae39fbf9 100644 --- a/rust/nasl-syntax/src/cursor.rs +++ b/rust/nasl-syntax/src/cursor.rs @@ -15,6 +15,8 @@ pub struct Cursor<'a> { /// is needed to calculate the length when e.g. tokenizing initial_len: usize, chars: Chars<'a>, + // extend line with byte position and save all previous lines + // this is handy for lookups that are not line specific. line: usize, col: usize, } diff --git a/rust/nasl-syntax/src/error.rs b/rust/nasl-syntax/src/error.rs index 238cd8c02..5c3ee15ab 100644 --- a/rust/nasl-syntax/src/error.rs +++ b/rust/nasl-syntax/src/error.rs @@ -64,6 +64,7 @@ impl SyntaxError { /// syntax_error!( /// ErrorKind::UnexpectedToken(Token { /// category: TokenCategory::UnknownSymbol, +/// line_column: (42, 42), /// position: (42, 42), /// }) /// ); @@ -85,6 +86,7 @@ macro_rules! syntax_error { /// use nasl_syntax::{unexpected_token, Token, TokenCategory}; /// unexpected_token!(Token { /// category: TokenCategory::UnknownSymbol, +/// line_column: (42, 42), /// position: (42, 42), /// }); /// ``` @@ -144,6 +146,7 @@ macro_rules! unclosed_statement { /// unclosed_token!(Token { /// category: TokenCategory::UnknownSymbol, /// position: (42, 42), +/// line_column: (42, 42), /// }); /// ``` #[macro_export] diff --git a/rust/nasl-syntax/src/grouping_extension.rs b/rust/nasl-syntax/src/grouping_extension.rs index ae9bb3396..487cc825b 100644 --- a/rust/nasl-syntax/src/grouping_extension.rs +++ b/rust/nasl-syntax/src/grouping_extension.rs @@ -15,7 +15,7 @@ pub(crate) trait Grouping { /// Parses (...) fn parse_paren(&mut self, token: Token) -> Result; /// Parses {...} - fn parse_block(&mut self, token: Token) -> Result; + fn parse_block(&mut self, token: Token) -> Result<(Token, Statement), SyntaxError>; /// General Grouping parsing. Is called within prefix_extension. fn parse_grouping(&mut self, token: Token) -> Result<(End, Statement), SyntaxError>; } @@ -23,10 +23,9 @@ pub(crate) trait Grouping { impl<'a> Lexer<'a> { fn parse_brace(&mut self, token: Token) -> Result { let (end, right) = self.parse_comma_group(Category::RightBrace)?; - if !end { - Err(unclosed_token!(token)) - } else { - Ok(Statement::Parameter(right)) + match end { + End::Done(_end) => Ok(Statement::Parameter(right)), + End::Continue => Err(unclosed_token!(token)), } } } @@ -49,19 +48,19 @@ impl<'a> Grouping for Lexer<'a> { } } - fn parse_block(&mut self, token: Token) -> Result { + fn parse_block(&mut self, kw: Token) -> Result<(Token, Statement), SyntaxError> { let mut results = vec![]; while let Some(token) = self.peek() { if token.category() == &Category::RightCurlyBracket { self.token(); - return Ok(Statement::Block(results)); + return Ok((token.clone(), Statement::Block(kw, results, token))); } let (end, stmt) = self.statement(0, &|cat| cat == &Category::Semicolon)?; if end.is_done() && !matches!(stmt, Statement::NoOp(_)) { results.push(stmt); } } - Err(unclosed_token!(token)) + Err(unclosed_token!(kw)) } fn parse_grouping(&mut self, token: Token) -> Result<(End, Statement), SyntaxError> { @@ -69,7 +68,7 @@ impl<'a> Grouping for Lexer<'a> { Category::LeftParen => self.parse_paren(token).map(|stmt| (End::Continue, stmt)), Category::LeftCurlyBracket => self .parse_block(token) - .map(|stmt| (End::Done(Category::LeftCurlyBracket), stmt)), + .map(|(end, stmt)| (End::Done(end), stmt)), Category::LeftBrace => self.parse_brace(token).map(|stmt| (End::Continue, stmt)), _ => Err(unexpected_token!(token)), } @@ -78,14 +77,8 @@ impl<'a> Grouping for Lexer<'a> { #[cfg(test)] mod test { - use crate::{ - parse, - token::{Category, Token}, - {AssignOrder, Statement}, - }; + use crate::{parse, Statement}; - use crate::IdentifierType::Undefined; - use Category::*; use Statement::*; fn result(code: &str) -> Statement { @@ -94,9 +87,8 @@ mod test { #[test] fn variables() { - assert_eq!( - result( - r" + let stmt = result( + r" { a = b + 1; b = a - --c; @@ -104,69 +96,8 @@ mod test { d = 23; } } - " - ), - Block(vec![ - Assign( - Equal, - AssignOrder::AssignReturn, - Box::new(Variable(Token { - category: Identifier(Undefined("a".to_owned())), - position: (3, 17) - })), - Box::new(Operator( - Plus, - vec![ - Variable(Token { - category: Identifier(Undefined("b".to_owned())), - position: (3, 21) - }), - Primitive(Token { - category: Number(1), - position: (3, 25) - }) - ] - )) - ), - Assign( - Equal, - AssignOrder::AssignReturn, - Box::new(Variable(Token { - category: Identifier(Undefined("b".to_owned())), - position: (4, 17) - },)), - Box::new(Operator( - Minus, - vec![ - Variable(Token { - category: Identifier(Undefined("a".to_owned())), - position: (4, 21) - }), - Assign( - MinusMinus, - AssignOrder::AssignReturn, - Box::new(Variable(Token { - category: Identifier(Undefined("c".to_owned())), - position: (4, 27) - },)), - Box::new(NoOp(None)) - ) - ] - )) - ), - Block(vec![Assign( - Equal, - AssignOrder::AssignReturn, - Box::new(Variable(Token { - category: Identifier(Undefined("d".to_owned())), - position: (6, 20) - },)), - Box::new(Primitive(Token { - category: Number(23), - position: (6, 24) - })) - )]) - ]) + ", ); + assert!(matches!(stmt, Block(..))); } } diff --git a/rust/nasl-syntax/src/infix_extension.rs b/rust/nasl-syntax/src/infix_extension.rs index c199b26e1..6a2371710 100644 --- a/rust/nasl-syntax/src/infix_extension.rs +++ b/rust/nasl-syntax/src/infix_extension.rs @@ -105,7 +105,9 @@ impl<'a> Infix for Lexer<'a> { // when the right side is a parameter list than it is an array let lhs = { match rhs { - Statement::Parameter(_) => Statement::Array(var.clone(), None), + Statement::Parameter(..) => { + Statement::Array(var.clone(), None, None) + } _ => lhs, } }; @@ -116,7 +118,7 @@ impl<'a> Infix for Lexer<'a> { Box::new(rhs), ) } - Statement::Array(_, _) => Statement::Assign( + Statement::Array(_, _, _) => Statement::Assign( category, AssignOrder::AssignReturn, Box::new(lhs), @@ -222,13 +224,6 @@ mod test { } } - fn token(category: Category, start: usize, end: usize) -> Token { - Token { - category, - position: (start, end), - } - } - fn result(code: &str) -> Statement { crate::parse(code).next().unwrap().unwrap() } @@ -274,11 +269,22 @@ mod test { use Category::*; use Statement::*; fn expected(category: Category, shift: usize) -> Statement { + let a = Token { + category: Identifier(Undefined("a".to_owned())), + line_column: (1, 1), + position: (0, 1), + }; + let no = Token { + category: Number(1), + line_column: (1, 6 + shift), + position: (5 + shift, 6 + shift), + }; + Assign( category, AssignOrder::AssignReturn, - Box::new(Variable(token(Identifier(Undefined("a".to_owned())), 1, 1))), - Box::new(Primitive(token(Number(1), 1, 6 + shift))), + Box::new(Variable(a)), + Box::new(Primitive(no)), ) } assert_eq!(result("a += 1;"), expected(PlusEqual, 0)); @@ -296,19 +302,18 @@ mod test { use Category::*; use Statement::*; fn expected(category: Category, shift: i32) -> Statement { - Operator( - category, - vec![ - Variable(Token { - category: Identifier(Undefined("a".to_owned())), - position: (1, 1), - }), - Primitive(Token { - category: Data(vec![49]), - position: (1, (6 + shift) as usize), - }), - ], - ) + let a = Token { + category: Identifier(Undefined("a".to_owned())), + line_column: (1, 1), + position: (0, 1), + }; + let data = Token { + category: Data(vec![49]), + line_column: (1, (6 + shift) as usize), + position: ((5 + shift) as usize, (8 + shift) as usize), + }; + + Operator(category, vec![Variable(a), Primitive(data)]) } assert_eq!(result("a !~ '1';"), expected(BangTilde, 0)); assert_eq!(result("a =~ '1';"), expected(EqualTilde, 0)); @@ -325,19 +330,18 @@ mod test { #[test] fn logical_operator() { fn expected(category: Category, shift: usize) -> Statement { - Operator( - category, - vec![ - Variable(Token { - category: Identifier(Undefined("a".to_owned())), - position: (1, 1), - }), - Primitive(Token { - category: Number(1), - position: (1, 6 + shift), - }), - ], - ) + let a = Token { + category: Identifier(Undefined("a".to_owned())), + line_column: (1, 1), + position: (0, 1), + }; + + let no = Token { + category: Number(1), + line_column: (1, 6 + shift), + position: (5 + shift, 6 + shift), + }; + Operator(category, vec![Variable(a), Primitive(no)]) } assert_eq!(result("a && 1;"), expected(AmpersandAmpersand, 0)); assert_eq!(result("a || 1;"), expected(PipePipe, 0)); @@ -345,52 +349,68 @@ mod test { #[test] fn assignment() { + let a = Token { + category: Identifier(Undefined("a".to_owned())), + line_column: (1, 1), + position: (0, 1), + }; + let no = Token { + category: Number(1), + line_column: (1, 5), + position: (4, 5), + }; assert_eq!( result("a = 1;"), Assign( Category::Equal, AssignOrder::AssignReturn, - Box::new(Variable(token(Identifier(Undefined("a".to_owned())), 1, 1))), - Box::new(Primitive(Token { - category: Number(1), - position: (1, 5) - })) + Box::new(Variable(a)), + Box::new(Primitive(no)) ) ); + let a = Token { + category: Identifier(Undefined("a".to_owned())), + line_column: (1, 2), + position: (1, 2), + }; + let no = Token { + category: Number(1), + line_column: (1, 6), + position: (5, 6), + }; + assert_eq!( result("(a = 1);"), Assign( Category::Equal, AssignOrder::AssignReturn, - Box::new(Variable(token(Identifier(Undefined("a".to_owned())), 1, 2))), - Box::new(Primitive(Token { - category: Number(1), - position: (1, 6) - })) + Box::new(Variable(a)), + Box::new(Primitive(no)) ) ); } #[test] fn repeat_call() { + let x = Token { + category: Identifier(Undefined("x".to_owned())), + line_column: (1, 1), + position: (0, 1), + }; + let end = Token { + category: RightParen, + line_column: (1, 3), + position: (2, 3), + }; + let no = Token { + category: Number(2), + line_column: (1, 7), + position: (6, 7), + }; + assert_eq!( result("x() x 2;"), - Operator( - X, - vec![ - Call( - Token { - category: Identifier(Undefined("x".to_owned())), - position: (1, 1) - }, - vec![] - ), - Primitive(Token { - category: Number(2), - position: (1, 7) - }) - ] - ) + Operator(X, vec![Call(x, vec![], end,), Primitive(no)]) ); } } diff --git a/rust/nasl-syntax/src/keyword_extension.rs b/rust/nasl-syntax/src/keyword_extension.rs index 6c71594d7..97ed1746f 100644 --- a/rust/nasl-syntax/src/keyword_extension.rs +++ b/rust/nasl-syntax/src/keyword_extension.rs @@ -9,7 +9,7 @@ use crate::{ token::{Category, IdentifierType, Token}, unclosed_statement, unclosed_token, unexpected_end, unexpected_statement, unexpected_token, variable_extension::CommaGroup, - DeclareScope, Statement, + Statement, }; pub(crate) trait Keywords { @@ -22,7 +22,7 @@ pub(crate) trait Keywords { } impl<'a> Lexer<'a> { - fn parse_declaration(&mut self, scope: DeclareScope) -> Result<(End, Statement), SyntaxError> { + fn parse_declaration(&mut self, token: Token) -> Result<(End, Statement), SyntaxError> { let (end, params) = self.parse_comma_group(Category::Semicolon)?; if end == End::Continue { return Err(unexpected_end!("expected a finished statement.")); @@ -33,21 +33,21 @@ impl<'a> Lexer<'a> { { return Err(unexpected_statement!(errstmt.clone())); } - let result = Statement::Declare(scope, params); - Ok((End::Done(Category::Semicolon), result)) - } - fn parse_if(&mut self) -> Result<(End, Statement), SyntaxError> { - let token = self.token().ok_or_else(|| unexpected_end!("if parsing"))?; - let condition = match token.category() { - Category::LeftParen => self.parse_paren(token.clone()), - _ => Err(unexpected_token!(token.clone())), + let result = Statement::Declare(token, params); + Ok((end, result)) + } + fn parse_if(&mut self, kw: Token) -> Result<(End, Statement), SyntaxError> { + let ptoken = self.token().ok_or_else(|| unexpected_end!("if parsing"))?; + let condition = match ptoken.category() { + Category::LeftParen => self.parse_paren(ptoken.clone()), + _ => Err(unexpected_token!(ptoken.clone())), }? .as_returnable_or_err()?; let (end, body) = self.statement(0, &|cat| cat == &Category::Semicolon)?; if end == End::Continue { - return Err(unclosed_token!(token)); + return Err(unclosed_token!(ptoken)); } - let r#else: Option = { + let (ekw, r#else, end) = { match self.peek() { Some(token) => match token.category() { Category::Identifier(IdentifierType::Else) => { @@ -56,16 +56,22 @@ impl<'a> Lexer<'a> { if end == End::Continue { return Err(unexpected_statement!(stmt)); } - Some(stmt) + (Some(token), Some(stmt), end) } - _ => None, + _ => (None, None, end), }, - None => None, + None => (None, None, end), } }; Ok(( - End::Done(Category::Semicolon), - Statement::If(Box::new(condition), Box::new(body), r#else.map(Box::new)), + end, + Statement::If( + kw, + Box::new(condition), + Box::new(body), + ekw, + r#else.map(Box::new), + ), )) } @@ -80,43 +86,49 @@ impl<'a> Lexer<'a> { } } - fn parse_call_return_params(&mut self) -> Result { + fn parse_call_return_params(&mut self) -> Result<(End, Statement), SyntaxError> { self.jump_to_left_parenthesis()?; let (end, parameter) = self.statement(0, &|cat| cat == &Category::RightParen)?; let parameter = parameter.as_returnable_or_err()?; - if end.is_done() { - Ok(parameter) - } else { - Err(unexpected_end!("exit")) + match end { + End::Done(end) => Ok((End::Done(end), parameter)), + End::Continue => Err(unexpected_end!("exit")), } } - fn parse_exit(&mut self) -> Result<(End, Statement), SyntaxError> { - let parameter = self.parse_call_return_params()?; + fn parse_exit(&mut self, token: Token) -> Result<(End, Statement), SyntaxError> { + // TODO maybe refactor to reuse function call and hindsight verification + let (end, parameter) = self.parse_call_return_params()?; let (_, should_be_semicolon) = self.statement(0, &|cat| cat == &Category::Semicolon)?; - if matches!(should_be_semicolon, Statement::NoOp(_)) { - Ok(( - End::Done(Category::Semicolon), - Statement::Exit(Box::new(parameter)), - )) - } else { - Err(unexpected_statement!(should_be_semicolon)) + + if !matches!(should_be_semicolon, Statement::NoOp(_)) { + // exit must be followed by ; nothing else + return Err(unexpected_statement!(should_be_semicolon)); + } + match end { + End::Done(end) => Ok(( + End::Done(end.clone()), + Statement::Exit(token, Box::new(parameter), end), + )), + End::Continue => Err(unexpected_statement!(parameter)), } } - fn parse_include(&mut self) -> Result<(End, Statement), SyntaxError> { - let parameter = self.parse_call_return_params()?; + fn parse_include(&mut self, token: Token) -> Result<(End, Statement), SyntaxError> { + // TODO maybe refactor to reuse function call and hindsight verification + let (end, parameter) = self.parse_call_return_params()?; let (_, should_be_semicolon) = self.statement(0, &|cat| cat == &Category::Semicolon)?; - if matches!(should_be_semicolon, Statement::NoOp(_)) { - match parameter { - Statement::Primitive(_) | Statement::Variable(_) | Statement::Array(_, _) => Ok(( - End::Done(Category::RightParen), - Statement::Include(Box::new(parameter)), - )), - _ => Err(unexpected_statement!(parameter)), - } - } else { - Err(unexpected_statement!(should_be_semicolon)) + + if !matches!(should_be_semicolon, Statement::NoOp(_)) { + // exit must be followed by ; nothing else + return Err(unexpected_statement!(should_be_semicolon)); + } + match end { + End::Done(end) => Ok(( + End::Done(end.clone()), + Statement::Include(token, Box::new(parameter), end), + )), + End::Continue => Err(unexpected_statement!(parameter)), } } @@ -136,10 +148,11 @@ impl<'a> Lexer<'a> { if !matches!(paren.category(), Category::LeftParen) { return Err(unexpected_token!(paren)); } - let (end, parameter) = self.parse_comma_group(Category::RightParen)?; - if !end { - return Err(unclosed_token!(token)); - } + let (gend, parameter) = self.parse_comma_group(Category::RightParen)?; + let parameter_end_token = match gend { + End::Done(t) => t, + End::Continue => return Err(unclosed_token!(token)), + }; let block = self .token() @@ -147,50 +160,58 @@ impl<'a> Lexer<'a> { if !matches!(block.category(), Category::LeftCurlyBracket) { return Err(unexpected_token!(block)); } - let block = self.parse_block(block)?; + let (end, block) = self.parse_block(block)?; Ok(( - End::Done(Category::RightCurlyBracket), - Statement::FunctionDeclaration(id, parameter, Box::new(block)), + End::Done(end), + Statement::FunctionDeclaration( + token, + id, + parameter, + parameter_end_token, + Box::new(block), + ), )) } - fn parse_return(&mut self) -> Result<(End, Statement), SyntaxError> { - let token = self.peek(); - if let Some(token) = token { - if matches!(token.category(), Category::Semicolon) { + fn parse_return(&mut self, token: Token) -> Result<(End, Statement), SyntaxError> { + if let Some(sc) = self.peek() { + if matches!(sc.category(), Category::Semicolon) { self.token(); return Ok(( - End::Done(Category::Semicolon), - Statement::Return(Box::new(Statement::NoOp(Some(token)))), + End::Done(sc.clone()), + Statement::Return(token, Box::new(Statement::NoOp(Some(sc)))), )); } } let (end, parameter) = self.statement(0, &|cat| cat == &Category::Semicolon)?; let parameter = parameter.as_returnable_or_err()?; if let End::Done(cat) = end { - Ok((End::Done(cat), Statement::Return(Box::new(parameter)))) + Ok(( + End::Done(cat), + Statement::Return(token, Box::new(parameter)), + )) } else { Err(unexpected_end!("exit")) } } - fn parse_continue(&mut self) -> Result<(End, Statement), SyntaxError> { + fn parse_continue(&mut self, kw: Token) -> Result<(End, Statement), SyntaxError> { let token = self.peek(); if let Some(token) = token { if matches!(token.category(), Category::Semicolon) { self.token(); - return Ok((End::Done(Category::Semicolon), Statement::Continue)); + return Ok((End::Done(token), Statement::Continue(kw))); } else { return Err(unexpected_token!(token)); } } Err(unexpected_end!("exit")) } - fn parse_break(&mut self) -> Result<(End, Statement), SyntaxError> { + fn parse_break(&mut self, kw: Token) -> Result<(End, Statement), SyntaxError> { let token = self.peek(); if let Some(token) = token { if matches!(token.category(), Category::Semicolon) { self.token(); - return Ok((End::Done(Category::Semicolon), Statement::Break)); + return Ok((End::Done(token), Statement::Break(kw))); } else { return Err(unexpected_token!(token)); } @@ -202,13 +223,14 @@ impl<'a> Lexer<'a> { match e.kind() { crate::ErrorKind::UnexpectedToken(k) => unclosed_token!(Token { category: Category::LeftParen, - position: k.position + line_column: k.line_column, + position: k.position, }), _ => e, } } - fn parse_for(&mut self) -> Result<(End, Statement), SyntaxError> { + fn parse_for(&mut self, kw: Token) -> Result<(End, Statement), SyntaxError> { self.jump_to_left_parenthesis()?; let (end, assignment) = self.statement(0, &|c| c == &Category::Semicolon)?; if !matches!( @@ -230,19 +252,29 @@ impl<'a> Lexer<'a> { // no update statement provided Some(Token { category: Category::RightParen, - position: _, + line_column, + position, }) => { self.token(); - (End::Done(Category::RightParen), Statement::NoOp(None)) + ( + End::Done(Token { + category: Category::RightParen, + line_column, + position, + }), + Statement::NoOp(None), + ) } _ => self .statement(0, &|c| c == &Category::RightParen) .map_err(Self::map_syntax_error_to_unclosed_left_paren)?, }; - if !matches!(end, End::Done(Category::RightParen)) { + if !matches!(end.category(), Some(Category::RightParen)) { + let ut = update.as_token(); return Err(unclosed_token!(Token { category: Category::LeftParen, - position: update.as_token().map_or_else(|| (0, 0), |t| t.position) + line_column: ut.map_or_else(|| (0, 0), |t| t.line_column), + position: ut.map_or_else(|| (0, 0), |t| t.position) })); } let (end, body) = self.statement(0, &|c| c == &Category::Semicolon)?; @@ -250,6 +282,7 @@ impl<'a> Lexer<'a> { End::Done(cat) => Ok(( End::Done(cat), Statement::For( + kw, Box::new(assignment), Box::new(condition), Box::new(update), @@ -265,21 +298,23 @@ impl<'a> Lexer<'a> { let (end, condition) = self .statement(0, &|c| c == &Category::RightParen) .map_err(Self::map_syntax_error_to_unclosed_left_paren)?; - if !matches!(end, End::Done(Category::RightParen)) { + let ct = condition.as_token(); + if !matches!(end.category(), Some(Category::RightParen)) { return Err(unclosed_token!(Token { category: Category::LeftParen, - position: condition.as_token().map_or_else(|| (0, 0), |t| t.position) + line_column: ct.map_or_else(|| (0, 0), |t| t.line_column), + position: ct.map_or_else(|| (0, 0), |t| t.position), })); } let condition = condition.as_returnable_or_err()?; let (end, body) = self.statement(0, &|c| c == &Category::Semicolon)?; - if !end { - return Err(unclosed_token!(token)); + match end { + End::Done(end) => Ok(( + End::Done(end), + Statement::While(token, Box::new(condition), Box::new(body)), + )), + End::Continue => Err(unclosed_token!(token)), } - Ok(( - End::Done(Category::Semicolon), - Statement::While(Box::new(condition), Box::new(body)), - )) } fn parse_repeat(&mut self, token: Token) -> Result<(End, Statement), SyntaxError> { let (end, body) = self.statement(0, &|c| c == &Category::Semicolon)?; @@ -287,25 +322,25 @@ impl<'a> Lexer<'a> { if !end { return Err(unclosed_token!(token)); } - let until: Statement = { + let (until, end): (Statement, Token) = { match self.token() { Some(token) => match token.category() { Category::Identifier(IdentifierType::Until) => { let (end, stmt) = self.statement(0, &|cat| cat == &Category::Semicolon)?; - if !end { - return Err(unclosed_token!(token)); + match end { + End::Done(end) => Ok((stmt, end)), + End::Continue => return Err(unclosed_token!(token)), } - Ok(stmt) } _ => Err(unexpected_token!(token)), }, None => Err(unexpected_end!("in repeat")), }? - .as_returnable_or_err()? }; + let until = until.as_returnable_or_err()?; Ok(( - End::Done(Category::Semicolon), - Statement::Repeat(Box::new(body), Box::new(until)), + End::Done(end), + Statement::Repeat(token, Box::new(body), Box::new(until)), )) } @@ -329,13 +364,12 @@ impl<'a> Lexer<'a> { }? }; let (end, block) = self.statement(0, &|cat| cat == &Category::Semicolon)?; - if !end { - Err(unclosed_token!(token)) - } else { - Ok(( - End::Done(Category::Semicolon), - Statement::ForEach(variable, Box::new(r#in), Box::new(block)), - )) + match end { + End::Done(end) => Ok(( + End::Done(end), + Statement::ForEach(token, variable, Box::new(r#in), Box::new(block)), + )), + End::Continue => Err(unclosed_token!(token)), } } fn parse_fct_anon_args(&mut self, keyword: Token) -> Result<(End, Statement), SyntaxError> { @@ -345,16 +379,15 @@ impl<'a> Lexer<'a> { self.token(); let (end, lookup) = self.statement(0, &|c| c == &Category::RightBrace)?; let lookup = lookup.as_returnable_or_err()?; - if end == End::Continue { - Err(unclosed_token!(token)) - } else { - Ok(( + match end { + End::Done(end) => Ok(( End::Continue, - Statement::Array(keyword, Some(Box::new(lookup))), - )) + Statement::Array(keyword, Some(Box::new(lookup)), Some(end)), + )), + End::Continue => Err(unclosed_token!(token)), } } - _ => Ok((End::Continue, Statement::Array(keyword, None))), + _ => Ok((End::Continue, Statement::Array(keyword, None, None))), }, None => Err(unexpected_end!("in fct_anon_args")), } @@ -368,29 +401,26 @@ impl<'a> Keywords for Lexer<'a> { token: Token, ) -> Result<(End, Statement), SyntaxError> { match keyword { - IdentifierType::For => self.parse_for(), + IdentifierType::For => self.parse_for(token), IdentifierType::ForEach => self.parse_foreach(token), - IdentifierType::If => self.parse_if(), + IdentifierType::If => self.parse_if(token), IdentifierType::Else => Err(unexpected_token!(token)), // handled in if IdentifierType::While => self.parse_while(token), IdentifierType::Repeat => self.parse_repeat(token), IdentifierType::Until => Err(unexpected_token!(token)), // handled in repeat - IdentifierType::LocalVar => self.parse_declaration(DeclareScope::Local), - IdentifierType::GlobalVar => self.parse_declaration(DeclareScope::Global), + IdentifierType::LocalVar | IdentifierType::GlobalVar => self.parse_declaration(token), IdentifierType::Null => Ok((End::Continue, Statement::Primitive(token))), - IdentifierType::Return => self.parse_return(), - IdentifierType::Include => self.parse_include(), - IdentifierType::Exit => self.parse_exit(), + IdentifierType::Return => self.parse_return(token), + IdentifierType::Include => self.parse_include(token), + IdentifierType::Exit => self.parse_exit(token), IdentifierType::FCTAnonArgs => self.parse_fct_anon_args(token), IdentifierType::True => Ok((End::Continue, Statement::Primitive(token))), IdentifierType::False => Ok((End::Continue, Statement::Primitive(token))), IdentifierType::Function => self.parse_function(token), - IdentifierType::ACT(category) => { - Ok((End::Continue, Statement::AttackCategory(category))) - } + IdentifierType::ACT(_) => Ok((End::Continue, Statement::AttackCategory(token))), IdentifierType::Undefined(_) => Err(unexpected_token!(token)), - IdentifierType::Continue => self.parse_continue(), - IdentifierType::Break => self.parse_break(), + IdentifierType::Continue => self.parse_continue(token), + IdentifierType::Break => self.parse_break(token), } } } @@ -401,10 +431,9 @@ mod test { use crate::{ parse, token::{Category, IdentifierType, Token}, - AssignOrder, DeclareScope, SyntaxError, + Statement, }; - use crate::IdentifierType::Undefined; use crate::Statement::*; use crate::TokenCategory::*; @@ -414,116 +443,90 @@ mod test { .next() .unwrap() .unwrap(); - assert_eq!( - actual, - If( - Box::new(Variable(Token { - category: Identifier(Undefined("description".to_owned())), - position: (1, 5) - })), - Box::new(Call( - Token { - category: Identifier(Undefined("script_oid".to_owned())), - position: (1, 18) - }, - vec![Primitive(Token { - category: Data(vec![49]), - position: (1, 29) - })] - )), - Some(Box::new(Call( - Token { - category: Identifier(Undefined("display".to_owned())), - position: (1, 40) - }, - vec![Primitive(Token { - category: Data(vec![104, 105]), - position: (1, 48) - })] - ))) - ) - ); - parse("if( version[1] ) report += '\nVersion: ' + version[1];") + match actual { + If(_, _, _, Some(_), Some(_)) => {} + _ => unreachable!("{actual} must be if with else stmt."), + } + + let actual = parse("if( version[1] ) report += '\nVersion: ' + version[1];") .next() .unwrap() .unwrap(); + match actual { + If(_, _, _, None, None) => {} + _ => unreachable!("{actual} must be if without else stmt."), + } } #[test] fn if_block() { let actual = parse("if (description) { ; }").next().unwrap().unwrap(); - assert_eq!( - actual, - If( - Box::new(Variable(Token { - category: Identifier(Undefined("description".to_owned())), - position: (1, 5) - })), - Box::new(Block(vec![])), - None - ) - ); + match actual { + If(_, _, b, _, _) => match *b { + Block(_, v, _) => { + assert_eq!(v, vec![]); + } + _ => unreachable!("{b} must be a block stmt."), + }, + _ => unreachable!("{actual} must be an if stmt."), + } } #[test] - fn local_var() -> Result<(), SyntaxError> { - let expected = |scope: DeclareScope, offset: usize| { - Declare( - scope, - vec![ - Variable(Token { - category: Identifier(Undefined("a".to_owned())), - position: (1, 11 + offset), - }), - Variable(Token { - category: Identifier(Undefined("b".to_owned())), - position: (1, 14 + offset), - }), - Variable(Token { - category: Identifier(Undefined("c".to_owned())), - position: (1, 17 + offset), - }), - ], - ) + fn local_var() { + let expected = |actual: Statement, scope: Category| match actual { + Declare(a, vars) => { + assert_eq!(a.category(), &scope); + assert_eq!(vars.len(), 3); + } + _ => unreachable!("{actual} must be an declare stmt."), }; - assert_eq!( + expected( parse("local_var a, b, c;").next().unwrap().unwrap(), - expected(DeclareScope::Local, 0) + Category::Identifier(IdentifierType::LocalVar), ); - assert_eq!( + expected( parse("global_var a, b, c;").next().unwrap().unwrap(), - expected(DeclareScope::Global, 1) + Category::Identifier(IdentifierType::GlobalVar), ); - Ok(()) } #[test] fn null() { - assert_eq!( - parse("NULL;").next().unwrap().unwrap(), + match parse("NULL;").next().unwrap().unwrap() { Primitive(Token { category: Identifier(IdentifierType::Null), - position: (1, 1) - }) - ); + line_column: _, + position: _, + }) => { + // correct + } + actual => unreachable!("{actual} must be a primitive stmt."), + } } #[test] fn boolean() { - assert_eq!( - parse("TRUE;").next().unwrap().unwrap(), + match parse("TRUE;").next().unwrap().unwrap() { Primitive(Token { category: Identifier(IdentifierType::True), - position: (1, 1) - }) - ); - assert_eq!( - parse("FALSE;").next().unwrap().unwrap(), + line_column: _, + position: _, + }) => { + // correct + } + actual => unreachable!("{actual} must be a primitive stmt."), + } + match parse("FALSE;").next().unwrap().unwrap() { Primitive(Token { category: Identifier(IdentifierType::False), - position: (1, 1) - }) - ); + line_column: _, + position: _, + }) => { + // correct + } + actual => unreachable!("{actual} must be a primitive stmt."), + } } #[test] fn exit() { @@ -536,7 +539,10 @@ mod test { ]; for call in test_cases { assert!( - matches!(parse(&format!("{call};")).next().unwrap().unwrap(), Exit(_),), + matches!( + parse(&format!("{call};")).next().unwrap().unwrap(), + Exit(..), + ), "{}", call ); @@ -556,7 +562,7 @@ mod test { assert!( matches!( parse(&format!("{call};")).next().unwrap().unwrap(), - Return(_), + Return(..), ), "{}", call @@ -567,27 +573,21 @@ mod test { #[test] fn for_loop() { let code = "for (i = 0; i < 10; i++) display('hi');"; - assert!(matches!( - parse(code).next().unwrap().unwrap(), - For(_, _, _, _) - )); + assert!(matches!(parse(code).next().unwrap().unwrap(), For(..))); let code = "for (i = 0; i < 10; ) i = 10;"; - assert!(matches!( - parse(code).next().unwrap().unwrap(), - For(_, _, _, _) - )) + assert!(matches!(parse(code).next().unwrap().unwrap(), For(..))) } #[test] fn while_loop() { let code = "while (TRUE) ;"; - assert!(matches!(parse(code).next().unwrap().unwrap(), While(_, _))) + assert!(matches!(parse(code).next().unwrap().unwrap(), While(..))) } #[test] fn repeat_loop() { let code = "repeat ; until 1 == 1;"; - assert!(matches!(parse(code).next().unwrap().unwrap(), Repeat(_, _))) + assert!(matches!(parse(code).next().unwrap().unwrap(), Repeat(..))) } #[test] @@ -600,7 +600,7 @@ mod test { assert!( matches!( parse(&format!("{call};")).next().unwrap().unwrap(), - ForEach(_, _, _), + ForEach(..), ), "{}", call @@ -612,92 +612,54 @@ mod test { fn include() { assert!(matches!( parse("include('test.inc');").next().unwrap().unwrap(), - Include(_) + Include(..) )) } #[test] fn function() { - assert_eq!( + assert!(matches!( parse("function register_packages( buf ) { return 1; }") .next() .unwrap() .unwrap(), - FunctionDeclaration( - Token { - category: Identifier(Undefined("register_packages".to_owned())), - position: (1, 10) - }, - vec![Variable(Token { - category: Identifier(Undefined("buf".to_owned())), - position: (1, 29) - })], - Box::new(Block(vec![Return(Box::new(Primitive(Token { - category: Number(1), - position: (1, 44) - })))])) - ) - ); - assert_eq!( + FunctionDeclaration(..) + )); + assert!(matches!( parse("function register_packages( ) { return 1; }") .next() .unwrap() .unwrap(), - FunctionDeclaration( - Token { - category: Identifier(Undefined("register_packages".to_owned())), - position: (1, 10) - }, - vec![], - Box::new(Block(vec![Return(Box::new(Primitive(Token { - category: Number(1), - position: (1, 40) - })))])) - ) - ); + FunctionDeclaration(..) + )); } #[test] fn fct_anon_args() { - assert_eq!( - parse("arg1 = _FCT_ANON_ARGS[0];").next().unwrap(), - Ok(Assign( - Category::Equal, - AssignOrder::AssignReturn, - Box::new(Variable(Token { - category: Category::Identifier(Undefined("arg1".to_owned())), - position: (1, 1) - },)), - Box::new(Array( - Token { - category: Category::Identifier(IdentifierType::FCTAnonArgs), - position: (1, 8), - }, - Some(Box::new(Primitive(Token { - category: Category::Number(0), - position: (1, 23) - }))) - )) - )) - ); - assert_eq!( - parse("arg1 = _FCT_ANON_ARGS;").next().unwrap(), - Ok(Assign( - Category::Equal, - AssignOrder::AssignReturn, - Box::new(Variable(Token { - category: Category::Identifier(Undefined("arg1".to_owned())), - position: (1, 1) - },)), - Box::new(Array( - Token { - category: Category::Identifier(IdentifierType::FCTAnonArgs), - position: (1, 8), - }, - None - )) - )) - ); + match parse("_FCT_ANON_ARGS[0];").next().unwrap().unwrap() { + Array( + Token { + category: Category::Identifier(IdentifierType::FCTAnonArgs), + line_column: _, + position: _, + }, + Some(_), + Some(_), + ) => {} + actual => unreachable!("{actual} must be an array."), + } + match parse("_FCT_ANON_ARGS;").next().unwrap().unwrap() { + Array( + Token { + category: Category::Identifier(IdentifierType::FCTAnonArgs), + line_column: _, + position: _, + }, + None, + None, + ) => {} + actual => unreachable!("{actual} must be an array."), + } } #[test] diff --git a/rust/nasl-syntax/src/lexer.rs b/rust/nasl-syntax/src/lexer.rs index d54e3fe1b..85f8745f6 100644 --- a/rust/nasl-syntax/src/lexer.rs +++ b/rust/nasl-syntax/src/lexer.rs @@ -17,12 +17,14 @@ use crate::{ /// Is used to parse Token to Statement pub struct Lexer<'a> { + // TODO: change to iterator of Token instead of Tokenizer + // to allopw statements of a Vec tokenizer: Tokenizer<'a>, } #[derive(Clone, Debug, PartialEq, Eq)] pub enum End { - Done(Category), + Done(Token), Continue, } @@ -33,6 +35,13 @@ impl End { End::Continue => false, } } + + pub fn category(&self) -> Option { + match self { + End::Done(t) => Some(t.category.clone()), + End::Continue => None, + } + } } impl Not for End { @@ -92,14 +101,11 @@ impl<'a> Lexer<'a> { return Err(unexpected_token!(token)); } if abort(token.category()) { - return Ok(( - End::Done(Category::UnknownSymbol), - Statement::NoOp(Some(token)), - )); + return Ok((End::Done(token.clone()), Statement::NoOp(Some(token)))); } self.prefix_statement(token, abort) }) - .unwrap_or(Ok((End::Done(Category::UnknownSymbol), Statement::EoF)))?; + .unwrap_or(Ok((End::Done(Token::unexpected_none()), Statement::EoF)))?; match state { End::Continue => {} end => return Ok((end, left)), @@ -109,7 +115,7 @@ impl<'a> Lexer<'a> { while let Some(token) = self.peek() { if abort(token.category()) { self.token(); - end_statement = End::Done(token.category().clone()); + end_statement = End::Done(token.clone()); break; } let op = @@ -117,8 +123,8 @@ impl<'a> Lexer<'a> { if self.needs_postfix(op.clone()) { let (end, stmt) = self - .postfix_statement(op, token, left) - .expect("needs postfix should have been validated before")?; + .postfix_statement(op, token.clone(), left) + .ok_or_else(|| unexpected_token!(token.clone()))??; self.token(); left = stmt; if let End::Done(cat) = end { diff --git a/rust/nasl-syntax/src/lib.rs b/rust/nasl-syntax/src/lib.rs index b23cf93c5..f0565f887 100644 --- a/rust/nasl-syntax/src/lib.rs +++ b/rust/nasl-syntax/src/lib.rs @@ -72,24 +72,29 @@ mod tests { vec![ Token { category: Category::Identifier(IdentifierType::LocalVar), - position: (1, 1) + line_column: (1, 1), + position: (0, 9) }, Token { category: Category::Identifier(IdentifierType::Undefined("hello".to_owned())), - position: (1, 11) + line_column: (1, 11), + position: (10, 15) }, Token { category: Category::Equal, - position: (1, 17) + line_column: (1, 17), + position: (16, 17) }, Token { category: Category::Data("World!".as_bytes().to_vec()), - position: (1, 19) + line_column: (1, 19), + position: (18, 26) }, Token { category: Category::Semicolon, - position: (1, 27) - } + line_column: (1, 27), + position: (26, 27) + }, ] ); } @@ -108,11 +113,13 @@ mod tests { AssignOrder::AssignReturn, Box::new(Variable(Token { category: Identifier(IdentifierType::Undefined("a".to_owned())), - position: (1, 1) + line_column: (1, 1), + position: (0, 1), },)), Box::new(Primitive(Token { category: Number(23), - position: (1, 5) + line_column: (1, 5), + position: (4, 6), })) )), Ok(Assign( @@ -120,11 +127,13 @@ mod tests { AssignOrder::AssignReturn, Box::new(Variable(Token { category: Identifier(IdentifierType::Undefined("b".to_owned())), - position: (1, 8) + line_column: (1, 8), + position: (7, 8), },)), Box::new(Primitive(Token { category: Number(1), - position: (1, 12) + line_column: (1, 12), + position: (11, 12), })) )) ] diff --git a/rust/nasl-syntax/src/loader.rs b/rust/nasl-syntax/src/loader.rs index 70360de82..93fde1af6 100644 --- a/rust/nasl-syntax/src/loader.rs +++ b/rust/nasl-syntax/src/loader.rs @@ -35,6 +35,8 @@ impl Display for LoadError { } } +impl std::error::Error for LoadError {} + /// Loads the content of the path to String by parsing each byte to a character. /// /// Unfortunately the feed is not completely written in utf8 enforcing us to parse the content diff --git a/rust/nasl-syntax/src/naslvalue.rs b/rust/nasl-syntax/src/naslvalue.rs index 8a983e35b..55f197f24 100644 --- a/rust/nasl-syntax/src/naslvalue.rs +++ b/rust/nasl-syntax/src/naslvalue.rs @@ -254,7 +254,7 @@ impl From for NaslValue { Array(x) => Self::Array(x.into_iter().map(Self::from).collect()), Dict(x) => Self::Dict(x.into_iter().map(|(k, v)| (k, Self::from(v))).collect()), Boolean(x) => Self::Boolean(x), - Null => todo!(), + Null => Self::Null, } } } diff --git a/rust/nasl-syntax/src/postfix_extension.rs b/rust/nasl-syntax/src/postfix_extension.rs index 5b06b1c3a..1031c4afa 100644 --- a/rust/nasl-syntax/src/postfix_extension.rs +++ b/rust/nasl-syntax/src/postfix_extension.rs @@ -42,12 +42,12 @@ impl<'a> Lexer<'a> { Box::new(Statement::NoOp(None)), ), ))), - Statement::Array(token, resolver) => Some(Ok(( + Statement::Array(token, resolver, end) => Some(Ok(( End::Continue, Statement::Assign( assign, AssignOrder::ReturnAssign, - Box::new(Statement::Array(token, resolver)), + Box::new(Statement::Array(token, resolver, end)), Box::new(Statement::NoOp(None)), ), ))), @@ -108,7 +108,8 @@ mod test { vec![ Primitive(Token { category: Number(1), - position: (1, 1), + line_column: (1, 1), + position: (0, 1), }), Operator( Star, @@ -118,13 +119,15 @@ mod test { AssignOrder::ReturnAssign, Box::new(Variable(Token { category: Identifier(Undefined("a".to_owned())), - position: (1, 5), + line_column: (1, 5), + position: (4, 5), })), Box::new(NoOp(None)), ), Primitive(Token { category: Number(1), - position: (1, 11), + line_column: (1, 11), + position: (10, 11), }), ], ), @@ -145,12 +148,19 @@ mod test { Box::new(Array( Token { category: Identifier(Undefined("a".to_owned())), - position: (1, 1), + line_column: (1, 1), + position: (0, 1), }, Some(Box::new(Primitive(Token { category: Number(1), - position: (1, 3), + line_column: (1, 3), + position: (2, 3), }))), + Some(Token { + category: RightBrace, + line_column: (1, 4), + position: (3, 4), + }), )), Box::new(NoOp(None)), ) diff --git a/rust/nasl-syntax/src/prefix_extension.rs b/rust/nasl-syntax/src/prefix_extension.rs index dec45c2b8..e3b1807f4 100644 --- a/rust/nasl-syntax/src/prefix_extension.rs +++ b/rust/nasl-syntax/src/prefix_extension.rs @@ -50,10 +50,10 @@ impl<'a> Lexer<'a> { Box::new(Statement::Variable(value)), Box::new(Statement::NoOp(None)), )), - (_, Statement::Array(token, resolver)) => Ok(Statement::Assign( + (_, Statement::Array(token, resolver, end)) => Ok(Statement::Assign( assign, AssignOrder::AssignReturn, - Box::new(Statement::Array(token, resolver)), + Box::new(Statement::Array(token, resolver, end)), Box::new(Statement::NoOp(None)), )), _ => Err(unexpected_token!(token)), @@ -86,7 +86,7 @@ impl<'a> Prefix for Lexer<'a> { .map(|stmt| (Continue, stmt)), Operation::Assign(_) => Err(unexpected_token!(token)), Operation::Keyword(keyword) => self.parse_keyword(keyword, token), - Operation::NoOp => Ok((Done(token.category().clone()), Statement::NoOp(Some(token)))), + Operation::NoOp => Ok((Done(token.clone()), Statement::NoOp(Some(token)))), } } } @@ -107,18 +107,17 @@ mod test { fn result(code: &str) -> Statement { parse(code).next().unwrap().unwrap() } - fn token(category: Category, start: usize, end: usize) -> Token { - Token { - category, - position: (start, end), - } - } #[test] fn operations() { - fn expected(category: Category) -> Statement { - Statement::Operator(category, vec![Statement::Primitive(token(Number(1), 1, 2))]) - } + let no = Token { + category: Number(1), + line_column: (1, 2), + position: (1, 2), + }; + let expected = |category: Category| -> Statement { + Statement::Operator(category, vec![Statement::Primitive(no.clone())]) + }; assert_eq!(result("-1;"), expected(Category::Minus)); assert_eq!(result("+1;"), expected(Category::Plus)); @@ -128,8 +127,19 @@ mod test { #[test] fn single_statement() { - assert_eq!(result("1;"), Primitive(token(Number(1), 1, 1))); - assert_eq!(result("'a';"), Primitive(token(Data(vec![97]), 1, 1))); + let no = Token { + category: Number(1), + line_column: (1, 1), + position: (0, 1), + }; + let data = Token { + category: Data(vec![97]), + line_column: (1, 1), + position: (0, 3), + }; + + assert_eq!(result("1;"), Primitive(no)); + assert_eq!(result("'a';"), Primitive(data)); } #[test] @@ -140,7 +150,8 @@ mod test { vec![ Primitive(Token { category: Number(1), - position: (1, 1), + line_column: (1, 1), + position: (0, 1), }), Operator( Star, @@ -150,13 +161,15 @@ mod test { AssignOrder::AssignReturn, Box::new(Variable(Token { category: Identifier(Undefined("a".to_owned())), - position: (1, 7), + line_column: (1, 7), + position: (6, 7), })), Box::new(NoOp(None)), ), Primitive(Token { category: Number(1), - position: (1, 11), + line_column: (1, 11), + position: (10, 11), }), ], ), @@ -176,12 +189,19 @@ mod test { Box::new(Array( Token { category: Identifier(Undefined("a".to_owned())), - position: (1, 3), + line_column: (1, 3), + position: (2, 3), }, Some(Box::new(Primitive(Token { category: Number(0), - position: (1, 5), + line_column: (1, 5), + position: (4, 5), }))), + Some(Token { + category: RightBrace, + line_column: (1, 6), + position: (5, 6), + }), )), Box::new(NoOp(None)), ) diff --git a/rust/nasl-syntax/src/statement.rs b/rust/nasl-syntax/src/statement.rs index 066830a42..69668be60 100644 --- a/rust/nasl-syntax/src/statement.rs +++ b/rust/nasl-syntax/src/statement.rs @@ -3,9 +3,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later use core::fmt; -use std::fmt::Display; - -use crate::ACT; +use std::ops::Range; use crate::{unexpected_statement, SyntaxError, Token, TokenCategory}; @@ -18,49 +16,35 @@ pub enum AssignOrder { ReturnAssign, } -/// Specifies the scope of a declaration -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum DeclareScope { - /// Variable is globally reachable - Global, - /// Variable is locally reachable - Local, -} - -impl Display for DeclareScope { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - DeclareScope::Global => write!(f, "global_var"), - DeclareScope::Local => write!(f, "local_var"), - } - } -} - /// Is a executable step. #[derive(Clone, Debug, PartialEq, Eq)] +// TODO: change from enum to struct that contains a Kind. This would allow us to redefine Statement +// to contain start an end token, may comments so that a future formatter can just depend on +// Statement rather than have to reimplement logic pub enum Statement { /// Either a Number, String, Boolean or Null Primitive(Token), /// Attack category set by script_category - AttackCategory(ACT), + AttackCategory(Token), /// Is a variable Variable(Token), /// Is a array variable, it contains the lookup token as well as an optional lookup statement - Array(Token, Option>), + Array(Token, Option>, Option), /// Is a call of a function - Call(Token, Vec), + // TODO: change to Box and use Parameter + Call(Token, Vec, Token), /// Special exit call - Exit(Box), + Exit(Token, Box, Token), /// Special Return statement - Return(Box), + Return(Token, Box), /// Special Break statement - Break, + Break(Token), /// Special Continue statement - Continue, + Continue(Token), /// Special include call - Include(Box), + Include(Token, Box, Token), /// Declares a new variable in either global or local scope - Declare(DeclareScope, Vec), + Declare(Token, Vec), /// Parameter within a function Parameter(Vec), /// Named parameter on a function @@ -70,25 +54,34 @@ pub enum Statement { /// An Operator (e.g. +, -, *) Operator(TokenCategory, Vec), /// If statement, containing a condition, expression to be executed when the condition is true and an optional else expression - If(Box, Box, Option>), + If( + Token, + Box, + Box, + Option, + Option>, + ), /// For statement, containing a declaration/assignment, a condition, a execution per round before body execution, body execution /// e.g. `for (i = 0; i < 10; i++) display("hi");` For( + Token, Box, Box, Box, Box, ), /// While statement, containing a condition and a block - While(Box, Box), + While(Token, Box, Box), /// repeat statement, containing a block and a condition - Repeat(Box, Box), + Repeat(Token, Box, Box), /// foreach statement, containing a variable in array and a block - ForEach(Token, Box, Box), + ForEach(Token, Token, Box, Box), /// A set of expression within { ... } - Block(Vec), + Block(Token, Vec, Token), /// Function declaration; contains an identifier token, parameter statement and a block statement - FunctionDeclaration(Token, Vec, Box), + // TODO: change to Box as Parameter statement for statements instead of + // Vec + FunctionDeclaration(Token, Token, Vec, Token, Box), /// An empty operation, e.g. ; NoOp(Option), /// End of File @@ -105,15 +98,15 @@ impl Statement { self, Statement::Primitive(_) | Statement::Variable(_) - | Statement::Call(_, _) - | Statement::Return(_) + | Statement::Call(_, _, _) + | Statement::Return(_, _) | Statement::Assign( _, AssignOrder::AssignReturn | AssignOrder::ReturnAssign, _, _ ) - | Statement::Array(_, _) + | Statement::Array(_, _, _) | Statement::Operator(_, _) ) } @@ -140,30 +133,255 @@ impl Statement { /// Returns None on EoF, when a slice of vectors is empty or on AttackCategory pub fn as_token(&self) -> Option<&Token> { match self { - Statement::Primitive(token) => Some(token), + Statement::Continue(token) + | Statement::Break(token) + | Statement::AttackCategory(token) + | Statement::Primitive(token) => Some(token), Statement::Variable(token) => Some(token), - Statement::Array(token, _) => Some(token), - Statement::Call(token, _) => Some(token), - Statement::Exit(stmt) => stmt.as_token(), - Statement::Return(stmt) => stmt.as_token(), - Statement::Include(stmt) => stmt.as_token(), + Statement::Array(token, _, _) => Some(token), + Statement::Call(token, _, _) => Some(token), + Statement::Exit(_, stmt, _) => stmt.as_token(), + Statement::Return(_, stmt) => stmt.as_token(), + Statement::Include(_, stmt, _) => stmt.as_token(), Statement::Declare(_, stmts) => Statement::first_stmts_token(stmts), Statement::Parameter(stmts) => Statement::first_stmts_token(stmts), Statement::NamedParameter(token, _) => Some(token), Statement::Assign(_, _, stmt, _) => stmt.as_token(), Statement::Operator(_, stmts) => Statement::first_stmts_token(stmts), - Statement::If(stmt, _, _) => stmt.as_token(), - Statement::For(stmt, _, _, _) => stmt.as_token(), - Statement::While(stmt, _) => stmt.as_token(), - Statement::Repeat(_, stmt) => stmt.as_token(), - Statement::ForEach(token, _, _) => Some(token), - Statement::Block(stmts) => Statement::first_stmts_token(stmts), - Statement::FunctionDeclaration(token, _, _) => Some(token), + Statement::FunctionDeclaration(kw, _, _, _, _) + | Statement::Block(kw, _, _) + | Statement::If(kw, _, _, _, _) + | Statement::While(kw, _, _) + | Statement::Repeat(kw, _, _) + | Statement::ForEach(kw, _, _, _) + | Statement::For(kw, _, _, _, _) => Some(kw), Statement::NoOp(token) => token.as_ref(), Statement::EoF => None, - Statement::AttackCategory(_) => None, - Statement::Continue => None, - Statement::Break => None, + } + } + + /// Retrieves the stored token in a Statement. + /// + /// If a Statement contains multiple Statements (e.g. Declare) than just the first one is returned. + /// Returns None on EoF, when a slice of vectors is empty or on AttackCategory + pub fn as_tokens(&self) -> Vec<&Token> { + match self { + Statement::AttackCategory(token) + | Statement::Continue(token) + | Statement::Break(token) + | Statement::NoOp(Some(token)) + | Statement::Array(token, None, _) + | Statement::Primitive(token) + | Statement::Variable(token) => vec![token], + Statement::Array(token, Some(stmt), end) => { + let mut results = vec![token]; + results.extend(stmt.as_tokens()); + if let Some(end) = end { + results.push(end) + } + results + } + Statement::Block(kw, stmts, end) | Statement::Call(kw, stmts, end) => { + let mut results = Vec::with_capacity(stmts.len() + 2); + results.push(kw); + for stmt in stmts { + results.extend(stmt.as_tokens()); + } + results.push(end); + results + } + Statement::Include(kw, stmt, end) | Statement::Exit(kw, stmt, end) => { + let mut results = Vec::with_capacity(3); + results.push(kw); + results.extend(stmt.as_tokens()); + results.push(end); + results + } + Statement::NamedParameter(kw, stmt) | Statement::Return(kw, stmt) => { + let mut results = Vec::with_capacity(2); + results.push(kw); + results.extend(stmt.as_tokens()); + results + } + Statement::Declare(kw, stmts) => { + let mut results = Vec::with_capacity(2); + results.push(kw); + for stmt in stmts { + results.extend(stmt.as_tokens()); + } + results + } + Statement::Parameter(stmts) => stmts.iter().flat_map(|stmt| stmt.as_tokens()).collect(), + Statement::Assign(_, _, stmt1, stmt2) => { + let mut tokens = stmt1.as_tokens(); + tokens.extend(stmt2.as_tokens()); + tokens + } + Statement::Operator(_, stmts) => { + let mut results = Vec::with_capacity(stmts.len()); + for stmt in stmts { + results.extend(stmt.as_tokens()); + } + results + } + Statement::If(kw, cond, stmt, ekw, estmt) => { + let mut results = vec![kw]; + results.extend(cond.as_tokens()); + results.extend(stmt.as_tokens()); + if let Some(ekw) = ekw { + results.push(ekw); + } + if let Some(estmt) = estmt { + results.extend(estmt.as_tokens()); + } + results + } + Statement::For(kw, decl, cond, post, stmt) => { + let mut results = vec![kw]; + results.extend(decl.as_tokens()); + results.extend(cond.as_tokens()); + results.extend(post.as_tokens()); + results.extend(stmt.as_tokens()); + results + } + Statement::Repeat(kw, cond, stmt) | Statement::While(kw, cond, stmt) => { + let mut results = vec![kw]; + results.extend(cond.as_tokens()); + results.extend(stmt.as_tokens()); + results + } + Statement::ForEach(kw, token, arr, stmt) => { + let mut results = vec![kw, token]; + results.extend(arr.as_tokens()); + results.extend(stmt.as_tokens()); + results + } + Statement::FunctionDeclaration(kw, name, params, rp, stmt) => { + let mut results = vec![kw, name]; + for stmt in params { + results.extend(stmt.as_tokens()); + } + results.push(rp); + results.extend(stmt.as_tokens()); + results + } + Statement::EoF | Statement::NoOp(None) => vec![], + } + } + + /// Calculates the position of the statement + pub fn position(&self) -> (usize, usize) { + match self { + Statement::Array(id, _, Some(end)) | Statement::Call(id, _, end) => { + (id.position.0, end.position.1) + } + _ => { + let tokens = self.as_tokens(); + if let (Some(t1), Some(t2)) = (tokens.first(), tokens.last()) { + (t1.position.0, t2.position.1) + } else { + (0, 0) + } + } + } + } + + /// Calculates the byte range of the statement + pub fn range(&self) -> Range { + let (start, end) = self.position(); + Range { start, end } + } + + /// Finds all statements in itself or itself that matches the wanted function + /// + /// Example: + /// ``` + /// let code = r#" + /// function test(a, b) { + /// return funker(a + b); + /// } + /// a = funker(1); + /// while (funker(1) == 1) { + /// if (funker(2) == 2) { + /// return funker(2); + /// } else { + /// for ( i = funker(3); i < funker(5) + funker(5); i + funker(1)) + /// exit(funker(10)); + /// } + /// } + /// "#; + /// let results: usize = nasl_syntax::parse(code) + /// .filter_map(|s| s.ok()) + /// .map(|s| s.find(&|s| matches!(s, nasl_syntax::Statement::Call(..))).len()) + /// .sum(); + /// + /// assert_eq!(results, 10); + /// + /// ``` + /// + pub fn find<'a, 'b, F>(&'a self, wanted: &'b F) -> Vec<&'a Statement> + where + F: Fn(&'a Statement) -> bool, + { + if wanted(self) { + vec![self] + } else { + let mut results = vec![]; + match self { + Statement::Primitive(_) + | Statement::AttackCategory(_) + | Statement::Variable(_) + | Statement::NoOp(_) + | Statement::EoF + | Statement::Break(_) + | Statement::Array(_, None, _) + | Statement::Continue(_) => { + // doesn't contain further statements + } + Statement::Parameter(stmts) + | Statement::Call(_, stmts, _) + | Statement::Declare(_, stmts) + | Statement::Operator(_, stmts) + | Statement::Block(_, stmts, _) => { + for s in stmts { + results.extend(Self::find(s, wanted)) + } + } + Statement::NamedParameter(_, stmt) + | Statement::Exit(_, stmt, _) + | Statement::Return(_, stmt) + | Statement::Include(_, stmt, _) + | Statement::Array(_, Some(stmt), _) => { + results.extend(Self::find(stmt, wanted)); + } + Statement::While(_, stmt, stmt2) + | Statement::Repeat(_, stmt, stmt2) + | Statement::ForEach(_, _, stmt, stmt2) + | Statement::Assign(_, _, stmt, stmt2) => { + results.extend(Self::find(stmt, wanted)); + results.extend(Self::find(stmt2, wanted)); + } + Statement::If(_, stmt, stmt2, _, stmt3) => { + results.extend(Self::find(stmt, wanted)); + results.extend(Self::find(stmt2, wanted)); + if let Some(stmt3) = stmt3 { + results.extend(Self::find(stmt3, wanted)); + } + } + Statement::For(_, stmt, stmt2, stmt3, stmt4) => { + results.extend(Self::find(stmt, wanted)); + results.extend(Self::find(stmt2, wanted)); + results.extend(Self::find(stmt3, wanted)); + results.extend(Self::find(stmt4, wanted)); + } + Statement::FunctionDeclaration(_, _, stmts, _, stmt) => { + results.extend(Self::find(stmt, wanted)); + for stmt in stmts { + results.extend(Self::find(stmt, wanted)); + } + } + }; + results } } } @@ -180,18 +398,18 @@ impl fmt::Display for Statement { Statement::Primitive(x) => write!(f, "{}", x.category()), Statement::AttackCategory(x) => write!(f, "{x:?}"), Statement::Variable(x) => write!(f, "{}", x.category()), - Statement::Array(x, e) => match e { + Statement::Array(x, e, _) => match e { Some(e) => { write!(f, "{}[{e}]", x.category()) } None => write!(f, "{}", x.category()), }, - Statement::Call(name, args) => { - write!(f, "{}({})", name.category(), as_str_list(args)) + Statement::Call(name, args, _) => { + write!(f, "{}({});", name.category(), as_str_list(args)) } - Statement::Exit(x) => write!(f, "exit({x})"), - Statement::Return(x) => write!(f, "return {x}"), - Statement::Include(x) => write!(f, "include({x})"), + Statement::Exit(_, x, _) => write!(f, "exit({x});"), + Statement::Return(_, x) => write!(f, "return {x};"), + Statement::Include(_, x, _) => write!(f, "include({x});"), Statement::Declare(s, x) => { write!(f, "{s} {}", as_str_list(x),) } @@ -207,26 +425,88 @@ impl fmt::Display for Statement { [l] => write!(f, "{o}{l}"), _ => write!(f, "({o} ({}))", as_str_list(args)), }, - Statement::If(c, x, e) => { - let r = write!(f, "if ({c}) {{{x}}}"); + Statement::If(_, c, x, _, e) => { + let r = write!(f, "if ({c}) {x}"); if let Some(e) = e { - write!(f, " else {{{e}}}") + write!(f, " else {e}") } else { r } } - Statement::For(i, c, u, e) => write!(f, "for ({i}; {c}; {u}) {{ {e} }}"), - Statement::While(c, e) => write!(f, "while ({c}) {{{e}}}"), - Statement::Repeat(e, c) => write!(f, "repeat {e} until {c}"), - Statement::ForEach(v, a, e) => write!(f, "foreach {}({a}) {{{e}}}", v.category()), - Statement::Block(_) => write!(f, "{{ ... }}"), - Statement::FunctionDeclaration(n, p, _) => { + Statement::For(_, i, c, u, e) => write!(f, "for ({i}; {c}; {u}) {{ {e} }}"), + Statement::While(_, c, e) => write!(f, "while ({c}) {{{e}}}"), + Statement::Repeat(_, e, c) => write!(f, "repeat {e} until {c}"), + Statement::ForEach(_, v, a, e) => write!(f, "foreach {}({a}) {{{e}}}", v.category()), + Statement::Block(..) => write!(f, "{{ ... }}"), + Statement::FunctionDeclaration(_, n, p, _, _) => { write!(f, "function {}({}) {{ ... }}", n.category(), as_str_list(p)) } Statement::NoOp(_) => write!(f, "NoOp"), Statement::EoF => write!(f, "EoF"), - Statement::Break => write!(f, "break"), - Statement::Continue => write!(f, "continue"), + Statement::Break(_) => write!(f, "break"), + Statement::Continue(_) => write!(f, "continue"), + } + } +} + +#[cfg(test)] +mod position { + use crate::parse; + + #[test] + fn assignment() { + let code = r#" + a = 1 + 1; + b = 2 * 2; + a = ++a; + arr = mkarray(a, b, c ); + arr[++a]; + exit(1); + return 1; + include('test.inc'); + local_var a, b, c; + global_var a, b, c; + if (a) display(1); else display(2); + for (i = 1; i < 10; i++) display(i); + while(TRUE) display(i); + foreach a(q) display(a); + repeat display("q"); until 1; + { + a; + b; + } + function register_packages( buf ) { return 1; } + "#; + let parser = parse(code); + let expected = [ + "a = 1 + 1", + "b = 2 * 2", + "a = ++a", + "arr = mkarray(a, b, c )", + "arr[++a]", + "exit(1)", + "return 1", + "include('test.inc')", + "local_var a, b, c", + "global_var a, b, c", + "if (a) display(1); else display(2)", + "for (i = 1; i < 10; i++) display(i)", + "while(TRUE) display(i)", + "foreach a(q) display(a)", + "repeat display(\"q\"); until 1", + r#"{ + a; + b; + }"#, + "function register_packages( buf ) { return 1; }", + ]; + let ranges: Vec<_> = parser.map(|x| x.unwrap().range()).collect(); + + let mut ri = expected.iter(); + assert_eq!(ranges.len(), expected.len()); + for range in ranges { + let a: &str = ri.next().unwrap(); + assert_eq!(&code[range], a); } } } diff --git a/rust/nasl-syntax/src/token.rs b/rust/nasl-syntax/src/token.rs index d7f6b28e6..020577f6e 100644 --- a/rust/nasl-syntax/src/token.rs +++ b/rust/nasl-syntax/src/token.rs @@ -82,6 +82,25 @@ impl IdentifierType { } + /// Returns the length of the identifier + pub fn len(&self) -> usize { + $( + if self == &$define { + return stringify!($matcher).len(); + } + )* + if let IdentifierType::Undefined(r) = self { + return r.len(); + } else { + return 0; + } + } + + /// Returns true when len == 0 + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + } impl Display for IdentifierType { @@ -363,12 +382,12 @@ impl Display for Category { Category::X => write!(f, "X"), Category::String(x) => write!(f, "\"{x}\""), Category::Number(x) => write!(f, "{x}"), - Category::IPv4Address(_) => write!(f, "IPv4Address"), + Category::IPv4Address(x) => write!(f, "{x}"), Category::IllegalIPv4Address => write!(f, "IllegalIPv4Address"), Category::IllegalNumber(_) => write!(f, "IllegalNumber"), Category::Comment => write!(f, "Comment"), Category::Identifier(x) => write!(f, "{}", x), - Category::Unclosed(x) => write!(f, "{x:?}"), + Category::Unclosed(x) => write!(f, "Unclosed{x:?}"), Category::UnknownBase => write!(f, "UnknownBase"), Category::UnknownSymbol => write!(f, "UnknownSymbol"), Category::Data(x) => write!(f, "{x:?}"), @@ -382,15 +401,28 @@ pub struct Token { /// The category or kind of a token pub category: Category, /// The line and the column of the start of the token + pub line_column: (usize, usize), + /// Byte position pub position: (usize, usize), } +impl Token { + /// Returns UnknownSymbol without line column or position + pub fn unexpected_none() -> Self { + Self { + category: Category::UnknownSymbol, + line_column: (0, 0), + position: (0, 0), + } + } +} + impl Display for Token { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}:{} {}", - self.position.0, self.position.1, self.category + self.line_column.0, self.line_column.1, self.category ) } } @@ -748,7 +780,12 @@ impl<'a> Iterator for Tokenizer<'a> { current if current.is_alphabetic() || current == '_' => self.tokenize_identifier(start), _ => UnknownSymbol, }; - Some(Token { category, position }) + let byte_position = (start, self.cursor.len_consumed()); + Some(Token { + category, + line_column: position, + position: byte_position, + }) } } @@ -756,285 +793,169 @@ impl<'a> Iterator for Tokenizer<'a> { mod tests { use super::*; - fn build_token(input: (Category, usize, usize)) -> Token { - let (category, start, end) = input; - Token { - category, - position: (start, end), - } - } - // use macro instead of a method to have correct line numbers on failure macro_rules! verify_tokens { ($code:expr, $expected:expr) => {{ + use std::string::String; let tokenizer = Tokenizer::new($code); - let actual: Vec = tokenizer.clone().collect(); - let expected: Vec = $expected.iter().map(|x| build_token(x.clone())).collect(); + let actual: Vec = tokenizer.map(|t| t.category().to_string()).collect(); + let expected: Vec = $expected.iter().map(|s| s.to_string()).collect(); assert_eq!(actual, expected); - (tokenizer, actual) }}; } #[test] fn skip_white_space() { - verify_tokens!(" ( ", [(Category::LeftParen, 1, 6)]); + verify_tokens!(" ( ", ["("]); } #[test] fn single_symbol_tokens() { - verify_tokens!("(", [(Category::LeftParen, 1, 1)]); - verify_tokens!(")", [(Category::RightParen, 1, 1)]); - verify_tokens!("[", [(Category::LeftBrace, 1, 1)]); - verify_tokens!("]", [(Category::RightBrace, 1, 1)]); - verify_tokens!("{", [(Category::LeftCurlyBracket, 1, 1)]); - verify_tokens!("}", [(Category::RightCurlyBracket, 1, 1)]); - verify_tokens!(",", [(Category::Comma, 1, 1)]); - verify_tokens!(".", [(Category::Dot, 1, 1)]); - verify_tokens!("-", [(Category::Minus, 1, 1)]); - verify_tokens!("+", [(Category::Plus, 1, 1)]); - verify_tokens!("%", [(Category::Percent, 1, 1)]); - verify_tokens!(";", [(Category::Semicolon, 1, 1)]); - verify_tokens!("/", [(Category::Slash, 1, 1)]); - verify_tokens!("*", [(Category::Star, 1, 1)]); - verify_tokens!(":", [(Category::DoublePoint, 1, 1)]); - verify_tokens!("~", [(Category::Tilde, 1, 1)]); - verify_tokens!("&", [(Category::Ampersand, 1, 1)]); - verify_tokens!("|", [(Category::Pipe, 1, 1)]); - verify_tokens!("^", [(Category::Caret, 1, 1)]); + verify_tokens!("(", ["("]); + verify_tokens!(")", [")"]); + verify_tokens!("[", ["["]); + verify_tokens!("]", ["]"]); + verify_tokens!("{", ["{"]); + verify_tokens!("}", ["}"]); + verify_tokens!(",", [","]); + verify_tokens!(".", ["."]); + verify_tokens!("-", ["-"]); + verify_tokens!("+", ["+"]); + verify_tokens!("%", ["%"]); + verify_tokens!(";", [";"]); + verify_tokens!("/", ["/"]); + verify_tokens!("*", ["*"]); + verify_tokens!(":", [":"]); + verify_tokens!("~", ["~"]); + verify_tokens!("&", ["&"]); + verify_tokens!("|", ["|"]); + verify_tokens!("^", ["^"]); } #[test] fn two_symbol_tokens() { - verify_tokens!("&", [(Category::Ampersand, 1, 1)]); - verify_tokens!("&&", [(Category::AmpersandAmpersand, 1, 1)]); - verify_tokens!("|", [(Category::Pipe, 1, 1)]); - verify_tokens!("||", [(Category::PipePipe, 1, 1)]); - verify_tokens!("!", [(Category::Bang, 1, 1)]); - verify_tokens!("!=", [(Category::BangEqual, 1, 1)]); - verify_tokens!("!~", [(Category::BangTilde, 1, 1)]); - verify_tokens!("=", [(Category::Equal, 1, 1)]); - verify_tokens!("==", [(Category::EqualEqual, 1, 1)]); - verify_tokens!("=~", [(Category::EqualTilde, 1, 1)]); - verify_tokens!(">", [(Category::Greater, 1, 1)]); - verify_tokens!(">>", [(Category::GreaterGreater, 1, 1)]); - verify_tokens!(">=", [(Category::GreaterEqual, 1, 1)]); - verify_tokens!("><", [(Category::GreaterLess, 1, 1)]); - verify_tokens!("<", [(Category::Less, 1, 1)]); - verify_tokens!("<<", [(Category::LessLess, 1, 1)]); - verify_tokens!("<=", [(Category::LessEqual, 1, 1)]); - verify_tokens!("-", [(Category::Minus, 1, 1)]); - verify_tokens!("--", [(Category::MinusMinus, 1, 1)]); - verify_tokens!("+", [(Category::Plus, 1, 1)]); - verify_tokens!("+=", [(Category::PlusEqual, 1, 1)]); - verify_tokens!("++", [(Category::PlusPlus, 1, 1)]); - verify_tokens!("/", [(Category::Slash, 1, 1)]); - verify_tokens!("/=", [(Category::SlashEqual, 1, 1)]); - verify_tokens!("*", [(Category::Star, 1, 1)]); - verify_tokens!("**", [(Category::StarStar, 1, 1)]); - verify_tokens!("*=", [(Category::StarEqual, 1, 1)]); + verify_tokens!("&", ["&"]); + verify_tokens!("&&", ["&&"]); + verify_tokens!("|", ["|"]); + verify_tokens!("||", ["||"]); + verify_tokens!("!", ["!"]); + verify_tokens!("!=", ["!="]); + verify_tokens!("!~", ["!~"]); + verify_tokens!("=", ["="]); + verify_tokens!("==", ["=="]); + verify_tokens!("=~", ["=~"]); + verify_tokens!(">", [">"]); + verify_tokens!(">>", [">>"]); + verify_tokens!(">=", [">="]); + verify_tokens!("><", ["><"]); + verify_tokens!("<", ["<"]); + verify_tokens!("<<", ["<<"]); + verify_tokens!("<=", ["<="]); + verify_tokens!("-", ["-"]); + verify_tokens!("--", ["--"]); + verify_tokens!("+", ["+"]); + verify_tokens!("+=", ["+="]); + verify_tokens!("++", ["++"]); + verify_tokens!("/", ["/"]); + verify_tokens!("/=", ["/="]); + verify_tokens!("*", ["*"]); + verify_tokens!("**", ["**"]); + verify_tokens!("*=", ["*="]); } #[test] fn three_symbol_tokens() { - verify_tokens!(">>>", [(Category::GreaterGreaterGreater, 1, 1)]); - verify_tokens!(">>=", [(Category::GreaterGreaterEqual, 1, 1)]); - verify_tokens!(">!<", [(Category::GreaterBangLess, 1, 1)]); - verify_tokens!("<<=", [(Category::LessLessEqual, 1, 1)]); + verify_tokens!(">>>", [">>>"]); + verify_tokens!(">>=", [">>="]); + verify_tokens!(">!<", [">!<"]); + verify_tokens!("<<=", ["<<="]); } #[test] fn four_symbol_tokens() { - verify_tokens!(">>>=", [(Category::GreaterGreaterGreaterEqual, 1, 1)]); + verify_tokens!(">>>=", [">>>="]); } #[test] fn unquotable_string() { - let code = "\"hello I am a closed string\\\""; - verify_tokens!( - code, - [( - Category::String("hello I am a closed string\\".to_owned()), - 1, - 1, - )] - ); - let code = "\"hello I am a unclosed string\\"; verify_tokens!( - code, - [(Category::Unclosed(UnclosedCategory::String), 1, 1,)] + "\"hello I am a closed string\\\"", + ["\"hello I am a closed string\\\""] ); + verify_tokens!("\"hello I am a unclosed string\\", ["UnclosedString"]); } #[test] fn quotable_string() { - let code = "'Hello \\'you\\'!'"; verify_tokens!( - code, - [(Category::Data("Hello \\'you\\'!".as_bytes().to_vec()), 1, 1)] + "'Hello \\'you\\'!'", + ["[72, 101, 108, 108, 111, 32, 92, 39, 121, 111, 117, 92, 39, 33]"] ); - let code = "'Hello \\'you\\'!\\'"; - verify_tokens!(code, [(Category::Unclosed(UnclosedCategory::Data), 1, 1)]); + verify_tokens!("'Hello \\'you\\'!\\'", ["UnclosedData"]); } #[test] fn numbers() { - use Base::*; - use Category::*; - verify_tokens!("0", [(Number(0), 1, 1)]); - verify_tokens!("0b01", [(Number(1), 1, 1)]); - verify_tokens!("1234567890", [(Number(1234567890), 1, 1)]); - verify_tokens!("012345670", [(Number(2739128), 1, 1)]); - verify_tokens!("0x1234567890ABCDEF", [(Number(1311768467294899695), 1, 1)]); - // That would be later illegal because a number if followed by a number - // but within tokenizing I think it is the best to ignore that and let it be handled by AST - verify_tokens!("0b02", [(Number(0), 1, 1), (Number(2), 1, 4)]); - verify_tokens!("0b2", [(IllegalNumber(Binary), 1, 1), (Number(2), 1, 3)]); + verify_tokens!("0", ["0"]); + verify_tokens!("0b01", ["1"]); + verify_tokens!("1234567890", ["1234567890"]); + verify_tokens!("012345670", ["2739128"]); + verify_tokens!("0x1234567890ABCDEF", ["1311768467294899695"]); + // // That would be later illegal because a number if followed by a number + // // but within tokenizing I think it is the best to ignore that and let it be handled by AST + verify_tokens!("0b02", ["0", "2"]); + verify_tokens!("0b2", ["IllegalNumber", "2"]); } #[test] fn single_line_comments() { - use Category::*; - verify_tokens!( - "# this is a comment\n;", - [(Comment, 1, 1), (Semicolon, 2, 1)] - ); + verify_tokens!("# this is a comment\n;", ["Comment", ";"]); } #[test] fn identifier() { - use Category::*; - use IdentifierType::*; - verify_tokens!( - "help_lo", - [(Identifier(Undefined("help_lo".to_owned())), 1, 1)] - ); - verify_tokens!( - "_hello", - [(Identifier(Undefined("_hello".to_owned())), 1, 1)] - ); - verify_tokens!( - "_h4llo", - [(Identifier(Undefined("_h4llo".to_owned())), 1, 1)] - ); - verify_tokens!( - "4_h4llo", - [ - (Number(4), 1, 1), - (Identifier(Undefined("_h4llo".to_owned())), 1, 2) - ] - ); + verify_tokens!("help_lo", ["help_lo"]); + verify_tokens!("_hello", ["_hello"]); + verify_tokens!("_h4llo", ["_h4llo"]); + verify_tokens!("4_h4llo", ["4", "_h4llo",]); } #[test] fn keywords() { - use Category::*; - use IdentifierType::*; - verify_tokens!("for", [(Identifier(For), 1, 1)]); - verify_tokens!("foreach", [(Identifier(ForEach), 1, 1)]); - verify_tokens!("if", [(Identifier(If), 1, 1)]); - verify_tokens!("else", [(Identifier(Else), 1, 1)]); - verify_tokens!("while", [(Identifier(While), 1, 1)]); - verify_tokens!("repeat", [(Identifier(Repeat), 1, 1)]); - verify_tokens!("until", [(Identifier(Until), 1, 1)]); - verify_tokens!("local_var", [(Identifier(LocalVar), 1, 1)]); - verify_tokens!("global_var", [(Identifier(GlobalVar), 1, 1)]); - verify_tokens!("NULL", [(Identifier(Null), 1, 1)]); - verify_tokens!("return", [(Identifier(Return), 1, 1)]); - verify_tokens!("include", [(Identifier(Include), 1, 1)]); - verify_tokens!("exit", [(Identifier(Exit), 1, 1)]); - verify_tokens!("break", [(Identifier(Break), 1, 1)]); - verify_tokens!("continue", [(Identifier(Continue), 1, 1)]); + verify_tokens!("for", ["for"]); + verify_tokens!("foreach", ["foreach"]); + verify_tokens!("if", ["if"]); + verify_tokens!("else", ["else"]); + verify_tokens!("while", ["while"]); + verify_tokens!("repeat", ["repeat"]); + verify_tokens!("until", ["until"]); + verify_tokens!("local_var", ["local_var"]); + verify_tokens!("global_var", ["global_var"]); + verify_tokens!("NULL", ["NULL"]); + verify_tokens!("return", ["return"]); + verify_tokens!("include", ["include"]); + verify_tokens!("exit", ["exit"]); + verify_tokens!("break", ["break"]); + verify_tokens!("continue", ["continue"]); } #[test] fn string_quoting() { - use Category::*; verify_tokens!( r"'webapps\\appliance\\'", - [(Data("webapps\\\\appliance\\\\".as_bytes().to_vec()), 1, 1)] + [ + r"[119, 101, 98, 97, 112, 112, 115, 92, 92, 97, 112, 112, 108, 105, 97, 110, 99, 101, 92, 92]", + ] ); } #[test] fn simplified_ipv4_address() { - use Category::*; - verify_tokens!( - "10.187.76.12", - [(IPv4Address("10.187.76.12".to_owned()), 1, 1)] - ); + verify_tokens!("10.187.76.12", ["10.187.76.12",]); } #[test] fn repeat_x_times() { - use Category::*; - verify_tokens!( - "x() x 10;", - vec![ - (Identifier(IdentifierType::Undefined("x".to_owned())), 1, 1), - (LeftParen, 1, 2), - (RightParen, 1, 3), - (X, 1, 5), - (Number(10), 1, 7), - (Semicolon, 1, 9), - ] - ); - } - - #[test] - fn tokenize_description_block() { - use Category::*; - use IdentifierType::*; - - let code = r#" -if(description) -{ - script_oid("1.3.6.1.4.1.25623.1.0.99999"); - exit(0); -} - -j = 123; -j >>>= 8; -display(j); -exit(1); -"#; - verify_tokens!( - code, - vec![ - (Identifier(If), 2, 1), - (LeftParen, 2, 3), // start expression block - (Identifier(Undefined("description".to_owned())), 2, 4), // verify is description is true - (RightParen, 2, 15), // end expression block - (LeftCurlyBracket, 3, 1), // start execution block - (Identifier(Undefined("script_oid".to_owned())), 4, 3), // lookup function script_oid - (LeftParen, 4, 13), // start parameter expression block - (String("1.3.6.1.4.1.25623.1.0.99999".to_owned()), 4, 14), // resolve prime to "1.3.6.1.4.1.25623.1.0.99999" - (RightParen, 4, 43), // end expression block - (Semicolon, 4, 44), // finish execution - (Identifier(Exit), 5, 3), // lookup keyword exit - (LeftParen, 5, 7), // start parameter expression block - (Number(0), 5, 8), // call exit with 0 - (RightParen, 5, 9), // end expression block - (Semicolon, 5, 10), // finish execution - (RightCurlyBracket, 6, 1), // finish expression block - (Identifier(Undefined("j".to_owned())), 8, 1), // lookup j - (Equal, 8, 3), // assign to j - (Number(123), 8, 5), // number 123 - (Semicolon, 8, 8), // finish execution - (Identifier(Undefined("j".to_owned())), 9, 1), // lookup j - (GreaterGreaterGreaterEqual, 9, 3), // shift j and assign to j - (Number(8), 9, 8), // 8 - (Semicolon, 9, 9), // finish execution - (Identifier(Undefined("display".to_owned())), 10, 1), // lookup display - (LeftParen, 10, 8), // start parameter expression block - (Identifier(Undefined("j".to_owned())), 10, 9), // resolve j primitive - (RightParen, 10, 10), // finish parameter expression block - (Semicolon, 10, 11), // finish execution - (Identifier(Exit), 11, 1), // lookup keyword exit - (LeftParen, 11, 5), // start parameter expression block - (Number(1), 11, 6), // call exit with 1 - (RightParen, 11, 7), // finish parameter expression block - (Semicolon, 11, 8) // finish execution - ] - ); + verify_tokens!("x() x 10;", ["x", "(", ")", "X", "10", ";"]); } } diff --git a/rust/nasl-syntax/src/variable_extension.rs b/rust/nasl-syntax/src/variable_extension.rs index f704f2ab5..bd3585362 100644 --- a/rust/nasl-syntax/src/variable_extension.rs +++ b/rust/nasl-syntax/src/variable_extension.rs @@ -31,7 +31,7 @@ impl<'a> CommaGroup for Lexer<'a> { while let Some(token) = self.peek() { if *token.category() == category { self.token(); - end = End::Done(category); + end = End::Done(token); break; } let (stmtend, param) = @@ -42,8 +42,8 @@ impl<'a> CommaGroup for Lexer<'a> { } match stmtend { End::Done(endcat) => { - if endcat == category { - end = End::Done(category); + if endcat.category() == &category { + end = End::Done(endcat); break; } } @@ -69,20 +69,22 @@ impl<'a> Variables for Lexer<'a> { Category::LeftParen => { self.token(); let (end, params) = self.parse_comma_group(Category::RightParen)?; - if end == End::Continue { - return Err(unclosed_token!(nt)); - } - return Ok((Continue, Statement::Call(token, params))); + return match end { + Done(end) => Ok((Continue, Statement::Call(token, params, end))), + Continue => Err(unclosed_token!(nt)), + }; } Category::LeftBrace => { self.token(); let (end, lookup) = self.statement(0, &|c| c == &Category::RightBrace)?; let lookup = lookup.as_returnable_or_err()?; - if end == End::Continue { - return Err(unclosed_token!(token)); - } else { - return Ok((Continue, Statement::Array(token, Some(Box::new(lookup))))); - } + return match end { + Done(end) => Ok(( + Continue, + Statement::Array(token, Some(Box::new(lookup)), Some(end)), + )), + Continue => Err(unclosed_token!(token)), + }; } _ => {} } @@ -94,189 +96,48 @@ impl<'a> Variables for Lexer<'a> { #[cfg(test)] mod test { use crate::{ - parse, - token::{Category, Token}, - {AssignOrder, Statement}, + parse, {AssignOrder, Statement}, }; - use crate::IdentifierType::*; - use Category::*; use Statement::*; - fn token(category: Category, start: usize, end: usize) -> Token { - Token { - category, - position: (start, end), - } - } - fn result(code: &str) -> Statement { parse(code).next().unwrap().unwrap() } #[test] fn variables() { - assert_eq!( - result("a;"), - Variable(token(Identifier(Undefined("a".to_owned())), 1, 1)) - ); + assert!(matches!(result("a;"), Variable(_))); } #[test] fn arrays() { - assert_eq!( - result("a[0];"), - Array( - token(Identifier(Undefined("a".to_owned())), 1, 1), - Some(Box::new(Primitive(token(Number(0), 1, 3)))) - ) - ); - - assert_eq!( - result("a = [1, 2, 3];"), - Assign( - Equal, - AssignOrder::AssignReturn, - Box::new(Array( - Token { - category: Identifier(Undefined("a".to_owned())), - position: (1, 1) - }, - None - )), - Box::new(Parameter(vec![ - Primitive(Token { - category: Number(1), - position: (1, 6) - }), - Primitive(Token { - category: Number(2), - position: (1, 9) - }), - Primitive(Token { - category: Number(3), - position: (1, 12) - }) - ])) - ) - ); + assert!(matches!(result("a[0];"), Array(..))); + match result("a = [1, 2, 3];") { + Assign(super::Category::Equal, AssignOrder::AssignReturn, arr, _) => { + assert!(matches!(*arr, Array(..))) + } + actual => unreachable!("{actual} must be an assign statement"), + } - assert_eq!( - result("a[0] = [1, 2, 4];"), - Assign( - Equal, - AssignOrder::AssignReturn, - Box::new(Array( - Token { - category: Identifier(Undefined("a".to_owned())), - position: (1, 1) - }, - Some(Box::new(Primitive(Token { - category: Number(0), - position: (1, 3) - }))) - )), - Box::new(Parameter(vec![ - Primitive(Token { - category: Number(1), - position: (1, 9) - }), - Primitive(Token { - category: Number(2), - position: (1, 12) - }), - Primitive(Token { - category: Number(4), - position: (1, 15) - }) - ])) - ) - ); + match result("a[0] = [1, 2, 4];") { + Assign(super::Category::Equal, AssignOrder::AssignReturn, arr, _) => { + assert!(matches!(*arr, Array(..))) + } + actual => unreachable!("{actual} must be an assign statement"), + } } #[test] fn anon_function_call() { - let fn_name = token(Identifier(Undefined("a".to_owned())), 1, 1); - let args = vec![ - Primitive(token(Number(1), 1, 3)), - Primitive(token(Number(2), 1, 6)), - Primitive(token(Number(3), 1, 9)), - ]; - - assert_eq!(result("a(1, 2, 3);"), Call(fn_name, args)); + assert!(matches!(result("a(1, 2, 3);"), Call(..))) } #[test] fn named_function_call() { - use Statement::*; - assert_eq!( + assert!(matches!( result("script_tag(name:\"cvss_base\", value:1 + 1 % 2);"), - Call( - Token { - category: Identifier(Undefined("script_tag".to_owned())), - position: (1, 1) - }, - vec![ - NamedParameter( - Token { - category: Identifier(Undefined("name".to_owned())), - position: (1, 12) - }, - Box::new(Primitive(Token { - category: String("cvss_base".to_owned()), - position: (1, 17) - })) - ), - NamedParameter( - Token { - category: Identifier(Undefined("value".to_owned())), - position: (1, 30) - }, - Box::new(Operator( - Plus, - vec![ - Primitive(Token { - category: Number(1), - position: (1, 36) - }), - Operator( - Percent, - vec![ - Primitive(Token { - category: Number(1), - position: (1, 40) - }), - Primitive(Token { - category: Number(2), - position: (1, 44) - }) - ] - ) - ] - )) - ) - ] - ) - ); - - assert_eq!( - result("script_tag(name: 2);"), - Call( - Token { - category: Identifier(Undefined("script_tag".to_owned())), - position: (1, 1) - }, - vec![NamedParameter( - Token { - category: Identifier(Undefined("name".to_owned())), - position: (1, 12) - }, - Box::new(Primitive(Token { - category: Number(2), - position: (1, 18) - })) - )] - ) - ); + Call(..) + )); } } diff --git a/rust/openvasd/src/controller/mod.rs b/rust/openvasd/src/controller/mod.rs index 7942a91c0..2391dc4e3 100644 --- a/rust/openvasd/src/controller/mod.rs +++ b/rust/openvasd/src/controller/mod.rs @@ -295,7 +295,7 @@ mod tests { let cid = Arc::new(RwLock::new(ClientIdentifier::Known("42".into()))); let resp = entrypoint(req, Arc::clone(&ctx), cid).await.unwrap(); let resp = hyper::body::to_bytes(resp.into_body()).await.unwrap(); - + serde_json::from_slice::>(&resp).unwrap() } let scan: models::Scan = models::Scan::default(); diff --git a/rust/openvasd/src/tls.rs b/rust/openvasd/src/tls.rs index a595599b6..70ae4271a 100644 --- a/rust/openvasd/src/tls.rs +++ b/rust/openvasd/src/tls.rs @@ -260,7 +260,7 @@ impl rustls::server::ClientCertVerifier for ClientSnitch { *ci = ClientIdentifier::Known(end_entity.into()); Ok(r) } - Err(_) => todo!(), + Err(e) => Err(e), } } } diff --git a/rust/osp/src/response.rs b/rust/osp/src/response.rs index 926745812..6e7982aa5 100644 --- a/rust/osp/src/response.rs +++ b/rust/osp/src/response.rs @@ -613,7 +613,7 @@ impl From for models::Status { - i.count_alive.content.0 - i.host.len() as u32, finished: i.count_alive.content.0, - scanning: Some(scanning) + scanning: Some(scanning), }), } } diff --git a/rust/smoketest/src/config.rs b/rust/smoketest/src/config.rs index db291a0c3..e45410570 100644 --- a/rust/smoketest/src/config.rs +++ b/rust/smoketest/src/config.rs @@ -28,20 +28,19 @@ impl Args { pub fn api_key(&self) -> Option<&String> { match self.api_key.is_empty() { true => None, - false => Some(&self.api_key) + false => Some(&self.api_key), } } pub fn key(&self) -> Option<&String> { match self.key.is_empty() { true => None, - false => Some(&self.key) + false => Some(&self.key), } - } pub fn cert(&self) -> Option<&String> { match self.cert.is_empty() { true => None, - false => Some(&self.cert) + false => Some(&self.cert), } } @@ -51,8 +50,7 @@ impl Args { scan_config: env::var("SCAN_CONFIG").unwrap_or_default(), api_key: env::var("API_KEY").unwrap_or_default(), key: env::var("CLIENT_KEY").unwrap_or_default(), - cert: env::var("CLIENT_CERT").unwrap_or_default() + cert: env::var("CLIENT_CERT").unwrap_or_default(), } } } - diff --git a/rust/smoketest/src/lib.rs b/rust/smoketest/src/lib.rs index 256e963b2..c09aadb37 100644 --- a/rust/smoketest/src/lib.rs +++ b/rust/smoketest/src/lib.rs @@ -75,12 +75,7 @@ pub async fn create_scan( } /// Sends an action for the given ScanID. -pub async fn scan_action( - cli: &reqwest::Client, - server: &String, - id: &str, - action: String, -) -> bool { +pub async fn scan_action(cli: &reqwest::Client, server: &String, id: &str, action: String) -> bool { let mut path = String::from(server); path.push_str("/scans/"); path.push_str(id); diff --git a/rust/smoketest/tests/tests.rs b/rust/smoketest/tests/tests.rs index d93ce274f..d80d80e44 100644 --- a/rust/smoketest/tests/tests.rs +++ b/rust/smoketest/tests/tests.rs @@ -14,23 +14,14 @@ pub mod tests { async fn check_server() { let args = Args::get_all(); let cli = new_client(args.api_key(), args.cert(), args.key()); - + let status = match cli.head(args.openvasd()).send().await { - Ok(res) => { - if res.status().is_success() { - true - } else { - false - } - } - Err(_) => { - false - } + Ok(res) => res.status().is_success(), + Err(_) => false, }; - assert_eq!(status, true); - + assert!(status); } - + #[tokio::test] /// Run a successful scan. Get results and delete the scan async fn run_scan() { @@ -48,7 +39,7 @@ pub mod tests { match scan_status(&cli, args.openvasd(), &id).await { Some(Phase::Succeeded) => { if let Some(results) = scan_results(&cli, args.openvasd(), &id).await { - assert_eq!(results.is_empty(), false) + assert!(!results.is_empty()) } assert!(delete_scan(&cli, args.openvasd(), &id).await); success = true; From a4bb04e0fd425fa8987b0f16fe2d73c732c34dce Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Wed, 22 Nov 2023 10:17:14 +0100 Subject: [PATCH 2/2] Refactor: documentation changes. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Christoph Krämer --- rust/examples/nasl-cli/transpile.toml | 37 ++++++++++++++++++--------- rust/feed/src/transpile/mod.rs | 9 +++---- rust/feed/src/verify/mod.rs | 1 - rust/nasl-cli/src/main.rs | 4 +-- 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/rust/examples/nasl-cli/transpile.toml b/rust/examples/nasl-cli/transpile.toml index af5b7bbde..e6969e974 100644 --- a/rust/examples/nasl-cli/transpile.toml +++ b/rust/examples/nasl-cli/transpile.toml @@ -6,28 +6,33 @@ # nasl-cli -v feed transpile -p /tmp/feed -r example/replace.toml # ``` +# Transforms +# > register_product(cpe: ..., location: ..., port: ..., service: 'www') +# To: +# > register_product(cpe: ..., location: ..., port: ..., service: 'www', service_to_be: 'world-wide-web') [[cmds]] [cmds.find] FunctionByNameAndParameter = [ "register_product", [ - { Name = "cpe" }, - { Name = "location" }, - { Name = "port" }, - { NameValue = [ - "service", - "\"www\"", -] }, -], + { Name = "cpe" }, + { Name = "location" }, + { Name = "port" }, + { NameValue = [ + "service", + "\"www\"", + ] }, + ], ] [cmds.with.Parameter.Push] -Named = [ - "service_to_be", - "\"world-wide-web\"", -] +Named = [ "service_to_be", "\"world-wide-web\"", ] +# Transforms +# > register_product(cpe: ..., location: ..., port: ..., service: 'www', service_to_be: 'world-wide-web') +# To: +# > register_product(cpe: ..., location: ..., port: ..., service_to_be: 'world-wide-web') [[cmds]] [cmds.find] @@ -45,6 +50,10 @@ FunctionByNameAndParameter = [ [cmds.with.Parameter] RemoveNamed = "service" +# Transforms +# > register_product(cpe: ..., location: ..., port: ..., service_to_be: 'world-wide-web') +# To: +# > register_product(cpe: ..., location: ..., port: ..., service: 'world-wide-web') [[cmds]] [cmds.find] @@ -54,6 +63,10 @@ FunctionByName = "register_product" previous = "service_to_be" new = "service" +# Transforms +# > register_host_detail +# To: +# > add_host_detail [[cmds]] [cmds.find] diff --git a/rust/feed/src/transpile/mod.rs b/rust/feed/src/transpile/mod.rs index 744b7f24b..a4eedc6a8 100644 --- a/rust/feed/src/transpile/mod.rs +++ b/rust/feed/src/transpile/mod.rs @@ -11,7 +11,7 @@ use crate::{verify, NaslFileFinder}; pub enum FindParameter { /// Find a parameter by name Name(String), - /// Find a parameter by name + /// Find a parameter by name and value NameValue(String, String), /// Find a parameter by index Index(usize), @@ -59,7 +59,7 @@ pub enum ParameterOperation { RemoveNamed(String), /// Removes a parameter found by index Remove(usize), - /// Removes a parameter found by index + /// Removes all parameter RemoveAll, /// Renames a parameter Rename { @@ -107,7 +107,7 @@ impl std::fmt::Display for ParameterOperation { pub enum Replace { /// Replaces name of a function Name(String), - /// Replaces name of a function + /// Remove finding Remove, /// Replace parameter Parameter(ParameterOperation), @@ -258,7 +258,6 @@ impl<'a> Matcher for FunctionNameMatcher<'a> { impl Find { /// Checks if statement matches the wanted search operation pub fn matches(&self, s: &Statement) -> bool { - // FunctionNameMatcher { name, parameter }.matches(s) let (name, parameter) = match self { Find::FunctionByName(name) => (Some(name as &str), None), Find::FunctionByParameter(x) => (None, Some(x as &[_])), @@ -270,7 +269,7 @@ impl Find { } #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -/// Describes what should be replaces +/// Describes what should be replaced pub struct ReplaceCommand { /// The identifier to find pub find: Find, diff --git a/rust/feed/src/verify/mod.rs b/rust/feed/src/verify/mod.rs index 596474888..617b7bf86 100644 --- a/rust/feed/src/verify/mod.rs +++ b/rust/feed/src/verify/mod.rs @@ -264,7 +264,6 @@ impl Hasher { /// Loads a given hashsums file and lazily verifies the loaded filename key of the sums file and verifies /// the hash within the sums file with an calculated hash of the found content. -// pub struct HashSumNameLoader<'a, R> { reader: &'a dyn AsBufReader, hasher: Hasher, diff --git a/rust/nasl-cli/src/main.rs b/rust/nasl-cli/src/main.rs index da07ddeea..0ebc3628e 100644 --- a/rust/nasl-cli/src/main.rs +++ b/rust/nasl-cli/src/main.rs @@ -95,7 +95,7 @@ enum FeedAction { /// /// The rules describe how to find a certain element and how to replace it. /// Currently only toml in the following format is supported: - /// ```text + /// ```toml /// [[cmds]] /// /// [cmds.find] @@ -169,7 +169,6 @@ impl RunAction<()> for FeedAction { cmds: Vec, } - // TODO add from impl let rules = std::fs::read_to_string(rules).unwrap(); let rules: Wrapper = toml::from_str(&rules).unwrap(); let rules = rules.cmds; @@ -470,7 +469,6 @@ When piping a scan json it is enriched with the scan-config xml and may the port verbose: verbose > 0, }, } - // ah } _ => unreachable!("subcommand_required prevents None"), },