feat: revamp plugin system - #1683
Conversation
I'm skipping this from the architecture guide for now, since it's only relevant to plugin users
✅ Deploy Preview for endearing-brigadeiros-63f9d0 ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1683 +/- ##
==========================================
- Coverage 86.05% 85.98% -0.07%
==========================================
Files 101 101
Lines 5571 5581 +10
Branches 995 1006 +11
==========================================
+ Hits 4794 4799 +5
- Misses 526 531 +5
Partials 251 251 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Went through this properly. Also had a go at the pull scanning problem you raised at the end of the description, since that seemed like the interesting part. Your chain handles it as-is, details at the bottom. What's goodSome of this is easy to skim past in a diff, so worth spelling out.
Moving plugins after the auth checks. I measured this on
Immutable chains kill both the splice mutation and the Docs are good too. The sideband transcript with Things I'd change before merge
A typo'd phase compiles, loads, and then matches nothing, silently unhooking the plugin. One word fixes it, and it's what makes the phase API type-safe at all. Plugins built against an older git-proxy get dropped silently. Smaller things: The sample doesn't run on tag pushes. The three "should load plugins" tests don't test anything any more. They compare against All three pass with no plugins loaded. Asserting on plugin identity rather than length would fix them, and would catch the three items above. Docs mention Trivia: On pull scanningI don't think the plugin system is the blocker. The problem is that nothing parses the upload-pack request. A client never asks for a branch. It asks for exact object IDs, and it says so before any data moves: That body is already buffered. Add a That also takes care of the TOCTOU issue you described. Scan-then-refetch is unsafe if you refetch by ref name, but an object ID is content addressed, so you inspect X, allow the fetch, and the client gets X regardless of what happens to the ref afterwards. I ran this against a live proxy and real GitHub rather than reasoning about it. Plugin loaded through the normal and rejecting on a want: Two things that will catch you outgit gzips upload-pack bodies once they pass 1024 bytes. Measured: 1024 plaintext, 1025 gzipped, which is roughly 18 short refs, so effectively every real repo. Nothing in the chain inflates, so a parser sees gzip bytes and reports no wants at all. This got past several rounds of my own review because all my fixtures came from a two-branch repo and were under 1 KB. There's a trap in the obvious fix. Don't inflate in
Which suggests an invariant worth writing down wherever the parsing ends up living: report a superset of what the server will actually serve, or mark the result incomplete. Over-reporting costs one redundant read. Under-reporting means content ships uninspected. Next stepsI can send One question back at you. With pull data available, does the pull chain want more than No attachment to my version of any of this. Happy to hand it over, split it differently, or fold it into this PR if you'd rather own it. |
Changelog
phase: Which phase in the chain to executedisplayName: User-facing string for sideband streaming and auditsisCollectible: Same as regular processors, non-critical errors allow chain to continue running, errors are collected in the endchains: Which push chains to run the plugin for (ATM: tags, branches or both)Description
Note: This PR was human-written, and I would appreciate your human thoughts on it 😃
It revamps the plugin system to allow inserting plugins into specific phases in each chain. It also adds
PluginOptionsto configure the plugindisplayName,isCollectibleandchainsproperties.Each chain is divided into
Phases, which are consideredChainElements. Both plugins and chain elements are resolved into executable actions (ProcessorExec).This allows plugins to access certain
Actionfields that get populated later on, such as the push diff in theAFTER_DIFFphase.For example:
Each phase along with the guaranteed properties is described in the updated plugin documentation.
CustomSecretScanner plugin execution
This simple plugin demonstrates how to access the diff and scan it - previously not possible:
Related Issue
Resolves #
The idea of phases was already introduced by @dcoric in #1639. I expanded on it and focused on plugin revamp specifically.
I think safe, accurate pull scanning (supply chain scans) cannot actually be done via plugins at the moment: if we naively pull the diff using
simpleGitwithin a plugin, we end up getting only the default branch (thus a user pulling a different, compromised branch wouldn't be detected), and on top of that the scan would complete first and then trigger a second "authorized" pull, thus if a vulnerability was introduced right after the scan finished, the user would be able to pull it anyways.As a follow-up to this PR. I'd like to rewrite the pull chain logic to actually obtain the data requested by the user
git pull <specific-branch>, store it in the action so it's extensible via plugins, and finally forward thegit pullresult to the user.Checklist
General
Documentation
Tests
npm test)npm run lintandnpm run format:check)npm run check-types)