Attempting Reverse Engineering with Gemini API and Google Apps Script
Abstract
Gemini API on Vertex AI/Studio unlocks new applications with data retrieval and content generation through function calls. This report explores using the API for reverse engineering with a sample interpreter in Google Apps Script.
Introduction
The recent release of the LLM model Gemini as an API on Vertex AI and Google AI Studio unlocks a vast potential for new applications and methodologies. It significantly expands capabilities across diverse situations, paving the way for groundbreaking applications. Notably, the Gemini API allows data retrieval and content generation through function calls. In my recent report, “Guide to Function Calling with Gemini and Google Apps Script”, I explore function calls as a launchpad for various applications. This report showcases reverse engineering using the Gemini API, with a sample interpreter for creating sample values from a given regex using Google Apps Script.
Principle
The above figure illustrates a workflow for this report using the Gemini API, implemented using Google Apps Script.
When Gemini directly creates sample values for a regex, there is a possibility that invalid values are included. To address this, this report proposes checking the created values by sending them to a validation function after they are generated. This process ensures that only valid values are obtained.
In this example, even though the function’s returned values are sent to the Gemini API, I believe they can be directly used as well since they represent the same results.
Usage
In order to test this script, please do the following steps.
1. Create an API key
Please access https://makersuite.google.com/app/apikey and create your API key. At that time, please enable Generative Language API at the API console. This API key is used for this sample script.
This official document can be also seen. Ref.
2. Create a Google Apps Script project
In this report, Google Apps Script is used. Of course, the method introducing this report can be also used in other languages.
Please create a standalone Google Apps Script project. Of course, this script can be also used with the container-bound script.
And, please open the script editor of the Google Apps Script project.
3. Base script
Please copy and paste the following script to the script editor, set your API key, and save the script. The function doGemini_
is used as the base script for using the following sample scripts.
const apiKey = "###"; // Please set your API key.
function doGemini_(regex) {
const functions = {
params_: {
getResults: {
description:
"All values created for the regex. No descriptions and explanations.",
parameters: {
type: "OBJECT",
properties: {
items: {
type: "ARRAY",
description: "All values created for the regex.",
},
},
required: ["items"],
},
},
},
getResults: ({ items }) => {
if (items && items.length > 0) {
console.log("Created values are checked with the function calling.");
const r = regex.constructor == RegExp ? regex : RegExp(regex);
return items.filter((e) => r.test(e));
}
return [];
},
};
const text = `You are the interpreter for outputting sample input texts from the following [regex]. Please follow to [Condition]. Return only results without any descriptions, explanations and comments.
[Regex]
${regex}
[Condition]
Answer by low-temperature parameter.
Create 50 values for matching the following regex.
Confirm whether the result 50 values can be correctly matched to the regex by yourself, return 30 valid values.
Return only results without any descriptions, explanations and comments.`;
const function_declarations = Object.keys(functions).flatMap((k) =>
k != "params_"
? {
name: k,
description: functions.params_[k].description,
parameters: functions.params_[k]?.parameters,
}
: []
);
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=${apiKey}`;
const contents = [{ parts: [{ text }], role: "user" }];
let check = true;
const results = [];
do {
const payload = { contents, tools: [{ function_declarations }] };
const res = UrlFetchApp.fetch(url, {
payload: JSON.stringify(payload),
contentType: "application/json",
muteHttpExceptions: true,
});
if (res.getResponseCode() == 500) {
console.warn("Retry by the status code 500.");
doGemini_(regex);
} else if (res.getResponseCode() != 200) {
throw new Error(res.getContentText());
}
const { candidates } = JSON.parse(res.getContentText());
if (!candidates[0].content?.parts) {
results.push(candidates[0]);
break;
}
const parts = candidates[0].content?.parts || [];
check = parts.find((o) => o.hasOwnProperty("functionCall"));
if (check) {
contents.push({ parts: parts.slice(), role: "model" });
const functionName = check.functionCall.name;
const res2 = functions[functionName](check.functionCall.args || null);
contents.push({
parts: [
{
functionResponse: {
name: functionName,
response: { name: functionName, content: res2 },
},
},
],
role: "function",
});
parts.push({ functionResponse: res2 });
}
results.push(...parts);
} while (check);
const output = results.pop();
if (output.finishReason && output.finishReason == "RECITATION") {
return "No values.";
}
return output.text.split("\n").map((e) => e.trim());
}
Sample 1
Here, the sample values for the regex of /^(?!.*(.).*\1)\d{10}$/
are created.
function sample1() {
const regex = /^(?!.*(.).*\1)\d{10}$/;
const res = doGemini_(regex);
console.log(res);
}
When the regex is const regex = /^(?!.*(.).*\1)\d{10}$/;
(10 digit numbers constructed by unique numbers: https://stackoverflow.com/a/20206052), the following result is returned.
[
"2513467890",
"1928473650",
"3965487201",
"1948762530",
"8234715690",
,
,
,
]
Sample 2
Here, the sample values for the regex of /^([a-zA-Z0-9_-]){3,5}$/
are created.
function sample2() {
const regex = /^([a-zA-Z0-9_-]){3,5}$/;
const res = doGemini_(regex);
console.log(res);
}
When the regex is const regex = /^([a-zA-Z0-9_-]){3,5}$/;
(Texts between 3 to 5 lengths: https://stackoverflow.com/a/4745130), the following result is returned.
[
"vfh2",
"h45l",
"am_m",
"ch2",
"2345",
,
,
,
]
Sample 3
Here, the sample values for the regex of /^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/im
are created.
function sample3() {
const regex = /^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/im;
const res = doGemini_(regex);
console.log(res);
}
When the regex is const regex = /^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/im;
(Phone number: https://stackoverflow.com/a/29767609), the following result is returned.
[
"+441539411123",
"+441539411123",
"+491234567891",
"+918888888888",
"+911234567890",
,
,
,
]
Sample 4
Here, the sample values for the regex of /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim
are created.
function sample4() {
const regex =
/(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
const res = doGemini_(regex);
console.log(res);
}
When the regex is const regex = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
(URL: https://stackoverflow.com/a/3890175), the following result is returned.
[
"https://www.example.com",
"ftp://example.com",
"https://example.com/path/to/file.html",
"https://example.com/path/to/file.php?query=string&foo=bar",
"https://example.com/path/to/file.html#fragment",
,
,
,
]
Sample 5
Here, the sample values for the regex of /^\d{5}(?:[-\s]\d{4})?$/
are created.
function sample5() {
const regex = /^\d{5}(?:[-\s]\d{4})?$/;
const res = doGemini_(regex);
console.log(res);
}
When the regex is const regex = /^\d{5}(?:[-\s]\d{4})?$/;
(Zip code: https://stackoverflow.com/a/2577239), the following result is returned.
[
"12345",
"12345-1234",
"12345 1234",
"99999",
"99999-9999",
,
,
,
]
Note
- If no values or only minimal values are returned, rerun the script as all generated values may be invalid.