Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions dist/evaluators/RepoPolicyEvaluator.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const ActionsChecks_1 = require("./repository/ActionsChecks");
const WorkflowsChecks_1 = require("./repository/WorkflowsChecks");
const RunnersChecks_1 = require("./repository/RunnersChecks");
const WebHooksChecks_1 = require("./repository/WebHooksChecks");
const AdminsChecks_1 = require("./repository/AdminsChecks");
// This class is the main Repository evaluator. It evaluates the policy for a given repository.
class RepoPolicyEvaluator {
policy; // The policy to be evaluated loaded later. This is always the repo policy
Expand Down Expand Up @@ -77,6 +78,11 @@ class RepoPolicyEvaluator {
logger_1.logger.debug(`Webhook checks results: ${JSON.stringify(webhook_checks)}`);
this.repositoryCheckResults.push(webhook_checks);
}
if (this.policy.admins) {
const admins_checks = await new AdminsChecks_1.AdminsChecks(this.policy, this.repository).checkAdmins();
logger_1.logger.debug(`Admins checks results: ${JSON.stringify(admins_checks)}`);
this.repositoryCheckResults.push(admins_checks);
}
}
// Run webhook checks
printCheckResults() {
Expand Down
48 changes: 48 additions & 0 deletions dist/evaluators/repository/AdminsChecks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AdminsChecks = void 0;
const Repositories_1 = require("../../github/Repositories");
class AdminsChecks {
policy;
repository;
constructor(policy, repository) {
this.policy = policy;
this.repository = repository;
}
// check whether the repository admins match the policy
async checkAdmins() {
const collaborators = await (0, Repositories_1.getRepoCollaborators)(this.repository.owner, this.repository.name);
// Filter collaborators to get only admins
const actualAdmins = collaborators
.filter((collaborator) => collaborator.permissions?.admin)
.map((collaborator) => collaborator.login);
const policyAdmins = this.policy.admins || [];
// Find admins in policy that are not in the repository
const missingAdmins = policyAdmins.filter((admin) => !actualAdmins.includes(admin));
// Find admins in the repository that are not in the policy
const extraAdmins = actualAdmins.filter((admin) => !policyAdmins.includes(admin));
return this.createResult(policyAdmins, actualAdmins, missingAdmins, extraAdmins);
}
createResult(policy_admins, actual_admins, missing_admins, extra_admins) {
let name = "Admins Check";
let pass = false;
let data = {};
if (missing_admins.length === 0 && extra_admins.length === 0) {
pass = true;
data = {
policy_admins,
actual_admins,
};
}
else {
data = {
policy_admins,
actual_admins,
missing_admins,
extra_admins,
};
}
return { name, pass, data };
}
}
exports.AdminsChecks = AdminsChecks;
62 changes: 62 additions & 0 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47878,6 +47878,7 @@ const ActionsChecks_1 = __nccwpck_require__(1635);
const WorkflowsChecks_1 = __nccwpck_require__(8336);
const RunnersChecks_1 = __nccwpck_require__(9863);
const WebHooksChecks_1 = __nccwpck_require__(1149);
const AdminsChecks_1 = __nccwpck_require__(2818);
// This class is the main Repository evaluator. It evaluates the policy for a given repository.
class RepoPolicyEvaluator {
policy; // The policy to be evaluated loaded later. This is always the repo policy
Expand Down Expand Up @@ -47945,6 +47946,11 @@ class RepoPolicyEvaluator {
logger_1.logger.debug(`Webhook checks results: ${JSON.stringify(webhook_checks)}`);
this.repositoryCheckResults.push(webhook_checks);
}
if (this.policy.admins) {
const admins_checks = await new AdminsChecks_1.AdminsChecks(this.policy, this.repository).checkAdmins();
logger_1.logger.debug(`Admins checks results: ${JSON.stringify(admins_checks)}`);
this.repositoryCheckResults.push(admins_checks);
}
}
// Run webhook checks
printCheckResults() {
Expand Down Expand Up @@ -48406,6 +48412,62 @@ class ActionsChecks {
exports.ActionsChecks = ActionsChecks;


/***/ }),

/***/ 2818:
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {

"use strict";

Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.AdminsChecks = void 0;
const Repositories_1 = __nccwpck_require__(3354);
class AdminsChecks {
policy;
repository;
constructor(policy, repository) {
this.policy = policy;
this.repository = repository;
}
// check whether the repository admins match the policy
async checkAdmins() {
const collaborators = await (0, Repositories_1.getRepoCollaborators)(this.repository.owner, this.repository.name);
// Filter collaborators to get only admins
const actualAdmins = collaborators
.filter((collaborator) => collaborator.permissions?.admin)
.map((collaborator) => collaborator.login);
const policyAdmins = this.policy.admins || [];
// Find admins in policy that are not in the repository
const missingAdmins = policyAdmins.filter((admin) => !actualAdmins.includes(admin));
// Find admins in the repository that are not in the policy
const extraAdmins = actualAdmins.filter((admin) => !policyAdmins.includes(admin));
return this.createResult(policyAdmins, actualAdmins, missingAdmins, extraAdmins);
}
createResult(policy_admins, actual_admins, missing_admins, extra_admins) {
let name = "Admins Check";
let pass = false;
let data = {};
if (missing_admins.length === 0 && extra_admins.length === 0) {
pass = true;
data = {
policy_admins,
actual_admins,
};
}
else {
data = {
policy_admins,
actual_admins,
missing_admins,
extra_admins,
};
}
return { name, pass, data };
}
}
exports.AdminsChecks = AdminsChecks;


/***/ }),

