ב-11 במרץ 2025, בין 18:42 ל-20:31 לפי שעון UTC, הושתל ב-reviewdog/action-setup@v1 קוד שכותב סודות חשופים אל יומן ההרצה של GitHub Actions. חמישה רכיבים אחרים של reviewdog שקוראים לאותו action נפגעו איתו, ולפי הרשומה זה קרה ללא תלות בגרסה או בשיטת ההצמדה שלהם. הציון הוא 8.6 מתוך 10, וסוכנות הסייבר האמריקאית CISA מסמנת את החולשה כמנוצלת בפועל.
מה החולשה?
צינור בנייה ב-GitHub Actions בנוי מצעדים, וכל צעד יכול לשלוף רכיב מוכן מתוך מאגר של מישהו אחר ולהריץ אותו. הרכיב הזה אינו רץ בארגז חול: הוא רץ בתוך הצינור שלכם, ומה שהצינור מחזיק זמין לו. זה כולל את הסודות שהוא מקבל לעבודה, כלומר מפתחות API, פרטי אימות לשרתי הפצה ו-token-ים לחתימה על גרסאות.
הדרך המקובלת להפנות לרכיב כזה היא לפי תגית, למשל v1. תגית אינה מצביע קבוע, היא שם שמישהו יכול להזיז. מי שכתב בצינור שלו את התגית v1 מקבל בכל הרצה את מה שהתגית מצביעה עליו כרגע, ולא את מה שהיא הצביעה עליו ביום שהשורה נכתבה.
מה שקרה כאן הוא בדיוק זה: התגית v1 הוזזה אל קוד שהוסיפו לו פעולה אחת, הדפסת הסודות שהצינור מחזיק אל יומן ההרצה. יומני הרצה אינם מקום סודי. הם נקראים בידי מי שיכול לקרוא את המאגר, ובמאגר ציבורי הם נקראים בידי כל אחד. המחלקה הזאת נקראת קוד זדוני שהושתל בתוך התוכנה, והתקיפה כולה היא תקיפת שרשרת אספקה.
כאן נמצא הפרט שהופך את המקרה הזה ללימוד ולא רק לאירוע. הרשומה מציינת שהרכיבים האחרים של reviewdog נפגעו ללא תלות בגרסה או בשיטת ההצמדה, מפני שהם עצמם קוראים ל-action-setup@v1 מבפנים. כלומר צוות שעשה את הדבר הנכון והצמיד את הרכיב שהוא מפנה אליו לגרסה מדויקת לא היה מוגן, כי מה שהוא הצמיד המשיך לקרוא בעצמו לתגית נעה. ההצמדה מגנה על החוליה הראשונה בשרשרת, לא על מה שנמצא אחריה.
שווה לדייק מה הרשומה כן טוענת ומה לא. הווקטור מסמן פגיעה גבוהה בסודיות בלבד, ואפס פגיעה בשלמות ובזמינות. כלומר מה שתועד הוא חשיפת סודות, לא השתלטות על התשתית שלכם. הסודות שנחשפו הם מה שמאפשר את השלב הבא, וזה עניין אחר.
מי מושפע?
הרכיב שנפגע במקור הוא reviewdog/action-setup בגרסה 1. חמישה רכיבים נוספים שקוראים לו מופיעים ברשומה עם הגרסה שסוגרת אותם: action-shellcheck מגרסה 1.29.2, action-ast-grep מגרסה 1.26.2, action-staticcheck מגרסה 1.26.2, action-typos מגרסה 1.17.2 ו-action-composite-template מגרסה 0.20.2.
זו החולשה היחידה מסוגה שנוגעת לא למפעילי מוצרי תשתית אלא לצוותי פיתוח רגילים. בישראל GitHub Actions הוא הצינור הנפוץ בחברות תוכנה, ו-reviewdog הוא כלי שמחבר בדיקות קוד להערות בבקשות משיכה, כך שהוא מופיע דווקא אצל צוותים שמקפידים על איכות קוד.
מצד שני, חלון החשיפה צר מאוד וזה משנה את התמונה: פחות משעתיים בתאריך אחד. מי שלא הריץ צינור בתוך החלון הזה לא נפגע, גם אם הוא משתמש ברכיבים האלה שנים.
האם מנוצלת בפועל?
כן, וכאן הניצול הוא האירוע עצמו: הקוד הזדוני הושתל ורץ אצל מי שהריץ צינור בזמן הנכון. ב-24 במרץ 2025 נוספה החולשה לקטלוג ה-KEV של CISA, הקטלוג שמרכז חולשות שידוע שתוקפים משתמשים בהן בפועל, והמועד שנקבע לגופים הפדרליים האמריקאיים לסיום הטיפול היה 14 באפריל 2025.
מה עושים?
1. בדקו קודם אם הרצתם צינור בחלון. היכנסו להיסטוריית ההרצות של המאגר, סננו לפי 11 במרץ 2025 וראו אם יש הרצה בין 18:42 ל-20:31 UTC שהשתמשה ב-action-setup או באחד מחמשת הרכיבים שלמעלה. אם אין, אתם מחוץ לאירוע.
2. החליפו כל סוד שהרצה כזאת יכלה להגיע אליו, אם מצאתם הרצה בחלון. זה הצעד היחיד שמשנה משהו, ואין לו תחליף: עדכון הרכיב אינו מבטל סוד שכבר נכתב ליומן. החליפו מפתחות API, פרטי אימות להפצה ו-token-ים לחתימה, ואל תסתפקו בבדיקה מה מהם נראה שנעשה בו שימוש.
3. הסתכלו ביומני ההרצה מאותו חלון וקבעו מי יכול לקרוא אותם. אם המאגר ציבורי, התייחסו לסודות כאילו פורסמו.
4. עדכנו לגרסאות שסוגרות: 1.29.2 ל-action-shellcheck, 1.26.2 ל-action-ast-grep ול-action-staticcheck, 1.17.2 ל-action-typos ו-0.20.2 ל-action-composite-template. ההודעה של reviewdog מרכזת את הפרטים.
5. הצמידו רכיבים לפי מזהה commit ולא לפי תגית, ודעו במה זה עוזר. זה מונע מהחוליה שאתם מפנים אליה להשתנות מתחת לידיים. את המקרה הזה זה לא היה מונע במלואו, כי הרכיבים שהצמדתם קראו בעצמם לתגית נעה, ולכן שווה גם לבדוק למה הרכיבים שאתם מריצים קוראים בפנים.