Skip to content
729 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays1//! The security service: secrets found in what is pushed and in history,
2//! vulnerable dependencies, and the upgrades g1t opens for them.
3//!
4//! The repos service reads git for it (`scan_history`, `find_lockfiles`)
5//! and asks it, during a push, which secrets have been allowed
6//! (`push_blocked`). Members of a workspace see and act on its findings;
7//! nobody else does, whether or not the repository is public.
8
9use serde::{Deserialize, Serialize};
10
11use crate::User;
12use crate::repos::RepoPath;
13
14/// Where a secret stands.
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
16#[serde(rename_all = "lowercase")]
17pub enum SecretStatus {
18 /// In the repository's history: it has to be rotated, then resolved.
19 Open,
20 /// A push carrying it was refused, so it never landed.
21 Blocked,
22 /// Someone said it is not a real secret; pushes carrying it go through.
23 Allowed,
24 /// Someone rotated or removed it.
25 Resolved,
26}
27
28impl SecretStatus {
29 pub fn as_str(self) -> &'static str {
30 match self {
31 SecretStatus::Open => "open",
32 SecretStatus::Blocked => "blocked",
33 SecretStatus::Allowed => "allowed",
34 SecretStatus::Resolved => "resolved",
35 }
36 }
37
38 pub fn parse(text: &str) -> Option<SecretStatus> {
39 Some(match text {
40 "open" => SecretStatus::Open,
41 "blocked" => SecretStatus::Blocked,
42 "allowed" => SecretStatus::Allowed,
43 "resolved" => SecretStatus::Resolved,
44 _ => return None,
45 })
46 }
47}
48
49/// Why a person dismissed an alert. The first four are for secrets, the
50/// rest for vulnerable dependencies; see [`DismissReason::for_secrets`].
51#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
52#[serde(rename_all = "snake_case")]
53pub enum DismissReason {
54 /// Not a secret at all.
55 FalsePositive,
56 /// A value made for tests or examples.
57 UsedInTests,
58 /// It was real, and has been revoked or rotated: the alert is fixed.
59 Revoked,
60 /// Real, and accepted as it is.
61 WontFix,
62 /// Someone is already upgrading it.
63 FixStarted,
64 /// Nobody can get to it now.
65 NoBandwidth,
66 /// The vulnerability does not matter for how this project uses it.
67 TolerableRisk,
68 /// The advisory is wrong about this package or version.
69 Inaccurate,
70 /// The vulnerable code is never called.
71 NotUsed,
72}
73
74impl DismissReason {
75 pub const ALL: [DismissReason; 9] = [
76 DismissReason::FalsePositive,
77 DismissReason::UsedInTests,
78 DismissReason::Revoked,
79 DismissReason::WontFix,
80 DismissReason::FixStarted,
81 DismissReason::NoBandwidth,
82 DismissReason::TolerableRisk,
83 DismissReason::Inaccurate,
84 DismissReason::NotUsed,
85 ];
86
87 pub fn as_str(self) -> &'static str {
88 match self {
89 DismissReason::FalsePositive => "false_positive",
90 DismissReason::UsedInTests => "used_in_tests",
91 DismissReason::Revoked => "revoked",
92 DismissReason::WontFix => "wont_fix",
93 DismissReason::FixStarted => "fix_started",
94 DismissReason::NoBandwidth => "no_bandwidth",
95 DismissReason::TolerableRisk => "tolerable_risk",
96 DismissReason::Inaccurate => "inaccurate",
97 DismissReason::NotUsed => "not_used",
98 }
99 }
100
101 pub fn parse(text: &str) -> Option<DismissReason> {
102 DismissReason::ALL.into_iter().find(|reason| reason.as_str() == text)
103 }
104
105 /// For people.
106 pub fn label(self) -> &'static str {
107 match self {
108 DismissReason::FalsePositive => "False positive",
109 DismissReason::UsedInTests => "Used in tests",
110 DismissReason::Revoked => "Revoked",
111 DismissReason::WontFix => "Won't fix",
112 DismissReason::FixStarted => "A fix has already been started",
113 DismissReason::NoBandwidth => "No bandwidth to fix this",
114 DismissReason::TolerableRisk => "Risk is tolerable to this project",
115 DismissReason::Inaccurate => "This alert is inaccurate or incorrect",
116 DismissReason::NotUsed => "Vulnerable code is not actually used",
117 }
118 }
119
120 /// Whether it closes a secret alert (the rest close dependency alerts).
121 pub fn for_secrets(self) -> bool {
122 matches!(
123 self,
124 DismissReason::FalsePositive | DismissReason::UsedInTests | DismissReason::Revoked | DismissReason::WontFix
125 )
126 }
127
128 /// The status a secret takes: revoked means fixed (`resolved`); the
129 /// others say it is no danger, so pushes carrying it go through
130 /// (`allowed`).
131 pub fn secret_status(self) -> SecretStatus {
132 if self == DismissReason::Revoked { SecretStatus::Resolved } else { SecretStatus::Allowed }
133 }
134}
135
136/// Where an alert stands, as the Security page's filters and the API put it.
137#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
138#[serde(rename_all = "lowercase")]
139pub enum AlertState {
140 /// Needs someone: a secret open or blocked, a dependency still vulnerable.
141 Open,
142 /// Someone said why it can stay.
143 Dismissed,
144 /// A secret revoked, or a dependency no longer vulnerable.
145 Fixed,
146}
147
148impl AlertState {
149 pub fn as_str(self) -> &'static str {
150 match self {
151 AlertState::Open => "open",
152 AlertState::Dismissed => "dismissed",
153 AlertState::Fixed => "fixed",
154 }
155 }
156
157 pub fn parse(text: &str) -> Option<AlertState> {
158 Some(match text {
159 "open" => AlertState::Open,
160 "dismissed" => AlertState::Dismissed,
161 "fixed" => AlertState::Fixed,
162 _ => return None,
163 })
164 }
165}
166
167/// A secret found in a repository. The secret itself is never kept: only
168/// a fingerprint, to know it again, and a preview a person recognises.
169#[derive(Clone, Debug, Serialize, Deserialize)]
170#[serde(rename_all = "camelCase")]
171pub struct SecretFinding {
172 pub id: String,
173 pub repo_id: String,
174 /// `aws_access_key`, `github_token`, …
175 pub kind: String,
176 /// "an AWS access key".
177 pub label: String,
178 pub path: String,
179 pub line: u32,
180 pub commit: String,
181 pub preview: String,
182 pub status: SecretStatus,
183 /// `push` or `history`.
184 pub source: String,
185 /// Who pushed it, for a push.
186 pub found_by: Option<String>,
187 /// RFC 3339.
188 pub found_at: String,
189 /// Who dismissed it (allowed or resolved it).
190 pub decided_by: Option<String>,
191 /// The comment given when it was dismissed.
192 pub reason: Option<String>,
193 pub decided_at: Option<String>,
194 /// Why it was dismissed. Absent on open alerts, and on alerts decided
195 /// before reasons were recorded.
196 #[serde(default)]
197 pub dismissed_reason: Option<DismissReason>,
198 /// Why the value looks made for tests or documentation (a documented
199 /// example key, a counting or repeating value), when it does. Such an
200 /// alert never stops a push and is never counted as critical.
201 #[serde(default)]
202 pub test_value: Option<String>,
203 /// Open, dismissed or fixed.
204 pub state: AlertState,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar205 /// What its issuer said when last asked: `active`, `inactive`,
206 /// `unknown` or `unsupported`; absent when never asked.
207 #[serde(default)]
208 pub validity: Option<String>,
209 #[serde(default)]
210 pub validity_checked_at: Option<String>,
211 /// How it got past push protection, when someone bypassed it.
212 #[serde(default)]
213 pub bypass: Option<crate::security_suite::Bypass>,
214 /// The custom pattern that found it, for a `custom_pattern` finding.
215 #[serde(default)]
216 pub pattern_id: Option<String>,
217 #[serde(default)]
218 pub pattern_name: Option<String>,
219 /// How many places it was found in.
220 #[serde(default)]
221 pub locations: u32,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays222}
223
224impl SecretStatus {
225 /// The alert state a secret in this status is in.
226 pub fn state(self) -> AlertState {
227 match self {
228 SecretStatus::Open | SecretStatus::Blocked => AlertState::Open,
229 SecretStatus::Allowed => AlertState::Dismissed,
230 SecretStatus::Resolved => AlertState::Fixed,
231 }
232 }
233}
234
235/// A secret as the repos service finds it, before it is stored.
236#[derive(Clone, Debug, Serialize, Deserialize)]
237#[serde(rename_all = "camelCase")]
238pub struct NewSecret {
239 pub fingerprint: String,
240 pub kind: String,
241 pub path: String,
242 pub line: u32,
243 pub commit: String,
244 pub preview: String,
245 /// Why it looks like a test value, from `g1t_scan::secrets::test_value`.
246 /// Such a secret is recorded but never stops a push.
247 #[serde(default, skip_serializing_if = "Option::is_none")]
248 pub test_value: Option<String>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar249 /// For a custom pattern's finding (`kind` `custom_pattern`): which
250 /// pattern, and its name.
251 #[serde(default, skip_serializing_if = "Option::is_none")]
252 pub pattern_id: Option<String>,
253 #[serde(default, skip_serializing_if = "Option::is_none")]
254 pub pattern_name: Option<String>,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays255}
256
257/// `push_blocked`: the secrets a push would add. Those allowed before are
258/// returned; the rest are recorded as blocked. Called by the repos service.
259/// Returns `PushVerdict`.
260#[derive(Debug, Serialize, Deserialize)]
261#[serde(rename_all = "camelCase")]
262pub struct PushBlockedArgs {
263 /// The repository the findings belong to: for a pull request's fork,
264 /// the repository it was made from.
265 pub repo_id: String,
266 pub path: RepoPath,
267 pub pusher: Option<String>,
268 pub secrets: Vec<NewSecret>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar269 /// Whether the repository is private, as repos read it.
270 #[serde(default, skip_serializing_if = "Option::is_none")]
271 pub private: Option<bool>,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays272}
273
274#[derive(Debug, Default, Serialize, Deserialize)]
275#[serde(rename_all = "camelCase")]
276pub struct PushVerdict {
277 /// Fingerprints that were allowed and so do not stop the push.
278 pub allowed: Vec<String>,
279 /// The finding recorded for each fingerprint that stops it.
280 pub ids: Vec<(String, String)>,
281}
282
283/// `scan_history` (repos): looks for secrets in a page of the default
284/// branch's history, newest first, each commit against its first parent.
285/// Returns `HistoryPage`.
286#[derive(Debug, Serialize, Deserialize)]
287#[serde(rename_all = "camelCase")]
288pub struct ScanHistoryArgs {
289 pub repo_id: String,
290 /// Where the last page stopped; `from`, or the head of the default
291 /// branch, when absent.
292 #[serde(default)]
293 pub after: Option<String>,
294 pub limit: u32,
295 /// For a pushed range: its newest commit, on whichever branch.
296 #[serde(default, skip_serializing_if = "Option::is_none")]
297 pub from: Option<String>,
298 /// For a pushed range: where the branch was before, which is not
299 /// scanned. The page ends there, with no next.
300 #[serde(default, skip_serializing_if = "Option::is_none")]
301 pub until: Option<String>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar302 /// Custom patterns to look for too.
303 #[serde(default, skip_serializing_if = "Vec::is_empty")]
304 pub patterns: Vec<crate::security_suite::PatternSpec>,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays305}
306
307#[derive(Debug, Default, Serialize, Deserialize)]
308#[serde(rename_all = "camelCase")]
309pub struct HistoryPage {
310 pub secrets: Vec<NewSecret>,
311 pub commits: u32,
312 /// Where the next page starts; none when the history is done.
313 pub next: Option<String>,
314 /// How many objects were read from the store: what the scan cost.
315 pub reads: u32,
316}
317
318/// `find_lockfiles` (repos): the lockfiles on the default branch.
319/// Returns `Lockfiles`.
320#[derive(Debug, Serialize, Deserialize)]
321#[serde(rename_all = "camelCase")]
322pub struct FindLockfilesArgs {
323 pub repo_id: String,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar324 /// A commit, branch or tag to read them at; the default branch when
325 /// absent.
326 #[serde(default, skip_serializing_if = "Option::is_none")]
327 pub git_ref: Option<String>,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays328}
329
330#[derive(Debug, Default, Serialize, Deserialize)]
331#[serde(rename_all = "camelCase")]
332pub struct Lockfiles {
333 /// The commit they were read at; none for an empty repository.
334 pub commit: Option<String>,
335 pub files: Vec<LockfileText>,
336}
337
338#[derive(Debug, Serialize, Deserialize)]
339pub struct LockfileText {
340 pub path: String,
341 pub text: String,
342}
343
344/// Where a vulnerable dependency stands.
345#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
346#[serde(rename_all = "lowercase")]
347pub enum VulnStatus {
348 Open,
349 /// The version in use is no longer affected.
350 Fixed,
351 /// Someone said why it can stay. Found again, it stays dismissed until
352 /// someone reopens it.
353 Dismissed,
354}
355
356impl VulnStatus {
357 pub fn as_str(self) -> &'static str {
358 match self {
359 VulnStatus::Open => "open",
360 VulnStatus::Fixed => "fixed",
361 VulnStatus::Dismissed => "dismissed",
362 }
363 }
364
365 pub fn parse(text: &str) -> Option<VulnStatus> {
366 Some(match text {
367 "open" => VulnStatus::Open,
368 "fixed" => VulnStatus::Fixed,
369 "dismissed" => VulnStatus::Dismissed,
370 _ => return None,
371 })
372 }
373
374 pub fn state(self) -> AlertState {
375 match self {
376 VulnStatus::Open => AlertState::Open,
377 VulnStatus::Fixed => AlertState::Fixed,
378 VulnStatus::Dismissed => AlertState::Dismissed,
379 }
380 }
381}
382
383/// Where the security update for a vulnerable package stands: the pull
384/// request g1t opens itself to upgrade it, on the branch
385/// `g1t/security/<package>-<version>`.
386#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
387#[serde(rename_all = "snake_case")]
388pub enum UpdateState {
389 /// A sandbox is making the change.
390 Requested,
391 /// Its pull request is open, going through the required checks.
392 Open,
393 Merged,
394 /// Closed without merging, by a person.
395 Closed,
396 /// A newer security update replaced it, or the package is no longer
397 /// vulnerable; g1t closed it.
398 Superseded,
Security and version updates open no issues on their own: an update whose version cannot be raised fails with why on its alert and is tried again a day later, and one whose required checks fail stays open for a person to decide on; an agent takes one only when a person asks with Fix with g1t. That issue now names the lockfiles the package is locked in and the commands that show it is done, which were left empty ("Locked in: .") because the lockfiles were never passed in.399 /// An earlier update g1t opened an issue for, when the version could
400 /// not be raised without changing code. New updates no longer reach it:
401 /// they fail with why, or their pull request stays open.
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays402 NeedsCode,
403 /// It could not be made; why is in `error`.
404 Failed,
405}
406
407impl UpdateState {
408 pub const ALL: [UpdateState; 7] = [
409 UpdateState::Requested,
410 UpdateState::Open,
411 UpdateState::Merged,
412 UpdateState::Closed,
413 UpdateState::Superseded,
414 UpdateState::NeedsCode,
415 UpdateState::Failed,
416 ];
417
418 pub fn as_str(self) -> &'static str {
419 match self {
420 UpdateState::Requested => "requested",
421 UpdateState::Open => "open",
422 UpdateState::Merged => "merged",
423 UpdateState::Closed => "closed",
424 UpdateState::Superseded => "superseded",
425 UpdateState::NeedsCode => "needs_code",
426 UpdateState::Failed => "failed",
427 }
428 }
429
430 pub fn parse(text: &str) -> Option<UpdateState> {
431 UpdateState::ALL.into_iter().find(|state| state.as_str() == text)
432 }
433
434 /// Whether g1t is still working on it.
435 pub fn in_progress(self) -> bool {
436 matches!(self, UpdateState::Requested | UpdateState::Open | UpdateState::NeedsCode)
437 }
438}
439
440/// The security update for one package.
441#[derive(Clone, Debug, Serialize, Deserialize)]
442#[serde(rename_all = "camelCase")]
443pub struct SecurityUpdate {
444 pub state: UpdateState,
445 /// The version it upgrades to.
446 pub target: String,
447 /// `g1t/security/<package>-<version>`.
448 pub branch: Option<String>,
449 /// The pull request g1t opened.
450 pub pull: Option<u32>,
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent451 /// The issue opened for g1t, when the upgrade needs code changes.
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays452 pub issue: Option<u32>,
453 /// Why it failed, when it did.
454 pub error: Option<String>,
455 /// RFC 3339.
456 pub updated_at: String,
457}
458
459/// The prefix every security update's branch starts with.
460pub const UPDATE_BRANCH_PREFIX: &str = "g1t/security/";
461
462/// The branch a security update is made on: `g1t/security/<package>-<version>`,
463/// with anything a branch name cannot hold (a scope's `@` and `/`) as `-`.
464pub fn update_branch(package: &str, version: &str) -> String {
465 let clean = |text: &str| -> String {
466 let mut out = String::new();
467 for c in text.chars() {
468 let c = if c.is_ascii_alphanumeric() || matches!(c, '.' | '_') { c } else { '-' };
469 if !(c == '-' && out.ends_with('-')) {
470 out.push(c);
471 }
472 }
473 out.trim_matches(['-', '.']).to_owned()
474 };
475 format!("{UPDATE_BRANCH_PREFIX}{}-{}", clean(package), clean(version))
476}
477
478/// One advisory against one package at the version a lockfile resolves.
479#[derive(Clone, Debug, Serialize, Deserialize)]
480#[serde(rename_all = "camelCase")]
481pub struct Vulnerability {
482 pub id: String,
483 pub repo_id: String,
484 /// `npm`, `crates.io`, `Go`, `PyPI`.
485 pub ecosystem: String,
486 pub package: String,
487 pub version: String,
488 /// The lockfile that resolves it.
489 pub manifest: String,
490 /// The id people know it by (its GHSA id when it has one).
491 pub advisory: String,
492 pub osv_id: String,
493 pub summary: String,
494 /// `critical`, `high`, `medium`, `low` or `unknown`.
495 pub severity: String,
496 pub fixed_version: Option<String>,
497 pub status: VulnStatus,
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent498 /// The issue opened to upgrade the package, when g1t was put on
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays499 /// it: the upgrade needs code changes, or predates security updates.
500 pub issue: Option<u32>,
501 /// RFC 3339.
502 pub found_at: String,
503 pub fixed_at: Option<String>,
504 /// Open, dismissed or fixed.
505 pub state: AlertState,
506 /// Who dismissed it, why, with what comment, and when.
507 #[serde(default)]
508 pub dismissed_by: Option<String>,
509 #[serde(default)]
510 pub dismissed_reason: Option<DismissReason>,
511 #[serde(default)]
512 pub dismissed_comment: Option<String>,
513 #[serde(default)]
514 pub dismissed_at: Option<String>,
515 /// The security update for its package, if g1t has started one.
516 #[serde(default)]
517 pub update: Option<SecurityUpdate>,
518}
519
520/// Open alerts by severity: vulnerabilities open and not dismissed, and
521/// secrets in the history that look real, as critical. A blocked secret
522/// never landed and a likely test value is no danger, so neither counts.
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9523#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays524pub struct SeverityCounts {
525 pub critical: u32,
526 pub high: u32,
527 pub medium: u32,
528 pub low: u32,
529 pub unknown: u32,
530}
531
532/// How a repository's history scan stands.
533#[derive(Clone, Debug, Default, Serialize, Deserialize)]
534#[serde(rename_all = "camelCase")]
535pub struct ScanState {
536 /// `pending`, `running`, `done` or `stopped` (over the workspace's limit).
537 pub history: String,
538 pub commits_scanned: u32,
539 /// RFC 3339.
540 pub history_finished_at: Option<String>,
541 pub dependencies_scanned_at: Option<String>,
542 /// Why the last dependency scan failed, if it did.
543 pub dependencies_error: Option<String>,
544 pub lockfiles: Vec<String>,
545}
546
547/// Secret alerts by where they stand.
548#[derive(Clone, Debug, Default, Serialize, Deserialize)]
549#[serde(rename_all = "camelCase")]
550pub struct SecretCounts {
551 /// In the history and looking real: rotate these.
552 pub open: u32,
553 /// Stopped at a push, so never landed, and looking real.
554 pub blocked: u32,
555 /// Open or blocked, but likely test values.
556 pub test_values: u32,
557 pub dismissed: u32,
558 pub fixed: u32,
559}
560
561/// One thing that happened to an alert, for its activity log.
562#[derive(Clone, Debug, Serialize, Deserialize)]
563#[serde(rename_all = "camelCase")]
564pub struct AlertActivity {
565 pub id: String,
566 /// The secret's or vulnerability's id.
567 pub alert_id: String,
568 /// `dismissed`, `reopened`, `update_requested`, `update_opened`,
569 /// `update_merged`, `update_closed`, `update_superseded`,
570 /// `update_needs_code` or `update_failed`.
571 pub action: String,
572 /// Who did it: a person's username, or `g1t`.
573 pub actor: Option<String>,
574 pub reason: Option<DismissReason>,
575 pub comment: Option<String>,
576 /// The pull request or issue it concerns.
577 pub number: Option<u32>,
578 /// RFC 3339.
579 pub at: String,
580}
581
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar582/// Version updates, and what the dependency update file asks of security
583/// updates: see [`crate::updates`].
584pub use crate::updates::{
585 UpdateAllow, UpdateGroup, UpdateIgnore, VersionUpdateEntry, VersionUpdatesState,
586};
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays587
588/// Everything the Security page shows for one repository.
589#[derive(Clone, Debug, Serialize, Deserialize)]
590#[serde(rename_all = "camelCase")]
591pub struct SecurityOverview {
592 pub repo_id: String,
593 /// Open alerts by severity; see [`SeverityCounts`].
594 pub counts: SeverityCounts,
595 pub secret_counts: SecretCounts,
596 pub secrets: Vec<SecretFinding>,
597 pub vulnerabilities: Vec<Vulnerability>,
598 /// What happened to the alerts, newest first.
599 pub activity: Vec<AlertActivity>,
600 pub scan: ScanState,
601 /// Security updates: whether g1t opens a pull request to upgrade each
602 /// vulnerable dependency that has a fix.
603 pub upkeep: bool,
604 pub version_updates: VersionUpdatesState,
605}
606
607/// `overview`: members of the workspace only. Returns
608/// `Outcome<SecurityOverview>`.
609#[derive(Debug, Serialize, Deserialize)]
610pub struct OverviewArgs {
611 pub repo: RepoPath,
612 pub viewer: Option<User>,
613}
614
615/// `dismiss`: closes an alert with a reason and an optional comment. A
616/// secret takes Admin on the repository (a dismissed secret is let through
617/// push protection, unless it was revoked); a vulnerable dependency takes
618/// Write. Returns `Outcome<AlertChange>`.
619#[derive(Debug, Serialize, Deserialize)]
620pub struct DismissArgs {
621 pub actor: User,
622 pub repo: RepoPath,
623 /// A secret's id (`sec_…`) or a vulnerability's (`vul_…`).
624 pub id: String,
625 pub reason: DismissReason,
626 #[serde(default)]
627 pub comment: String,
628}
629
630/// `reopen`: opens a dismissed alert again, with the same roles as
631/// `dismiss`. Returns `Outcome<AlertChange>`.
632#[derive(Debug, Serialize, Deserialize)]
633pub struct ReopenArgs {
634 pub actor: User,
635 pub repo: RepoPath,
636 pub id: String,
637}
638
639/// The alert `dismiss` or `reopen` changed, as it is now: one of the two.
640#[derive(Clone, Debug, Default, Serialize, Deserialize)]
641#[serde(rename_all = "camelCase")]
642pub struct AlertChange {
643 pub secret: Option<SecretFinding>,
644 pub vulnerability: Option<Vulnerability>,
645}
646
647/// `rescan`: scans the dependencies again now, and the history from the
648/// start. Members only. Returns `Outcome<ScanState>`.
649#[derive(Debug, Serialize, Deserialize)]
650pub struct RescanArgs {
651 pub actor: User,
652 pub repo: RepoPath,
653}
654
655/// `set_upkeep`: security updates on or off: whether g1t opens a pull
656/// request to upgrade each vulnerable dependency that has a fix. Maintain
657/// and up. Returns `Outcome<bool>`.
658#[derive(Debug, Serialize, Deserialize)]
659pub struct SetUpkeepArgs {
660 pub actor: User,
661 pub repo: RepoPath,
662 pub enabled: bool,
663}
664
665/// `workspace`: every repository of a workspace that has findings, for
666/// its members. Returns `Outcome<Vec<RepoSecurity>>`.
667#[derive(Debug, Serialize, Deserialize)]
668pub struct WorkspaceArgs {
669 pub workspace: String,
670 pub viewer: Option<User>,
671}
672
673#[derive(Clone, Debug, Serialize, Deserialize)]
674#[serde(rename_all = "camelCase")]
675pub struct RepoSecurity {
676 pub repo_id: String,
677 pub name: String,
678 pub counts: SeverityCounts,
679 pub secrets: u32,
680 pub vulnerabilities: u32,
681 pub upkeep: bool,
682 pub dependencies_scanned_at: Option<String>,
683}
684
685/// `bump` (runner): makes a security update in a sandbox. It clones the
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar686/// default branch (or `base`), raises `package` to `version` in each lockfile with the
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays687/// ecosystem's own tool (`npm`, `cargo`, `go`, `pip`), commits that as g1t
688/// (`g1t <g1t@users.noreply.g1t.sh>`) and pushes it to `branch`, which must
689/// start with [`UPDATE_BRANCH_PREFIX`]. The push is what tells the security
690/// service to open the pull request. Returns `Outcome<bool>`: whether the
691/// sandbox started.
692#[derive(Clone, Debug, Serialize, Deserialize)]
693#[serde(rename_all = "camelCase")]
694pub struct BumpArgs {
695 pub repo: RepoPath,
696 /// OSV's name for the ecosystem: `npm`, `crates.io`, `Go` or `PyPI`.
697 pub ecosystem: String,
698 pub package: String,
699 pub version: String,
700 /// The lockfiles that resolve a vulnerable version, from the root.
701 pub lockfiles: Vec<String>,
702 pub branch: String,
703 /// The commit's message.
704 pub message: String,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar705 /// `version` for a version update, whose branch is named by the
706 /// dependency update file (any branch but the default one); a security
707 /// update when absent.
708 #[serde(default, skip_serializing_if = "Option::is_none")]
709 pub kind: Option<String>,
710 /// Several packages raised in one commit, for a grouped update. When
711 /// given, `package` and `version` are its first.
712 #[serde(default, skip_serializing_if = "Vec::is_empty")]
713 pub packages: Vec<crate::updates::BumpPackage>,
714 /// How a manifest's requirement changes (`versioning-strategy`):
715 /// `increase` (the default), `increase-if-necessary`, `widen` or
716 /// `lockfile-only`.
717 #[serde(default, skip_serializing_if = "Option::is_none")]
718 pub strategy: Option<String>,
719 /// Replace the branch if it is there already: a rebase or a recreate.
720 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
721 pub force: bool,
722 /// Private registries the tools may read, with their credentials.
723 #[serde(default, skip_serializing_if = "Vec::is_empty")]
724 pub registries: Vec<crate::updates::BumpRegistry>,
725 /// The branch to start from, which its pull request merges into
726 /// (`target-branch`): the default branch when absent.
727 #[serde(default, skip_serializing_if = "Option::is_none")]
728 pub base: Option<String>,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays729}

This file's history is long; its oldest lines are credited to the oldest commit read.