Is Google Drive Mini safe?
Google Drive Mini is medium risk. Google Drive Mini requests full Drive OAuth plus offline access. Tokens are stored in chrome.storage.local; the popup uses them to list, rename, delete, and download Drive files. DA did not finish login: no traffic captured.…
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Full Google Drive OAuth Access With Offline Tokens
Google Drive Mini requests full Drive OAuth plus offline access.
Tokens are stored in chrome.storage.local; the popup uses them to list, rename, delete, and download Drive files.
DA did not finish login: no traffic captured.
You sign in to Google from the extension's popup.
The service worker builds a Google OAuth request for Drive and offline access.
The extension stores Google access credentials and uses them for Drive file operations.
The code paths cover file listing, starring, renaming, deleting, and downloading links returned by Drive.
| Field | Value | Why it matters | |
|---|---|---|---|
Full Google Drive permission | https://www.googleapis.com/auth/drive | This lets the extension reach files across your Google Drive rather than only files it created itself. | |
Offline access permission | https://www.googleapis.com/auth/offline | This lets access continue after the initial sign-in by using a refresh token. | |
Stored refresh token | chrome.storage.local service.refresh_token | This local stored value can be exchanged for a fresh Google access token after sign-in. | |
Stored access token | chrome.storage.local service.token | This local stored value is sent as the bearer token for Drive file requests. | |
Drive file endpoint | https://www.googleapis.com/drive/v3/files?pageSize=50&fields=* | This endpoint is where file listing and file actions are sent after sign-in. |
| Accept | application/json |
| Authorization | Bearer ${e.token} |
After sign-in, the extension keeps the Google access token and refresh token in local extension storage for later Drive requests.
chrome.storage.local key 'service'{
"token": "u.access_token",
"refresh_token": "u.refresh_token"
}The service worker requests offline Drive access and stores returned tokens
oauth2: {
client_id: "530311117568-qkkvrkfum8cnhmb87iddli004ns5aj0a.apps.googleusercontent.com",
scopes: ["https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/offline"]
},
S = async e => {
var o;
const n = (o = d.oauth2) == null ? void 0 : o.client_id,
c = "<redacted>",
s = "https://oauth2.googleapis.com/token",
a = {
refresh_token: e,
client_id: n,
client_secret: c,
grant_type: "refresh_token"
};
return fetch(s, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: new URLSearchParams(a)
}).then(t => t.json()).then(t => t).catch(t => console.error("Error refreshing access token:", t))
}, I = "<redacted>";
var _;
const m = (_ = d.oauth2) == null ? void 0 : _.client_id,
g = chrome.identity.getRedirectURL(),
T = async () => {
var o, t;
const {
token: e,
refresh_token: n
} = await r.service.get(), c = !e;
let s = "https://accounts.google.com/o/oauth2/auth?";
const a = {
client_id: m,
redirect_uri: g,
scope: (t = (o = d.oauth2) == null ? void 0 : o.scopes) == null ? void 0 : t.join(" ")
};
if (e) chrome.identity.removeCachedAuthToken({
token: e
}, async function() {
if (n) {
const i = await S(n);
await r.service.set({
refresh_token: i.refresh_token,
token: i.access_token
})
} else await r.service.set({
token: void 0
})
});
else {
const i = {
...a,
response_type: "code",
access_type: "offline",
login_hint: ""
},
p = new URLSearchParams(Object.entries(i));
p.toString(), s += p, chrome.identity.launchWebAuthFlow({
url: s,
interactive: c
}, async function(k) {
const y = new URL(k).searchParams.get("code"),
u = await (await fetch("https://oauth2.googleapis.com/token", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: new URLSearchParams({
code: y,
client_id: m,
client_secret: I,
redirect_uri: g,
grant_type: "authorization_code"
})
})).json();
await r.service.set({
refresh_token: u.refresh_token,
token: u.access_token
})
})
}
};The popup uses the bearer token for Drive file listing and file changes
const wO = 50,
CO = "https://www.googleapis.com/drive/v3/files?",
Of = async e => {
const t = `${e.nextPageToken?`pageToken=${e.nextPageToken}&`:""}${e.orderBy?`orderBy=${e.orderBy}${e.desc?"%20desc":""}&`:""}${e.isStarred||e.search?"q=":""}${e.isStarred?"starred":""}${e.isStarred&&e.search?"%20and%20":""}${e.search?`name%20contains%20'${e.search}'`:""}${e.isStarred||e.search?"&":""}pageSize=${wO}&fields=*`;
return fetch(`${CO}${t}`, {
headers: new Headers({
Authorization: `Bearer ${e.token}`,
Accept: "application/json"
})
}).then(r => r.json()).then(r => r)
}Ql = async (e, t, r) => fetch(`https://www.googleapis.com/drive/v3/files/${e}?key=${{}.REACT_APP_API_KEY}`, {
headers: new Headers({
Authorization: `Bearer ${r}`,
Accept: "application/json",
"Content-Type": "application/json"
}),
method: "PATCH",
body: JSON.stringify({
starred: t
})
}).then(n => n)nP = (e, t) => fetch(`https://www.googleapis.com/drive/v3/files/${e}`, {
headers: new Headers({
Authorization: `Bearer ${t}`,
Accept: "application/json",
"Content-Type": "application/json"
}),
method: "DELETE"
}).then(r => r)iP = (e, t, r) => fetch(`https://www.googleapis.com/drive/v3/files/${e}`, {
headers: new Headers({
Authorization: `Bearer ${r}`,
Accept: "application/json",
"Content-Type": "application/json"
}),
method: "PATCH",
body: JSON.stringify({
name: `${t}`
})
}).then(n => n)- accounts.google.com
Receives the browser OAuth authorization request for the Drive and offline scopes.
- oauth2.googleapis.com
Receives the authorization-code and refresh-token exchanges for Google access tokens.
- www.googleapis.com
Receives Google Drive files API calls for listing files and applying file changes.
Content script polls every page's links every second on all sites
The extension injects a content script into every website, including banking and email sites.
It runs before the page loads, then polls every anchor once per second via setInterval, scanning every page's full link inventory each tick.
You navigate to any website.
The manifest declares matches: ['<all_urls>'], run_at: 'document_start', all_frames: true.
The extension content script begins running in the page before it finishes loading, then polls all anchor elements on the page every second.
This occurs on every site, not just Google Drive or related domains.
Content script: full source (contentScript.js)
(function() {
"use strict";
// Runs after DOM is ready, on EVERY page
document.addEventListener("DOMContentLoaded", () => {
// Poll every 1 second for the lifetime of the tab
setInterval(() => {
scanLinks();
}, 1000);
});
const scanLinks = () => {
// Queries ALL anchor elements on the page, every tick
const links = document.querySelectorAll("a:not(.docs-creator)");
for (let i = 0; i < links.length; i++) {
links[i].classList.add("docs-creator"); // marks as visited
const href = links[i].getAttribute("href");
if (href && href.indexOf("drive.google") !== -1) {
// Only acts on drive.google links — but scans all links to find them
links[i].addEventListener("click", (event) => {
event.preventDefault();
const w = window.screen.width;
const h = window.screen.height;
window.open(
event.currentTarget.href,
"_blank",
`menubar=no,scrollbars=yes,resizable=yes,width=${w*60/100}px,height=${h*60/100}px,left=${w*20/100}px,top=${h*20/100}px`
);
});
}
}
};
})();Scans all anchor elements on the page every 1 second via setInterval, for as long as the tab remains open.
The extension's function — intercepting clicks on Google Drive links to open them in a sized popup window — requires DOM access only on pages that contain such links. The declared scope of `<all_urls>` with `all_frames: true` and `document_start` injection is broader than required for this behavior. The polling loop runs indefinitely on pages with no Drive links, including pages where DOM queries against unrelated anchor elements carry higher sensitivity (internal web apps, banking portals, email clients).
What it can do
Permissions this extension asks for, as declared in version 3.9.9. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.
Read and change your data on every site you visit
<all_urls>
Store data in your browser
storage
Start, monitor and manage your downloads
downloads
Sign you in with your Google account
identity
See the email address of your Google account
identity.email