/***/ 8377:
Expand Down
16 changes: 16 additions & 0 deletions policies/repository.readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,19 @@ webhooks:
- `allowed_events`: a list of events that can trigger the webhook.
- `mandatory_secret`: if set to `true`, a secret must be set to authenticate the webhook.

## Admins

The policy validates that the repository admins match the specified list.

```yml
admins:
- admin1
- admin2
```

GitArmor will check that the admins specified in the policy are matching the admins of the repository. The check will:
- Identify any admins in the policy that are not repository admins (missing admins)
- Identify any repository admins that are not in the policy (extra admins)
- Pass only if all admins match exactly


6 changes: 5 additions & 1 deletion policies/repository.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,8 @@ webhooks:
allow_insecure_ssl: false
allowed_events:
- pull
mandatory_secret: false
mandatory_secret: false

admins:
- admin1
- admin2
10 changes: 10 additions & 0 deletions src/evaluators/RepoPolicyEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ActionsChecks } from "./repository/ActionsChecks";
import { WorkflowsChecks } from "./repository/WorkflowsChecks";
import { RunnersChecks } from "./repository/RunnersChecks";
import { WebHooksChecks } from "./repository/WebHooksChecks";
import { AdminsChecks } from "./repository/AdminsChecks";

// This class is the main Repository evaluator. It evaluates the policy for a given repository.
export class RepoPolicyEvaluator {
Expand Down Expand Up @@ -134,6 +135,15 @@ export class RepoPolicyEvaluator {
logger.debug(`Webhook checks results: ${JSON.stringify(webhook_checks)}`);
this.repositoryCheckResults.push(webhook_checks);
}

if (this.policy.admins) {
const admins_checks = await new AdminsChecks(
this.policy,
this.repository,
).checkAdmins();
logger.debug(`Admins checks results: ${JSON.stringify(admins_checks)}`);
this.repositoryCheckResults.push(admins_checks);
}
}

// Run webhook checks
Expand Down
72 changes: 72 additions & 0 deletions src/evaluators/repository/AdminsChecks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { CheckResult, Repository } from "../../types/common/main";
import { getRepoCollaborators } from "../../github/Repositories";

export class AdminsChecks {
private policy: any;
private repository: Repository;

constructor(policy: any, repository: Repository) {
this.policy = policy;
this.repository = repository;
}

// check whether the repository admins match the policy
async checkAdmins(): Promise<any> {
const collaborators = await getRepoCollaborators(
this.repository.owner,
this.repository.name,
);

// Filter collaborators to get only admins
const actualAdmins = collaborators
.filter((collaborator) => collaborator.permissions?.admin)
.map((collaborator) => collaborator.login);

const policyAdmins = this.policy.admins || [];

// Find admins in policy that are not in the repository
const missingAdmins = policyAdmins.filter(
(admin: string) => !actualAdmins.includes(admin),
);

// Find admins in the repository that are not in the policy
const extraAdmins = actualAdmins.filter(
(admin) => !policyAdmins.includes(admin),
);

return this.createResult(
policyAdmins,
actualAdmins,
missingAdmins,
extraAdmins,
);
}

private createResult(
policy_admins: string[],
actual_admins: string[],
missing_admins: string[],
extra_admins: string[],
): CheckResult {
let name = "Admins Check";
let pass = false;
let data = {};

if (missing_admins.length === 0 && extra_admins.length === 0) {
pass = true;
data = {
policy_admins,
actual_admins,
};
} else {
data = {
policy_admins,
actual_admins,
missing_admins,
extra_admins,
};
}

return { name, pass, data };
}
}
1 change: 1 addition & 0 deletions src/types/common/main.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ interface RepoPolicy {
workflows: Workflows;
runners: Runners;
webhooks: WebHook;
admins: string[];
}

// Org Policy
Expand Down