{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Plotting notebook\n", "\n", "This notebook generates plots for paper submission. Prior to running this, make sure to run `temporal_trends` and `vulnerabilities` notebooks to produce necessary CSV files into `RESULTS_DIR` folder." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from pathlib import Path\n", "\n", "import matplotlib\n", "import matplotlib.pyplot as plt\n", "import pandas as pd\n", "import scipy.stats as stats\n", "import seaborn as sns\n", "\n", "from sec_certs.dataset import CCDataset\n", "\n", "RESULTS_DIR = Path(\"./results\")\n", "FIGURE_DIR = RESULTS_DIR / \"figures/\"\n", "\n", "if not FIGURE_DIR.exists():\n", " FIGURE_DIR.mkdir()\n", "\n", "# matplotlib.use(\"pgf\")\n", "sns.set_theme(style=\"white\")\n", "plt.rcParams[\"axes.linewidth\"] = 0.5\n", "plt.rcParams[\"legend.fontsize\"] = 6.5\n", "plt.rcParams[\"xtick.labelsize\"] = 8\n", "plt.rcParams[\"ytick.labelsize\"] = 8\n", "plt.rcParams[\"ytick.left\"] = True\n", "plt.rcParams[\"ytick.major.size\"] = 5\n", "plt.rcParams[\"ytick.major.width\"] = 0.5\n", "plt.rcParams[\"ytick.major.pad\"] = 0\n", "plt.rcParams[\"xtick.bottom\"] = True\n", "plt.rcParams[\"xtick.major.size\"] = 5\n", "plt.rcParams[\"xtick.major.width\"] = 0.5\n", "plt.rcParams[\"xtick.major.pad\"] = 0\n", "plt.rcParams[\"pgf.texsystem\"] = \"pdflatex\"\n", "plt.rcParams[\"font.family\"] = \"serif\"\n", "plt.rcParams[\"text.usetex\"] = True\n", "plt.rcParams[\"pgf.rcfonts\"] = False\n", "plt.rcParams[\"axes.titlesize\"] = 7\n", "plt.rcParams[\"axes.labelsize\"] = 7\n", "plt.rcParams[\"legend.handletextpad\"] = 0.3\n", "plt.rcParams[\"lines.markersize\"] = 4\n", "sns.set_palette(\"deep\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Histogram CVE disclosure dates vs. date of certification" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "df = pd.read_csv(RESULTS_DIR / \"exploded_cves.csv\").rename(columns={\"Unnamed: 0\": \"dgst\"}).set_index(\"dgst\")\n", "\n", "hist = sns.histplot(df.n_days_after_certification, kde=False)\n", "hist.set(\n", " xlim=(-2200, 4600),\n", " ylim=(0, 1800),\n", " xlabel=\"Number of days after date of certification\",\n", " ylabel=\"Frequency of CVEs\",\n", ")\n", "hist.axvline(0, color=\"red\", linewidth=\"1\", label=\"Certification date\")\n", "hist.legend(loc=\"upper right\")\n", "\n", "fig = matplotlib.pyplot.gcf()\n", "fig.set_size_inches(3.35, 2)\n", "fig.savefig(FIGURE_DIR / \"cve_hist.pgf\", bbox_inches=\"tight\")\n", "fig.savefig(FIGURE_DIR / \"cve_hist.pdf\", bbox_inches=\"tight\")\n", "plt.close(fig)\n", "\n", "# QQ plot of for vulnerability disclosure vs. certification date (compared against normal distribution)\n", "# stats.probplot(df.n_days_after_certification, dist=\"norm\", plot=plt)\n", "# plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Validity boxplot" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "df_validity = pd.read_csv(RESULTS_DIR / \"df_validity.csv\")\n", "\n", "box = sns.boxplot(data=df_validity, x=\"year_from\", y=\"validity_period\", linewidth=0.75, flierprops={\"marker\": \"x\"})\n", "box.set(\n", " xlabel=\"Year of certification\",\n", " ylabel=\"Lifetime of certificates (in years)\",\n", " title=\"Boxplot of certificate validity periods\",\n", ")\n", "box.tick_params(axis=\"x\", rotation=75)\n", "\n", "fig = matplotlib.pyplot.gcf()\n", "fig.set_size_inches(3.5, 2.5)\n", "fig.savefig(FIGURE_DIR / \"boxplot_validity.pgf\", bbox_inches=\"tight\")\n", "fig.savefig(FIGURE_DIR / \"boxplot_validity.pdf\", bbox_inches=\"tight\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Cells for 3-subplot figure\n", "\n", "Contains: Average EAL levels, Interesting schemes evolution, Stackplot of categories" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Downloading CC Dataset: 100%|██████████| 164M/164M [02:07<00:00, 1.35MB/s] \n" ] } ], "source": [ "figure_width = 2.3\n", "figure_height = 1.8\n", "\n", "dset = CCDataset.from_web() # local instantiation\n", "df = dset.to_pandas()" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "avg_levels = pd.read_csv(RESULTS_DIR / \"avg_eal.csv\")\n", "eal_to_num_mapping = {eal: index for index, eal in enumerate(df[\"eal\"].cat.categories)}\n", "avg_levels[\"smartcard_category\"] = avg_levels.category.map(\n", " lambda x: x if x == \"ICs, Smartcards\" else \"Other 14 categories\"\n", ")\n", "line = sns.lineplot(\n", " data=avg_levels,\n", " x=\"year_from\",\n", " y=\"eal_number\",\n", " hue=\"smartcard_category\",\n", " errorbar=None,\n", " style=\"smartcard_category\",\n", " markers=True,\n", ")\n", "line.set(xlabel=None, ylabel=None, title=None, xlim=(1999.6, 2023.4))\n", "ymin = 1\n", "ymax = 9\n", "ylabels = [\n", " x if \"+\" in x else x + r\"\\phantom{+}\" for x in list(eal_to_num_mapping.keys())[ymin : ymax + 1]\n", "] # this also aligns the labels by adding phantom spaces\n", "line.set_yticks(range(ymin, ymax + 1), ylabels)\n", "line.set_xticks([1998, 2003, 2008, 2013, 2018, 2023])\n", "line.legend(title=None, labels=avg_levels.smartcard_category.unique())\n", "\n", "fig = matplotlib.pyplot.gcf()\n", "fig.set_size_inches(figure_width, figure_height)\n", "fig.tight_layout(pad=0.1)\n", "fig.savefig(FIGURE_DIR / \"temporal_trends_categories.pgf\")\n", "fig.savefig(FIGURE_DIR / \"temporal_trends_categories.pdf\")\n", "plt.close()" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "interesting_schemes = pd.read_csv(RESULTS_DIR / \"interesting_schemes.csv\")\n", "\n", "line = sns.lineplot(\n", " data=interesting_schemes,\n", " x=\"year_from\",\n", " y=\"size\",\n", " hue=\"scheme\",\n", " style=\"scheme\",\n", " markers=True,\n", " dashes=True,\n", ")\n", "line.set(xlabel=None, ylabel=None, title=None, xlim=(1999.6, 2023.4), ylim=(0, 90))\n", "line.set_xticks([1998, 2003, 2008, 2013, 2018, 2023])\n", "line.legend(title=None)\n", "fig = matplotlib.pyplot.gcf()\n", "fig.set_size_inches(figure_width, figure_height)\n", "fig.tight_layout(pad=0.1)\n", "fig.savefig(FIGURE_DIR / \"temporal_trends_schemes.pgf\")\n", "fig.savefig(FIGURE_DIR / \"temporal_trends_schemes.pdf\")\n", "plt.close()" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "n_certs = pd.read_csv(RESULTS_DIR / \"popular_categories.csv\").astype({\"year_from\": \"category\"})\n", "dct = {\n", " \"ICs, Smart Cards and Smart Card-Related Devices and Systems\": \"ICs and Smart Cards\",\n", " \"Network and Network-Related Devices and Systems\": \"Network-Related Devices\",\n", " \"Other Devices and Systems\": \"Other Devices\",\n", " \"One of 11 other categories\": \"11 Other Categories\",\n", "}\n", "n_certs.popular_categories = n_certs.popular_categories.map(lambda x: dct.get(x, x))\n", "\n", "cats = n_certs.popular_categories.unique()\n", "years = n_certs.year_from.cat.categories[:-1]\n", "data = [n_certs.loc[n_certs.popular_categories == c, \"size\"].tolist()[:-1] for c in cats]\n", "\n", "# palette = sns.color_palette(\"Spectral\", 5).as_hex()\n", "# colors = \",\".join(palette)\n", "\n", "plt.stackplot(\n", " years,\n", " data,\n", " labels=cats,\n", ")\n", "plt.legend(loc=\"upper center\", bbox_to_anchor=(0.38, 1.02))\n", "plt.xticks([1998, 2003, 2008, 2013, 2018, 2023])\n", "# plt.title(\"(c) Popularity of categories\")\n", "plt.xlim(1997, 2023)\n", "\n", "fig = matplotlib.pyplot.gcf()\n", "fig.set_size_inches(figure_width, figure_height)\n", "fig.tight_layout(pad=0.1)\n", "fig.savefig(FIGURE_DIR / \"temporal_trends_stackplot.pdf\")\n", "fig.savefig(FIGURE_DIR / \"temporal_trends_stackplot.pgf\")\n", "plt.close()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.8.13 ('venv': venv)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.5" }, "orig_nbformat": 4, "vscode": { "interpreter": { "hash": "a5b8c5b127d2cfe5bc3a1c933e197485eb9eba25154c3661362401503b4ef9d4" } } }, "nbformat": 4, "nbformat_minor": 2 }