diff --git a/bin/sarif-extract-scans b/bin/sarif-extract-scans
index 6da1f44..c2d6e4c 100755
--- a/bin/sarif-extract-scans
+++ b/bin/sarif-extract-scans
@@ -12,7 +12,7 @@ import logging
import pandas as pd
import pathlib
import sarif_cli.table_joins as tj
-import sarif_cli.derived_joins as derived
+import sarif_cli.scan_tables as st
import sys
#
@@ -88,6 +88,16 @@ class ScanTables:
def __init__(self): pass
scantabs = ScanTables()
+@dataclass
+class ExternalInfo:
+ scan_id : int
+ ql_query_id : str
+
+external_info = ExternalInfo(
+ scan_spec['scan_id'],
+ 'deadbeef00', # TODO: Take ql_query_id from where?
+)
+
#
# Add dataframes for base tables
#
@@ -102,13 +112,10 @@ bt.relatedLocations = tj.joins_for_relatedLocations(tgraph, sf_2683)
bt.rules = tj.joins_for_rules(tgraph)
#
-# Form derived query tables
+# Form scan tables
#
-# XX
-# scantabs.project = derived.joins_for_project(bt)
-# scantabs.scans = derived.joins_for_scans(bt)
-# scantabs.results = derived.joins_for_results(bt)
-
+scantabs.results = st.joins_for_results(bt, external_info)
+scantabs.scans = st.joins_for_scans(bt, external_info)
#
# Replace the remaining internal ids with snowflake ids
diff --git a/notes/state-and-tables.drawio b/notes/state-and-tables.drawio
index 452c396..e048873 100644
--- a/notes/state-and-tables.drawio
+++ b/notes/state-and-tables.drawio
@@ -1 +1 @@
-7Z1Zb9u6Esc/TR4TiPKmPGbpdpEcFKf3nLZPASMztlpZdCkqifvpLymL8kLGcbYZFXeAArUoKbLIH6nRfxYf9M5m9x8Un08v5VjkB3E0vj/onR/EMRsxZv6zLYumJY6alonKxsu2aNXwJfstmgNda5WNRdm0LZu0lLnO5puNqSwKkeqNNq6UvNs87Ebm442GOZ8Ir+FLynO/9Ws21tNlazKIVu0fRTaZuiuzqNlzzdOfEyWrorleIQux3DPj7s80h5ZTPpZ3a029dwe9MyWlXn6a3Z+J3PbrZo+9f2Bv+5WVKPQ+J5wW2e11/El+LavLk3/U9K/PHz4eNn+l1AvXFWJseqbZlEpP5UQWPH+3aj2tb1fYv8rM1uqYCynnTeMPofWiGWZeaWmapnqWN3vFfaa/mc/R0aDZ+r6259xiFbmNhdsotFosT4oHbvv7+s7VefWWO9HvJnfPslJpc9O/vl2cSPb7mF195fpE/ffy6tPhYdxrSORqIvSuA4fLA23XrV2iGYYPQs6E+UbmACVyrrPbTeh4w+6kPW41iOZDM47hMd35vW95XjWXOoiHubmD03LOC/N5Un/mKrs5FPda8VQfzioz28xJB4PTw+nB4Lz5mK4+lrYbVSbVYZnyolztmNc75A8zMZvW+oCjH6U0F4tkpeeVPhxnyn0Lc1frX8RDcBOwu2mmxZc5r8fqzixAmzDx61LmlRYnKm2Aq1tXW317r1rJn+3UNkNzeiML/Z7PstxyMjP4pnIXLLdCaXG/c3CbvX23pjVr4SBptu9WC0vsiJmuLypuTXl9IIYeEMtBsUs217xeMZVds4SaZMXEG5DyLpvlvBCQ/ZYE+u3YLcjr/dY2hvqtudzfhk1eTMz9POl6bBi4npte7nI810IVXItTi23pjVZ7q88fwAHzBtDOPfvs09mNmb7lUVreLpseGzy3tttZkk6zfHzBF4YG01Jq8zBzW6dTqbLf5njuJprZrXQzqQxR60d8sWc2660SpTnms0OBbTVd8vuNAy94qZuGVOY5n5fZdfv9ZtzyeCq1lrPmoDB/N3k2//fBBX/3pNib0HgTmP6evLDes/jc42pJiM4hPJ2xR2fL5ZU5cxtI0926XZPPZG6Wnt55bTiZgczyfKuJ59mkMJu5uLGn2fHKjOV20jRra3TYB1tqVq6L+pjz/qrl76ZrbJM0597ktQ02zcZjUdTGjDYr4BI5S89cZoWu+25wav6ZHj6zZop9rJ2ZbbbaNv/s4UqfycLcC89q3ITh+U5YpvdeKndP+cfxXGyO/KPr1/BhHF/0mBn4dkdmnuP3BAAMAG4e4gHQ9wCoVEbDDzP8owR7+Aeh4T/lpfhEDwEgCFjUx6bAf9lYGqapHAvb8WSrQtiqveRR83EUwKLnznuisbrH5bpirY48Ph2ZZKw+d53an88dxmqIx7dbppIdGJDVCkdCyGqFJeHYI0FPleBjYgGahZAJC8qCs5TWWMhlynUmCyIBkoSgHQuLgq+5imJshrOaFUQBEAW9Pc3Wt6PA1zYNBReZ8/ISA2/OwADbVBz6umb9fklrASgHCbahOPTlzZoDWg0A3xsjdBPRVzmdt4tMRFAUeugmoi91kr8DUEJCNw59KZEcHtAUJOjmoa8kzkRZ2ohTYgCEgR5DNw19DXHp4fppTIKrOdfTuZKmq2fk+3p731e/1+DwsDMq7gXoSKIdeOwIJHzm5RB8XyNf3jSjW+W6vOJK8QU5wJ65ZrXz/yUOsL0oebUla+Trm1ss0MsMHA4BLxgwDr7Qaf2h723ABi0LYBwEPGDAHPhip6pyeqEBIyDk+QJGwNc5awToeQBIQcDzBUyBr3O2rnAyEOCBCLjBgIHw1c5VbAQ9HqA4CLjBgDnwVc+WA4qQgH6NDPjDgHHw5c91HMg5CghDwCMGDIOvg7YwUMwEgsiEbUUmvuK4CQStD4A4BHxlwDj4oiPF12KgEHKZAaPgC44tChRFAQdCD9uATHzFcR0ECqcAxWGIbUImvvrY4kBxFbAwHKObj74IWdfQEuML0iLRuOgzdDvS1yK3uSBJEgyHProt6UuS2ziQMglNxRDdsPSVyQAVJEAAMnGMbl36AuU2E6RTIkQ/YRuax75OGeSCVgtAKvrYZuZxKEZyy8ykFw9AIlyUIh4Rvmq5TQSJl3A8HGPbmMeBcEmfB9IwIakYxthW5nEggnKLCpIyYZkYoFuYvpTZMHBVjzmBAAPCCN2o9LXLucpmXC3c4mBfMj7yckpMACVdROhmpS9gbjHxZaVKvDeDI5TZX9CqAUVIjG5oBsTMKhfk5oBjYIBuVgbEy5oB0iAgM/SwLcn2h4IeqDZAlQYOgCoNDJ074Ymp/y1DT6s0sM/l2B4kAlQaYFFIOqVSAy9fsVaz/8+pNcCcgU3FBjoBBHq1ARZRmjkyA+iVBtpEd8ozR8QAv9wAiyjTvFtIoNceYJGvkVKuOQIJ6EUHWETZ5l0CAr36AIso37wzOOBXH2ARZZx3Cwn0GgTMSWCUc94JINBrEDBGWeddgQG9AgFzie+Ud46LAn4FAsYo87xLQKBXImCMcs+7gwN6JQLGKPu8k2SglyVgzNcnKf8cDQj8egSMUQZ6B7lAL0zAGOWgd44K9MIEjFEWeifJQC9PwFy0JuWhd4gL/PIELKZM9I4xgV6cgMW+kkm56IhEoBcnYDFlo3eQC/QiBSymfPSuUYFfpIDFlJHeDRTQaxOw2NczKScdmQr0QgUspqz0bjOCX7iAxZSXjk4BenECFlNmehdAQK9QwAa+TXlQJ6FvvW+U/+fp6XtC8PSUdNefbYT2vjniLpT7aSnpvc3L9Z6rlEKkpA98I9dM2yrV9LB67hrVTvcX5aLvU7PgFZco36olmRQOgFDuOTAAvslKPjVoDELp58AY+DYrOd3hQQgmoMOS4DJU1kigoBxgCkI558AU+C52CtkD5yCUcQ7MQexxQM4xYApCaebAFPiu9EbIqHJB6gWMetEPVLhrn9brHLQR4E9TL2L3w2E75As26Ih8MQyXS6J6ei9cntqZ/hINI0jlG65OgcykdRZIdgfEIaBoQOPgK5u0HICNf0DKgB5/X9Is+IzsVSgCQhoGNAK+qCkK27u0DoBRENAwoCkIVEMStyInBqAYCOgXwAyMfDXzpsrzc1GmKptb5zvRAEVDQMeApsFXNcup6SLCAeG9MVAuDxoHX9y0PxpBCEAhECiPB42Ar2zOlUizkpYCSAkJ3VYc+XpiKdJKZXpxWBqj0X4gHqB4CBTGg+bB1xQJA2AMQkXxoDEIRElW17QigKMQKIcHjYIvK2o+Ie8CLAeBOnjQHPjaouWAkkQhMQgUvYPGwBcXD+oYiDLlhbnaSSHuPB4oHOIl4RDuGdAmje8JQWtHPC0eYrQVf9HpeIjE1zlTOZtllM7x7GWpneF/UChE4guccyV/CErrgeQAPwbCSe1rHIyvr1IlzCq0rJlGMADBgB8Qkfga5zoMZqSIBRgWOhAakQSETmOxLheFq7FBgmiAogE/RCIJyJxLGuScYACFAT9WIvHFTi1lfkXRc5Ac4EdJJAGl03JgboLcopCvk/gREklA7LQo/KqEWlyRwgCOBH7ERBLIFOcquzE0FFpQvTNAtQndfDz2VcclC2ZcBdkNoDSgx0y01ZU8l0gjQVKi6Pbgs71HudnrUkDb3/rZcwlorconpoRuXY+FlpxkD8gAXCBx5GvfZJk80zn3nF+2RV9+fM3b+T7oSQRIArr3I458wbtWu21pdxK1AFFA933EUajY/1yWmZbmBbZSlAwGxQK+7yOOfLW7KeF9ZWzPSUVVbQBxQHd+xJGvdzsMyite8HzxmxKG4YCAdICcFtntdfxJfi2ry5N/1PSvzx8+Hg69sRbjiXCvoqarpnJi+iF/t2o1A2DeZ4T9q/ZVcHXMhbQDWzf+EFovmldSXmnbi1M9cy+s4j7T3+zoHcWDZvP72q5z9yJabyzcRmHud3nWwG1+X9+3Oq3ecuf5o9e8M5WyUqnYOaCNU8C8X0/EzqFvjC/bdzvHvS6Cnd2KjS/yBrPc92a4EE0lflUG9sAkJzliPzmC7aEPvGJI5l7X60hIZuu9oZDM13uqjPYmtDuyhO8voZBMcA7wRQm3dFFIJjoM+LIE87VqCslEYaEDsoSzhSgkE58GfFWC+eI1hWTiwIAekhkzX76mkExwDtBDMg2KYQ4oJBP4dRI9JDNmD0RpU0gmEhLoIZkxC0icFJKJojbhm4+B3/OikEwcGvBDMlkgWLtxfoj7rNRmQDwcyPfxnFDMYfK2ro/BH+T5iH2pk+ySl60+7UT+gzwfsS9ykucDnAN8z0fs65vk+cCBAd/zEVMxio6w0AHPR0zFKDpDA77nI6ZiFF2BAd/zET8gc5KCAckBvucjpmIUnUChA56PmIpRdAsJfM9HTMUoOsICvuej56uO5PlAogHS8xHM5el5Yw2Yy+M+1yk5Ry6t57FUnoP1RJ6jUZvY8/q5PE6f73AuT3BQ+3/6oMZvOKgum/FPG1TfvjeLsnVR2TPNWz+3y539Mnx8KIvc/82VNX/lhstxZ38+oez8VhX4wIMuDi1trUPwJT0WXxb/6Zcy7v3oLY6/9S6S/l+fkdY2hzTbRPoxonc9erYIfBT8YG+wJ2EPQPmub7lRGmmYu2ez2XPDm7sc/qqkbW/6qt02nya1sZLy4qg2bNxfMF9o+UeWB/gTpDZvvja8xpvjWk753B43u58oPp8eWasknZppdDSWaTUzw2FPuJtmWnyZL7/jnTnQnrlEpVnVHphWgTF9cKaNoq2pNgrYlFFoqj19pplNJW2/tvs+2Nu/lGNhj/gf7Z1bc9u21oZ/jSbthT0iqeOlHSc7yed0d+r2a3KloSVYokORCgnaVn79BkCCOmBJghxpgZ2uGU8iUqR4wENg8V0HtIK385f/ZOFi9jmdsLjltycvreCm5fvewAvEf3LNslrTDjrlmmkWTcp17dWKu+gH0xtWa4towvJqXbmKp2nMo8XmynGaJGzMN9aFWZY+b272kMaTjRWLcMqMFXfjMDbX/h1N+KxcO+i2V+s/sGg600f22tU381BvXK3IZ+EkfV5bFbxrBW+zNOXlp/nLWxbLu7d5X97v+LY+sYwl3GaH5berrz/yxfLdfTz+kCZ/x+NgclH9Ss6X+oLZRFx/tZhmfJZO0ySM363WXmdpkUyY/FVPLK22uU3TRbXykXG+rBozLHgqVs34PK6+ZS8R/yI+ty+71dLXtW9uJDxtvbDUCwnPluVOflcvf13/crWfWtI7lhcor2rnfdM3IS2yMdtzs/yKvzCbMr5nu6BuXfFcsHTOxPmI/TIWhzx62jyPsOJzWm+3akLxoWrFI1q0OsmnMC6qI7X8XixO9zpfhIn4PFWfwyx6uGAvPAvH/GJeiAdK7NTqXl/MWt2b6uN49TGXtyyL0uwiH4dJvvpiob5IH8WzV61VG1w+5qk4WDst+KLgF5Mo02chLmr9RAz+Nul6nkWc3S1C1S7Poo/ZJCm8z9O44OwqG1e0qbWrpY68Vp6l3+qnV7TM9UOa8PfhPIolJHPB7jitT+WJZZy97GfFbNtqh47utqrurjuolp9XfYfv98p1s/V+Q3cbP8PD1c3MZzG7/nzHR8XV44dPt79/uRh03D7i/fVnvP26Z9w78Izva8+TPPmB+eSDN9sfunz0A+PRvw9zVj+F6gLuYzGabgORP0fzOEwY5qMxAB6NoR5W1x+NeiV0v6rD/SG6nzCZxuy443k94Hh+sHm4MOYsS0LOruVDkRutVF/q6xuuA/TZbWXA8OhBdND55Th/Klcdajv9XMtHZjyL4sltuBTNL9bkPBx/00vXszSLfojtQ/3Eiq8zXj3Pooda3+JO7lk9ahnLxTa/62b3tlZ9Dl82NrwNc16tGKdxHC7y6L4+v7l4nKLkOuU8ne99kh/iaPH/G+P5ARSPGZarX/E3celY0uIFr6LT4mgDiM0eOptdg82aypHYcRtH0Sq8HnPfpnGaifVJWvIZxfHWqjCOpolYjNmD3E02aySM76tqNZfDjTRcxlEyvVXb3HRWa/6o7oxclYp9H2JlYM+iyYQlahjjoeryKnYWaZRwdeu61+JP3OC30gaVZstbseytlsWf3Dzjb9NEXEsYKbKYoPmZSaJ/pp/c3QWYcC432/1g39XbDeNPDS09g4JIGAgv1Pwozd/1HTd/32j+Iouo8VEavz9w3PgDqPGvhWX5kbp/HAS8dscxA8MdFuo4nTB528lCRbBQO96G0dj1AKuxD1ARBINX2ah6t+YbqfpOrPGpySQj9XS91A51Y4+RCuF4tk7K8/ZQQOYqGgiQuYoLgimC81nGwgmhgIwCZLziomCKonE6DnmUJgQCIgigCYtLgqmysmQiWrOYJwQBDgSBpcV6PghMOVNAcBuJViQEUBDoOrcSTS1TvVlST4CJwcC5jWhqmgoD6gvwXhjbzq1DU9vU3i2yDjFJCJxbh6bCSS4OPOXItV3omwIiOTmQIRi4tgx9Uz+cszyXEcKEAAYCgRbu3CFgKoelV+ubMAdGi5DPFlkq7vSc/F1n93d1gqoxdvuf/ACAY9DeQ8eegMFXHs6Bu8s3RU3RuEXM81GYZeGSfF6n67HKHuEYp5cVJafrsUxZc4sFeo/BwwFwfSHjYAqc0gf6XsZoULeAxgHg90LmwFQ5syKmtxk0AiCHFzICpsKpEKDxAJECwOOFTIEpcdYOcDIQ8IEA/F/IQJhK5yoigoYHLA4ABxguB4EpedYcUGAE9msk4ApDxsEUP9dxILcoIgyANwwZBlMGrWGgYAkHIpNrKzLYE0ZJYRPYOACeMmQcTNGRompdoAB5zJBRMAXHGgWKoMADIXBuQJqK4zoIFEuBikPPuQlpqo81DhRVgQvD0Ln5aIqQqiQSm9ySFumMi47n3I40tchtLkiSRMOh49qW7JiS5DYOpExiU9FzbVh2TGUSoIIECEQmhq6tS91V7WGCdEoH0U+uDc0OFBkJcEG9BSIVHddmZgeKkdwyM+nFA5GIvnNL01Qtt4kg8RKPh6FzGxMIlzR5IA0Tk4qe79zKBCIot6ggKROXia5zC9OUMisGRqrNCQQcEPrOjUpTu1xk0TzMlrpzkC8ZH8J8RkwgJV20XZuVXVPA3GLibqVKvBeNwzLxfUK9BhYhvmtDswuImUXMyM2Bx0DXtVmpkwQNBkiDwMzQc21Jdk2tsrVWa4DqDLRw6gz0BpUld2Tif03QcXUGbA7nWXCIMffLwdxyGrVO1V11d0DalDoDXUgxpToDjnBwXmegCwimlF+OSIDzCgNdSi93DoH7IgNdSi9vFBDO6w10Kb28CRw4LzPQM6VQSi93hoPzagM9Si9vCgzuaw30TAGU0ssdAuG83kCP0ssbhIPzagM9Si9vCArOKw30KL28CSC4rzPQo/TyBuHgvNpAj9LLGwOD81oDPUovbyIXzssO9Ci9vDk4uK820DclSUovd02F86IDfUovbxoTzksO6PxESi9vFBfOyw70Kb28cVS4LzrQp/TyZhHhvOBAn9LLm8SD83IDfSBaktLLHVPhvOhAn9LLG8aE+5IDfUovbwQIzusM9Cm9vGlMOC85MDAFTEovbxAh7gsQDCi93DUDzksM6FhfSi93iYHzKgODXenlW+8Y+b88xfz1COxPK9e3s466ts3z1uHZx6WVB5uHC16riiKklQ9MvVQ8ssWY0zB1OjgHx+eT21QdOF33ZEqkJImiNT+UP47b/KYiSp4zZAigFHJcCEwBlNzq6BiASeS4HJiiJ4Xc4DIA5Y3jMmDqnRSMh00BlDWOSsHQVDjJBYbLAJQqjsuAqWFWwkURM1IrUNSKDlCVzusBFNRR3cepFb42/fbIFV63GXLFENZTqQbeyREtn/xjNAuQyfP1TED45zoJJLDjwQAoGMgwmComdQVYrQ9IF8itb8qXSTgnKxWp/SHNAhkAU8Bkiby31AdgMQBoFsgMAEno7InFRAASAYBegUyAqVw+FHF8w/JxFi2ke51YQGIB0C2QWTAVzHwmbhDBgP+mCJS4w4XBa5tKppzXgQhAIgCoaYdNgKljLjI2jnLqCBAlI9c2oqdjgteHBTYusogvL3JhLcoPhAMSDkA9O2wcTA2RKMClACpmh00BEAJZ3FN/gE0CUMcOmwRTSOThlHwJqBgAFeywMTDlRIkB5XwiUgDUq8OmwBQUWyrWIR+HiTjaVcKeDRwo7GFfox8Ie+hUv7N7Mj4QgdqEOC7sob8VZtHksAevbWqb43Q+jyhN44R4Vo98c2MevDaUiJ4+MkrXweTAebiD55mC5uR+NM6Y6IXKqmcEAxIMzqMfPM/UNtdhEC1FLOCw4D4SwtO20LqYIezVslMYTQQSRAMWDc5jIjwPEDhLGtIFwYAKg/PwCM8zdU6epvGIYuUwOXAeGuF5gMopORCXS+5QzNdJ94ERHqB0ShS+FyxbjkhhQEfCfaSEB+SBh1n0IGhIOKP6ZYhqk3vz0VQdSxZEuzKyG1BpwIyWuLqZ+Sxm15/v+Ki4evzw6fb3LxdAJqj4V37oTbm69nalRdZpob1YfVFtRq6SFQ/Vb7LJcZQccqBUo4d2oIAvoJ3jmNntQKmdEtq09f2hHaN19c7zuFBAfE1dhKdi+V5eUMYWcTimbJEtejcfS5uubXfH0QinCnh6pkSipFNZ8ZsUkteNcK/AANOnAp6eKY5U5XxHYpCaFlT1AosETIcKeHqmPKJ9rGTxolGA6koBz8+URkgVcUICphsFPL9digi9BSOTgOlDAc8P0EOkO436AiwCML0n4PkBeaXkXndAAqrzBD4/MwaLfOsuSMD0mcDnZ0qj5FhHhgDTWQKfHyQwklcdmwP3bhJoqo9OPp6xeUgQoECAmlMKnx+QUqpeGak3QAUBM6UUPj9g8uGCpqBGhQAzoRQ+P1NKFG+L0UM45jlBgAMBZj4pfH6mijhWBfX/jyoRYVFQF8h2R4GpIGYsL2LqCLAQ6Di3DQEJkc3nMbt8SLN5yO8WbBw9RCwjInCI6Lk2En1ASSyJyNMiG7NbCkBABWLo2mD0TUFxkkXiSklSxIxIcm0y+qakmGbTMIl+hFTAFI+Djmuj0d8x1QUBgANA37XJ6JtyYjUekJ6ISsLQuakI6IlskeYRT7PlXzRnLxIIPd+5iWhqihl7UvWMP5KchERBF9FC3JGhZ4oJLVWkrgpZpin6LJp+RytXO+gydLWGaNnodazakbPxbR3PgyIbBg0pSwcoFxQLebqc0PLpbkTq1I72N4UKypVwQIL7inSAVkFpdC5QcF+PDpIr6neUUZHRlFxYLDSgHh0gXVBepSsc3BekA/QLjUE+CpMwXv6gNHw8INwXpdO/vNbabDJl+mVU3KxZOhV3In63WiuaQLzRqNoZ8mVwtc1tKptWrXxknC+rl9Kw4PI+zvhcv7Kyl4h/ke13KYAsF7+ufXWjX0XVwlIvJOKKy726evHr+ner3dSS3q+8QHlVO1uvWlW6e/feriqKSLxyT9m+pvfhhs9YLGyyp80zOUOrAimTVdX8jH0vBO3AU056hJ0e4VkIBCesk291vKbUyfepTj7CsOI3vU6+b8qiVCcfnQP3qkRg6pNUJ98NDO51iYDq5DeEhQboEoGpV1IivyMa3MsSAdXJbwoM7iWJgOrkN4AD93XyA6qT3wgUGlAnP6A6+c1Cwn2d/IDq5DeEBfd18gOqk98YGjALwMBn2PWM1kZ0aenPyjN1qb1bhzxarXV/1mW/9m8huLR0vGPDXVpazPzntqqP2ara0d/0VjVf/EXHLN1Ucl/x5h/KLk+eTji5SJN4aUCw5rXccDy2bbu5/d6+7UmqgcEu0N7W9e5t0N7j7PvJe2Yaw6WIqpr7Pi5nVKiKGrR/2XfH/n1+3ldO/FGBaotJd9g1R8FB97hRcLdL2Nuc+KPXCUwoB0PzaGee92PHnTMN9VaVELHxJ1/f2h9/+7N84F/xd3EhW7IMbhOfvjGzsyCr75VWX9nnHONxBgE8o9FnvgKAkNT1oo9DTbGlQmguSWZAwwpwYGNjtSu/a+uvFKIUV3d//vHxt//sR0vhNI24GpCkeKX7v/RBDuIz2dd/j/XvStgYCRpY1AGecmTqtJF5iLo4HVeVKNapI0xQMIGc6NicmBEVICdzlucq0J8wwccE8K5jY+IfhckovZeRemLFf68/geOYGr6IICyCAJc8NkFQUe1ddg2BgQUG4KPHBsOM1dgNxrcomYwWIZ+NFlkqWmAuTzN8kuPSJMp5lKhOp9I25W7v5WKUfCs7oXL3es8ilxWYKmM5Vz6e9oPgyG/fp+JuEIQ4EELRAdgQmoEiIISlKDriy8WWKbSH1hq3Vn/F4AbCxBkOZ0DIATZnpvBOo6B7MCCXDDIYlhL3OJ2w96IlLAVIxVGsxr+2mixb7i9bUvl1oNG09JDJ3/ztr9tbuUIMj9lzlLNfiUcsHoEICGweLcVw6qgwwYCmxcEGw1LOLm3wMhnjNkqkuSRvH8lGeLAAU+cgw9K3VKHXYRHNTaygswLMsIPNiqUSXbHCkgl1K05QAebhwUbFUo1eoUKdigNSoLl6sEkh1bmJYAAz+GCDYak6S+2YjFiHqABT+2CjYqkNr1Ch0cYFKcCcP9ikWKq7ihQyYJ2FBLo3YC3VXg0KdSguOAFmDcLmhFTYJoIBzCaEDcZxKmwdjgV6rBU9M3HaLLuI2RMr+5oHcROvxKc3YTbN35S7vKlKP1aLjI8viTos6oCZi5Cp03NWWA1bmrmj8iNKCsWHksMSQPbCxgVnmsFchbULQ5uzubilxCI+i9DkSdgsmnLxr/U1EwLnRwCYOQkbgYMysJHUmqQTNtrIbDXNJkpuPXtya69rjmX1wU6e3NrvAL2VFgA3jxZsHg4juXVwUKJWI+Ofs0gSq/55LHJpzIUtGZCYiMZNpq2NxK/1KJ8KfXpvPFnPWPY7R2WywrSdrWM8KG4rpEL1U7JLlJyoHLBQ3QCKfUaDBcpPRYbFUt7e+tuIPyRYUGCB0kqRYbFUuLf+qrB5YgafGTDHFBkaS7V7608FJkfJRNyRHZHOahTjw/5wqLcjqHCggjJSQaiGZ4PKUhrf+lMvgBZQee1+d6ST43PCCxcvXTPDYZ9lKbCbA92xkudDlsq8C53YkRNlWJQNzApbyJTpUMojKauLMRwAbZ0vnZkvaRr5BBmWGOB5ziE7GFhNwjkmEbrstDsiAnNw03P7sReVRj81cPhXq+Kn1r99wHlyygn/upuHa/R8f7p4MM33d85OqXzkj9GskUtud8xRiub7Q+fA/Xx/OjWA5vtzDoP7+f46pm+U5vtzwkID5vvrmF5Nmu/PEQ3u5/vrmG5Lmu/PDQzu5/vrmG5Jmu8PnQP38/11TE8jzffn4nXS/Xx/HdM/SPP9uUTC/Xx/HUD2pPn+nKhNzs3Hrqk60nx/jmhowHx/HaO1MWaG07O81Qtrc8PtmeRtXwOdZuq37j9k6jcodLUXa4jFNw9hdaG970Uq11e3q14Wn6bqqRbvjpeqB9C/IE6p/JFyAwOQsh/4u2LW32zafBYu5Hbzl2kWLmaX8vEdz8KMX07ScSHT1uQOz7OIs7tFeY7PYkO5Z0lLNdef1VO3f/q5fnvTGRT0gc63DTxu+hH8mXa7upn5LGbXn+/4qLh6/PDp9vcvFz4UD0jNttVs3lazDZw3GxRxR8221Wxbnl6oWhpys5lWrzIcZM5HWex+zqR//KDL/5U2wf77NdQ1U+rcejMoq6uFhfX7VVtwJ79hQI1uHR0h1ibs+dCN+nfFRhzOGNyDpW3ARMekYnhkrunuaInDx+roCaHXD1ZXMTtPtATMpsuZMOVPFUn0vVjNkxEKe3eZq6TEX5JUzb0he/Vf5XI1TyKVDX9Fp7mnYzomhgPk9hSvUfD5WYbkr4lvh6c6tJnnsAoUUdCVRoV4A3mKpOT7kfjD4g+IHUHmzzJmfz2uyKqTrDpDtRdN3ooGFBB/gguUbflyKr+FSAUUiYKMhWWlciNurX1z9ec7qPiWjFfgEWnOaAwB8SvIDPnHMyTD3SCEiCJHFAGBL8gUUXnzBmIBxMEgY2Fb3Hw7fHLnAHXPplGSlHWGyk3flz1N+Romf0j894sMroiksih/odQDJvdKDBAXFsqakvQ2hqUGACE4yBTa1k3fCtvcN8QRh/84DoG4H2QOac7NBmIBhAAhY2Eppa8FEluqlbKUg6pRW04/HU85zfGLhRUQS4SMlaUIvhmXvJMsBZR/KYtEVjh5lzp+gnA6N07QfJvIOFlq2kZY88FZgbd8Ksp+Ghc5V9VCagOKOEPhDJiqE5cz29LuEh1iAoUJYEpOZCZsZ+TcDpzeHs2qAsZMb9vSFSHFB7lbS4cT8LLIcfkaR5ihYAYEqCFjZkrhVJAIDwBolk5cAAJz7NEl+inkDi/kzuttls0H4uA8LziOi91BdxZH62ijZCNOtr91OISou8ByIDxL1B31gifpBctO5qjgOQi/83WClg7hKqryWNAUWSqMmQKY0KCCIuJwobL0D5fqgV1AZssuJlMJoOp3JWqMkmuRmIOC5nCZs3Q+6+Lam8wRJBiQgDF0uJRYOofrUscECTokUJAcLiSWnltd27qe5vS/15/AEYxcvJj8QOFxuPxYungJC0wsoPA4XCyOmVX7W5RMRmoqmkWWivsvvWOz8EmOSBNVKlt1OFVJBe2jlRPglh1QuXu9Z5GrvFteaeHzlfR9n9Ksa2gv+1BsHC6Clm7dauIsvlwcCD9ZY7WGrdVfEbgBMFGGQhkU+YZKGVBancY/51hAkW+4WFjK2XpKIku5UVEUq5GvzdPW5iy0v0DjaJrEVVr5b3/d3soVYmDMnqOcUXguFo1QwBwujZbCN3VSiFiAgW+4WFhK16XlXWax3EaJNJPkzSOZCA0VKHYNFxXbdKc1VERjEynYpEARbbik2KYklaSwZEJdigtQoJg0XFAslecVKNSh4HMChq7hckIKcwOx0K8U7rCwVJilTkymqztQes5NV0sdeAUKjTMOOBm6NlyBcvW7OSGz1VW4n2uztWup7GpMqDNxQEnHtdGqw1LJaG0SFroimDssjlNc62Ar0Cut2JmJs2bZRcyeWNnPPKRlRs+bMJvmb8pd3mTseyFuebXI+PiSmENibuja/u3aSrdywNLEHZXzUDIoPpQUlvixFzYuONME5ipYXZjXnMnJAohEdBJ7vnML25SGKQsVEQD9huMOAFPyTdIJG1Eq6r52P08qaqAdNXtSUbV08vOpqBZHC3RJxc2jBZuHQ0hF7R4UnKtiC6qQgvrnscilmRa2ZDhhIppWVc9bS9Vaj9MpexN6GzxZv9c9etKGHaydq9s7KFUroEL1S7JDlJSorK1QXT5FLWOhAmSTIqNiKVZv/W1EDxIqGKgASaC4qOgY1yNRqcLdiRh0YqCMUGRkLLXrrT8VUhwlE3E/dsQoq/GLD/vDod6OkEJBCsgfhZEangspS6F760+99lkg5bX73ZFOY88JLlS4tDHirr+ylMvNIe5YCfMhUzU5dToGVeXEYmxgLQScizFLeXzrry6aYDOvWUWXzqGXLI18Qgzp9d/zXCN2MCSaZHBEHnTJgjPwIBazVE7DvdIj5WzZn8WwIrf4Hw==
\ No newline at end of file
+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
\ No newline at end of file
diff --git a/sarif_cli/scan_tables.py b/sarif_cli/scan_tables.py
new file mode 100644
index 0000000..9c4292c
--- /dev/null
+++ b/sarif_cli/scan_tables.py
@@ -0,0 +1,165 @@
+""" Collection of joins for the derived tables
+
+"""
+import pandas as pd
+from . import snowflake_id
+
+# id --
+# commit_id -- pathval(r02s01, 'commit_sha')
+# project_id -- project.id
+# db_create_start -- pathval(r02s01, 'created_at')
+# db_create_stop
+# scan_start_date
+# scan_stop_date
+# tool_name -- pathval(r02s01, 'tool', 'name')
+# tool_version -- pathval(r02s01, 'tool', 'version')
+# tool_query_commit_id -- pathval(r02, 0, 'tool', 'version') is sufficient
+# sarif_content -- r02s02
+# sarif_file_name -- used on upload
+# sarif_id -- pathval(r02s01, 'sarif_id')
+# results_count -- pathval(r02s01, 'results_count')
+# rules_count -- pathval(r02s01, 'rules_count')
+#
+def joins_for_scans(basetables, external_info):
+ """
+ Return the `scans` table
+ """
+ # XX
+ pass
+
+#
+# Results table
+#
+def joins_for_results(basetables, external_info):
+ """
+ Form and return the `results` table
+ """
+ # Get one table per result_type, then stack them,
+ # (kind_problem,
+ # kind_pathproblem,
+ # )
+ return pd.concat([_results_from_kind_problem(basetables, external_info),
+ _results_from_kind_pathproblem(basetables, external_info)])
+
+def _results_from_kind_problem(basetables, external_info):
+ b = basetables; e = external_info
+ flakegen = snowflake_id.Snowflake(2)
+ res = pd.DataFrame(data={
+ 'id': [flakegen.next() for _ in range(len(b.kind_problem))],
+
+ 'scan_id' : e.scan_id,
+ 'query_id' : e.ql_query_id,
+
+ 'result_type' : "kind_problem",
+ 'codeFlow_id' : 0, # link to codeflows (kind_pathproblem only, NULL here)
+
+ 'message': b.kind_problem.message_text,
+ 'message_object' : pd.NA,
+ 'location': b.kind_problem.location_uri,
+
+ # for kind_problem, use the same location for source and sink
+ 'source_startLine' : b.kind_problem.location_startLine,
+ 'source_startCol' : b.kind_problem.location_startColumn,
+ 'source_endLine' : b.kind_problem.location_endLine,
+ 'source_endCol' : b.kind_problem.location_endColumn,
+
+ 'sink_startLine' : b.kind_problem.location_startLine,
+ 'sink_startCol' : b.kind_problem.location_startColumn,
+ 'sink_endLine' : b.kind_problem.location_endLine,
+ 'sink_endCol' : b.kind_problem.location_endColumn,
+
+ 'source_object' : pd.NA, # TODO: find high-level info from query name or tags?
+ 'sink_object' : pd.NA,
+ })
+ return res
+
+
+def _results_from_kind_pathproblem(basetables, external_info):
+ #
+ # Only get source and sink, no paths. This implies one codeflow_index and one
+ # threadflow_index, no repetitions.
+ #
+ b = basetables; e = external_info
+ flakegen = snowflake_id.Snowflake(3)
+
+ # The sarif tables have relatedLocation information, which result in multiple
+ # results for a single codeFlows_id -- the expression
+ # b.kind_pathproblem[b.kind_pathproblem['codeFlows_id'] == cfid0]
+ # produces multiple rows.
+ #
+ # The `result` table has no entry to distinguish these, so we use a simplified
+ # version of `kind_pathproblem`.
+
+ reduced_kind_pathp = b.kind_pathproblem.drop(
+ columns=[
+ 'relatedLocation_array_index',
+ 'relatedLocation_endColumn',
+ 'relatedLocation_endLine',
+ 'relatedLocation_id',
+ 'relatedLocation_index',
+ 'relatedLocation_message',
+ 'relatedLocation_startColumn',
+ 'relatedLocation_startLine',
+ 'relatedLocation_uri',
+ 'relatedLocation_uriBaseId',
+ ])
+
+ # Per codeflow_id taken from b.kind_pathproblem table, it should suffice to
+ # take one codeflow_index, one threadflow_index, first and last location_index
+ # from the b.codeflows table.
+ #
+ # To ensure nothing is missed, collect all the entries and then check for
+ # unique rows.
+ cfids = reduced_kind_pathp['codeFlows_id'].unique()
+
+ source_sink_coll = []
+ for cfid0 in cfids:
+ cfid0t0 = b.codeflows[b.codeflows['codeflow_id'] == cfid0]
+ cfid0ppt0 = reduced_kind_pathp[reduced_kind_pathp['codeFlows_id'] ==
+ cfid0].drop_duplicates()
+ assert cfid0ppt0.shape[0] == 1, \
+ "Reduced kind_pathproblem table still has multiple entries"
+ for cfi0 in range(0, cfid0t0['codeflow_index'].max()+1):
+ cf0 = cfid0t0[cfid0t0['codeflow_index'] == cfi0]
+ for tfi0 in range(0, cf0['threadflow_index'].max()+1):
+ tf0 = cf0[ cf0['threadflow_index'] == tfi0 ]
+ loc_first = tf0['location_index'].min()
+ loc_last = tf0['location_index'].max()
+ source = tf0[tf0['location_index'] == loc_first]
+ sink = tf0[tf0['location_index'] == loc_last]
+ # Note that we're adding the unique row ids after the full table
+ # is done, below.
+ res = {
+ 'scan_id' : e.scan_id,
+ 'query_id' : e.ql_query_id,
+ #
+ 'result_type' : "kind_pathproblem",
+ 'codeFlow_id' : cfid0,
+ #
+ 'message': cfid0ppt0.message_text.values[0],
+ 'message_object' : pd.NA,
+ 'location': cfid0ppt0.location_uri.values[0],
+ #
+ 'source_location' : source.uri.values[0],
+ 'source_startLine' : source.startLine.values[0],
+ 'source_startCol' : source.startColumn.values[0],
+ 'source_endLine' : source.endLine.values[0],
+ 'source_endCol' : source.endColumn.values[0],
+ #
+ 'sink_location' : sink.uri.values[0],
+ 'sink_startLine' : sink.startLine.values[0],
+ 'sink_startCol' : sink.startColumn.values[0],
+ 'sink_endLine' : sink.endLine.values[0],
+ 'sink_endCol' : sink.endColumn.values[0],
+ #
+ 'source_object' : pd.NA, # TODO: find high-level info from
+ # query name or tags?
+ 'sink_object' : pd.NA,
+ }
+ source_sink_coll.append(res)
+ results0 = pd.DataFrame(data=source_sink_coll).drop_duplicates().reset_index(drop=True)
+
+ # Now add the snowflake ids
+ results0['id'] = [flakegen.next() for _ in range(len(results0))]
+
+ return results0
diff --git a/scripts/table-tests.sh b/scripts/table-tests.sh
index cabb6aa..e759647 100644
--- a/scripts/table-tests.sh
+++ b/scripts/table-tests.sh
@@ -5,3 +5,4 @@
#
( cd ../data/treeio/2021-12-09 && sarif-extract-tables results.sarif test-tables )
( cd ../data/treeio && sarif-extract-multi multi-sarif-01.json test-multi-table )
+( cd ../data/treeio && sarif-extract-scans scan-spec-0.json test-scan )