Skip to content

Instantly share code, notes, and snippets.

@LovelyBuggies
Created May 1, 2022 01:03
Show Gist options
  • Save LovelyBuggies/078139f8f3afe6d3b09c1120dd1c74e8 to your computer and use it in GitHub Desktop.
Save LovelyBuggies/078139f8f3afe6d3b09c1120dd1c74e8 to your computer and use it in GitHub Desktop.
Tensor Representation of Code for Vulnerability Detection
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"id": "73678dea-edb3-4ad2-a615-ea84f344542e",
"metadata": {},
"source": [
"# Code Vulnerability Detection on IBM/D2A "
]
},
{
"cell_type": "markdown",
"id": "c33dd29e-7bb1-4c39-8437-0fb3894cd736",
"metadata": {},
"source": [
"## 1. Introduction\n",
"\n",
"The current dataset contains the samples generated from 6 open-source projects, OpenSSL, FFmpeg, HTTPD, NGINX, Libtiff, and Libav. For each project, there are 3 pickle.gz files like `nginx_after_fix_extractor_0.pickle.gz`, `nginx_labeler_1.pickle.gz`, and `nginx_labeler_0.pickle.gz`, which are generated by two slightly different extractors. Each `pickle.gz file` contains compressed samples in JSON.\n",
"\n",
"The [field and discription](https://dax-cdn.cdn.appdomain.cloud/dax-d2a/1.0.0/data-preview/index.html?_ga=2.207765842.1169579792.1649770384-1526562817.1648132481) of the data is:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "cd232a8f-a906-4729-a862-0e8af1864a00",
"metadata": {},
"outputs": [],
"source": [
"# import split_data as sd\n",
"# data = sd.read_pickle_data_file(\"./dataset/httpd_labeler_0.pickle.gz\")"
]
},
{
"cell_type": "markdown",
"id": "a8628673-e813-46a4-91ef-67f9a65e6261",
"metadata": {},
"source": [
"## 2. Data Preparation and Preprocessing\n",
"\n",
"In Leaderboard Dataset, there are 4 directories corresponding to 4 tasks of the leaderboard. Each directory contains 3 csv files corresponding to the train (80%), dev (10%) and test (10%) split. The columns in the split files are identical except the test split which does not contain the label column. In this project, we are going to predict **whether a code snippet contains bugs or not** using the `Function` dataset.\n",
"\n",
"Firstly, we are going to obtain the codes of train, dev, and test."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "703bab51-b6d5-4d22-82f5-1213a048124b",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Loaded: 4643 samples in train; 596 samples in dev; 618 samples in test.\n"
]
}
],
"source": [
"import csv\n",
"\n",
"snippets_train = list()\n",
"snippets_dev = list()\n",
"snippets_test = list()\n",
"with open('./dataset/d2a_lbv1_function_train.csv') as csvfile:\n",
" reader = csv.reader(csvfile)\n",
" for i, row in enumerate(reader):\n",
" if i == 0: \n",
" continue\n",
" snippets_train.append((row[2], row[1]))\n",
"\n",
"with open('./dataset/d2a_lbv1_function_dev.csv') as csvfile:\n",
" reader = csv.reader(csvfile)\n",
" for i, row in enumerate(reader):\n",
" if i == 0: \n",
" continue\n",
" snippets_dev.append((row[2], row[1]))\n",
" \n",
"with open('./dataset/d2a_lbv1_function_test.csv') as csvfile:\n",
" reader = csv.reader(csvfile)\n",
" for i, row in enumerate(reader):\n",
" if i == 0: \n",
" continue\n",
" snippets_test.append(row[1])\n",
" \n",
"print(f\"Loaded: {len(snippets_train)} samples in train; {len(snippets_dev)} samples in dev; {len(snippets_test)} samples in test.\")"
]
},
{
"cell_type": "markdown",
"id": "bacf61a4-2f32-4fe9-a259-112f3c5166d6",
"metadata": {},
"source": [
"## 3. Syntax Tree and Code Tensorization\n",
"\n",
"Tree-sitter is a parser generator tool and an incremental parsing library. It can build a concrete syntax tree for a source file and efficiently update the syntax tree as the source file is edited. To use C++ parser in Python code:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "4e40daa1-1978-4b64-85d6-4d2cad265da4",
"metadata": {},
"outputs": [],
"source": [
"from tree_sitter import Language, Parser\n",
"\n",
"Language.build_library(\n",
" 'build/my-languages.so',\n",
" [\n",
" 'vendor/tree-sitter-cpp'\n",
" ]\n",
")\n",
"CPP_LANGUAGE = Language('build/my-languages.so', 'cpp')\n",
"parser = Parser()\n",
"parser.set_language(CPP_LANGUAGE)"
]
},
{
"cell_type": "markdown",
"id": "e5465da1-58f0-41b1-9377-6974792516f6",
"metadata": {},
"source": [
"Try to put the second snippet in training set to the [playground](https://tree-sitter.github.io/tree-sitter/playground)!"
]
},
{
"cell_type": "markdown",
"id": "26b3cd71-62a5-4722-bb4e-104a7eb8c6b9",
"metadata": {},
"source": [
"<details>\n",
"<summary>The second snippet:</summary>\n",
"\n",
"```cpp\n",
"static ngx_int_t\n",
"ngx_http_file_cache_lock(ngx_http_request_t *r, ngx_http_cache_t *c)\n",
"{\n",
" ngx_msec_t now, timer;\n",
" ngx_http_file_cache_t *cache;\n",
"\n",
" if (!c->lock) {\n",
" return NGX_DECLINED;\n",
" }\n",
"\n",
" now = ngx_current_msec;\n",
"\n",
" cache = c->file_cache;\n",
"\n",
" ngx_shmtx_lock(&cache->shpool->mutex);\n",
"\n",
" timer = c->node->lock_time - now;\n",
"\n",
" if (!c->node->updating || (ngx_msec_int_t) timer <= 0) {\n",
" c->node->updating = 1;\n",
" c->node->lock_time = now + c->lock_age;\n",
" c->updating = 1;\n",
" c->lock_time = c->node->lock_time;\n",
" }\n",
"\n",
" ngx_shmtx_unlock(&cache->shpool->mutex);\n",
"\n",
" ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n",
" \"http file cache lock u:%d wt:%M\",\n",
" c->updating, c->wait_time);\n",
"\n",
" if (c->updating) {\n",
" return NGX_DECLINED;\n",
" }\n",
"\n",
" if (c->lock_timeout == 0) {\n",
" return NGX_HTTP_CACHE_SCARCE;\n",
" }\n",
"\n",
" c->waiting = 1;\n",
"\n",
" if (c->wait_time == 0) {\n",
" c->wait_time = now + c->lock_timeout;\n",
"\n",
" c->wait_event.handler = ngx_http_file_cache_lock_wait_handler;\n",
" c->wait_event.data = r;\n",
" c->wait_event.log = r->connection->log;\n",
" }\n",
"\n",
" timer = c->wait_time - now;\n",
"\n",
" ngx_add_timer(&c->wait_event, (timer > 500) ? 500 : timer);\n",
"\n",
" r->main->blocked++;\n",
"\n",
" return NGX_AGAIN;\n",
"} \n",
"```\n",
"\n",
"</details>"
]
},
{
"cell_type": "markdown",
"id": "f9c5f568-1fb2-4c22-9ceb-a14e10ed0015",
"metadata": {},
"source": [
"To tensorize the data, we need to obtain tokens from the leaf nodes of the syntax tree. It's better to use DFS here, since it keeps the original order of the words in the code."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "e3cd7b63-a2da-413e-b73f-9efb2dda316e",
"metadata": {},
"outputs": [],
"source": [
"def code_tokenize(code: str):\n",
" tree = parser.parse(code.encode())\n",
" root = tree.root_node\n",
" tokens = list()\n",
"\n",
" def DFS(tree, root, tokens):\n",
" if not root.children:\n",
" tokens.append(code.encode()[root.start_byte:root.end_byte].decode())\n",
" return\n",
"\n",
" for child in root.children:\n",
" DFS(tree, child, tokens)\n",
" \n",
" \n",
" DFS(tree, root, tokens)\n",
" return tokens"
]
},
{
"cell_type": "markdown",
"id": "ccc813b0-e29a-460a-ac23-f128e05e4737",
"metadata": {},
"source": [
"After getting the tokens, we store them in a corpus, and use word2vec model to get the vector of each word. "
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "dbb47a96-2f56-496c-91b1-b3b167698e5f",
"metadata": {},
"outputs": [],
"source": [
"from nltk.tokenize import sent_tokenize, word_tokenize\n",
"\n",
"import gensim\n",
"from gensim.models import Word2Vec\n",
"import os\n",
"\n",
"corpus_train = [code_tokenize(code) for code, _ in snippets_train]\n",
"corpus_dev = [code_tokenize(code) for code, _ in snippets_dev]\n",
"\n",
"if os.path.exists(\"./model/word2vec.model\"):\n",
" model = Word2Vec.load(\"./model/word2vec.model\")\n",
"\n",
"else:\n",
" model = gensim.models.Word2Vec(corpus_train + corpus_dev, min_count = 1, vector_size = 64, window = 4)\n",
" model.save(\"./model/word2vec.model\")"
]
},
{
"cell_type": "markdown",
"id": "a44c78fd-eac0-4d3b-a4e6-58c85fca7f74",
"metadata": {},
"source": [
"Then, the training set with tensors could be generated. Since the length of the code snippets varies a lot, we use the `tensorflow.image.resize` to resize the variable-sized tensors to the same size tensors. "
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "cd40a26d-3d22-41c0-b842-1160a9d7f565",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2022-04-30 20:41:10.693662: I tensorflow/core/platform/cpu_feature_guard.cc:151] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations: AVX2 FMA\n",
"To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.\n"
]
}
],
"source": [
"import numpy as np\n",
"import tensorflow as tf\n",
"\n",
"y_train = tf.image.convert_image_dtype(np.asarray([s[1] for s in snippets_train]).astype('float32'), tf.float32)\n",
"y_dev = tf.image.convert_image_dtype(np.asarray([s[1] for s in snippets_dev]).astype('float32'), tf.float32)\n",
"\n",
"x_train_l, x_dev_l = list(), list()\n",
"for tokens in corpus_train:\n",
" code_matrix = np.asarray([model.wv[token] for token in tokens])\n",
" code_matrix_tf = tf.image.convert_image_dtype(\n",
" np.resize(code_matrix, (code_matrix.shape[0], code_matrix.shape[1], 1)),\n",
" tf.float32\n",
" )\n",
" code_matrix_resized = tf.image.resize(code_matrix_tf, (64, 64))\n",
" x_train_l.append(code_matrix_resized)\n",
"\n",
"for tokens in corpus_dev:\n",
" code_matrix = np.asarray([model.wv[token] for token in tokens])\n",
" code_matrix_tf = tf.image.convert_image_dtype(\n",
" np.resize(code_matrix, (code_matrix.shape[0], code_matrix.shape[1], 1)),\n",
" tf.float32\n",
" )\n",
" code_matrix_resized = tf.image.resize(code_matrix_tf, (64, 64))\n",
" x_dev_l.append(code_matrix_resized)\n",
" \n",
"x_train = tf.image.convert_image_dtype(np.asarray(x_train_l, dtype=float), tf.float32)\n",
"x_dev = tf.image.convert_image_dtype(np.asarray(x_dev_l, dtype=float), tf.float32)"
]
},
{
"cell_type": "markdown",
"id": "e69c19d1-4bf7-4db5-b983-3ebe02e9197d",
"metadata": {},
"source": [
"## 4. Binary Classification\n",
"\n",
"Afterward, the tensors of codes can be trained in convolutional neural network, just like the image binary classification task. Here we use accuracy, loss, recall, precision, and f1 as metrics."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "3ace61b9-98cd-44ba-ba9f-dadb5be9f70a",
"metadata": {},
"outputs": [],
"source": [
"from keras import backend as K\n",
"\n",
"def recall_m(y_true, y_pred):\n",
" true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))\n",
" possible_positives = K.sum(K.round(K.clip(y_true, 0, 1)))\n",
" recall = true_positives / (possible_positives + K.epsilon())\n",
" return recall\n",
"\n",
"def precision_m(y_true, y_pred):\n",
" true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))\n",
" predicted_positives = K.sum(K.round(K.clip(y_pred, 0, 1)))\n",
" precision = true_positives / (predicted_positives + K.epsilon())\n",
" return precision\n",
"\n",
"def f1_m(y_true, y_pred):\n",
" precision = precision_m(y_true, y_pred)\n",
" recall = recall_m(y_true, y_pred)\n",
" return 2*((precision*recall)/(precision+recall+K.epsilon()))"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "d148b990-96ba-4d1e-b014-fd682138d7da",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2022-04-30 20:47:03.108646: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"INFO:tensorflow:Assets written to: ./model/cls-model/assets\n",
"146/146 [==============================] - 2s 13ms/step - loss: 0.2433 - accuracy: 0.9164 - f1_m: 0.9142 - precision_m: 0.9836 - recall_m: 0.8584\n",
"19/19 [==============================] - 0s 13ms/step - loss: 3.3293 - accuracy: 0.5470 - f1_m: 0.4966 - precision_m: 0.5963 - recall_m: 0.4497\n"
]
},
{
"data": {
"text/plain": [
"[3.3292691707611084,\n",
" 0.5469798445701599,\n",
" 0.49656686186790466,\n",
" 0.5962578654289246,\n",
" 0.44966161251068115]"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"if os.path.exists(\"./model/cls-model\"):\n",
" cls_model = tf.keras.models.load_model(\"./model/cls-model\")\n",
"\n",
"else:\n",
" cls_model = tf.keras.models.Sequential([\n",
" tf.keras.layers.Conv2D(16, (1,1), activation='relu', input_shape=(64, 64, 1)),\n",
" tf.keras.layers.MaxPooling2D(2, 2),\n",
" tf.keras.layers.Conv2D(32, (1,1), activation='relu'),\n",
" tf.keras.layers.MaxPooling2D(2,2),\n",
" tf.keras.layers.Conv2D(64, (1,1), activation='relu'),\n",
" tf.keras.layers.MaxPooling2D(2,2),\n",
" tf.keras.layers.Conv2D(64, (1,1), activation='relu'),\n",
" tf.keras.layers.MaxPooling2D(2,2),\n",
" tf.keras.layers.Conv2D(64, (1,1), activation='relu'),\n",
" tf.keras.layers.MaxPooling2D(2,2),\n",
" tf.keras.layers.Flatten(),\n",
" tf.keras.layers.Dense(128, activation='relu'),\n",
" tf.keras.layers.Dense(1, activation='sigmoid')\n",
" ])\n",
" cls_model.compile(\n",
" loss='binary_crossentropy',\n",
" optimizer=tf.keras.optimizers.RMSprop(learning_rate=0.001),\n",
" metrics=['accuracy', f1_m, precision_m, recall_m]\n",
" )\n",
" history = cls_model.fit(\n",
" x_train,\n",
" y_train,\n",
" epochs=100,\n",
" verbose=0\n",
" )\n",
" cls_model.save(\n",
" './model/cls-model',\n",
" overwrite=True,\n",
" include_optimizer=True,\n",
" save_format=None,\n",
" signatures=None,\n",
" options=None,\n",
" save_traces=True\n",
" )\n",
" \n",
"cls_model.evaluate(x_train, y_train)\n",
"cls_model.evaluate(x_dev, y_dev)"
]
},
{
"cell_type": "markdown",
"id": "e54d5633-36ad-440a-910c-ddd1372996d0",
"metadata": {},
"source": [
"Here are the curves with respect to different metrics during training."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "b7ed7bd8-d95d-4d72-88d8-dbdbdae4c3db",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"import matplotlib.pyplot as plt\n",
"\n",
"plt.plot(history.history['accuracy'], label='Accuracy')\n",
"plt.plot(history.history['loss'], label='Loss')\n",
"plt.plot(history.history['f1_m'], label='F1')\n",
"plt.plot(history.history['precision_m'], label='Precision')\n",
"plt.plot(history.history['recall_m'], label='Recall')\n",
"plt.legend()\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d6767fc2-6426-47ee-b0ba-421d44fe1f60",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "hist",
"language": "python",
"name": "hist"
},
"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.9.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment