\s*/g)).toHaveLength(10);
+ });
+
+ it('preserves complete Docker command placeholders and corrected security guidance', () => {
+ expect(html).toContain('docker stop コンテナ名またはID');
+ expect(html).toContain('docker rm コンテナ名またはID');
+ expect(html).toContain('docker push イメージ名');
+ expect(html).toContain('docker pull イメージ名');
+ expect(html).not.toContain('第1章の「テスト駆動開発(TDD)」');
+ expect(html).toContain('出力エンコーディング');
+ expect(html).toContain('パラメータ化クエリ');
+ expect(html).toContain('CSRFトークンまたはSameSite Cookie');
+ });
+
+ it('models DNS as a prerequisite and the HTTP request path from user to firewall', () => {
+ expect(html).toContain('User["ユーザー"] --> FW');
+ expect(html).not.toContain('User["ユーザー"] --> DNS');
+ expect(html).toContain('DNS -.-> User');
+ expect(html).toContain('構成例');
+ });
+
+ it('configures and awaits Mermaid safely and exposes active navigation semantics', () => {
+ expect(html).toMatch(/htmlLabels:\s*true,[\s\S]*flowchart:\s*\{\s*useMaxWidth:/);
+ expect(html).not.toMatch(/flowchart:\s*\{[^}]*htmlLabels:/s);
+ expect(html).toMatch(/try\s*\{\s*await mermaid\.run/);
+ expect(html).toContain('図の描画に失敗しました');
+ expect(html).toContain("link.setAttribute('aria-current', 'location')");
+ expect(html).toContain("link.removeAttribute('aria-current')");
+ });
+});
diff --git a/__tests__/aws/solutions-architect-associate/domain4/page.test.tsx b/__tests__/aws/solutions-architect-associate/domain4/page.test.tsx
index ba67ef53e..1b2426067 100644
--- a/__tests__/aws/solutions-architect-associate/domain4/page.test.tsx
+++ b/__tests__/aws/solutions-architect-associate/domain4/page.test.tsx
@@ -31,19 +31,29 @@ describe('AWS SAA Domain 4 Guide Page', () => {
expect(Object.keys(DIAGRAMS).length).toBe(29);
});
- it('renders heading structure and section counts accurately', () => {
+ it('renders the expected top-level heading structure', () => {
const { container } = render( );
- const h2Headings = container.querySelectorAll('h2');
- const h3Headings = container.querySelectorAll('h3');
-
- expect(h2Headings.length).toBe(6);
- expect(h3Headings.length).toBe(15);
+ const main = container.querySelector('main.content');
+ expect(main).not.toBeNull();
+
+ const h2Ids = Array.from(main!.querySelectorAll('h2')).map((heading) => heading.id);
+ expect(h2Ids).toEqual([
+ 'intro',
+ 'common-tools',
+ 'task-4-1',
+ 'task-4-2',
+ 'task-4-3',
+ 'task-4-4',
+ 'references',
+ ]);
+
+ expect(main!.querySelectorAll('h3').length).toBeGreaterThanOrEqual(15);
});
it('renders tables correctly with expected headers and row cells', () => {
const { container } = render( );
const tables = container.querySelectorAll('table');
- expect(tables.length).toBe(10);
+ expect(tables.length).toBeGreaterThanOrEqual(10);
tables.forEach((table) => {
const ths = table.querySelectorAll('thead th');
@@ -52,10 +62,15 @@ describe('AWS SAA Domain 4 Guide Page', () => {
expect(trs.length).toBeGreaterThan(0);
});
- expect(tables[0].textContent).toContain('AWS Organizations 連結請求(マルチアカウント請求)');
- expect(tables[1].textContent).toContain('S3 Standard');
- expect(tables[2].textContent).toContain('Intelligent-Tiering');
- expect(tables[3].textContent).toContain('Savings Plans');
+ const tableContents = Array.from(tables).map((table) => table.textContent ?? '');
+ expect(
+ tableContents.some((content) =>
+ content.includes('AWS Organizations 連結請求(マルチアカウント請求)')
+ )
+ ).toBe(true);
+ expect(tableContents.some((content) => content.includes('S3 Standard'))).toBe(true);
+ expect(tableContents.some((content) => content.includes('Intelligent-Tiering'))).toBe(true);
+ expect(tableContents.some((content) => content.includes('Savings Plans'))).toBe(true);
});
it('renders all 29 figure IDs and supplementary-skill text correctly', () => {
@@ -77,12 +92,80 @@ describe('AWS SAA Domain 4 Guide Page', () => {
expect(screen.getByText(/スキル: コスト効率の良いデータベースタイプの判断\(時系列・列指向\)/i)).toBeInTheDocument();
});
- it('renders source and reference links with exact count and correct href attributes', () => {
+ it('renders source and reference links with correct href attributes', () => {
const { container } = render( );
- const links = container.querySelectorAll('a[target="_blank"]');
- expect(links.length).toBe(10);
-
- const hrefs = Array.from(links).map((a) => a.getAttribute('href'));
- expect(hrefs.some((h) => h?.includes('wellarchitected/latest/cost-optimization-pillar'))).toBe(true);
+ const references = container.querySelector('#references + .ref-grid');
+ expect(references).toBeInTheDocument();
+
+ const expectedReferences = [
+ ['AWS Cost Explorer とは', 'https://docs.aws.amazon.com/cost-management/latest/userguide/ce-what-is.html'],
+ ['AWS Budgets を使用したコスト管理', 'https://docs.aws.amazon.com/cost-management/latest/userguide/budgets-managing-costs.html'],
+ ['AWS Cost and Usage Report とは', 'https://docs.aws.amazon.com/cur/latest/userguide/what-is-cur.html'],
+ ['コスト配分タグの使用', 'https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/cost-alloc-tags.html'],
+ ['AWS Organizations の連結請求', 'https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_accounts_consolidated-billing.html'],
+ ['AWS Trusted Advisor', 'https://docs.aws.amazon.com/awssupport/latest/user/trusted-advisor.html'],
+ ['AWS Compute Optimizer とは', 'https://docs.aws.amazon.com/compute-optimizer/latest/ug/what-is.html'],
+ ['AWS Well-Architected Framework - コスト最適化の柱', 'https://docs.aws.amazon.com/wellarchitected/latest/cost-optimization-pillar/welcome.html'],
+ ['Amazon S3 とは', 'https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html'],
+ ['Amazon S3 ストレージクラス', 'https://docs.aws.amazon.com/AmazonS3/latest/userguide/storage-class-intro.html'],
+ ['S3 オブジェクトライフサイクル管理', 'https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html'],
+ ['Requester Pays バケットの使用', 'https://docs.aws.amazon.com/AmazonS3/latest/userguide/RequesterPaysBuckets.html'],
+ ['Amazon EBS ボリュームタイプ', 'https://docs.aws.amazon.com/ebs/latest/userguide/ebs-volume-types.html'],
+ ['Amazon EFS とは', 'https://docs.aws.amazon.com/efs/latest/ug/whatisefs.html'],
+ ['Amazon FSx', 'https://aws.amazon.com/fsx/'],
+ ['AWS DataSync とは', 'https://docs.aws.amazon.com/datasync/latest/userguide/what-is-datasync.html'],
+ ['AWS Transfer Family とは', 'https://docs.aws.amazon.com/transfer/latest/userguide/what-is-aws-transfer-family.html'],
+ ['AWS Storage Gateway とは', 'https://docs.aws.amazon.com/storagegateway/latest/userguide/WhatIsStorageGateway.html'],
+ ['AWS Snow Family', 'https://aws.amazon.com/snow/'],
+ ['AWS Backup とは', 'https://docs.aws.amazon.com/aws-backup/latest/devguide/whatisbackup.html'],
+ ['S3 Glacier Vault Lock', 'https://docs.aws.amazon.com/amazonglacier/latest/dev/vault-lock.html'],
+ ['Amazon EC2 インスタンス購入オプション', 'https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-purchasing-options.html'],
+ ['Spotインスタンスの使用', 'https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-spot-instances.html'],
+ ['Savings Plans とは', 'https://docs.aws.amazon.com/savingsplans/latest/userguide/what-is-savings-plans.html'],
+ ['リザーブドインスタンス', 'https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-reserved-instances.html'],
+ ['Amazon EC2 インスタンスタイプ', 'https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html'],
+ ['Amazon EC2 Auto Scaling とは', 'https://docs.aws.amazon.com/autoscaling/ec2/userguide/what-is-amazon-ec2-auto-scaling.html'],
+ ['EC2 インスタンスの休止(Hibernate)', 'https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Hibernate.html'],
+ ['AWS Lambda とは', 'https://docs.aws.amazon.com/lambda/latest/dg/welcome.html'],
+ ['AWS Fargate とは', 'https://docs.aws.amazon.com/AmazonECS/latest/userguide/what-is-fargate.html'],
+ ['AWS Batch とは', 'https://docs.aws.amazon.com/batch/latest/userguide/what-is-batch.html'],
+ ['Elastic Load Balancing とは', 'https://docs.aws.amazon.com/elasticloadbalancing/latest/userguide/elastic-load-balancing.html'],
+ ['Application Load Balancer', 'https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html'],
+ ['Network Load Balancer', 'https://docs.aws.amazon.com/elasticloadbalancing/latest/network/introduction.html'],
+ ['Gateway Load Balancer', 'https://docs.aws.amazon.com/elasticloadbalancing/latest/gateway/introduction.html'],
+ ['AWS Outposts とは', 'https://docs.aws.amazon.com/outposts/latest/userguide/what-is-outposts.html'],
+ ['AWS Local Zones とは', 'https://docs.aws.amazon.com/local-zones/latest/ug/what-is-aws-local-zones.html'],
+ ['AWS Wavelength とは', 'https://docs.aws.amazon.com/wavelength/latest/developerguide/what-is-wavelength.html'],
+ ['Amazon RDS とは', 'https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Welcome.html'],
+ ['Amazon Aurora の概要', 'https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/CHAP_AuroraOverview.html'],
+ ['Amazon DynamoDB とは', 'https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html'],
+ ['DynamoDB の読み込み/書き込みキャパシティモード', 'https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode.html'],
+ ['Amazon RDS Proxy', 'https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.html'],
+ ['Amazon RDS の読み取りレプリカの使用', 'https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ReadRepl.html'],
+ ['Amazon ElastiCache とは', 'https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/WhatIs.html'],
+ ['DynamoDB Accelerator (DAX)', 'https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DAX.html'],
+ ['Amazon RDS の自動バックアップの使用', 'https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_WorkingWithAutomatedBackups.html'],
+ ['AWS Database Migration Service とは', 'https://docs.aws.amazon.com/dms/latest/userguide/Welcome.html'],
+ ['AWS Schema Conversion Tool', 'https://docs.aws.amazon.com/SchemaConversionTool/latest/userguide/CHAP_Welcome.html'],
+ ['NATゲートウェイ', 'https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html'],
+ ['AWS Direct Connect とは', 'https://docs.aws.amazon.com/directconnect/latest/UserGuide/Welcome.html'],
+ ['AWS Site-to-Site VPN', 'https://docs.aws.amazon.com/vpn/latest/s2svpn/VPC_VPN.html'],
+ ['AWS Transit Gateway とは', 'https://docs.aws.amazon.com/vpc/latest/tgw/what-is-transit-gateway.html'],
+ ['VPCピアリングとは', 'https://docs.aws.amazon.com/vpc/latest/peering/what-is-vpc-peering.html'],
+ ['VPCエンドポイント', 'https://docs.aws.amazon.com/vpc/latest/privatelink/vpc-endpoints.html'],
+ ['Amazon EC2 オンデマンド料金(データ転送)', 'https://aws.amazon.com/ec2/pricing/on-demand/'],
+ ['AWSの料金の仕組み: データ転送', 'https://docs.aws.amazon.com/whitepapers/latest/how-aws-pricing-works/data-transfer.html'],
+ ['Amazon CloudFront とは', 'https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Introduction.html'],
+ ['AWS Global Accelerator とは', 'https://docs.aws.amazon.com/global-accelerator/latest/dg/what-is-global-accelerator.html'],
+ ['Amazon API Gateway でのリクエストスロットリング', 'https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html'],
+ ['AWS Certified Solutions Architect - Associate (SAA-C03) Exam Guide', 'https://docs.aws.amazon.com/aws-certification/latest/solutions-architect-associate-03/solutions-architect-associate-03.html'],
+ ['Content Domain 4: Design Cost-Optimized Architectures', 'https://docs.aws.amazon.com/aws-certification/latest/solutions-architect-associate-03/solutions-architect-associate-03-domain4.html'],
+ ] as const;
+
+ for (const [name, href] of expectedReferences) {
+ const link = references!.querySelector(`a[href="${href}"]`);
+ expect(link, name).not.toBeNull();
+ expect(link!.closest('li'), name).toHaveTextContent(name);
+ }
});
});
diff --git a/__tests__/cisco-ccde-guide.test.tsx b/__tests__/cisco-ccde-guide.test.tsx
index 501bcfaa9..46938aabc 100644
--- a/__tests__/cisco-ccde-guide.test.tsx
+++ b/__tests__/cisco-ccde-guide.test.tsx
@@ -132,9 +132,9 @@ describe('Cisco CCDE Complete Guide Page', () => {
expect(screen.getByText('Workforce Mobility')).toBeInTheDocument();
// 費用
- expect(screen.getByText('US$450')).toBeInTheDocument();
- expect(screen.getByText('US$1,600')).toBeInTheDocument();
- expect(screen.getByText('約 US$2,050')).toBeInTheDocument();
+ expect(screen.getByText('US$450(税別・受験料のみ)')).toBeInTheDocument();
+ expect(screen.getByText('US$1,600(税別・受験料のみ)')).toBeInTheDocument();
+ expect(screen.getByText('約 US$2,050(税別・受験料のみ)')).toBeInTheDocument();
// Explanatory Cells (説明セル)
expect(screen.getByText('なし(オープンな受験資格)')).toBeInTheDocument();
@@ -165,7 +165,7 @@ describe('Cisco CCDE Complete Guide Page', () => {
expect(screen.getByText('CCDE v3.1 Unified Exam Topics(公式PDF・出題ドメインと配点)')).toBeInTheDocument();
expect(screen.getByText('Recertification Policy(再認定ポリシー)')).toBeInTheDocument();
expect(screen.getByText('Cisco Continuing Education Program(CE単位による再認定)')).toBeInTheDocument();
- expect(screen.getByText('Exam, Testing, and Certification Policies(再受験の待機期間・18か月ルールなど)')).toBeInTheDocument();
+ expect(screen.getByText('Exam, Testing, and Certification Policies(再受験の待機期間など)')).toBeInTheDocument();
expect(screen.getByText(/Cisco Learning Network:CCDE v3.1 Unified Exam Topics and Study Guide/)).toBeInTheDocument();
});
diff --git a/__tests__/cisco/ccna/automation-application-deployment-security/page.test.tsx b/__tests__/cisco/ccna/automation-application-deployment-security/page.test.tsx
new file mode 100644
index 000000000..2f329ca9a
--- /dev/null
+++ b/__tests__/cisco/ccna/automation-application-deployment-security/page.test.tsx
@@ -0,0 +1,201 @@
+import { render, screen, fireEvent } from '@testing-library/react';
+import { describe, expect, it, vi } from 'vitest';
+import { readFileSync } from 'node:fs';
+import { join } from 'node:path';
+import CcnaAppDeploymentSecurityPage from '@/app/cisco/ccna/automation-application-deployment-security/page';
+
+const guideSource = readFileSync(
+ join(process.cwd(), 'app/cisco/ccna/automation-application-deployment-security/CcnaAppDeploymentSecurityGuide.tsx'),
+ 'utf8',
+);
+
+// Mock MermaidDiagram to avoid dynamic import / browser execution issues in Vitest
+vi.mock('@/components/MermaidDiagram', () => ({
+ MermaidDiagram: ({ chart, ariaLabel, preserveNaturalScale }: { chart: string; ariaLabel?: string; preserveNaturalScale?: boolean }) => (
+
+ Mermaid Diagram Mock
+
+ ),
+}));
+
+describe('CcnaAppDeploymentSecurityPage', () => {
+ it('renders main heading and hero eyebrow correctly', () => {
+ render( );
+
+ const mainHeading = screen.getByRole('heading', {
+ level: 1,
+ name: /Application Deployment and Security/i,
+ });
+ expect(mainHeading).toBeInTheDocument();
+
+ expect(screen.getByText(/CCNA AUTOMATION · 200-901 CCNAAUTO/i)).toBeInTheDocument();
+ });
+
+ it('renders all 12 chapters and reference section headings', () => {
+ render( );
+
+ const sectionTitles = [
+ '出題範囲の全体像',
+ 'エッジコンピューティングとアプリケーション展開モデル',
+ 'アプリケーション実行環境の比較:VM・ベアメタル・コンテナ',
+ 'CI/CDパイプラインの基礎',
+ 'Pythonユニットテストの構築',
+ 'Dockerfileの読み方とDockerイメージの活用',
+ 'アプリケーションセキュリティの基礎:シークレット保護・暗号化・データ取り扱い',
+ 'ネットワーク境界のセキュリティ要素:ファイアウォール・DNS・ロードバランサー・リバースプロキシ',
+ 'OWASPトップの脅威',
+ 'Bashコマンドの活用',
+ 'DevOpsの原則',
+ 'まとめ:ドメイン4.0 早見表',
+ '参考文献・出典',
+ ];
+
+ sectionTitles.forEach((title) => {
+ expect(
+ screen.getByRole('heading', { level: 2, name: new RegExp(title, 'i') }),
+ ).toBeInTheDocument();
+ });
+ });
+
+ it('renders subheadings correctly', () => {
+ render( );
+
+ const subHeadings = [
+ '4.1 エッジコンピューティングの利点',
+ '4.2 アプリケーション展開モデルの比較',
+ '3つの実行環境の構造比較',
+ '属性比較表',
+ 'CI/CDパイプラインの主な構成要素',
+ 'パイプライン全体の流れ',
+ 'テストの基本的な流れ(Arrange-Act-Assertパターン)',
+ '4.6 Dockerfileの内容を解釈する',
+ '4.7 ローカル開発環境でのDockerイメージの利用',
+ 'シークレット保護',
+ '暗号化(保存時・転送時)',
+ 'データ取り扱いの考え方',
+ 'リクエストが辿る経路',
+ '代表的な脅威の説明',
+ '最新の公式リストとの関係',
+ 'ファイル管理',
+ 'ディレクトリ操作',
+ '環境変数',
+ ];
+
+ subHeadings.forEach((subTitle) => {
+ expect(
+ screen.getByRole('heading', { level: 3, name: new RegExp(subTitle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i') }),
+ ).toBeInTheDocument();
+ });
+ });
+
+ it('renders sidebar navigation links correctly', () => {
+ const { container } = render( );
+
+ const tocNav = screen.getByRole('navigation', { name: /目次ナビゲーション/i });
+ expect(tocNav).toBeInTheDocument();
+
+ const links = screen.getAllByRole('link', { name: /出題範囲の全体像|CI\/CDパイプラインの基礎|DevOpsの原則/i });
+ expect(links.length).toBeGreaterThan(0);
+
+ const aside = container.querySelector('aside');
+ expect(aside).toHaveClass('sidebar');
+ });
+
+ it('renders all 10 mermaid diagrams with accessibility labels and natural scale enabled', () => {
+ const { container } = render( );
+
+ const diagrams = screen.getAllByTestId('mermaid-diagram');
+ expect(diagrams).toHaveLength(10);
+ diagrams.forEach((diagram) => {
+ expect(diagram).toHaveAttribute('data-natural-scale', 'true');
+ expect(diagram).toHaveAttribute('aria-label');
+ });
+
+ const diagramIds = [
+ 'diag-0', 'diag-1', 'diag-2', 'diag-3', 'diag-4',
+ 'diag-5', 'diag-6', 'diag-7', 'diag-8', 'diag-9'
+ ];
+ diagramIds.forEach((id) => {
+ expect(container.querySelector(`[data-diagram-id="${id}"]`)).toBeInTheDocument();
+ });
+ });
+
+ it('renders key tables and detailed content', () => {
+ render( );
+
+ expect(screen.getByRole('columnheader', { name: '配点比率' })).toBeInTheDocument();
+ expect(screen.getAllByText(/ハイブリッドクラウド/i).length).toBeGreaterThan(0);
+ expect(screen.getAllByText(/ベアメタル/i).length).toBeGreaterThan(0);
+ expect(screen.getAllByText(/Arrange-Act-Assert/i).length).toBeGreaterThan(0);
+ expect(screen.getAllByText(/OWASP Top 10/i).length).toBeGreaterThan(0);
+ });
+
+ it('ドメイン4.0の4.1〜4.12サブトピック対応表を省略せず描画する', () => {
+ render( );
+
+ for (let topic = 1; topic <= 12; topic += 1) {
+ expect(screen.getAllByRole('cell', { name: `4.${topic}` }).length).toBeGreaterThan(0);
+ }
+ expect(screen.getByText('DevOpsプラクティスの原則を説明する')).toBeInTheDocument();
+ });
+
+ it('ガイド本体はServer Componentで、main直下に中央寄せ用ラッパーを持つ', () => {
+ const { container } = render( );
+
+ expect(guideSource).not.toMatch(/^['"]use client['"];?/);
+ expect(container.querySelector('.main > .content-inner')).toBeInTheDocument();
+ });
+
+ it('目次クリック時にURLフラグメントとアクティブ項目を更新する', () => {
+ const originalScrollIntoView = Object.getOwnPropertyDescriptor(Element.prototype, 'scrollIntoView');
+ const originalHash = window.location.hash;
+ const scrollIntoView = vi.fn();
+
+ try {
+ Element.prototype.scrollIntoView = scrollIntoView;
+ const { container } = render( );
+ const link = container.querySelector('a[href="#chapter4"]');
+
+ expect(link).not.toBeNull();
+ fireEvent.click(link!);
+
+ expect(window.location.hash).toBe('#chapter4');
+ expect(link).toHaveClass('active');
+ expect(scrollIntoView).toHaveBeenCalledWith({ behavior: 'smooth' });
+ } finally {
+ if (originalScrollIntoView) {
+ Object.defineProperty(Element.prototype, 'scrollIntoView', originalScrollIntoView);
+ } else {
+ Reflect.deleteProperty(Element.prototype, 'scrollIntoView');
+ }
+ window.location.hash = originalHash;
+ }
+ });
+
+ it('Docker CMDの説明で「最後」を正しく表示する', () => {
+ const { container } = render( );
+
+ expect(container).toHaveTextContent('1つのDockerfileにつき最後のCMDのみ有効');
+ expect(screen.queryByText(/最期の/)).not.toBeInTheDocument();
+ });
+
+ it('renders external reference links', () => {
+ const { container } = render( );
+
+ const ciscoLink = screen.getByRole('link', { name: /Cisco 200-901 CCNAAUTO Exam Topics/i });
+ expect(ciscoLink).toHaveAttribute('href', 'https://learningnetwork.cisco.com/s/ccnaauto-exam-topics');
+
+ const owaspLink = screen.getByRole('link', { name: /OWASP Top 10:2025.*A05:2025 Injection/i });
+ expect(owaspLink).toHaveAttribute('href', 'https://owasp.org/Top10/2025/A05_2025-Injection/');
+ expect(screen.getByText(/2025年版では、InjectionはA05:2025に分類される/)).toBeInTheDocument();
+ expect(container.querySelector('[data-diagram-id="diag-8"] [data-chart]')).toHaveAttribute(
+ 'data-chart',
+ expect.stringContaining('A05:2025 Injection'),
+ );
+ });
+});
diff --git a/__tests__/cisco/ccna/automation-cisco-platforms-and-development/page.test.tsx b/__tests__/cisco/ccna/automation-cisco-platforms-and-development/page.test.tsx
new file mode 100644
index 000000000..a9e1973ae
--- /dev/null
+++ b/__tests__/cisco/ccna/automation-cisco-platforms-and-development/page.test.tsx
@@ -0,0 +1,166 @@
+import { render, screen } from '@testing-library/react';
+import { describe, expect, it, vi } from 'vitest';
+import { existsSync, readFileSync } from 'node:fs';
+import { join } from 'node:path';
+import CcnaCiscoPlatformsDevelopmentPage from '@/app/cisco/ccna/automation-cisco-platforms-and-development/page';
+
+const routeDirectory = join(
+ process.cwd(),
+ 'app/cisco/ccna/automation-cisco-platforms-and-development',
+);
+const pageSource = readFileSync(join(routeDirectory, 'page.tsx'), 'utf8');
+const guideSource = readFileSync(
+ join(routeDirectory, 'CcnaCiscoPlatformsDevelopmentGuide.tsx'),
+ 'utf8',
+);
+
+// Mock MermaidDiagram to avoid dynamic import / browser execution issues in Vitest
+vi.mock('@/components/MermaidDiagram', () => ({
+ MermaidDiagram: ({ chart, ariaLabel, preserveNaturalScale }: { chart: string; ariaLabel?: string; preserveNaturalScale?: boolean }) => (
+
+ Mermaid Diagram Mock
+
+ ),
+}));
+
+describe('CcnaCiscoPlatformsDevelopmentPage', () => {
+ it('renders main heading and hero kicker correctly', () => {
+ render( );
+
+ const mainHeading = screen.getByRole('heading', {
+ level: 1,
+ name: /Cisco Platforms and Development/i,
+ });
+ expect(mainHeading).toBeInTheDocument();
+
+ expect(screen.getByText(/CCNA Automation 200-901 CCNAAUTO v1.1/i)).toBeInTheDocument();
+ });
+
+ it('renders all 13 section headings correctly', () => {
+ render( );
+
+ const sectionTitles = [
+ 'はじめに',
+ 'CCNA Automation試験の全体像とこのドメインの位置づけ',
+ 'Cisco Platforms and Developmentドメインの全体マップ',
+ '3.1 Cisco SDKを使ったPythonスクリプトの構築',
+ '3.2〜3.5 Cisco製品プラットフォームとAPIの全体像',
+ '3.6 IOS XE / NX-OSのデバイスレベルAPIと動的インターフェース',
+ '3.7 シナリオに応じたDevNetリソースの選択',
+ '3.8 モデル駆動型プログラマビリティ(YANG / NETCONF / RESTCONF)',
+ '3.9 実践:APIドキュメントを基にしたコード構築',
+ '学習ロードマップ:ハンズオンの進め方',
+ '試験対策のポイントとよくある誤解',
+ 'まとめ',
+ '参考文献・出典一覧',
+ ];
+
+ sectionTitles.forEach((title) => {
+ expect(
+ screen.getByRole('heading', { level: 2, name: new RegExp(title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i') }),
+ ).toBeInTheDocument();
+ });
+ });
+
+ it('renders subheadings correctly', () => {
+ render( );
+
+ const subHeadings = [
+ 'SDKとは何か、なぜ使うのか',
+ '基本的なワークフロー',
+ 'コード例:Meraki SDKで組織一覧を取得する',
+ '3.2 ネットワーク管理プラットフォームとAPI',
+ '3.3 コンピュート管理プラットフォームとAPI',
+ '3.4 コラボレーションプラットフォームとAPI',
+ '3.5 セキュリティプラットフォームとAPI',
+ 'なぜ「モデル駆動」なのか',
+ 'NETCONFとRESTCONFの比較',
+ '3.9.a:Meraki APIでネットワークデバイス一覧を取得する',
+ '3.9.b:Webex APIでスペース・参加者・メッセージを管理する',
+ ];
+
+ subHeadings.forEach((subTitle) => {
+ expect(
+ screen.getByRole('heading', { level: 3, name: new RegExp(subTitle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i') }),
+ ).toBeInTheDocument();
+ });
+ });
+
+ it('renders structured tables content correctly', () => {
+ render( );
+
+ // Domain table
+ expect(screen.getByText('Software Development and Design')).toBeInTheDocument();
+ expect(screen.getByText('Understanding and Using APIs')).toBeInTheDocument();
+
+ // Platform overview table
+ expect(screen.getAllByText(/3.2 ネットワーク管理/i)[0]).toBeInTheDocument();
+ expect(screen.getAllByText(/Meraki, Cisco DNA Center, ACI, Cisco SD-WAN, NSO/i)[0]).toBeInTheDocument();
+
+ // Security table
+ expect(screen.getByText('Secure Endpoint')).toBeInTheDocument();
+ expect(screen.getByText('Secure Malware Analytics')).toBeInTheDocument();
+
+ // NETCONF vs RESTCONF table
+ expect(screen.getByText('SSH(既定ポート830)')).toBeInTheDocument();
+ expect(screen.getByText('HTTPS(既定ポート443)')).toBeInTheDocument();
+ });
+
+ it('renders sidebar navigation links correctly', () => {
+ const { container } = render( );
+
+ const tocNav = screen.getByRole('navigation', { name: /目次ナビゲーション/i });
+ expect(tocNav).toBeInTheDocument();
+
+ const aside = container.querySelector('aside');
+ expect(aside).toHaveClass('sidebar');
+ });
+
+ it('renders all 10 Mermaid diagrams with ariaLabels and preserveNaturalScale', () => {
+ render( );
+
+ const diagrams = screen.getAllByTestId('mermaid-diagram');
+ expect(diagrams).toHaveLength(10);
+
+ diagrams.forEach((diagram) => {
+ expect(diagram.getAttribute('aria-label')).toBeTruthy();
+ expect(diagram.getAttribute('data-natural-scale')).toBe('true');
+ });
+ });
+
+ it('renders reference links correctly', () => {
+ render( );
+
+ const refLinks = screen.getAllByRole('link', { name: /^https:\/\//i });
+ expect(refLinks.length).toBeGreaterThanOrEqual(16);
+ });
+
+ it('ページスタイルをCSS ModuleとしてClient Componentから適用する', () => {
+ const modulePath = join(routeDirectory, 'page.module.css');
+
+ expect(existsSync(modulePath)).toBe(true);
+ expect(pageSource).not.toContain("import './page.css'");
+ expect(guideSource).toContain("import styles from './page.module.css'");
+ expect(guideSource).toMatch(/className=\{styles\.ccnaPlatformsDevPage\}/);
+ });
+
+ it('ローカルCSS変数を再定義せず、全幅の本文ラッパーを持つ', () => {
+ const modulePath = join(routeDirectory, 'page.module.css');
+ const moduleStyles = existsSync(modulePath) ? readFileSync(modulePath, 'utf8') : '';
+ const articleBodyRule = moduleStyles.match(
+ /\.ccnaPlatformsDevPage :global\(\.article-body\)\s*\{([^}]*)\}/,
+ )?.[1] ?? '';
+ const { container } = render( );
+
+ expect(moduleStyles).not.toMatch(/(?:^|[;{])\s*--[^:;{}]+\s*:/);
+ expect(articleBodyRule).toMatch(/width:\s*100%\s*;/);
+ expect(articleBodyRule).toMatch(/max-width:\s*none\s*;/);
+ expect(articleBodyRule).not.toMatch(/margin-inline:/);
+ expect(container.querySelector('.article-body')).toBeInTheDocument();
+ });
+});
diff --git a/__tests__/cisco/ccna/automation-software-development-design/page.test.tsx b/__tests__/cisco/ccna/automation-software-development-design/page.test.tsx
index 49ca6c2ce..8d8c5bacf 100644
--- a/__tests__/cisco/ccna/automation-software-development-design/page.test.tsx
+++ b/__tests__/cisco/ccna/automation-software-development-design/page.test.tsx
@@ -76,22 +76,9 @@ describe('CcnaSoftwareDevDesignPage', () => {
expect(diagrams).toHaveLength(12);
diagrams.forEach((diagram) => expect(diagram).toHaveAttribute('data-natural-scale', 'true'));
- const widths: Record = {
- 'diag-0': '760px',
- 'diag-1': '1200px',
- 'diag-2': '760px',
- 'diag-3': '760px',
- 'diag-4': '940px',
- 'diag-5': '940px',
- 'diag-6': '760px',
- 'diag-7': '900px',
- 'diag-8': '860px',
- 'diag-9': '900px',
- 'diag-10': '760px',
- 'diag-11': '760px',
- };
- Object.entries(widths).forEach(([id, width]) => {
- expect(container.querySelector(`[data-diagram-id="${id}"]`)).toHaveStyle({ maxWidth: width });
+ const diagramIds = ['diag-0', 'diag-1', 'diag-2', 'diag-3', 'diag-4', 'diag-5', 'diag-6', 'diag-7', 'diag-8', 'diag-9', 'diag-10', 'diag-11'];
+ diagramIds.forEach((id) => {
+ expect(container.querySelector(`[data-diagram-id="${id}"]`)).toHaveStyle({ maxWidth: '100%' });
});
});
diff --git a/__tests__/components/MermaidDiagram.test.tsx b/__tests__/components/MermaidDiagram.test.tsx
index 179cbc0e5..66957ed8b 100644
--- a/__tests__/components/MermaidDiagram.test.tsx
+++ b/__tests__/components/MermaidDiagram.test.tsx
@@ -1,7 +1,14 @@
import { describe, it, expect, vi } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
+import { readFileSync } from 'node:fs';
+import { join } from 'node:path';
import { MermaidDiagram, applySvgFixups } from '@/components/MermaidDiagram';
+const mermaidStyles = readFileSync(
+ join(process.cwd(), 'components/MermaidDiagram.module.css'),
+ 'utf8',
+);
+
/** viewBox 付き SVG 要素を生成するヘルパー */
const makeSvg = (viewBox?: string): SVGSVGElement => {
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') as SVGSVGElement;
@@ -12,6 +19,11 @@ const makeSvg = (viewBox?: string): SVGSVGElement => {
describe('MermaidDiagram', () => {
const sampleChart = 'flowchart LR\n A[Start] --> B[End]';
+ it('自然幅のSVGをflexで縮小せず、Mermaidの採寸値16pxと同じ文字サイズを使うこと', () => {
+ expect(mermaidStyles).toMatch(/\.mermaidTarget\s+:global\(svg\)[^{]*\{[^}]*flex-shrink:\s*0;/s);
+ expect(mermaidStyles).toMatch(/:global\(tspan\)[^{]*\{[^}]*font-size:\s*16px\s*!important;/s);
+ });
+
it('role="img" の wrapper を ariaLabel 付きで描画すること', () => {
render( );
const wrapper = screen.getByRole('img', { name: /サンプルフロー図/ });
@@ -108,13 +120,27 @@ describe('MermaidDiagram', () => {
expect(svg.getAttribute('viewBox')).toBe('0 0 250 615');
});
- it('個別指定された図は自然倍率を維持して文字を拡大縮小しないこと', () => {
+ it('個別指定された図は自然倍率(1.0倍)を維持し、文字が巨大化しないよう自然px幅を使用すること', () => {
const svg = makeSvg('0 0 250 600');
applySvgFixups(svg, 'flowchart TD\nA-->B', true);
+ // preserveNaturalScale=true: viewBox 由来の自然幅 250px をそのまま維持し 1rem 実寸で表示
expect(svg.style.width).toBe('250px');
- expect(svg.style.maxWidth).toBe('100%');
+ // preserveNaturalScale=true: コンテナ幅で縮小させないよう max-width:none を設定
+ expect(svg.style.maxWidth).toBe('none');
+ expect(svg.style.maxHeight).toBe('none');
+ });
+
+ it('個別指定された図でviewBox幅が十分大きい場合はそのままの幅を使用すること', () => {
+ const svg = makeSvg('0 0 800 600');
+
+ applySvgFixups(svg, 'flowchart TD\nA-->B', true);
+
+ // viewBox 幅 800 > 600 のため、そのまま 800px
+ expect(svg.style.width).toBe('800px');
+ // preserveNaturalScale=true: max-width:none でスクロール時縮小を防止
+ expect(svg.style.maxWidth).toBe('none');
expect(svg.style.maxHeight).toBe('none');
});
diff --git a/__tests__/gcl/associate-cloud-engineer/section1/theme-tokens.test.ts b/__tests__/gcl/associate-cloud-engineer/section1/theme-tokens.test.ts
new file mode 100644
index 000000000..9365dbf4d
--- /dev/null
+++ b/__tests__/gcl/associate-cloud-engineer/section1/theme-tokens.test.ts
@@ -0,0 +1,43 @@
+import { readFileSync } from 'node:fs';
+import { join } from 'node:path';
+import { describe, expect, it } from 'vitest';
+
+const readWorkspaceFile = (path: string): string =>
+ readFileSync(join(process.cwd(), path), 'utf8');
+
+const themeTokens = [
+ '--color-gcp-purple',
+ '--color-gcp-teal',
+ '--color-gcp-card-hover',
+ '--color-gcp-code-background',
+ '--color-gcp-border',
+ '--color-gcp-border-bright',
+ '--color-gcp-accent-glow',
+] as const;
+
+const extractCustomPropertyNames = (css: string): string[] =>
+ Array.from(css.matchAll(/(? match[1]);
+
+describe('ACE Section 1 theme token ownership', () => {
+ it('defines each shared GCP guide token once in globals.css and not on the page scope', () => {
+ const globals = readWorkspaceFile('app/globals.css');
+ const pageStyles = readWorkspaceFile(
+ 'app/gcl/associate-cloud-engineer/section1/page.css',
+ );
+ const globalPropertyNames = extractCustomPropertyNames(globals);
+ const pagePropertyNames = extractCustomPropertyNames(pageStyles);
+
+ for (const token of themeTokens) {
+ expect(globalPropertyNames.filter((name) => name === token)).toHaveLength(1);
+ expect(pagePropertyNames).not.toContain(token);
+ }
+ });
+
+ it('imports the dedicated stylesheet directly from the owning page', () => {
+ const page = readWorkspaceFile(
+ 'app/gcl/associate-cloud-engineer/section1/page.tsx',
+ );
+
+ expect(page).toContain("import './page.css';");
+ });
+});
diff --git a/__tests__/gcl/hands-on/terraform-gcp-challenge-lab-guide/page.test.tsx b/__tests__/gcl/hands-on/terraform-gcp-challenge-lab-guide/page.test.tsx
index e1c884ebc..b0496706f 100644
--- a/__tests__/gcl/hands-on/terraform-gcp-challenge-lab-guide/page.test.tsx
+++ b/__tests__/gcl/hands-on/terraform-gcp-challenge-lab-guide/page.test.tsx
@@ -1,9 +1,16 @@
// @vitest-environment jsdom
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
+import { readFileSync } from 'node:fs';
+import { join } from 'node:path';
import Page from '@/app/gcl/hands-on/terraform-gcp-challenge-lab-guide/page';
import { DIAGRAMS } from '@/app/gcl/hands-on/terraform-gcp-challenge-lab-guide/constants';
+const pageStyles = readFileSync(
+ join(process.cwd(), 'app/gcl/hands-on/terraform-gcp-challenge-lab-guide/page.css'),
+ 'utf8',
+);
+
// MermaidDiagram コンポーネントをモック化
vi.mock('@/components/MermaidDiagram', () => ({
MermaidDiagram: function DummyMermaidDiagram({
@@ -123,6 +130,13 @@ describe('Terraform GCP Challenge Lab 完全攻略ガイド ページ', () => {
});
});
+ it('アイコンフォントを外部 CDN ではなくローカル npm パッケージから読み込むこと', () => {
+ expect(pageStyles).toContain(
+ "@import '@tabler/icons-webfont/tabler-icons.min.css';",
+ );
+ expect(pageStyles).not.toMatch(/@import\s+['"]https?:\/\//);
+ });
+
it('目次リンクに対応する本文セクションが存在すること', () => {
const links = container.querySelectorAll('.sidebar-nav a[href^="#"]');
expect(links.length).toBeGreaterThan(0);
diff --git a/__tests__/guide-content-widths.test.ts b/__tests__/guide-content-widths.test.ts
new file mode 100644
index 000000000..90a0c92c1
--- /dev/null
+++ b/__tests__/guide-content-widths.test.ts
@@ -0,0 +1,88 @@
+import { readFileSync } from 'node:fs';
+import { join } from 'node:path';
+import { describe, expect, it } from 'vitest';
+
+const readWorkspaceFile = (path: string): string =>
+ readFileSync(join(process.cwd(), path), 'utf8');
+
+const escapeRegExp = (value: string): string =>
+ value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+
+const firstRule = (css: string, selector: string): string => {
+ const match = css.match(new RegExp(`${escapeRegExp(selector)}\\s*\\{([^}]*)\\}`));
+ expect(match, `missing CSS rule: ${selector}`).not.toBeNull();
+ return match?.[1] ?? '';
+};
+
+const rulesAfterFirst = (css: string, selector: string): string[] => {
+ const matches = [...css.matchAll(new RegExp(`${escapeRegExp(selector)}\\s*\\{([^}]*)\\}`, 'g'))];
+ return matches.slice(1).map((match) => match[1] ?? '');
+};
+
+const guideLayouts = [
+ ['app/aws/solutions-architect-associate/domain1/page.css', '.aws-saa-domain1-page #sidebar', '.aws-saa-domain1-page .main-content'],
+ ['app/aws/solutions-architect-associate/domain2/page.css', '.domain2-page .sidebar', '.domain2-page .content'],
+ ['app/aws/solutions-architect-associate/domain3/page.css', '.domain3-page .sidebar', '.domain3-page .content'],
+ ['app/aws/solutions-architect-associate/domain4/page.css', '.domain4-page .sidebar', '.domain4-page .content'],
+ ['app/aws/solutions-architect-associate/page.css', '.aws-saa-page .sidebar', '.aws-saa-page .main'],
+ ['app/cisco/ccna/automation-api-guide/page.css', '.ccna-automation-api-page .sidebar', '.ccna-automation-api-page .main'],
+ ['app/cisco/ccna/automation-application-deployment-security/page.css', '.ccna-app-deployment-security-page .sidebar', '.ccna-app-deployment-security-page .main'],
+ ['app/cisco/ccna/automation-cisco-platforms-and-development/page.module.css', '.ccnaPlatformsDevPage :global(.sidebar)', '.ccnaPlatformsDevPage :global(.content)'],
+ ['app/cisco/ccna/automation-software-development-design/page.css', '.ccna-software-dev-design-page .sidebar', '.ccna-software-dev-design-page .main'],
+ ['app/cisco/ccna/beginner-guide/page.css', '.ccna-beginner-page .sidebar', '.ccna-beginner-page main'],
+ ['app/cisco/ccna/ip-connectivity-guide/page.css', '.ccna-ip-connectivity-page .sidebar', '.ccna-ip-connectivity-page .main'],
+ ['app/cisco/ccna/ip-services-guide/page.css', '.ccna-ip-services-page .sidebar', '.ccna-ip-services-page .main'],
+ ['app/cisco/ccna/security-fundamentals/page.css', '.ccna-security-page .sidebar', '.ccna-security-page main'],
+ ['app/gcl/agwa/agwa.css', '.sidebar', '.main'],
+ ['app/gcl/agwa/section2/page.module.css', '.sidebar', '.main'],
+ ['app/gcl/associate-cloud-engineer/complete-advanced-guide/page.css', '.complete-guide-page .sidebar', '.complete-guide-page .main'],
+ ['app/gcl/associate-cloud-engineer/section1/page.css', '.ace-s1-page .sidebar', '.ace-s1-page .main'],
+ ['app/gcl/associate-cloud-engineer/section2/page.css', '.ace-s2-page .sidebar', '.ace-s2-page .main'],
+ ['app/gcl/associate-cloud-engineer/section3/page.module.css', '.sidebar', '.ace-section3-page :global(.main-content)'],
+ ['app/gcl/associate-cloud-engineer/section4/page.module.css', '.sidebar', '.main'],
+ ['app/gcl/hands-on/gke-private-cluster-security-guide/page.css', '.gke-security-guide-page .sidebar', '.gke-security-guide-page .main'],
+ ['app/gcl/hands-on/griffin-wordpress-gke-guide/page.css', '.griffin-wordpress-gke-guide-page .sidebar', '.griffin-wordpress-gke-guide-page .main'],
+ ['app/gcl/hands-on/iap-tcp-forwarding-best-practices-guide/page.css', '.iap-guide-page .sidebar', '.iap-guide-page main'],
+ ['app/gcl/hands-on/terraform-gcp-challenge-lab-guide/page.css', '.terraform-lab-guide-page .sidebar', '.terraform-lab-guide-page .main'],
+] as const;
+
+describe('all sidebar guide layouts', () => {
+ it.each(guideLayouts)('%s fixes a 280px sidebar to the far left', (stylesheet, sidebarSelector) => {
+ const rule = firstRule(readWorkspaceFile(stylesheet), sidebarSelector);
+
+ expect(rule).toMatch(/position:\s*fixed\s*;/);
+ expect(rule).toMatch(/left:\s*0\s*;/);
+ expect(rule).toMatch(/width:\s*280px\s*;/);
+ });
+
+ it.each(guideLayouts)('%s gives the main canvas all remaining desktop width', (stylesheet, _sidebarSelector, mainSelector) => {
+ const rule = firstRule(readWorkspaceFile(stylesheet), mainSelector);
+
+ expect(rule).toMatch(/margin-left:\s*280px\s*;/);
+ expect(rule).toMatch(/width:\s*calc\(100%\s*-\s*280px\)\s*;/);
+ expect(rule).toMatch(/max-width:\s*none\s*;/);
+ expect(rule).toMatch(/box-sizing:\s*border-box\s*;/);
+ });
+
+ it.each(guideLayouts)('%s restores a 100% main canvas in its responsive rule', (stylesheet, _sidebarSelector, mainSelector) => {
+ const responsiveRules = rulesAfterFirst(readWorkspaceFile(stylesheet), mainSelector);
+
+ expect(responsiveRules.some((rule) =>
+ /margin-left:\s*0\s*;/.test(rule)
+ && /width:\s*100%\s*;/.test(rule)
+ && /max-width:\s*none\s*;/.test(rule),
+ )).toBe(true);
+ });
+
+ it('removes narrow inner wrappers that previously re-constrained full-width guides', () => {
+ const constrainedWrappers = [
+ ['app/cisco/ccna/automation-application-deployment-security/page.css', '.ccna-app-deployment-security-page .content-inner'],
+ ['app/gcl/hands-on/gke-private-cluster-security-guide/page.css', '.gke-security-guide-page .content-inner'],
+ ['app/gcl/hands-on/griffin-wordpress-gke-guide/page.css', '.griffin-wordpress-gke-guide-page .content-inner'],
+ ] as const;
+
+ for (const [stylesheet, selector] of constrainedWrappers) {
+ expect(firstRule(readWorkspaceFile(stylesheet), selector)).toMatch(/max-width:\s*none\s*;/);
+ }
+ });
+});
diff --git a/__tests__/skills/fix-mermaid/render-pipeline.test.ts b/__tests__/skills/fix-mermaid/render-pipeline.test.ts
new file mode 100644
index 000000000..f7f626324
--- /dev/null
+++ b/__tests__/skills/fix-mermaid/render-pipeline.test.ts
@@ -0,0 +1,98 @@
+import { describe, expect, test } from 'vitest';
+import {
+ applyPipeline,
+ ensureInitFlags,
+ injectRenderLoop,
+} from '../../../.agents/skills/fix-mermaid/scripts/apply_render_pipeline.mjs';
+import { extractDiagramsDefinition } from '../../../.agents/skills/fix-mermaid/scripts/restore_diagrams.mjs';
+
+const FIXTURE = `
+
+
+
+ flowchart TD
+A --> B
+
+
+`;
+
+describe('ensureInitFlags', () => {
+ test('initialize 外の securityLevel は設定済みと誤認しない', () => {
+ const input = `
+ const unrelated = { securityLevel: 'strict' };
+ const label = "securityLevel: loose";
+ // securityLevel: strict
+ mermaid.initialize({ theme: 'dark' });
+ `;
+ const out = ensureInitFlags(input);
+
+ expect(out).toContain("mermaid.initialize({ securityLevel: 'loose', theme: 'dark' })");
+ expect(out.match(/securityLevel/g)?.length).toBe(4);
+ });
+});
+
+describe('applyPipeline', () => {
+ test('DIAGRAMS 未定義なら即座に失敗する', () => {
+ const input = FIXTURE.replace(/\s*const DIAGRAMS = \{[\s\S]*?\};/, '');
+
+ expect(() => applyPipeline(input)).toThrow(/DIAGRAMS/);
+ });
+});
+
+describe('JavaScript source scanning', () => {
+ test('制御文の閉じ括弧後にある正規表現を飛ばして実宣言を抽出する', () => {
+ const html = ``;
+
+ expect(extractDiagramsDefinition(html).diagrams).toEqual({
+ 'diag-1': 'flowchart TD\nA --> B',
+ });
+ });
+
+ test('メンバー名 return を正規表現開始キーワードとして扱わない', () => {
+ const html = ``;
+
+ expect(extractDiagramsDefinition(html).diagrams).toEqual({
+ 'diag-1': 'flowchart TD\nA --> B',
+ });
+ });
+
+ test("要素間のカンマがない DIAGRAMS 定義を拒否する", () => {
+ const html = ``;
+
+ expect(() => extractDiagramsDefinition(html)).toThrow(
+ "DIAGRAMS の値の後には ',' または '}' が必要です。",
+ );
+ });
+});
+
+describe('injectRenderLoop', () => {
+ test.each(['', ''])(
+ '閉じタグの表記ゆれ %s の直前へ注入する',
+ (closingTag) => {
+ const input = ` = {
+ 'diag-0': { frameWidth: 800 },
+ 'diag-1': { frameWidth: 800 },
+ 'diag-2': { frameWidth: 900 },
+ 'diag-3': { frameWidth: 900 },
+ 'diag-4': { frameWidth: 800 },
+ 'diag-5': { frameWidth: 800 },
+ 'diag-6': { frameWidth: 800 },
+ 'diag-7': { frameWidth: 850 },
+ 'diag-8': { frameWidth: 850 },
+ 'diag-9': { frameWidth: 800 },
+};
+
+function Diagram({ id, label }: { id: string; label: string }) {
+ const chart = DIAGRAMS[id];
+ if (!chart) return null;
+ const display = DIAGRAM_DISPLAY[id] ?? { frameWidth: 800 };
+ return (
+
+
+
+ );
+}
+
+export function CcnaAppDeploymentSecurityGuide() {
+ return (
+
+
+
+
+
+
+
+ CCNA AUTOMATION · 200-901 CCNAAUTO
+ Application Deployment and Security 完全解説ガイド
+
+ 本ガイドは、Cisco公式サイトおよび公式試験ガイドをもとに、CCNA Automation認定の試験「Automating Networks Using Cisco Platforms v1.1(200-901 CCNAAUTO)」の6つの出題ドメインのうち、ドメイン4.0「Application Deployment and Security(アプリケーションの展開とセキュリティ)」 (配点15%)を初学者向けに解説したものです。
+
+
+
+ {/* 第1章 */}
+
+ 第1章 出題範囲の全体像
+
+ CCNA Automation認定を取得するには、120分の試験「200-901 CCNAAUTO」に合格する必要があります。この試験は6つのドメインで構成されており、それぞれに配点比率が設定されています。
+
+
+
+
+
+
+ ドメイン番号
+ ドメイン名
+ 配点比率
+
+
+
+
+ 1.0
+ Software Development and Design(ソフトウェア開発と設計)
+ 15%
+
+
+ 2.0
+ Understanding and Using APIs(APIの理解と利用)
+ 20%
+
+
+ 3.0
+ Cisco Platforms and Development(Ciscoプラットフォームと開発)
+ 15%
+
+
+ 4.0
+ Application Deployment and Security(アプリケーションの展開とセキュリティ)
+ 15%
+
+
+ 5.0
+ Infrastructure and Automation(インフラストラクチャと自動化)
+ 20%
+
+
+ 6.0
+ Network Fundamentals(ネットワークの基礎)
+ 15%
+
+
+
+
+
+
+ 本ガイドが扱うドメイン4.0は、「作ったアプリケーションをどこに・どうやって・安全に動かすか」 という、開発から運用への橋渡しにあたる領域です。具体的には以下の12個のサブトピックで構成されます。
+
+
+
+
+
+
+ サブトピック
+ 内容
+
+
+
+
+ 4.1
+ エッジコンピューティングの利点を説明する
+
+
+ 4.2
+ 異なるアプリケーション展開モデル(プライベートクラウド、パブリッククラウド、ハイブリッドクラウド、エッジ)の属性を説明する
+
+
+ 4.3
+ 展開タイプ(仮想マシン/ベアメタル/コンテナ)の属性を説明する
+
+
+ 4.4
+ アプリケーション展開におけるCI/CDパイプラインの構成要素を説明する
+
+
+ 4.5
+ Pythonのユニットテストを構築する
+
+
+ 4.6
+ Dockerfileの内容を解釈する
+
+
+ 4.7
+ ローカル開発環境でDockerイメージを利用する
+
+
+ 4.8
+ シークレット保護、暗号化(保存時・転送時)、データ取り扱いに関するアプリケーションセキュリティの課題を説明する
+
+
+ 4.9
+ ファイアウォール、DNS、ロードバランサー、リバースプロキシのアプリケーション展開における役割を説明する
+
+
+ 4.10
+ OWASPのトップ脅威(XSS、SQLインジェクション、CSRFなど)を説明する
+
+
+ 4.11
+ Bashコマンド(ファイル管理、ディレクトリ操作、環境変数)を利用する
+
+
+ 4.12
+ DevOpsプラクティスの原則を説明する
+
+
+
+
+
+ このドメイン全体の位置づけを図にすると次のとおりです。
+
+
+
+
+ {/* 第2章 */}
+
+ 第2章 エッジコンピューティングとアプリケーション展開モデル(4.1・4.2)
+
+ 4.1 エッジコンピューティングの利点
+
+ 従来のクラウドコンピューティングでは、すべてのデータを遠くのデータセンター(パブリッククラウド)に送信して処理していました。しかし、IoT機器の爆発的増加やリアルタイム処理の要求に伴い、「データの発生源の近く(エッジ)」で処理を行うエッジコンピューティング が重要になっています。
+
+
+
+
+
+
+ 利点
+ 説明
+ 具体例
+
+
+
+
+ 低遅延(Low Latency)
+ データセンター往復の通信時間を削り、即座にレスポンスを返せる
+ 自動運転車の障害物検知、工場のロボット制御
+
+
+ 帯域幅の節約(Bandwidth Efficiency)
+ 生のビデオ映像などをすべてクラウドへ送らず、エッジ側で集約・フィルタリングする
+ 防犯カメラの映像解析(異常検知の通知ログのみクラウドへ送信)
+
+
+ プライバシー・データローカリティ
+ 機密性の高い個人情報や社内データを組織外に出さずに現地で処理できる
+ 店舗POSの決済データ処理、医療機器のデータ解析
+
+
+ オフライン自律性(Resilience)
+ クラウドへのWAN回線が切断されても、エッジノード単体で処理を継続できる
+ 遠隔地のスマート農業センサー、船舶内のシステム
+
+
+
+
+
+ 4.2 アプリケーション展開モデルの比較
+
+ アプリケーションをデプロイ(展開)する場所の選択肢には、主に以下の4つのモデルがあります。
+
+
+
+
+
+
+ 展開モデル
+ 概要
+ メリット
+ デメリット・考慮点
+
+
+
+
+ プライベートクラウド(Private Cloud)
+ 自社専用の仮想化・クラウド基盤(オンプレミスデータセンター等)
+ 完全な制御権、高いカスタマイズ性、高いセキュリティ
+ 初期コスト(CapEx)が高額、運用保守の手間
+
+
+ パブリッククラウド(Public Cloud)
+ AWS, GCP, Azure等の事業者が提供する共有クラウド基盤
+ 従量課金、無制限の拡張性(スケーラビリティ)、保守不要
+ 通信遅延、データローカリティの制約、継続的コスト(OpEx)
+
+
+ ハイブリッドクラウド(Hybrid Cloud)
+ プライベートクラウドとパブリッククラウドを連携させた構成
+ 重要なデータは自社、急増アクセスはパブリックへ逃す柔軟性
+ ネットワーク構成・アイデンティティ統合の複雑化
+
+
+ エッジ(Edge)
+ 現場近くのネットワークエッジ(ルータ、ローカルサーバー、IoTゲートウェイ)
+ 極めて低い遅延、帯域節約、ローカルでのデータ保持
+ 計算リソース(CPU/メモリ)の制約、拠点ごとの管理負荷
+
+
+
+
+
+
+
+
+ {/* 第3章 */}
+
+ 第3章 アプリケーション実行環境の比較:VM・ベアメタル・コンテナ(4.3)
+
+ 3つの実行環境の構造比較
+
+ アプリケーションを動作させる実行環境(環境アーキテクチャ)には、ベアメタル(物理サーバー)、仮想マシン(VM)、コンテナ(Container)の3種類があります。試験ではこれらの違いとトレードオフが問われます。
+
+
+
+
+ 属性比較表
+
+
+
+
+ 比較項目
+ ベアメタル(物理サーバー)
+ 仮想マシン(VM)
+ コンテナ(Docker等)
+
+
+
+
+ 隔離レベル
+ ハードウェアレベルで完全分離
+ ハイパーバイザーによる強固なOS分離
+ ホストOSカーネル共有のプロセス分離(Namespaces / cgroups)
+
+
+ 起動時間
+ 数分~十数分(物理ブート)
+ 数分(ゲストOSの立ち上がり)
+ 数ミリ秒~数秒 (プロセス起動と同等)
+
+
+ リソースオーバーヘッド
+ なし(100%ハードウェア活用)
+ 大(ゲストOSごとにメモリ・CPUを消費)
+ 極めて小 (OSを共有しプロセスのみ動作)
+
+
+ 可搬性(Portability)
+ 低(特定のハードウェアに依存)
+ 中(VMイメージフォーマットに依存)
+ 最高 (「どこでも動く」Dockerイメージ)
+
+
+ 主な用途
+ 最高性能が必要なDB、レガシー基盤
+ 異なるOSの混在運用、マルチテナント環境
+ マイクロサービス、CI/CD、クラウドネイティブアプリ
+
+
+
+
+
+
+ {/* 第4章 */}
+
+ 第4章 CI/CDパイプラインの基礎(4.4)
+
+ CI/CDパイプラインの主な構成要素
+
+ CI/CD(Continuous Integration / Continuous Delivery & Deployment)は、コードの変更からテスト、本番適用までを自動化するDevOpsの中核概念です。
+
+
+
+
+
+
+ ステージ/用語
+ 英語表記
+ 役割と内容
+
+
+
+
+ 継続的インテグレーション
+ Continuous Integration (CI)
+ 開発者がコードを頻繁にリポジトリへ統合し、ビルドと自動テストを毎回実行してバグを早期発見する
+
+
+ 継続的デリバリー
+ Continuous Delivery (CD)
+ テスト済みのコードをいつでも本番リリースできる状態に自動維持する(本番適用には手動の承認ステップが入る)
+
+
+ 継続的デプロイ
+ Continuous Deployment (CD)
+ テスト成功後、本番環境へのデプロイまで人の手を介さず完全自動で実行する
+
+
+ ビルドステージ
+ Build Stage
+ ソースコードのコンパイル、依存ライブラリのロード、実行可能ファイルの作成を行う
+
+
+ テストステージ
+ Test Stage
+ ユニットテスト、統合テスト、静的コード解析を実行し、品質基準を満たしているか確認する
+
+
+ リファクタリング・フィードバック
+ Feedback Loop
+ ビルドやテストが失敗した場合、直ちに開発チームへ通知してコードを修復させる
+
+
+
+
+
+ パイプライン全体の流れ
+
+
+
+ {/* 第5章 */}
+
+ 第5章 Pythonユニットテストの構築(4.5)
+
+ テストの基本的な流れ(Arrange-Act-Assertパターン)
+
+ ソフトウェアテストの標準的なフレームワークとして、Arrange-Act-Assert (AAA) パターン が用いられます。
+
+
+
+
+
+
+ ステップ
+ 内容
+ 具体的なコード例
+
+
+
+
+ 1. Arrange(準備)
+ テストに必要なデータや前提条件(モックや入力値)を用意する
+ x = 10; y = 20
+
+
+ 2. Act(実行)
+ テスト対象となる関数やメソッドを実際に呼び出す
+ result = add_numbers(x, y)
+
+
+ 3. Assert(検証)
+ 実行結果が期待通り(Expected)かアサーションで確認する
+ assert result == 30
+
+
+
+
+
+
+
+ コード例
+ Pythonの組み込みライブラリ unittest を使用した基本的なコード例です。
+
+
+
+ app_test.py
+ Python
+
+
import unittest
+
from app import calculate_bandwidth
+
+
class TestNetworkUtils (unittest.TestCase):
+
def test_calculate_bandwidth_success (self):
+
# Arrange
+
bytes_sent = 1000000
+
seconds = 2
+
+
# Act
+
mbps = calculate_bandwidth(bytes_sent, seconds)
+
+
# Assert
+
self.assertEqual(mbps, 4.0 )
+
+
+
+ {/* 第6章 */}
+
+ 第6章 Dockerfileの読み方とDockerイメージの活用(4.6・4.7)
+
+ 4.6 Dockerfileの内容を解釈する
+
+ Dockerfileはコンテナイメージを作成するための設計図(テキストファイル)です。主要な命令の意味を理解することが出題されます。
+
+
+
+
+
+
+ Dockerfile命令
+ 役割
+ 注意点・特徴
+
+
+
+
+ FROM
+ ベースとなる既存のイメージを指定(必須)
+ FROM python:3.11-slim など
+
+
+ WORKDIR
+ 以降の命令が実行される作業ディレクトリを設定
+ 存在しない場合は自動生成される
+
+
+ COPY / ADD
+ ホストOSのファイルをイメージ内にコピーする
+ ADDはリモートURL取得やtar解凍にも対応するがCOPYが推奨される
+
+
+ RUN
+ イメージビルド時にコマンドを実行しレイヤーを追加
+ RUN pip install -r requirements.txt
+
+
+ EXPOSE
+ コンテナが公開するポート番号を明示(ドキュメント的役割)
+ 実際のポートフォワードは docker run -p で行う
+
+
+ ENV
+ コンテナ内の環境変数を設定
+ ENV PORT=8080
+
+
+ CMD
+ コンテナ起動時に実行するデフォルトコマンド
+ 1つのDockerfileにつき最後のCMDのみ有効
+
+
+
+
+
+ サンプルDockerfile
+
+
+ Dockerfile
+ Docker
+
+
FROM python:3.11-slim
+
WORKDIR /app
+
COPY requirements.txt .
+
RUN pip install --no-cache-dir -r requirements.txt
+
COPY . .
+
EXPOSE 8000
+
CMD ["python", "main.py"]
+
+
+ 4.7 ローカル開発環境でのDockerイメージの利用
+
+ 作成したDockerイメージを操作するための主要なCLIコマンドです。
+
+
+
+
+
+
+ コマンド
+ 説明
+
+
+
+
+ docker build -t myapp:1.0 .
+ カレントディレクトリのDockerfileからタグ名myapp:1.0でイメージを生成
+
+
+ docker images
+ ローカルに保存されているイメージの一覧を表示
+
+
+ docker run -d -p 8080:8000 myapp:1.0
+ バックグラウンド(-d)でコンテナを起動。ホスト8080ポートをコンテナ8000ポートへ転送
+
+
+ docker ps / docker ps -a
+ 稼働中(-aで全状態)のコンテナ一覧を表示
+
+
+ docker stop <container_id>
+ 実行中のコンテナを安全に停止
+
+
+
+
+
+ イメージのライフサイクル
+
+
+
+ {/* 第7章 */}
+
+ 第7章 アプリケーションセキュリティの基礎:シークレット保護・暗号化・データ取り扱い(4.8)
+
+ シークレット保護
+
+ パスワード、APIキー、データベース接続文字列、SSH秘密鍵などの重要データ(シークレット)をソースコードに直書き(ハードコーディング)することは厳禁です。
+
+
+
+
+
+
+ 対策手法
+ 概要
+ ベストプラクティス
+
+
+
+
+ 環境変数の利用
+ .envファイルやOS環境変数経由でアプリに注入する
+ .gitignoreで.envファイルをリポジトリ除外する
+
+
+ 専用シークレット管理ツールの活用
+ HashiCorp Vault, AWS Secrets Manager, Cisco DNA Center Key Manager等
+ 動的シークレット発行・自動ローテーションを有効化する
+
+
+
+
+
+ 暗号化(保存時・転送時)
+
+
+
+
+ 暗号化の分類
+ 対象データ
+ プロトコル・技術
+
+
+
+
+ 転送時暗号化(Data in Transit)
+ ネットワーク上を流れる通信データ
+ TLS 1.3, HTTPS, SSH, IPsec VPN
+
+
+ 保存時暗号化(Data at Rest)
+ ストレージやDBに書き込まれた静止データ
+ AES-256, LUKS, EBS暗号化, DB透過的暗号化(TDE)
+
+
+
+
+
+ データ取り扱いの考え方
+
+ 個人情報(PII)や機密データを扱う際は、最小権限の原則(Principle of Least Privilege)およびデータの最小化原則を徹底します。
+
+
+ セキュアなデータフローのイメージ
+
+
+
+ {/* 第8章 */}
+
+ 第8章 ネットワーク境界のセキュリティ要素:ファイアウォール・DNS・ロードバランサー・リバースプロキシ(4.9)
+
+
+
+
+
+ コンポーネント
+ 主な役割
+ セキュリティ上の機能
+
+
+
+
+ ファイアウォール(FW / WAF)
+ L3/L4またはL7でのパケットフィルタリング
+ 不正ポート・IPからのアクセス遮断、Web攻撃(SQLi, XSS)防御
+
+
+ DNS(Domain Name System)
+ ドメイン名とIPアドレスの相互変換
+ DNSSEC(偽装防止)、Anycast(DDoS耐性)
+
+
+ ロードバランサー(LB)
+ 複数サーバーへのトラフィック分散
+ ヘルスチェック(障害検知)、DDoS攻撃の負荷緩和
+
+
+ リバースプロキシ(Reverse Proxy)
+ クライアントとWebサーバー間の代理応答
+ TLSオフロード(暗号化処理集約)、オリジンIPの隠蔽、キャッシュ
+
+
+
+
+
+ リクエストが辿る経路
+
+
+
+ {/* 第9章 */}
+
+ 第9章 OWASPトップの脅威(4.10)
+
+ 代表的な脅威の説明
+
+ OWASP(Open Web Application Security Project)が公表する主要なWeb脆弱性・脅威について理解しておく必要があります。
+
+
+
+
+
+
+ 脅威分類
+ 内容
+ 対策手法
+
+
+
+
+ SQLインジェクション (SQLi)
+ 悪意あるSQL文を入力欄に注入してデータベースを不正操作する
+ プレースホルダ(パラメータ化クエリ)の徹底利用
+
+
+ クロスサイトスクリプティング (XSS)
+ 攻撃者がWebページに悪意あるJavaScriptを埋め込み閲覧者のブラウザで実行させる
+ 出力時のHTMLエスケープ、CSP(Content Security Policy)の設定
+
+
+ クロスサイトリクエストフォージェリ (CSRF)
+ ログイン済みのユーザーのブラウザを利用し意図しないリクエストを送信させる
+ ワンタイムCSRFトークンの検証、SameSiteクッキー属性の設定
+
+
+ アクセス制御の不備 (Broken Access Control)
+ 他人のデータや管理者機能へ未認証・未認可でアクセスできてしまう
+ サーバー側での厳格なロールベースアクセス制御(RBAC)実装
+
+
+
+
+
+ 最新の公式リストとの関係
+
+ 2025年版では、InjectionはA05:2025に分類されるため、SQLインジェクションやXSSを含む入力処理の安全性をこの分類に沿って確認します。
+
+
+
+
+ {/* 第10章 */}
+
+ 第10章 Bashコマンドの活用(4.11)
+
+ アプリケーションの自動化やコンテナ内での調査に不可欠なBash基本コマンドの一覧です。
+
+ ファイル管理
+
+
+
+
+ コマンド
+ 用途
+ 使用例
+
+
+
+
+ cat / less
+ ファイル内容の閲覧
+ cat config.json
+
+
+ grep
+ テキスト内のパターン検索
+ grep -i "error" app.log
+
+
+ chmod / chown
+ パーミッション変更 / 所有者変更
+ chmod 600 id_rsa
+
+
+
+
+
+ ディレクトリ操作
+
+
+
+
+ コマンド
+ 用途
+ 使用例
+
+
+
+
+ ls -la
+ 隠しファイルを含む詳細なディレクトリ一覧表示
+ ls -la /app
+
+
+ mkdir -p
+ 階層ディレクトリの作成
+ mkdir -p /var/log/myapp
+
+
+ pwd
+ 現在の作業ディレクトリパスを表示
+ pwd
+
+
+
+
+
+ 環境変数
+
+
+
+
+ コマンド
+ 用途
+ 使用例
+
+
+
+
+ export
+ 環境変数の設定・出力
+ export ENV=production
+
+
+ env / printenv
+ 現在のすべての環境変数を表示
+ printenv PORT
+
+
+
+
+
+
+ {/* 第11章 */}
+
+ 第11章 DevOpsの原則(4.12)
+
+
+ DevOpsは「開発(Development)」と「運用(Operations)」が連携し、迅速かつ高品質に価値を提供する文化とプラクティスの総称です。
+
+
+
+
+
+
+ DevOpsの柱
+ 原則
+ 具体的アプローチ
+
+
+
+
+ Culture(文化)
+ サイロの解体と共同責任
+ 開発と運用の目的共有、失敗を責めない文化(Blameless Post-mortem)
+
+
+ Automation(自動化)
+ 手動作業(トイル)の排除
+ CI/CD、インフラのコード化(IaC)、自動テスト
+
+
+ Lean(リーン)
+ バッチサイズの縮小と無駄の削減
+ 小規模な頻繁リリース、リードタイムの短縮
+
+
+ Measurement(計測)
+ 定量データに基づく改善
+ メトリクス収集(SLO/SLA)、ログ一元化(ELK/Datadog)
+
+
+ Sharing(共有)
+ ナレッジとフィードバックの共有
+ ドキュメント化、オープンなコミュニケーションチャネル
+
+
+
+
+
+
+
+
+ {/* 第12章 */}
+
+ 第12章 まとめ:ドメイン4.0 早見表
+
+
+
+
+
+ トピック番号
+ トピック名
+ 試験で問われる最重要キーワード
+
+
+
+
+ 4.1
+ エッジコンピューティング
+ 低遅延、帯域節約、データローカリティ、自律性
+
+
+ 4.2
+ アプリケーション展開モデル
+ プライベート / パブリック / ハイブリッドクラウド, エッジ
+
+
+ 4.3
+ 実行環境比較
+ ベアメタル(高パフォーマンス)、VM(ハイパーバイザー強分離)、コンテナ(軽量・高可搬性)
+
+
+ 4.4
+ CI/CDパイプライン
+ CI(自動テスト統合)、CD(自動配信・デプロイ)、ビルド/テスト/リリース
+
+
+ 4.5
+ Pythonユニットテスト
+ Arrange-Act-Assert(AAAパターン)、unittestモジュール
+
+
+ 4.6 / 4.7
+ Dockerfile & イメージ
+ FROM, WORKDIR, COPY, RUN, EXPOSE, CMD, docker build, docker run
+
+
+ 4.8
+ セキュリティ基礎
+ シークレット分離、Vault, TLS1.3(転送時), AES-256(保存時)
+
+
+ 4.9
+ ネットワーク境界
+ FW, DNS, LB, リバースプロキシ(TLS終端)
+
+
+ 4.10
+ OWASPトップの脅威
+ SQLi(パラメータ化)、XSS(エスケープ)、CSRF(トークン)
+
+
+ 4.11
+ Bashコマンド
+ grep, chmod, ls -la, mkdir -p, export, printenv
+
+
+ 4.12
+ DevOps原則
+ CALS(Culture, Automation, Lean, Measurement, Sharing)
+
+
+
+
+
+
+ {/* 参考文献 */}
+
+
+
+
+
+ );
+}
diff --git a/app/cisco/ccna/automation-application-deployment-security/NavBar.tsx b/app/cisco/ccna/automation-application-deployment-security/NavBar.tsx
new file mode 100644
index 000000000..b7da3e39a
--- /dev/null
+++ b/app/cisco/ccna/automation-application-deployment-security/NavBar.tsx
@@ -0,0 +1,61 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+import { NAVBAR_ITEMS } from './constants';
+
+export function NavBar() {
+ const [activeId, setActiveId] = useState('chapter1');
+
+ useEffect(() => {
+ const handleScroll = () => {
+ const scrollPosition = window.scrollY + 120;
+
+ for (const item of NAVBAR_ITEMS) {
+ const element = document.getElementById(item.id);
+ if (element) {
+ const top = element.offsetTop;
+ const height = element.offsetHeight;
+
+ if (scrollPosition >= top && scrollPosition < top + height) {
+ setActiveId(item.id);
+ break;
+ }
+ }
+ }
+ };
+
+ window.addEventListener('scroll', handleScroll, { passive: true });
+ handleScroll();
+
+ return () => window.removeEventListener('scroll', handleScroll);
+ }, []);
+
+ return (
+
+ );
+}
diff --git a/app/cisco/ccna/automation-application-deployment-security/constants.ts b/app/cisco/ccna/automation-application-deployment-security/constants.ts
new file mode 100644
index 000000000..36ccdecbd
--- /dev/null
+++ b/app/cisco/ccna/automation-application-deployment-security/constants.ts
@@ -0,0 +1,147 @@
+export interface NavItem {
+ id: string;
+ title: string;
+}
+
+export const NAVBAR_ITEMS: NavItem[] = [
+ { id: 'chapter1', title: '第1章 出題範囲の全体像' },
+ { id: 'chapter2', title: '第2章 エッジ・展開モデル(4.1・4.2)' },
+ { id: 'chapter3', title: '第3章 実行環境の比較(4.3)' },
+ { id: 'chapter4', title: '第4章 CI/CDパイプライン(4.4)' },
+ { id: 'chapter5', title: '第5章 Pythonユニットテスト(4.5)' },
+ { id: 'chapter6', title: '第6章 Dockerfile・イメージ(4.6・4.7)' },
+ { id: 'chapter7', title: '第7章 セキュリティ基礎(4.8)' },
+ { id: 'chapter8', title: '第8章 ネットワーク境界セキュリティ(4.9)' },
+ { id: 'chapter9', title: '第9章 OWASPトップの脅威(4.10)' },
+ { id: 'chapter10', title: '第10章 Bashコマンドの活用(4.11)' },
+ { id: 'chapter11', title: '第11章 DevOpsの原則(4.12)' },
+ { id: 'chapter12', title: '第12章 まとめ:早見表' },
+ { id: 'references', title: '参考文献・出典' },
+];
+
+export const DIAGRAMS: Record = {
+ 'diag-0': `flowchart TB
+ EXAM["200-901 CCNAAUTO 試験 (120分)"]
+ D1["1.0 Software Development and Design (15%)"]
+ D2["2.0 Understanding and Using APIs (20%)"]
+ D3["3.0 Cisco Platforms and Development (15%)"]
+ D4["4.0 Application Deployment and Security (15%)"]
+ D5["5.0 Infrastructure and Automation (20%)"]
+ D6["6.0 Network Fundamentals (15%)"]
+ EXAM --> D1
+ EXAM --> D2
+ EXAM --> D3
+ EXAM --> D4
+ EXAM --> D5
+ EXAM --> D6
+ D1 ~~~ D2 ~~~ D3 ~~~ D4 ~~~ D5 ~~~ D6
+ style D4 fill:#ffe08a,stroke:#d99a00,stroke-width:2px,color:#241a00`,
+
+ 'diag-1': `flowchart TB
+ IoT["IoTデバイス / 店舗POS / センサー"]
+ EdgeNode["エッジサーバー (現地で低遅延処理)"]
+ Pub["パブリッククラウド"]
+ Priv["プライベートクラウド"]
+ Hyb["ハイブリッドクラウド構成"]
+
+ IoT --> EdgeNode
+ EdgeNode -->|"集約データのみ送信"| Pub
+ EdgeNode -.->|"機密データは社内で保持"| Priv
+ Priv <--> Pub
+ Priv --- Hyb
+ Pub --- Hyb`,
+
+ 'diag-2': `flowchart TB
+ subgraph BareMetal["ベアメタル"]
+ direction TB
+ BM_App["アプリケーション"]
+ BM_OS["OS (ホストに直接インストール)"]
+ BM_HW["物理ハードウェア"]
+ BM_App --> BM_OS --> BM_HW
+ end
+
+ subgraph VM["仮想マシン"]
+ direction TB
+ VM_App["アプリケーション"]
+ VM_GuestOS["ゲストOS (複数台分)"]
+ VM_Hyper["ハイパーバイザー"]
+ VM_HW["物理ハードウェア"]
+ VM_App --> VM_GuestOS --> VM_Hyper --> VM_HW
+ end
+
+ subgraph Container["コンテナ"]
+ direction TB
+ C_App["アプリケーション"]
+ C_Engine["コンテナエンジン (Dockerなど)"]
+ C_OS["ホストOS (共有)"]
+ C_HW["物理ハードウェア"]
+ C_App --> C_Engine --> C_OS --> C_HW
+ end
+
+ BareMetal ~~~ VM ~~~ Container`,
+
+ 'diag-3': `flowchart TB
+ Dev["開発者がコードをコミット"] --> VCS["バージョン管理システム (Git)"]
+ VCS --> Build["ビルドステージ (依存関係解決・コンパイル)"]
+ Build --> Test["自動テストステージ (ユニット・統合テスト)"]
+ Test -->|"成功"| Package["アーティファクト作成 (コンテナイメージ等)"]
+ Test -->|"失敗"| Dev
+ Package --> Registry["イメージ / パッケージレジストリ"]
+ Registry --> DeployStg["ステージング環境へ自動展開"]
+ DeployStg --> Approve["承認 / 追加テスト"]
+ Approve -->|"承認"| DeployProd["本番環境へ展開"]
+ Approve -->|"却下"| Dev
+ DeployProd --> Monitor["監視・フィードバック収集"]
+ Monitor -.->|"改善点を反映"| Dev`,
+
+ 'diag-4': `flowchart TB
+ Start["テスト対象の関数を用意"] --> Arrange["Arrange: テストデータ・前提条件を準備"]
+ Arrange --> Act["Act: テスト対象の関数を実行"]
+ Act --> Assert["Assert: 期待した結果と実際の結果を比較"]
+ Assert -->|"一致"| Pass["テスト成功 (PASS)"]
+ Assert -->|"不一致"| Fail["テスト失敗 (FAIL) 原因を調査"]`,
+
+ 'diag-5': `flowchart TB
+ Dockerfile["Dockerfileを作成"] --> Build["docker buildでイメージを生成"]
+ Build --> LocalImage["ローカルイメージ (docker imagesで確認)"]
+ LocalImage --> Run["docker runでコンテナ起動"]
+ Run --> Container["稼働中のコンテナ (docker psで確認)"]
+ Container -->|"docker stop"| Stopped["停止したコンテナ"]
+ LocalImage -->|"docker push"| Registry["イメージレジストリ (Docker Hub等)"]
+ Registry -->|"docker pull"| LocalImage`,
+
+ 'diag-6': `flowchart TB
+ Client["クライアント (ブラウザ / アプリ)"] -->|"HTTPS (TLSで暗号化)"| LB["ロードバランサー"]
+ LB -->|"内部ネットワーク"| App["アプリケーションサーバー"]
+ App -->|"シークレットを取得 (コードに直書きしない)"| Vault["シークレット管理 (Vault等)"]
+ App -->|"暗号化して書き込み"| DB["データベース (保存時暗号化)"]
+ DB -->|"復号して返却"| App`,
+
+ 'diag-7': `flowchart TB
+ User["ユーザー"] --> DNS["DNSで名前解決 (ドメイン IPアドレス)"]
+ DNS --> FW["ファイアウォール (不要な通信を遮断)"]
+ FW --> LB["ロードバランサー (複数サーバーへ振り分け)"]
+ LB --> RP["リバースプロキシ (TLS終端・キャッシュ・経路制御)"]
+ RP --> Srv1["アプリケーションサーバー1"]
+ RP --> Srv2["アプリケーションサーバー2"]`,
+
+ 'diag-8': `flowchart TB
+ Top["OWASP Top 10:2025 A05:2025 Injection"] --> Input
+ Input["ユーザー入力 (フォーム・URLパラメータ等)"] --> Validate{"入力を検証・サニタイズしているか?"}
+ Validate -->|"していない"| Risk1["SQLインジェクションのリスク (意図しないSQL文が実行される)"]
+ Validate -->|"していない"| Risk2["XSSのリスク (悪意あるスクリプトが実行される)"]
+ Validate -->|"している"| Safe["安全な処理へ"]
+ Session["認証済みユーザーのセッション"] --> CSRFCheck{"CSRFトークンを検証しているか?"}
+ CSRFCheck -->|"していない"| Risk3["CSRFのリスク (意図しないリクエストが実行される)"]
+ CSRFCheck -->|"している"| Safe`,
+
+ 'diag-9': `flowchart TB
+ Plan["Plan: 計画"] --> Code["Code: コーディング"]
+ Code --> Build["Build: ビルド"]
+ Build --> Test["Test: テスト"]
+ Test --> Release["Release: リリース準備"]
+ Release --> Deploy["Deploy: 展開"]
+ Deploy --> Operate["Operate: 運用"]
+ Operate --> Monitor["Monitor: 監視"]
+ Monitor -.->|"フィードバック"| Plan`,
+};
diff --git a/app/cisco/ccna/automation-application-deployment-security/page.css b/app/cisco/ccna/automation-application-deployment-security/page.css
new file mode 100644
index 000000000..ac21895a0
--- /dev/null
+++ b/app/cisco/ccna/automation-application-deployment-security/page.css
@@ -0,0 +1,285 @@
+.ccna-app-deployment-security-page {
+ background: var(--color-background, #08090f);
+ color: var(--color-foreground, #e2e8f0);
+ font-family: var(--font-body, 'Noto Sans JP', sans-serif);
+ font-size: 16px;
+ line-height: 1.85;
+ min-height: 100vh;
+}
+
+.ccna-app-deployment-security-page .layout {
+ display: block;
+ position: relative;
+ min-height: 100vh;
+}
+
+/* ---------- Sidebar ---------- */
+.ccna-app-deployment-security-page .sidebar {
+ position: fixed;
+ top: calc(var(--header-h, 60px) + var(--disclaimer-height, 0px));
+ left: 0;
+ bottom: 0;
+ width: 280px;
+ overflow-y: auto;
+ background: var(--color-card-secondary, #0d1b2e);
+ border-right: 1px solid var(--color-border, #1e2840);
+ padding: 24px 18px 36px;
+ z-index: 40;
+}
+
+.ccna-app-deployment-security-page .toc-title {
+ font-family: var(--font-display, sans-serif);
+ font-weight: 700;
+ font-size: 0.9rem;
+ color: var(--color-primary, #7c9eff);
+ margin-bottom: 12px;
+ padding-bottom: 6px;
+ border-bottom: 1px solid var(--color-border, #1e2840);
+}
+
+.ccna-app-deployment-security-page .toc-list {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.ccna-app-deployment-security-page .toc-link {
+ display: block;
+ padding: 6px 10px;
+ border-radius: 6px;
+ color: var(--color-muted-foreground, #94a3b8);
+ text-decoration: none;
+ font-size: 0.82rem;
+ line-height: 1.5;
+ transition: background 0.15s ease, color 0.15s ease;
+}
+
+.ccna-app-deployment-security-page .toc-link:hover {
+ background: rgba(124, 158, 255, 0.08);
+ color: var(--color-foreground, #e2e8f0);
+}
+
+.ccna-app-deployment-security-page .toc-link.active {
+ background: rgba(124, 158, 255, 0.15);
+ color: var(--color-primary, #7c9eff);
+ font-weight: 600;
+}
+
+/* ---------- Main Content ---------- */
+.ccna-app-deployment-security-page .main {
+ margin-left: 280px;
+ width: calc(100% - 280px);
+ max-width: none;
+ box-sizing: border-box;
+ padding: 32px 40px 80px;
+}
+
+.ccna-app-deployment-security-page .content-inner {
+ width: 100%;
+ max-width: none;
+}
+
+.ccna-app-deployment-security-page .hero {
+ margin-bottom: 48px;
+ padding-bottom: 24px;
+ border-bottom: 1px solid var(--color-border, #1e2840);
+}
+
+.ccna-app-deployment-security-page .hero-eyebrow {
+ display: inline-block;
+ font-size: 0.8rem;
+ font-weight: 700;
+ letter-spacing: 0.1em;
+ color: var(--color-primary, #7c9eff);
+ text-transform: uppercase;
+ margin-bottom: 8px;
+}
+
+.ccna-app-deployment-security-page .hero h1 {
+ font-family: var(--font-display, sans-serif);
+ font-size: 2.2rem;
+ font-weight: 800;
+ line-height: 1.3;
+ color: var(--color-foreground, #f8fafc);
+ margin: 0 0 16px;
+}
+
+.ccna-app-deployment-security-page .hero-description {
+ font-size: 1.05rem;
+ color: var(--color-muted-foreground, #cbd5e1);
+ background: rgba(124, 158, 255, 0.05);
+ border-left: 4px solid var(--color-primary, #7c9eff);
+ padding: 16px 20px;
+ border-radius: 0 8px 8px 0;
+ margin-top: 16px;
+}
+
+/* ---------- Sections ---------- */
+.ccna-app-deployment-security-page .section-block {
+ scroll-margin-top: calc(var(--header-h, 60px) + var(--disclaimer-height, 0px) + 24px);
+ margin-bottom: 56px;
+}
+
+.ccna-app-deployment-security-page h2 {
+ font-family: var(--font-display, sans-serif);
+ font-size: 1.6rem;
+ font-weight: 700;
+ color: var(--color-foreground, #f8fafc);
+ margin: 0 0 20px;
+ padding-bottom: 10px;
+ border-bottom: 2px solid var(--color-border, #1e2840);
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.ccna-app-deployment-security-page h3 {
+ font-size: 1.25rem;
+ font-weight: 600;
+ color: var(--color-primary, #7c9eff);
+ margin: 32px 0 16px;
+}
+
+.ccna-app-deployment-security-page p {
+ margin-bottom: 16px;
+ color: var(--color-foreground, #e2e8f0);
+}
+
+.ccna-app-deployment-security-page ul,
+.ccna-app-deployment-security-page ol {
+ margin-bottom: 20px;
+ padding-left: 24px;
+}
+
+.ccna-app-deployment-security-page li {
+ margin-bottom: 8px;
+}
+
+/* ---------- Cards & Callouts ---------- */
+.ccna-app-deployment-security-page .card {
+ background: var(--color-card, #0f172a);
+ border: 1px solid var(--color-border, #1e2840);
+ border-radius: 12px;
+ padding: 24px;
+ margin-bottom: 24px;
+}
+
+.ccna-app-deployment-security-page .callout {
+ background: rgba(124, 158, 255, 0.08);
+ border: 1px solid rgba(124, 158, 255, 0.2);
+ border-radius: 8px;
+ padding: 16px 20px;
+ margin-bottom: 24px;
+}
+
+.ccna-app-deployment-security-page .callout-warning {
+ background: rgba(245, 158, 11, 0.08);
+ border: 1px solid rgba(245, 158, 11, 0.3);
+}
+
+/* ---------- Tables ---------- */
+.ccna-app-deployment-security-page .table-wrapper {
+ overflow-x: auto;
+ margin-bottom: 24px;
+ border-radius: 8px;
+ border: 1px solid var(--color-border, #1e2840);
+}
+
+.ccna-app-deployment-security-page table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 0.92rem;
+ text-align: left;
+}
+
+.ccna-app-deployment-security-page th,
+.ccna-app-deployment-security-page td {
+ padding: 12px 16px;
+ border-bottom: 1px solid var(--color-border, #1e2840);
+}
+
+.ccna-app-deployment-security-page th {
+ background: var(--color-card-secondary, #0d1b2e);
+ color: var(--color-primary, #7c9eff);
+ font-weight: 600;
+}
+
+.ccna-app-deployment-security-page tr:last-child td {
+ border-bottom: none;
+}
+
+.ccna-app-deployment-security-page tr:hover td {
+ background: rgba(255, 255, 255, 0.02);
+}
+
+/* ---------- Code Blocks ---------- */
+.ccna-app-deployment-security-page .code-block {
+ background: #0d1117;
+ border: 1px solid var(--color-border, #1e2840);
+ border-radius: 8px;
+ padding: 16px;
+ margin-bottom: 24px;
+ overflow-x: auto;
+ font-family: var(--font-mono, monospace);
+ font-size: 0.88rem;
+ line-height: 1.6;
+}
+
+.ccna-app-deployment-security-page .code-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 8px 16px;
+ background: #161b22;
+ border-bottom: 1px solid #21262d;
+ border-radius: 8px 8px 0 0;
+ font-size: 0.78rem;
+ color: var(--color-muted-foreground, #8b949e);
+ font-family: var(--font-mono, monospace);
+ margin: -16px -16px 16px -16px;
+}
+
+.ccna-app-deployment-security-page .code-line {
+ white-space: pre;
+}
+
+.ccna-app-deployment-security-page .code-comment {
+ color: #8b949e;
+}
+
+.ccna-app-deployment-security-page .code-keyword {
+ color: #ff7b72;
+}
+
+.ccna-app-deployment-security-page .code-string {
+ color: #a5d6ff;
+}
+
+.ccna-app-deployment-security-page .code-function {
+ color: #d2a8ff;
+}
+
+/* ---------- Mermaid Diagram Wrapper ---------- */
+.ccna-app-deployment-security-page .mermaid-wrap {
+ margin: 24px auto;
+ display: flex;
+ justify-content: center;
+ width: 100%;
+}
+
+/* ---------- Responsive ---------- */
+@media (max-width: 900px) {
+ .ccna-app-deployment-security-page .sidebar {
+ display: none;
+ }
+
+ .ccna-app-deployment-security-page .main {
+ margin-left: 0;
+ width: 100%;
+ max-width: none;
+ padding: 24px 20px 60px;
+ }
+}
diff --git a/app/cisco/ccna/automation-application-deployment-security/page.tsx b/app/cisco/ccna/automation-application-deployment-security/page.tsx
new file mode 100644
index 000000000..31c65d322
--- /dev/null
+++ b/app/cisco/ccna/automation-application-deployment-security/page.tsx
@@ -0,0 +1,16 @@
+import type { Metadata } from 'next';
+import { CcnaAppDeploymentSecurityGuide } from './CcnaAppDeploymentSecurityGuide';
+import './page.css';
+
+export const metadata: Metadata = {
+ title: 'CCNA Automation認定「Application Deployment and Security」完全ガイド | Cloud Infrastructure Studies',
+ description:
+ 'CCNA Automation認定(200-901 CCNAAUTO)の試験ドメイン「4.0 Application Deployment and Security」を初学者向けにステップバイステップで解説する完全ガイド',
+};
+
+/**
+ * Renders the CCNA Automation Application Deployment and Security guide page.
+ */
+export default function CcnaAppDeploymentSecurityPage() {
+ return ;
+}
diff --git a/app/cisco/ccna/automation-cisco-platforms-and-development/CcnaCiscoPlatformsDevelopmentGuide.tsx b/app/cisco/ccna/automation-cisco-platforms-and-development/CcnaCiscoPlatformsDevelopmentGuide.tsx
new file mode 100644
index 000000000..80f113261
--- /dev/null
+++ b/app/cisco/ccna/automation-cisco-platforms-and-development/CcnaCiscoPlatformsDevelopmentGuide.tsx
@@ -0,0 +1,844 @@
+'use client';
+
+import { memo } from 'react';
+import { MermaidDiagram } from '@/components/MermaidDiagram';
+import { NavBar } from './NavBar';
+import { DIAGRAMS } from './constants';
+import styles from './page.module.css';
+
+const Diagram = memo(function Diagram({ id, label }: { id: string; label: string }) {
+ const chart = DIAGRAMS[id];
+ if (!chart) return null;
+ return (
+
+ );
+});
+
+export function CcnaCiscoPlatformsDevelopmentGuide() {
+ return (
+
+
+
+
+
+
+ CCNA Automation 200-901 CCNAAUTO v1.1
+ Cisco Platforms and Development 徹底解説ガイド
+
+ CCNA Automation認定試験(旧称:Cisco Certified DevNet Associate)の6ドメインのうち、「3.0 Cisco Platforms and Development」(配点15%)を初学者向けにステップバイステップで解説します。
+ 図解はすべてMermaid、表はすべてMarkdown相当の構造化テーブルで構成し、ASCIIアートは一切使用していません。
+
+
+
試験コード 200-901 CCNAAUTO v1.1
+
試験時間 120分
+
受験言語 英語 / 日本語
+
受験料 US$300(Cisco Learning Credits可)
+
+
+ 本ガイドについて: 2026年2月3日より、本試験は旧称「DevNet Associate(DEVASC)」から「CCNA Automation」へ名称変更されましたが、試験内容そのものは変更されていません。本ガイドは公式ブループリントに準拠した最新の製品名称(Secure Endpoint、XDRなど)を使用しつつ、旧名称(AMP、ThreatGridなど)にも適宜言及します。
+
+
+
+
+
+ {/* ============ はじめに ============ */}
+
+ はじめに
+
+
+ CCNA Automation認定は、Ciscoが提供する自動化・プログラマビリティ分野の入門レベル認定です。
+ ネットワークエンジニアがソフトウェア開発のスキルを身につけ、逆にソフトウェア開発者がネットワークの基礎を理解するための「橋渡し」となる資格として位置づけられています。
+
+
+ このガイドで扱う「3.0 Cisco Platforms and Development」 ドメインは、平たく言えば「Ciscoの各種製品(ネットワーク管理・コンピュート・コラボレーション・セキュリティ)を、それぞれのAPIを使ってプログラムから操作する方法を理解しているか」を問う分野です。
+ 個々の製品の細かい操作方法を丸暗記するのではなく、「どの製品が何のためにあり、どんな種類のAPIを持っているか」を体系的に把握すること が合格への近道になります。
+
+
+
+
+ {/* ============ 試験全体像 ============ */}
+
+ CCNA Automation試験の全体像とこのドメインの位置づけ
+
+
+ CCNA Automation認定を取得するには、単一の試験「Automating Networks Using Cisco Platforms(200-901 CCNAAUTO)v1.1 」に合格する必要があります。
+ 試験時間は120分、受験言語は英語と日本語に対応しています。試験は以下の6つのドメインで構成されており、それぞれに配点比率が定められています。
+
+
+
+
+
+
+
+ ドメイン番号
+ ドメイン名(英語)
+ 配点比率
+
+
+
+
+ 1.0
+ Software Development and Design
+ 15%
+
+
+ 2.0
+ Understanding and Using APIs
+ 20%
+
+
+ 3.0
+ Cisco Platforms and Development
+ 15%
+
+
+ 4.0
+ Application Deployment and Security
+ 15%
+
+
+ 5.0
+ Infrastructure and Automation
+ 20%
+
+
+ 6.0
+ Network Fundamentals
+ 15%
+
+
+
+
+
+
+
+
+
+ 「2.0 Understanding and Using APIs」がAPIの一般的な仕組み (REST、HTTPメソッド、認証方式など)を扱うのに対し、
+ 「3.0 Cisco Platforms and Development」は、その知識をCisco固有の製品 に適用する力を問う点が違いです。両ドメインはセットで学習すると理解が深まります。
+
+
+
+
+ 💡 ポイント
+ Ciscoは認定試験のブループリントを定期的に見直しており、DevNet Associate(v1.0)からCCNA Automation(v1.1)への移行時にも、
+ セキュリティ製品の名称更新(AMP→Secure Endpoint、ThreatGrid→Secure Malware Analytics、XDRの追加)など細かな改訂が行われています。
+ 学習の際は必ず最新の公式ブループリントを確認してください。
+
+
+
+ {/* ============ ドメイン全体マップ ============ */}
+
+ Cisco Platforms and Developmentドメインの全体マップ
+
+
+ このドメインは、公式ブループリント上で以下の9つの学習項目(3.1〜3.9)に細分化されています。
+
+
+
+
+
+
+
+ 番号
+ 学習項目(要約)
+
+
+
+
+ 3.1
+ Cisco SDKのドキュメントを基にPythonスクリプトを構築する
+
+
+ 3.2
+ ネットワーク管理プラットフォームとAPIの機能を説明する(Meraki, Cisco DNA Center, ACI, Cisco SD-WAN, NSO)
+
+
+ 3.3
+ コンピュート管理プラットフォームとAPIの機能を説明する(UCS Manager, UCS Director, Intersight)
+
+
+ 3.4
+ コラボレーションプラットフォームとAPIの機能を説明する(Webex, Webex Devices, CUCM(AXL/UDS), Finesse)
+
+
+ 3.5
+ セキュリティプラットフォームとAPIの機能を説明する(XDR, Firepower, Umbrella, Secure Endpoint, ISE, Secure Malware Analytics)
+
+
+ 3.6
+ IOS XEおよびNX-OSのデバイスレベルAPIと動的インターフェースを説明する
+
+
+ 3.7
+ シナリオに応じて適切なDevNetリソースを特定する(Sandbox, Code Exchange, サポート, フォーラム, Learning Labs, APIドキュメント)
+
+
+ 3.8
+ モデル駆動型プログラマビリティの概念を適用する(YANG, RESTCONF, NETCONF)
+
+
+ 3.9
+ 要件とAPIリファレンスドキュメントに基づき、特定の操作を行うコードを構築する
+
+
+
+
+
+
+
+
+
+ 前半(3.1〜3.6)が「各プラットフォームを知る 」フェーズ、後半(3.7〜3.9)が「学んだ知識を使う 」フェーズだとイメージすると理解しやすくなります。
+
+
+
+
+ {/* ============ 3.1 ============ */}
+
+ 3.1 Cisco SDKを使ったPythonスクリプトの構築
+
+ SDKとは何か、なぜ使うのか
+
+
+ SDK(Software Development Kit)とは、APIを直接叩く(HTTPリクエストを自分で組み立てる)代わりに、
+ あらかじめ用意された関数やクラスを呼び出すだけでAPI操作ができるようにしたライブラリです。
+ 生のREST APIをrequestsライブラリで叩く場合と比較すると、次のようなメリットがあります。
+
+
+ 認証ヘッダーの付与やURLの組み立てを自動化してくれる
+ レスポンスのJSONを、扱いやすいPythonオブジェクトとして受け取れる
+ エラー発生時に、わかりやすい例外(Exception)として通知してくれる
+
+
+
+ 基本的なワークフロー
+
+
+ コード例:Meraki SDKで組織一覧を取得する
+
+
import meraki
+
+
# APIキーはMerakiダッシュボードの
+
# Organization > Configure > API & Webhooks から発行する
+
API_KEY = "YOUR_API_KEY"
+
+
# SDKクライアントを初期化する
+
dashboard = meraki.DashboardAPI(API_KEY, suppress_logging=True )
+
+
# 所属する組織(Organization)の一覧を取得する
+
organizations = dashboard.organizations.getOrganizations()
+
+
for org in organizations:
+
print (f"組織名: {org['name']} / ID: {org['id']}" )
+
+
+
+
+ このように、Meraki公式のPython SDK(merakiパッケージ)を使うと、認証ヘッダーの組み立てやページネーション処理をSDKが肩代わりしてくれるため、
+ 開発者は「何を取得したいか」に集中できます。試験では、SDKのドキュメント(メソッド名・引数・戻り値)を読んで、空欄になったコードを補完する形式の設問が出題される傾向があります。
+
+
+
+
+ 💡 試験対策
+ SDKの内部実装を暗記する必要はありません。「このSDKはどの製品向けか」「認証にはどんな情報が必要か」「戻り値はどんな形(リスト/辞書)か」を、公式ドキュメントを見ながら読み解く練習をしておきましょう。
+
+
+
+ {/* ============ 3.2-3.5 ============ */}
+
+
+ {/* ============ 3.6 ============ */}
+
+ 3.6 IOS XE / NX-OSのデバイスレベルAPIと動的インターフェース
+
+
+ 3.2〜3.5がコントローラー経由の管理であったのに対し、3.6ではデバイス単体(IOS XEを搭載したルーター/スイッチ、NX-OSを搭載したデータセンタースイッチ)に直接組み込まれたプログラマビリティ機能 を扱います。
+
+
+ NETCONF / RESTCONF :デバイス上で直接有効化できる、標準化されたモデル駆動型のプロトコル(詳細は次章で解説)。
+ gRPC / gNMI :ストリーミングテレメトリなど、より高頻度・低遅延なデータ収集に向く比較的新しいインターフェース。
+ NX-API :NX-OSデバイス向けのREST風API。CLIコマンドをそのままJSON/XML経由で実行できる点が特徴。
+ Guest Shell / On-box Python :デバイスのOS内に隔離されたLinuxコンテナ環境(Guest Shell)を用意し、その中でPythonスクリプトを直接実行できる「動的インターフェース」。デバイス自身にちょっとした自動化ロジックを持たせたい場合に使う。
+ EEM(Embedded Event Manager) :デバイス内で発生したイベント(インターフェースダウンなど)をトリガーに、あらかじめ登録したアクション(Tclスクリプトなど)を自動実行する仕組み。
+
+
+
+ コード例:NETCONFでIOS XEデバイスの設定を取得する(ncclientライブラリ)
+
+
from ncclient import manager
+
+
# DevNet常設Sandbox(IOS XE on CSR)に接続する
+
with manager.connect(
+
host="ios-xe-mgmt.cisco.com" ,
+
port=10000,
+
username="developer" ,
+
password="C1sco12345" ,
+
) as m:
+
# YANGフィルタを使ってinterface設定のみを取得する
+
filter_xml = """
+
<filter>
+
<native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native">
+
<interface/>
+
</native>
+
</filter>
+
"""
+
result = m.get_config(source="running" , filter=filter_xml)
+
print (result)
+
+
+
+ 💡 試験対策
+ 「コントローラー経由(3.2〜3.5)」と「デバイス直接(3.6)」の違いを混同しないようにしましょう。同じNETCONF/RESTCONFという技術要素でも、3.6ではデバイス単体への適用、3.8ではその背景にあるモデル駆動の考え方そのものが問われます。
+
+
+
+ {/* ============ 3.7 ============ */}
+
+ 3.7 シナリオに応じたDevNetリソースの選択
+
+
+ Cisco DevNetは、開発者向けに複数の学習・検証リソースを無償で提供しています。
+ 試験では「このような状況で、あなたはどのリソースを使うべきか」という選択式の設問が出題されます。
+
+
+
+
+
+
+
+
+
+ リソース
+ 主な用途
+
+
+
+
+ DevNet Sandbox
+ 実機相当の仮想環境を無料で予約・常設利用し、APIコールやコードの動作確認を行う(Always-On型/Reservation型の2種類がある)
+
+
+ Code Exchange
+ Ciscoおよびコミュニティが公開するサンプルコード・SDK・Ansible/Terraformコンテンツを検索・参照する
+
+
+ API Documentation
+ 各プラットフォームのエンドポイント仕様・パラメータ・認証方式を確認する一次情報源
+
+
+ Support / Community Forums
+ 技術的な質問や不具合の相談、他の開発者との情報交換を行う場
+
+
+ Learning Labs
+ ステップバイステップで体系的に学べる学習コンテンツ(試験対策にも活用しやすい)
+
+
+
+
+
+
+ 💡 豆知識
+ DevNet SandboxにはAlways-On(予約不要・常時稼働・共有環境)とReservation(事前予約制・自分専用の隔離環境)の2種類があります。管理者権限が必要な検証を行いたい場合はReservation型を選ぶ必要がある、という違いも押さえておきましょう。
+
+
+
+ {/* ============ 3.8 ============ */}
+
+ 3.8 モデル駆動型プログラマビリティ(YANG / NETCONF / RESTCONF)
+
+ なぜ「モデル駆動」なのか
+
+
+ 従来、ネットワーク機器の設定はCLI(コマンドラインインターフェース)を通じて行われてきました。しかしCLIの出力形式はベンダーやOSバージョンによってまちまちで、プログラムから解析するのは非常に手間がかかります。
+ そこで登場したのが、設定項目の構造とデータ型をあらかじめ「モデル」として定義しておき、そのモデルに沿ってプログラムから安全に設定を読み書きする という考え方です。この中核を担うのがYANG というデータモデリング言語です。
+
+
+
+
+
+ NETCONFとRESTCONFの比較
+
+
+
+
+ 項目
+ NETCONF
+ RESTCONF
+
+
+
+
+ トランスポート
+ SSH(既定ポート830)
+ HTTPS(既定ポート443)
+
+
+ データ形式
+ XML
+ JSONまたはXML
+
+
+ 操作モデル
+ RPCベース(get-config、edit-configなど)
+ HTTP動詞ベース(GET/POST/PUT/PATCH/DELETE)
+
+
+ データストアの扱い
+ running/candidate/startupを明確に区別できる
+ 基本的にrunning相当のデータストアのみを対象とする
+
+
+ ベースとなるモデル
+ YANG
+ YANG
+
+
+ 向いている用途
+ トランザクション性を要する一括設定変更
+ REST APIに慣れた開発者による単純なCRUD操作
+
+
+
+
+
+ コード例:RESTCONFでIOS XEデバイスのインターフェース一覧を取得する
+
+
import requests
+
+
# DevNet常設Sandbox(IOS XE on CSR)のRESTCONFエンドポイント
+
url = "https://ios-xe-mgmt.cisco.com:9443/restconf/data/ietf-interfaces:interfaces"
+
headers = {"Accept" : "application/yang-data+json" }
+
auth = ("developer" , "C1sco12345" )
+
+
resp = requests.get(url, headers=headers, auth=auth)
+
print (resp.json())
+
+
+
+ 💡 試験対策
+ NETCONF・RESTCONFの「既定ポート」と「DevNet Sandboxで実際に使われるポート」は異なる場合があります(例:常設Sandboxでは踏み台の都合上、NETCONFが10000番、RESTCONFが9443番になっていることがある)。
+ 試験では一般的な既定値(NETCONF=830、RESTCONF=443)で問われることが多いため、まずは標準ポートを正確に覚え、その上で実機演習時の差異は「環境固有の設定」として区別して理解しましょう。
+
+
+
+ {/* ============ 3.9 ============ */}
+
+ 3.9 実践:APIドキュメントを基にしたコード構築
+
+
+ 3.9は、これまで学んだ知識を統合し、「与えられたAPIリファレンスドキュメントを読んで、具体的な操作を行うコードを完成させる」実践的な項目です。
+ 公式に例示されている代表的なシナリオは次の2つです。
+
+
+ 3.9.a :Meraki、Cisco DNA Center、ACI、Cisco SD-WAN、NSOのいずれかを使って、ネットワークデバイスの一覧を取得する
+ 3.9.b :Webex(Webex Teams)でスペース・参加者・メッセージを管理する
+
+
+
+ 3.9.a:Meraki APIでネットワークデバイス一覧を取得する
+
+
+ Merakiの管理構造は「組織(Organization)→ ネットワーク(Network)→ デバイス(Device)」という階層になっており、
+ デバイス一覧を得るには、まず組織を取得し、その中のネットワークをたどってデバイスを収集するという流れになります。
+
+
+
+
+
+
+
import meraki
+
+
dashboard = meraki.DashboardAPI("YOUR_API_KEY" , suppress_logging=True )
+
+
# 1. 組織一覧を取得する
+
orgs = dashboard.organizations.getOrganizations()
+
+
# 2. 各組織のネットワークをたどり、デバイスを収集する
+
for org in orgs:
+
networks = dashboard.organizations.getOrganizationNetworks(org["id" ])
+
for net in networks:
+
devices = dashboard.networks.getNetworkDevices(net["id" ])
+
for device in devices:
+
print (f"{org['name']} / {net['name']} / {device.get('name', device['serial'])}" )
+
+
+ 3.9.b:Webex APIでスペース・参加者・メッセージを管理する
+
+
+
+
+
from webexteamssdk import WebexTeamsAPI
+
+
api = WebexTeamsAPI()
+
+
# 1. 新しいスペース(部屋)を作成する
+
room = api.rooms.create(title="自動化通知スペース" )
+
+
# 2. 参加者(メンバー)を追加する
+
api.memberships.create(room.id, personEmail="teammate@example.com" )
+
+
# 3. メッセージを送信する
+
api.messages.create(room.id, text="スペースの準備が整いました" )
+
+
+
+
+ このように、3.9は単体の知識ではなく、「どのAPIをどの順番で呼び出せば目的を達成できるか」という設計力 を問う項目です。
+ 試験ではコードの一部が空欄になった穴埋め形式(ドラッグ&ドロップ)で出題される傾向があるため、単純な暗記ではなく「このAPI呼び出しの後には、論理的に何をする必要があるか」を考える練習をしておくとよいでしょう。
+
+
+
+
+ {/* ============ 学習ロードマップ ============ */}
+
+ 学習ロードマップ:ハンズオンの進め方
+
+
+ 知識のインプットだけでなく、実際に手を動かすことがこのドメインの理解を大きく助けます。以下の順序でDevNet Sandboxを活用した学習を進めることをおすすめします。
+
+
+
+
+
+
+ Step 1 :developer.cisco.comでDevNetアカウントを作成し、Sandboxカタログにアクセスできる状態にする。
+ Step 2 :Meraki Always-On Sandboxを使い、組織・ネットワーク・デバイスの階層構造を実際のAPIレスポンスで確認する。
+ Step 3 :Cisco DNA Center(Catalyst Center)Sandboxで認証トークンを取得し、Intent APIでデバイス一覧を取得する。
+ Step 4 :自分のWebexアカウントでアクセストークンを発行し、スペース作成からメッセージ送信までを自動化するスクリプトを書く。
+ Step 5 :IOS XE Always-On Sandboxに対して、ncclient(NETCONF)とrequests(RESTCONF)の両方でアクセスし、挙動の違いを体感する。
+ Step 6 :Meraki+Webexなど複数のAPIを組み合わせ、「特定の条件を満たしたらWebexへ通知する」といった3.9形式の複合シナリオを自作してみる。
+
+
+
+ {/* ============ 試験対策のポイント ============ */}
+
+ 試験対策のポイントとよくある誤解
+
+
+ 製品名の変更に注意する :AMP→Secure Endpoint、ThreatGrid→Secure Malware Analytics、DNA Center→Catalyst Centerなど、Ciscoは製品ブランドを頻繁に見直しています。ブループリント上の表記と、実際の開発現場での最新名称の両方を把握しておきましょう。
+ NETCONFとRESTCONFのポート番号を混同しない :一般的な既定値はNETCONF=830(SSH)、RESTCONF=443(HTTPS)です。DevNet Sandbox特有の代替ポート(例:10000番、9443番)は環境固有の設定であり、試験の一般知識としては標準ポートを優先して覚えましょう。
+ Meraki APIの階層構造を理解する :Organization(組織)> Network(ネットワーク)> Device(デバイス)という3階層をたどらないとデバイス情報にたどり着けない点は頻出のポイントです。
+ CUCMのAXLとUDSを混同しない :AXLは管理者向けのSOAP API(設定変更を伴う操作向け)、UDSはエンドユーザー向けのREST API(ディレクトリ検索など、比較的軽量な参照操作向け)という役割の違いを押さえましょう。
+ 「コントローラー経由」と「デバイス直接」を区別する :3.2〜3.5で学ぶプラットフォームは基本的に複数デバイスをまとめて管理するコントローラー層であり、3.6・3.8で学ぶNETCONF/RESTCONF/YANGはデバイス単体(またはコントローラーの背後にある技術基盤)に関する知識です。
+ DevNetリソースの使い分け :「試したい」ならSandbox、「コードが欲しい」ならCode Exchange、「仕様を調べたい」ならAPI Documentation、「相談したい」ならSupport/Forums、「体系的に学びたい」ならLearning Labs、という対応関係を整理しておくと選択式問題で迷いません。
+
+
+
+
+ {/* ============ まとめ ============ */}
+
+ まとめ
+
+
+ 「3.0 Cisco Platforms and Development」ドメインは、Cisco製品群を横断的に俯瞰する力を問う分野です。個々のAPIエンドポイントを丸暗記するのではなく、
+
+
+ その製品が何を管理するものか (ネットワーク/コンピュート/コラボレーション/セキュリティ/デバイス単体)
+ どんな種類のAPIを提供しているか (REST/SOAP/XML/NETCONF/RESTCONF)
+ どのDevNetリソースを使えば効率よく学べるか
+
+
+ という3つの軸で整理しながら学習を進めることで、着実に得点力を伸ばすことができます。ぜひDevNet Sandboxを積極的に活用し、実際にAPIを呼び出しながら知識を定着させてください。
+
+
+
+
+ {/* ============ 参考文献 ============ */}
+
+ 参考文献・出典一覧
+
+ 本ガイドの作成にあたり、以下の一次情報源を参照しました。試験直前には必ず最新の公式情報をご確認ください。
+
+
+
+ © CCNA Automation 学習ガイドシリーズ/本ページは学習目的の非公式解説であり、Cisco Systems, Inc. の公式資料ではありません。
+
+
+
+
+
+
+ );
+}
diff --git a/app/cisco/ccna/automation-cisco-platforms-and-development/NavBar.tsx b/app/cisco/ccna/automation-cisco-platforms-and-development/NavBar.tsx
new file mode 100644
index 000000000..225bbcd26
--- /dev/null
+++ b/app/cisco/ccna/automation-cisco-platforms-and-development/NavBar.tsx
@@ -0,0 +1,65 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+
+const NAV_ITEMS = [
+ { id: 'intro', label: 'はじめに' },
+ { id: 'overview', label: '試験全体像とドメインの位置づけ' },
+ { id: 'map', label: 'ドメイン全体マップ' },
+ { id: 's31', label: '3.1 SDKとPythonスクリプト' },
+ { id: 'platforms', label: '3.2〜3.5 プラットフォーム全体像' },
+ { id: 's36', label: '3.6 デバイスレベルAPI' },
+ { id: 's37', label: '3.7 DevNetリソースの選択' },
+ { id: 's38', label: '3.8 モデル駆動型プログラマビリティ' },
+ { id: 's39', label: '3.9 実践コード構築' },
+ { id: 'roadmap', label: '学習ロードマップ' },
+ { id: 'tips', label: '試験対策のポイント' },
+ { id: 'summary', label: 'まとめ' },
+ { id: 'references', label: '参考文献・出典一覧' },
+];
+
+export function NavBar() {
+ const [activeId, setActiveId] = useState('intro');
+
+ useEffect(() => {
+ if (typeof IntersectionObserver === 'undefined') return;
+
+ const observer = new IntersectionObserver(
+ (entries) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) {
+ setActiveId(entry.target.id);
+ }
+ });
+ },
+ { rootMargin: '-20% 0px -70% 0px' }
+ );
+
+ const sections = document.querySelectorAll('.ccna-platforms-dev-page section[id], .ccna-platforms-dev-page footer[id]');
+ sections.forEach((sec) => observer.observe(sec));
+
+ return () => observer.disconnect();
+ }, []);
+
+ return (
+
+ CCNA Automation 解説
+ 3.0 Cisco Platforms and Development
+
+
+
+
+ );
+}
diff --git a/app/cisco/ccna/automation-cisco-platforms-and-development/constants.ts b/app/cisco/ccna/automation-cisco-platforms-and-development/constants.ts
new file mode 100644
index 000000000..7498085bd
--- /dev/null
+++ b/app/cisco/ccna/automation-cisco-platforms-and-development/constants.ts
@@ -0,0 +1,118 @@
+export const DIAGRAMS: Record = {
+ 'diag-overview': `flowchart TB
+A["CCNA Automation 200-901 CCNAAUTO v1.1"] --> D1["1.0 Software Development and Design (15%)"]
+A --> D2["2.0 Understanding and Using APIs (20%)"]
+A --> D3["3.0 Cisco Platforms and Development (15%) "]
+A --> D4["4.0 Application Deployment and Security (15%)"]
+A --> D5["5.0 Infrastructure and Automation (20%)"]
+A --> D6["6.0 Network Fundamentals (15%)"]
+D1 ~~~ D2 ~~~ D3 ~~~ D4 ~~~ D5 ~~~ D6
+style D3 fill:#ffe08a,stroke:#d99a00,stroke-width:2px,color:#241a00`,
+
+ 'diag-map': `flowchart TB
+Start(["3.0 Cisco Platforms and Development"]) --> T1["3.1 SDKで Pythonスクリプトを構築"]
+T1 --> T2["3.2 ネットワーク管理 プラットフォームAPI"]
+T2 --> T3["3.3 コンピュート管理 プラットフォームAPI"]
+T3 --> T4["3.4 コラボレーション プラットフォームAPI"]
+T4 --> T5["3.5 セキュリティ プラットフォームAPI"]
+T5 --> T6["3.6 IOS XE/NX-OS デバイスレベルAPI"]
+T6 --> T7["3.7 DevNetリソースの 活用"]
+T7 --> T8["3.8 モデル駆動型 プログラマビリティ"]
+T8 --> T9["3.9 実践:APIドキュメント からコードを構築"]`,
+
+ 'diag-workflow': `flowchart TB
+S1["SDKをインストールする 例: pip install meraki"] --> S2["認証情報を準備する APIキー / アクセストークン"]
+S2 --> S3["SDKクライアントを初期化する"]
+S3 --> S4["SDKのメソッドを呼び出す 例: dashboard.organizations.getOrganizations()"]
+S4 --> S5{"エラーは 発生したか"}
+S5 -->|"Yes"| S6["例外をキャッチして ログに出力する"]
+S5 -->|"No"| S7["取得したデータを 業務ロジックで利用する"]`,
+
+ 'diag-platforms': `flowchart TB
+subgraph NW["ネットワーク管理プラットフォーム 3.2"]
+direction TB
+N1["Meraki"]
+N2["Cisco DNA Center (現Catalyst Center)"]
+N3["ACI"]
+N4["Cisco SD-WAN"]
+N5["NSO"]
+N1 ~~~ N2 ~~~ N3 ~~~ N4 ~~~ N5
+end
+
+subgraph CP["コンピュート管理プラットフォーム 3.3"]
+direction TB
+C1["UCS Manager"]
+C2["UCS Director"]
+C3["Intersight"]
+C1 ~~~ C2 ~~~ C3
+end
+
+subgraph CL["コラボレーションプラットフォーム 3.4"]
+direction TB
+L1["Webex"]
+L2["Webex Devices"]
+L3["CUCM (AXL / UDS)"]
+L4["Finesse"]
+L1 ~~~ L2 ~~~ L3 ~~~ L4
+end
+
+subgraph SEC["セキュリティプラットフォーム 3.5"]
+direction TB
+S1["XDR"]
+S2["Firepower"]
+S3["Umbrella"]
+S4["Secure Endpoint"]
+S5["ISE"]
+S6["Secure Malware Analytics"]
+S1 ~~~ S2 ~~~ S3 ~~~ S4 ~~~ S5 ~~~ S6
+end
+
+NW ~~~ CP ~~~ CL ~~~ SEC`,
+
+ 'diag-north-south': `flowchart TB
+App["外部アプリケーション 自動化スクリプト"] -->|"Northbound API: REST"| Ctrl["コントローラー層 DNA Center / Meraki Dashboard APIC / vManage / NSO"]
+Ctrl -->|"Southbound: NETCONF・RESTCONF・CLIなど"| Dev["ネットワークデバイス群 スイッチ・ルーター・WLC"]`,
+
+ 'diag-devnet': `flowchart TB
+Q["利用シーン別 DevNetリソースの選び方"] --> R1["実機相当の環境で 動作を試したい → DevNet Sandbox"]
+R1 --> R2["すぐに使える サンプルコードが欲しい → Code Exchange"]
+R2 --> R3["APIのエンドポイントや 認証方式を確認したい → API Documentation"]
+R3 --> R4["技術的な疑問や 不具合を相談したい → Support / Community Forums"]
+R4 --> R5["体系立てて 基礎から学びたい → Learning Labs"]`,
+
+ 'diag-yang': `flowchart TB
+Y["YANGモデル 設定項目の構造とデータ型を定義"] --> P{"どちらのプロトコルで アクセスするか"}
+P -->|"NETCONF"| N["XMLベース SSH (既定ポート830) を利用 candidate/running設定を明確に区別"]
+P -->|"RESTCONF"| R["HTTP(S) ベース JSONまたはXML GET/POST/PUT/PATCH/DELETEで操作"]
+N --> D["ネットワークデバイスの 設定へ反映"]
+R --> D`,
+
+ 'diag-seq-meraki': `sequenceDiagram
+participant Dev as 自動化スクリプト
+participant API as Meraki Dashboard API
+
+Dev->>API: GET /organizations
+API-->>Dev: 200 OK (organizations一覧)
+Dev->>API: GET /organizations/{orgId}/networks
+API-->>Dev: 200 OK (networks一覧)
+Dev->>API: GET /networks/{networkId}/devices
+API-->>Dev: 200 OK (devices一覧)`,
+
+ 'diag-seq-webex': `sequenceDiagram
+participant Bot as Webexボット/スクリプト
+participant WebexAPI as Webex API
+
+Bot->>WebexAPI: POST /v1/rooms (スペースを作成)
+WebexAPI-->>Bot: 200 OK (roomId)
+Bot->>WebexAPI: POST /v1/memberships (参加者を追加)
+WebexAPI-->>Bot: 200 OK
+Bot->>WebexAPI: POST /v1/messages (メッセージを送信)
+WebexAPI-->>Bot: 200 OK (messageId)`,
+
+ 'diag-roadmap': `flowchart TB
+L1["Step 1 DevNet Sandboxアカウントを準備する"] --> L2["Step 2 Meraki SandboxでREST APIの基本を体験する"]
+L2 --> L3["Step 3 Cisco DNA Center (Catalyst Center) SandboxでIntent APIを試す"]
+L3 --> L4["Step 4 Webex APIでメッセージ送信を自動化する"]
+L4 --> L5["Step 5 IOS XE SandboxでNETCONF/RESTCONFを試す"]
+L5 --> L6["Step 6 3.9形式の複合シナリオに挑戦する"]`,
+};
diff --git a/app/cisco/ccna/automation-cisco-platforms-and-development/page.module.css b/app/cisco/ccna/automation-cisco-platforms-and-development/page.module.css
new file mode 100644
index 000000000..3e8d1c6a7
--- /dev/null
+++ b/app/cisco/ccna/automation-cisco-platforms-and-development/page.module.css
@@ -0,0 +1,447 @@
+.ccnaPlatformsDevPage {
+ min-height: 100vh;
+ background: var(--color-guide-background);
+ color: var(--color-ccna-platforms-foreground);
+ font-family: var(--font-ccna-platforms-sans);
+ line-height: 1.9;
+ -webkit-font-smoothing: antialiased;
+}
+
+.ccnaPlatformsDevPage :global(a) {
+ color: var(--color-guide-accent);
+ text-decoration: none;
+}
+
+.ccnaPlatformsDevPage :global(a:hover) {
+ text-decoration: underline;
+}
+
+/* ---------- Layout ---------- */
+.ccnaPlatformsDevPage :global(.layout) {
+ display: block;
+ min-height: 100vh;
+ position: relative;
+}
+
+.ccnaPlatformsDevPage :global(.sidebar) {
+ position: fixed;
+ top: calc(var(--header-h, 60px) + var(--disclaimer-height, 0px));
+ left: 0;
+ width: 280px;
+ height: calc(100vh - var(--header-h, 60px) - var(--disclaimer-height, 0px));
+ overflow-y: auto;
+ background: var(--color-ccna-platforms-surface-1);
+ border-right: 1px solid var(--color-guide-accent-glow);
+ padding: 32px 22px 48px;
+ z-index: 20;
+ scrollbar-width: thin;
+}
+
+.ccnaPlatformsDevPage :global(.sidebar .brand) {
+ font-weight: 700;
+ font-size: 1.05rem;
+ letter-spacing: 0.02em;
+ color: var(--color-ccna-platforms-foreground);
+ margin-bottom: 4px;
+ display: block;
+}
+
+.ccnaPlatformsDevPage :global(.sidebar .brand-sub) {
+ font-size: 1rem;
+ color: var(--color-ccna-platforms-foreground-faint);
+ margin-bottom: 28px;
+ display: block;
+}
+
+.ccnaPlatformsDevPage :global(.sidebar nav ul) {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+.ccnaPlatformsDevPage :global(.sidebar nav li) {
+ margin-bottom: 2px;
+}
+
+.ccnaPlatformsDevPage :global(.sidebar nav a) {
+ display: block;
+ padding: 9px 12px;
+ font-size: 1rem;
+ color: var(--color-ccna-platforms-foreground-muted);
+ border-left: 2px solid transparent;
+ border-radius: 6px;
+ transition: all 0.15s ease;
+}
+
+.ccnaPlatformsDevPage :global(.sidebar nav a:hover) {
+ color: var(--color-ccna-platforms-foreground);
+ background: rgba(124, 158, 255, 0.06);
+ text-decoration: none;
+}
+
+.ccnaPlatformsDevPage :global(.sidebar nav a.active) {
+ color: var(--color-guide-accent);
+ border-left: 2px solid var(--color-guide-accent);
+ background: rgba(124, 158, 255, 0.09);
+ font-weight: 600;
+}
+
+/* ---------- Main Content ---------- */
+.ccnaPlatformsDevPage :global(.content) {
+ margin-left: 280px;
+ width: calc(100% - 280px);
+ max-width: none;
+ box-sizing: border-box;
+ padding: 0 64px 100px;
+}
+
+.ccnaPlatformsDevPage :global(.article-body) {
+ width: 100%;
+ max-width: none;
+}
+
+.ccnaPlatformsDevPage :global(.hero) {
+ padding: 72px 0 40px;
+ border-bottom: 1px solid var(--color-ccna-platforms-border-soft);
+ margin-bottom: 48px;
+}
+
+.ccnaPlatformsDevPage :global(.hero .kicker) {
+ display: inline-block;
+ font-size: 1rem;
+ font-weight: 600;
+ letter-spacing: 0.08em;
+ color: var(--color-guide-accent);
+ background: rgba(124, 158, 255, 0.1);
+ border: 1px solid var(--color-guide-accent-glow);
+ padding: 6px 14px;
+ border-radius: 999px;
+ margin-bottom: 22px;
+}
+
+.ccnaPlatformsDevPage :global(.hero h1) {
+ font-size: clamp(2rem, 4vw, 3.1rem);
+ line-height: 1.35;
+ margin: 0 0 20px;
+ font-weight: 900;
+ background: linear-gradient(135deg, var(--color-guide-accent) 0%, var(--color-ccna-platforms-accent-secondary) 100%);
+ -webkit-background-clip: text;
+ background-clip: text;
+ -webkit-text-fill-color: transparent;
+ color: transparent;
+}
+
+.ccnaPlatformsDevPage :global(.hero .lead) {
+ font-size: 1.15rem;
+ color: var(--color-ccna-platforms-foreground-muted);
+ max-width: none;
+ margin: 0 0 28px;
+ line-height: 1.8;
+}
+
+.ccnaPlatformsDevPage :global(.meta-bar) {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 14px;
+ margin-top: 8px;
+}
+
+.ccnaPlatformsDevPage :global(.meta-chip) {
+ background: var(--color-ccna-platforms-surface-2);
+ border: 1px solid var(--color-guide-accent-glow);
+ border-radius: 10px;
+ padding: 12px 18px;
+ font-size: 1rem;
+ color: var(--color-ccna-platforms-foreground-muted);
+}
+
+.ccnaPlatformsDevPage :global(.meta-chip b) {
+ color: var(--color-ccna-platforms-foreground);
+ display: block;
+ font-size: 1.05rem;
+ margin-bottom: 2px;
+}
+
+.ccnaPlatformsDevPage :global(.notice-box) {
+ background: var(--color-ccna-platforms-surface-2);
+ border: 1px solid var(--color-guide-accent-glow);
+ border-left: 3px solid var(--color-guide-accent);
+ border-radius: 10px;
+ padding: 20px 24px;
+ margin: 28px 0;
+ color: var(--color-ccna-platforms-foreground-muted);
+ font-size: 1rem;
+ line-height: 1.8;
+}
+
+.ccnaPlatformsDevPage :global(.notice-box strong) {
+ color: var(--color-ccna-platforms-foreground);
+}
+
+/* ---------- Sections ---------- */
+.ccnaPlatformsDevPage :global(.section) {
+ padding-top: 12px;
+ margin-bottom: 64px;
+ scroll-margin-top: calc(var(--header-h, 60px) + var(--disclaimer-height, 0px) + 24px);
+}
+
+.ccnaPlatformsDevPage :global(.section h2) {
+ font-size: 1.75rem;
+ color: var(--color-ccna-platforms-foreground);
+ margin: 0 0 16px;
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ border-bottom: none;
+ padding-bottom: 0;
+}
+
+.ccnaPlatformsDevPage :global(.section h2 .num) {
+ color: var(--color-ccna-platforms-accent-secondary);
+ font-family: var(--font-ccna-platforms-mono);
+ font-size: 1.1rem;
+}
+
+.ccnaPlatformsDevPage :global(.section h3) {
+ font-size: 1.3rem;
+ color: var(--color-guide-accent);
+ margin: 40px 0 14px;
+}
+
+.ccnaPlatformsDevPage :global(.section h4) {
+ font-size: 1.08rem;
+ color: var(--color-ccna-platforms-foreground);
+ margin: 26px 0 10px;
+}
+
+.ccnaPlatformsDevPage :global(.prose p) {
+ margin: 0 0 18px;
+ color: var(--color-ccna-platforms-foreground);
+ font-size: 1rem;
+ line-height: 1.9;
+}
+
+.ccnaPlatformsDevPage :global(.prose ul),.ccnaPlatformsDevPage :global(.prose ol) {
+ margin: 0 0 20px;
+ padding-left: 1.4em;
+ color: var(--color-ccna-platforms-foreground);
+}
+
+.ccnaPlatformsDevPage :global(.prose li) {
+ margin-bottom: 8px;
+}
+
+.ccnaPlatformsDevPage :global(.prose strong) {
+ color: var(--color-ccna-platforms-accent-secondary);
+}
+
+/* ---------- Callouts ---------- */
+.ccnaPlatformsDevPage :global(.callout) {
+ background: var(--color-ccna-platforms-accent-secondary-soft);
+ border: 1px solid rgba(255, 212, 121, 0.35);
+ border-left: 3px solid var(--color-ccna-platforms-accent-secondary);
+ border-radius: 10px;
+ padding: 18px 22px;
+ margin: 26px 0;
+ font-size: 1rem;
+ line-height: 1.8;
+}
+
+.ccnaPlatformsDevPage :global(.callout .callout-label) {
+ font-weight: 700;
+ color: var(--color-ccna-platforms-accent-secondary);
+ display: block;
+ margin-bottom: 6px;
+}
+
+/* ---------- Tables ---------- */
+.ccnaPlatformsDevPage :global(.table-wrapper) {
+ overflow-x: auto;
+ margin: 26px 0;
+ border: none;
+ border-radius: 0;
+}
+
+.ccnaPlatformsDevPage :global(table) {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 1rem;
+ background: var(--color-ccna-platforms-surface-2);
+ border-radius: var(--radius-ccna-platforms);
+ overflow: hidden;
+}
+
+.ccnaPlatformsDevPage :global(thead th) {
+ text-align: left;
+ padding: 14px 18px;
+ background: rgba(124, 158, 255, 0.12);
+ color: var(--color-guide-accent);
+ font-weight: 700;
+ border-bottom: 1px solid var(--color-guide-accent-glow);
+ white-space: nowrap;
+}
+
+.ccnaPlatformsDevPage :global(tbody td) {
+ padding: 13px 18px;
+ border-bottom: 1px solid var(--color-ccna-platforms-border-soft);
+ color: var(--color-ccna-platforms-foreground);
+ vertical-align: top;
+}
+
+.ccnaPlatformsDevPage :global(tbody tr:last-child td) {
+ border-bottom: none;
+}
+
+.ccnaPlatformsDevPage :global(tbody tr:hover td) {
+ background: rgba(124, 158, 255, 0.04);
+}
+
+.ccnaPlatformsDevPage :global(tr.highlight td) {
+ background: rgba(255, 212, 121, 0.08);
+}
+
+.ccnaPlatformsDevPage :global(tr.highlight td:first-child) {
+ border-left: 3px solid var(--color-ccna-platforms-accent-secondary);
+}
+
+/* ---------- Diagrams ---------- */
+.ccnaPlatformsDevPage :global(.diagram-wrapper) {
+ overflow-x: auto;
+ margin: 30px 0;
+ background: var(--color-ccna-platforms-surface-2);
+ border: 1px solid var(--color-guide-accent-glow);
+ border-radius: var(--radius-ccna-platforms);
+ padding: 28px 12px;
+ max-width: 100%;
+}
+
+.ccnaPlatformsDevPage :global(.mermaid-wrap) {
+ display: flex;
+ justify-content: safe center;
+ width: 100%;
+ overflow-x: auto;
+}
+
+.ccnaPlatformsDevPage :global(.mermaid-wrap svg) {
+ max-width: 100% !important;
+ height: auto !important;
+}
+
+/* Ensure yellow node text is dark and high-contrast (#241a00) */
+.ccnaPlatformsDevPage :global(#D3 .nodeLabel),.ccnaPlatformsDevPage :global(#D3 .nodeLabel *),.ccnaPlatformsDevPage :global(#D3 span),.ccnaPlatformsDevPage :global(#D3 div),.ccnaPlatformsDevPage :global(#D3 p),.ccnaPlatformsDevPage :global([id*="D3"] .nodeLabel),.ccnaPlatformsDevPage :global([id*="D3"] .nodeLabel *),.ccnaPlatformsDevPage :global(.node[style*="ffe08a" i] .nodeLabel),.ccnaPlatformsDevPage :global(.node[style*="ffe08a" i] .nodeLabel *),.ccnaPlatformsDevPage :global(.node[style*="FFE08A" i] .nodeLabel),.ccnaPlatformsDevPage :global(.node[style*="FFE08A" i] .nodeLabel *),.ccnaPlatformsDevPage :global(.node[style*="255, 224, 138" i] .nodeLabel),.ccnaPlatformsDevPage :global(.node[style*="255, 224, 138" i] .nodeLabel *),.ccnaPlatformsDevPage :global(.node:has([style*="ffe08a" i]) .nodeLabel),.ccnaPlatformsDevPage :global(.node:has([style*="ffe08a" i]) .nodeLabel *),.ccnaPlatformsDevPage :global(.node:has([fill*="ffe08a" i]) .nodeLabel),.ccnaPlatformsDevPage :global(.node:has([fill*="ffe08a" i]) .nodeLabel *),.ccnaPlatformsDevPage :global(.node:has([fill*="FFE08A" i]) .nodeLabel),.ccnaPlatformsDevPage :global(.node:has([fill*="FFE08A" i]) .nodeLabel *),.ccnaPlatformsDevPage :global(.node:has([fill*="#ffe08a" i]) .nodeLabel),.ccnaPlatformsDevPage :global(.node:has([fill*="#ffe08a" i]) .nodeLabel *) {
+ color: #241a00 !important;
+ fill: #241a00 !important;
+}
+
+/* ---------- Code Blocks ---------- */
+.ccnaPlatformsDevPage :global(.code-block) {
+ background: var(--color-ccna-platforms-code-background);
+ border: 1px solid var(--color-guide-accent-glow);
+ border-radius: var(--radius-ccna-platforms);
+ padding: 22px 24px;
+ overflow-x: auto;
+ margin: 14px 0 26px;
+ font-size: 1rem;
+ line-height: 1.7;
+ font-family: var(--font-ccna-platforms-mono);
+}
+
+.ccnaPlatformsDevPage :global(.code-keyword) { color: #ff7b72; font-weight: 600; }
+.ccnaPlatformsDevPage :global(.code-string) { color: #a5d6ff; }
+.ccnaPlatformsDevPage :global(.code-comment) { color: #8b949e; font-style: italic; }
+
+/* ---------- Roadmap Steps ---------- */
+.ccnaPlatformsDevPage :global(.step-list) {
+ counter-reset: step;
+ list-style: none;
+ padding: 0;
+ margin: 24px 0;
+}
+
+.ccnaPlatformsDevPage :global(.step-list li) {
+ counter-increment: step;
+ position: relative;
+ padding: 6px 0 6px 46px;
+ margin-bottom: 16px;
+ color: var(--color-ccna-platforms-foreground);
+ line-height: 1.8;
+}
+
+.ccnaPlatformsDevPage :global(.step-list li::before) {
+ content: counter(step);
+ position: absolute;
+ left: 0;
+ top: 2px;
+ width: 32px;
+ height: 32px;
+ background: rgba(124, 158, 255, 0.14);
+ border: 1px solid var(--color-guide-accent-glow);
+ color: var(--color-guide-accent);
+ font-weight: 700;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 1rem;
+}
+
+/* ---------- Footer ---------- */
+.ccnaPlatformsDevPage :global(.footer) {
+ border-top: 1px solid var(--color-ccna-platforms-border-soft);
+ padding-top: 32px;
+ color: var(--color-ccna-platforms-foreground-faint);
+ font-size: 1rem;
+ scroll-margin-top: calc(var(--header-h, 60px) + var(--disclaimer-height, 0px) + 24px);
+}
+
+.ccnaPlatformsDevPage :global(.refs-list) {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ counter-reset: ref;
+}
+
+.ccnaPlatformsDevPage :global(.refs-list li) {
+ counter-increment: ref;
+ padding: 12px 0 12px 40px;
+ position: relative;
+ border-bottom: 1px solid var(--color-ccna-platforms-border-soft);
+ font-size: 1rem;
+}
+
+.ccnaPlatformsDevPage :global(.refs-list li::before) {
+ content: counter(ref);
+ position: absolute;
+ left: 0;
+ top: 12px;
+ color: var(--color-ccna-platforms-accent-secondary);
+ font-family: var(--font-ccna-platforms-mono);
+ font-weight: 700;
+}
+
+.ccnaPlatformsDevPage :global(.refs-list a) {
+ color: var(--color-guide-accent);
+ word-break: break-all;
+}
+
+.ccnaPlatformsDevPage :global(.refs-list .ref-desc) {
+ color: var(--color-ccna-platforms-foreground-muted);
+ display: block;
+ margin-bottom: 4px;
+}
+
+/* ---------- Responsive ---------- */
+@media (max-width: 960px) {
+ .ccnaPlatformsDevPage :global(.sidebar) {
+ display: none;
+ }
+ .ccnaPlatformsDevPage :global(.content) {
+ margin-left: 0;
+ width: 100%;
+ max-width: none;
+ padding: 0 24px 80px;
+ }
+ .ccnaPlatformsDevPage :global(.hero) {
+ padding: 48px 0 32px;
+ }
+}
diff --git a/app/cisco/ccna/automation-cisco-platforms-and-development/page.tsx b/app/cisco/ccna/automation-cisco-platforms-and-development/page.tsx
new file mode 100644
index 000000000..464209f39
--- /dev/null
+++ b/app/cisco/ccna/automation-cisco-platforms-and-development/page.tsx
@@ -0,0 +1,11 @@
+import type { Metadata } from 'next';
+import { CcnaCiscoPlatformsDevelopmentGuide } from './CcnaCiscoPlatformsDevelopmentGuide';
+
+export const metadata: Metadata = {
+ title: 'Cisco Platforms and Development 徹底解説ガイド | CCNA Automation',
+ description: 'CCNA Automation (200-901 CCNAAUTO v1.1) ドメイン3.0 Cisco Platforms and Developmentの徹底解説ガイド。SDK、ネットワーク/コンピュート/コラボレーション/セキュリティAPI、NETCONF/RESTCONF、YANGなどを網羅。',
+};
+
+export default function CcnaCiscoPlatformsDevelopmentPage() {
+ return ;
+}
diff --git a/app/cisco/ccna/automation-software-development-design/CcnaSoftwareDevDesignGuide.tsx b/app/cisco/ccna/automation-software-development-design/CcnaSoftwareDevDesignGuide.tsx
index 2a4fbfcc2..7e661619b 100644
--- a/app/cisco/ccna/automation-software-development-design/CcnaSoftwareDevDesignGuide.tsx
+++ b/app/cisco/ccna/automation-software-development-design/CcnaSoftwareDevDesignGuide.tsx
@@ -1,24 +1,10 @@
'use client';
+import React, { memo } from 'react';
import { MermaidDiagram } from '@/components/MermaidDiagram';
import { DIAGRAMS } from './constants';
import { NavBar } from './NavBar';
-const DIAGRAM_DISPLAY: Record = {
- 'diag-0': { frameWidth: 760 },
- 'diag-1': { frameWidth: 1200 },
- 'diag-2': { frameWidth: 760 },
- 'diag-3': { frameWidth: 760 },
- 'diag-4': { frameWidth: 940 },
- 'diag-5': { frameWidth: 940 },
- 'diag-6': { frameWidth: 760 },
- 'diag-7': { frameWidth: 900 },
- 'diag-8': { frameWidth: 860 },
- 'diag-9': { frameWidth: 900 },
- 'diag-10': { frameWidth: 760 },
- 'diag-11': { frameWidth: 760 },
-};
-
/**
* Renders a Mermaid diagram with its accessibility label and configured display width.
*
@@ -26,20 +12,19 @@ const DIAGRAM_DISPLAY: Record = {
* @param label - The accessible label for the diagram.
* @returns The diagram element, or `null` when no diagram matches `id`.
*/
-function Diagram({ id, label }: { id: string; label: string }) {
+const Diagram = memo(function Diagram({ id, label }: { id: string; label: string }) {
const chart = DIAGRAMS[id];
if (!chart) return null;
- const display = DIAGRAM_DISPLAY[id] ?? { frameWidth: 760 };
return (
);
-}
+});
/**
* Renders the complete CCNA Automation guide to software development and design.
diff --git a/app/cisco/ccna/automation-software-development-design/page.css b/app/cisco/ccna/automation-software-development-design/page.css
index b63b53e0b..84bc4ed47 100644
--- a/app/cisco/ccna/automation-software-development-design/page.css
+++ b/app/cisco/ccna/automation-software-development-design/page.css
@@ -99,6 +99,9 @@
/* ---------- Main Content ---------- */
.ccna-software-dev-design-page .main {
margin-left: 280px;
+ width: calc(100% - 280px);
+ max-width: none;
+ box-sizing: border-box;
padding: 0 0 100px;
}
@@ -142,7 +145,7 @@
font-size: 1.05rem;
color: var(--color-muted-foreground, #94a3b8);
max-width: 800px;
- margin: 0 0 24px;
+ margin: 0 auto 24px;
}
.ccna-software-dev-design-page .hero-net {
@@ -170,8 +173,8 @@
.ccna-software-dev-design-page .hero > *,
.ccna-software-dev-design-page .section > * {
- max-width: 1000px;
- margin-inline: auto;
+ width: 100%;
+ max-width: none;
}
.ccna-software-dev-design-page .section {
@@ -367,14 +370,16 @@
.ccna-software-dev-design-page .diagram-frame {
margin: 24px auto 16px;
display: flex;
- justify-content: center;
+ justify-content: safe center;
+ overflow-x: auto;
}
.ccna-software-dev-design-page .mermaid-wrap {
display: flex;
- justify-content: center;
+ justify-content: safe center;
width: 100%;
margin: 0 auto;
+ overflow-x: auto;
}
.ccna-software-dev-design-page .mermaid-wrap > div {
@@ -495,6 +500,8 @@
}
.ccna-software-dev-design-page .main {
margin-left: 0;
+ width: 100%;
+ max-width: none;
}
.ccna-software-dev-design-page .hero,
.ccna-software-dev-design-page .section,
diff --git a/app/cisco/ccna/beginner-guide/page.css b/app/cisco/ccna/beginner-guide/page.css
index fe1c3dcc9..aeea9ba76 100644
--- a/app/cisco/ccna/beginner-guide/page.css
+++ b/app/cisco/ccna/beginner-guide/page.css
@@ -25,7 +25,7 @@
.ccna-beginner-page .layout {
display: block;
min-height: 100vh;
- padding-left: 280px;
+ padding-left: 0;
}
/* ---------- Sidebar ---------- */
@@ -100,9 +100,11 @@
/* ---------- Main ---------- */
.ccna-beginner-page main {
+ margin-left: 280px;
+ width: calc(100% - 280px);
padding: 3rem 4rem 6rem;
- max-width: 1000px;
- margin-inline: auto;
+ max-width: none;
+ box-sizing: border-box;
}
@media (max-width: 900px) {
@@ -113,7 +115,9 @@
display: none;
}
.ccna-beginner-page main {
+ margin-left: 0;
width: 100%;
+ max-width: none;
padding: 2rem 1.25rem 4rem;
}
}
diff --git a/app/cisco/ccna/ip-connectivity-guide/page.css b/app/cisco/ccna/ip-connectivity-guide/page.css
index 10ee67f9e..e60cefb15 100644
--- a/app/cisco/ccna/ip-connectivity-guide/page.css
+++ b/app/cisco/ccna/ip-connectivity-guide/page.css
@@ -10,7 +10,7 @@
display: block;
min-height: 100vh;
width: 100%;
- padding-left: 280px;
+ padding-left: 0;
}
/* Sidebar (Fixed position with header & disclaimer height sync) */
@@ -92,8 +92,10 @@
/* Main Content */
.ccna-ip-connectivity-page .main {
- max-width: 1000px;
- margin-inline: auto;
+ margin-left: 280px;
+ width: calc(100% - 280px);
+ max-width: none;
+ box-sizing: border-box;
padding: 3rem 4rem 6rem;
}
@@ -325,7 +327,9 @@
display: none;
}
.ccna-ip-connectivity-page .main {
+ margin-left: 0;
width: 100%;
+ max-width: none;
padding: 2rem 1.25rem 4rem;
}
}
diff --git a/app/cisco/ccna/ip-services-guide/page.css b/app/cisco/ccna/ip-services-guide/page.css
index 700c5ca13..f6e1ba06b 100644
--- a/app/cisco/ccna/ip-services-guide/page.css
+++ b/app/cisco/ccna/ip-services-guide/page.css
@@ -105,14 +105,15 @@
/* Main Content */
.ccna-ip-services-page .main {
margin-left: 280px;
+ width: calc(100% - 280px);
padding: 3rem 4rem 8rem;
- max-width: 100%;
+ max-width: none;
+ box-sizing: border-box;
}
.ccna-ip-services-page .section {
padding-top: 1.2rem;
- max-width: 1000px;
- margin-inline: auto;
+ max-width: none;
scroll-margin-top: calc(var(--header-h, 60px) + var(--disclaimer-height, 0px) + 2rem);
}
@@ -362,6 +363,8 @@
}
.ccna-ip-services-page .main {
margin-left: 0;
+ width: 100%;
+ max-width: none;
padding: 2rem 1.5rem 6rem;
}
}
diff --git a/app/cisco/ccna/security-fundamentals/page.css b/app/cisco/ccna/security-fundamentals/page.css
index f0f7313cc..9c66fb621 100644
--- a/app/cisco/ccna/security-fundamentals/page.css
+++ b/app/cisco/ccna/security-fundamentals/page.css
@@ -20,7 +20,7 @@
position: fixed;
top: calc(var(--header-h, 60px) + var(--disclaimer-height, 0px));
left: 0;
- width: 290px;
+ width: 280px;
bottom: 0;
overflow-y: auto;
background: var(--color-card, #0b1830);
@@ -100,13 +100,14 @@
/* ---- Main Content ---- */
.ccna-security-page main {
- margin-left: 290px;
+ margin-left: 280px;
padding-top: calc(var(--disclaimer-height, 0px) + 2.5rem);
padding-bottom: 6rem;
padding-left: 3.5rem;
padding-right: 3.5rem;
- max-width: calc(100% - 290px);
- width: 100%;
+ max-width: none;
+ width: calc(100% - 280px);
+ box-sizing: border-box;
}
@media (max-width: 900px) {
@@ -119,7 +120,8 @@
}
.ccna-security-page main {
margin-left: 0;
- max-width: 100%;
+ width: 100%;
+ max-width: none;
padding: calc(var(--disclaimer-height, 0px) + 1.5rem) 1.25rem 4rem;
}
}
diff --git a/app/constants.ts b/app/constants.ts
index d51d96179..b2ba5ca20 100644
--- a/app/constants.ts
+++ b/app/constants.ts
@@ -331,6 +331,16 @@ const ALL_EXAMS: Exam[] = [
href: '/cisco/ccna/security-fundamentals',
pct: '15%',
},
+ {
+ label: '4.0 Application Deployment and Security',
+ href: '/cisco/ccna/automation-application-deployment-security',
+ pct: '15%',
+ },
+ {
+ label: '3.0 Cisco Platforms and Development',
+ href: '/cisco/ccna/automation-cisco-platforms-and-development',
+ pct: '15%',
+ },
],
badge: 'ネットワーク基礎',
icon: '🌐',
diff --git a/app/gcl/agwa/agwa.css b/app/gcl/agwa/agwa.css
index ec2c81346..a6827d42e 100644
--- a/app/gcl/agwa/agwa.css
+++ b/app/gcl/agwa/agwa.css
@@ -7,9 +7,11 @@
flex-shrink: 0;
background: var(--color-card);
border-right: 1px solid var(--color-border);
- position: sticky;
+ position: fixed;
+ left: 0;
top: var(--fixed-offset);
height: calc(100vh - var(--fixed-offset));
+ z-index: 100;
overflow-y: auto;
padding: 0 0 2rem;
}
@@ -83,8 +85,11 @@
.main {
flex: 1 1 0;
min-width: 0;
+ margin-left: 280px;
+ width: calc(100% - 280px);
padding: 3rem 3.5rem;
- max-width: 100%;
+ max-width: none;
+ box-sizing: border-box;
}
.hero {
margin-bottom: 3.5rem;
@@ -514,6 +519,9 @@ tr:hover td {
display: none;
}
.main {
+ margin-left: 0;
+ width: 100%;
+ max-width: none;
padding: 2rem 1.5rem;
}
.hero h1 {
diff --git a/app/gcl/agwa/section2/page.module.css b/app/gcl/agwa/section2/page.module.css
index e95032529..bc243824c 100644
--- a/app/gcl/agwa/section2/page.module.css
+++ b/app/gcl/agwa/section2/page.module.css
@@ -1,6 +1,6 @@
/* SIDEBAR */
.sidebar {
- width: 260px;
+ width: 280px;
background: #0a0e1a;
color: #fff;
position: fixed;
@@ -15,15 +15,19 @@
/* MAIN */
.main {
flex: 1 1 0;
+ margin-left: 280px;
+ width: calc(100% - 280px);
padding: 0 1rem 3rem;
min-width: 0;
- max-width: 100%;
+ max-width: none;
+ box-sizing: border-box;
}
@media (min-width: 768px) {
.main {
- margin-left: 260px;
+ margin-left: 280px;
+ width: calc(100% - 280px);
padding: 0 2rem 4rem;
- max-width: calc(100% - 260px);
+ max-width: none;
}
}
.sidebar-header {
@@ -636,6 +640,11 @@
bottom: auto;
min-height: auto;
}
+ .main {
+ margin-left: 0;
+ width: 100%;
+ max-width: none;
+ }
.hero {
margin-left: -1rem;
margin-right: -1rem;
diff --git a/app/gcl/associate-cloud-engineer/complete-advanced-guide/page.css b/app/gcl/associate-cloud-engineer/complete-advanced-guide/page.css
index d47e6886c..5c812e9b5 100644
--- a/app/gcl/associate-cloud-engineer/complete-advanced-guide/page.css
+++ b/app/gcl/associate-cloud-engineer/complete-advanced-guide/page.css
@@ -214,6 +214,9 @@
/* ===== MAIN CONTENT ===== */
.complete-guide-page .main {
margin-left: 280px;
+ width: calc(100% - 280px);
+ max-width: none;
+ box-sizing: border-box;
min-height: 100vh;
position: relative;
z-index: 1;
@@ -1025,6 +1028,8 @@
}
.complete-guide-page .main {
margin-left: 0;
+ width: 100%;
+ max-width: none;
}
.complete-guide-page .hero,
.complete-guide-page .content {
diff --git a/app/gcl/associate-cloud-engineer/section1/page.css b/app/gcl/associate-cloud-engineer/section1/page.css
index 947cecc18..8f5733810 100644
--- a/app/gcl/associate-cloud-engineer/section1/page.css
+++ b/app/gcl/associate-cloud-engineer/section1/page.css
@@ -1,8 +1,7 @@
/* Scoped CSS for GCP ACE Section 1 Complete Guide
* 正本: Ace-section1-complete-guide.html の
+
+
+
+
+
+
+
+ CCNA Automation Certification ガイド
+
+ 200-901 CCNAAUTO ドメイン4.0 Application Deployment and Security
+ 完全解説
+
+
+ Cisco公式サイトおよび公式出題範囲PDFをもとに、CCNA
+ Automation認定試験「Automating Networks Using Cisco Platforms v1.1(200-901
+ CCNAAUTO)」の6ドメインのうち、ドメイン4.0「Application Deployment and
+ Security(アプリケーションの展開とセキュリティ)」 (配点15%)を、初学者でも理解できるようにステップバイステップで解説します。
+
+
+ 配点 15%
+ サブトピック 4.1〜4.12
+ 試験時間 120分
+ 対応言語:英語・日本語
+
+
+
+
+
このガイドの使い方
+
+ 各章は試験ガイドのサブトピック番号(4.1、4.2…)に対応しています。図解はすべてMermaid によるフローチャート、比較情報はすべて表 で示しており、ASCIIアートは使用していません。前提知識としてPythonの基礎、Linux/Bashの基本操作、Dockerの概念に軽く触れたことがあると理解がスムーズですが、未経験でも読み進められるように用語から説明しています。各章末の「この章のポイント」と、最後の第12章の早見表を試験直前の見直しに活用してください。
+
+
+
+
+
+ 第1章 出題範囲の全体像
+
+ CCNA Automation認定を取得するには、120分の試験「200-901
+ CCNAAUTO」に合格する必要があります。この試験は6つのドメインで構成されており、それぞれに配点比率が設定されています。
+
+
+
+
+
+ ドメイン番号
+ ドメイン名
+ 配点比率
+
+
+
+
+ 1.0
+ Software Development and Design(ソフトウェア開発と設計)
+ 15%
+
+
+ 2.0
+ Understanding and Using APIs(APIの理解と利用)
+ 20%
+
+
+ 3.0
+
+ Cisco Platforms and Development(Ciscoプラットフォームと開発)
+
+ 15%
+
+
+ 4.0
+
+ Application Deployment and
+ Security(アプリケーションの展開とセキュリティ)
+
+ 15%
+
+
+ 5.0
+
+ Infrastructure and Automation(インフラストラクチャと自動化)
+
+ 20%
+
+
+ 6.0
+ Network Fundamentals(ネットワークの基礎)
+ 15%
+
+
+
+
+
+ 本ガイドが扱うドメイン4.0は、「作ったアプリケーションをどこに・どうやって・安全に動かすか」 という、開発から運用への橋渡しにあたる領域です。具体的には以下の12個のサブトピックで構成されます。
+
+
+
+
+
+ サブトピック
+ 内容
+
+
+
+
+ 4.1
+ エッジコンピューティングの利点を説明する
+
+
+ 4.2
+
+ 異なるアプリケーション展開モデル(プライベートクラウド、パブリッククラウド、ハイブリッドクラウド、エッジ)の属性を説明する
+
+
+
+ 4.3
+
+ 展開タイプ(仮想マシン/ベアメタル/コンテナ)の属性を説明する
+
+
+
+ 4.4
+
+ アプリケーション展開におけるCI/CDパイプラインの構成要素を説明する
+
+
+
+ 4.5
+ Pythonのユニットテストを構築する
+
+
+ 4.6
+ Dockerfileの内容を解釈する
+
+
+ 4.7
+ ローカル開発環境でDockerイメージを利用する
+
+
+ 4.8
+
+ シークレット保護、暗号化(保存時・転送時)、データ取り扱いに関するアプリケーションセキュリティの課題を説明する
+
+
+
+ 4.9
+
+ ファイアウォール、DNS、ロードバランサー、リバースプロキシのアプリケーション展開における役割を説明する
+
+
+
+ 4.10
+
+ OWASPのトップ脅威(XSS、SQLインジェクション、CSRFなど)を説明する
+
+
+
+ 4.11
+
+ Bashコマンド(ファイル管理、ディレクトリ操作、環境変数)を利用する
+
+
+
+ 4.12
+ DevOpsプラクティスの原則を説明する
+
+
+
+
+ このドメイン全体の位置づけを図にすると次のとおりです。
+
+
+
CCNAAUTO試験の6ドメインと、ドメイン4.0の位置づけを示す図です。
+
+
+
+
この章のポイント
+
+ ドメイン4.0は試験全体の15%を占め、「開発したアプリをどこでどう動かし、どう守るか」を扱う。12個のサブトピックは、大きく「展開モデル・展開先の選定(4.1〜4.3)」「CI/CDと自動テスト(4.4・4.5)」「コンテナ実務(4.6・4.7)」「セキュリティ(4.8〜4.10)」「運用スキル(4.11・4.12)」の5グループに整理できる。
+
+
+
+
+
+
+ 第2章 エッジコンピューティングとアプリケーション展開モデル(4.1・4.2)
+
+ 4.1 エッジコンピューティングの利点
+
+ エッジコンピューティングとは、データを中央のクラウドやデータセンターまで送らず、データが発生する場所(ネットワークの「端=エッジ」)に近い場所で処理する という考え方です。工場のセンサー、店舗のPOSレジ、IoTデバイスなどが典型例です。
+
+
+
+
+
+ 利点
+ 説明
+
+
+
+
+ 低遅延(レイテンシ削減)
+
+ クラウドまでの往復通信が不要になり、リアルタイム性が向上する
+
+
+
+ 帯域幅の節約
+
+ 現地で処理・要約してから必要なデータだけをクラウドに送るため、通信量を削減できる
+
+
+
+ オフライン耐性
+ クラウドとの接続が一時的に切れても現地で処理を継続できる
+
+
+ データローカリティ/プライバシー
+
+ 機密データを現地にとどめたまま処理でき、規制対応がしやすい場合がある
+
+
+
+
+
+ 4.2 アプリケーション展開モデルの比較
+
+ アプリケーションをどこで動かすかという「展開モデル」には、主に次の4種類があります。
+
+
+
+
+
+ 展開モデル
+ 管理主体
+ 主な特徴
+ 典型的な用途
+
+
+
+
+ プライベートクラウド
+ 自社(または委託先が自社専用に構築)
+
+ 自由度が高く、既存のセキュリティ・コンプライアンス要件に合わせやすいが、初期投資と運用負荷が大きい
+
+ 金融・医療など規制が厳しい業界の基幹システム
+
+
+ パブリッククラウド
+ クラウド事業者(AWS、Azure、Google Cloudなど)
+
+ 従量課金で始めやすく、拡張性が高いが、事業者のインフラに依存する
+
+ Webサービス、需要変動の大きいアプリケーション
+
+
+ ハイブリッドクラウド
+ 自社とクラウド事業者の組み合わせ
+
+ 機密データはプライベート側、負荷変動の大きい処理はパブリック側、というように使い分けられる
+
+ 既存の社内システムとクラウドサービスを連携させたい企業
+
+
+ エッジ
+ 現地(店舗・工場・デバイス側)
+ クラウドに比べて計算資源は限られるが、低遅延処理が可能
+ IoT、産業オートメーション、リアルタイム映像解析
+
+
+
+
+ これらの関係をフローで示すと次のようになります。
+
+
+
IoTデバイス、エッジ、パブリック・プライベート・ハイブリッドクラウドの関係図です。
+
+
+
+
この章のポイント
+
+ エッジコンピューティングの利点は「低遅延・帯域節約・オフライン耐性・データローカリティ」の4つに集約できる。展開モデルは「誰が管理するか」「どこにデータ・計算資源があるか」で分類され、要件(コスト・拡張性・規制・遅延)に応じて選択する。
+
+
+
+
+
+
+ 第3章 アプリケーション実行環境の比較:VM・ベアメタル・コンテナ(4.3)
+
+ 展開モデル(どこで動かすか)が決まったら、次は「どの単位でアプリケーションをパッケージ化して動かすか」を選びます。試験ガイドでは以下の3タイプが挙げられています。
+
+
+ 4.3.a 仮想マシン(Virtual Machine)
+ 4.3.b ベアメタル(Bare Metal)
+ 4.3.c コンテナ(Container)
+
+
+ 3つの実行環境の構造比較
+
+
+
ベアメタル、仮想マシン、コンテナのソフトウェア層を比較する図です。
+
+
+ 属性比較表
+
+
+
+ 項目
+ ベアメタル
+ 仮想マシン(VM)
+ コンテナ
+
+
+
+
+ 分離レベル
+ 物理サーバー単位(最も高い)
+ ハイパーバイザーによるハードウェアレベルの分離
+ OSカーネルを共有するプロセスレベルの分離
+
+
+ 起動速度
+ サーバーの起動時間に依存(遅い)
+ 数十秒〜数分
+ 数百ミリ秒〜数秒(非常に速い)
+
+
+ リソースオーバーヘッド
+ なし(すべての資源を占有)
+ 大きい(ゲストOSごとに必要)
+ 小さい(OSを共有するため軽量)
+
+
+ 移植性(ポータビリティ)
+ 低い(ハードウェアに強く依存)
+ 中程度(イメージ化して移動可能)
+ 高い(同じイメージがどこでも同じ動作)
+
+
+ 主なユースケース
+ 高いパフォーマンスが必須の基幹システム、レガシーアプリ
+ 複数OSの混在環境、既存VM資産の活用
+ マイクロサービス、CI/CDでの高速な使い捨て環境
+
+
+
+
+
+
この章のポイント
+
+ 分離の強さと起動の速さはトレードオフの関係にある:ベアメタル・VMは分離が強いが重く、コンテナは軽量だがOSカーネルを共有する。コンテナはCI/CDやマイクロサービスとの相性が良く、次章のCI/CDパイプラインとも密接に関係する。
+
+
+
+
+
+
+ 第4章 CI/CDパイプラインの基礎(4.4)
+
+ CI/CD(Continuous Integration / Continuous Delivery(or
+ Deployment)=継続的インテグレーション/継続的デリバリー(デプロイ))は、コードの変更を自動的にビルド・テスト・展開するための仕組みです。
+
+
+ CI/CDパイプラインの主な構成要素
+
+
+
+ ステージ
+ 目的
+
+
+
+
+ ソース管理(Git等)
+
+ コードの変更履歴を管理し、変更をトリガーにパイプラインを起動する
+
+
+
+ ビルド
+ 依存関係の解決、コンパイル、静的解析などを行う
+
+
+ テスト
+ ユニットテスト・統合テストなどを自動実行し、品質を検証する
+
+
+ パッケージング/アーティファクト作成
+ コンテナイメージなど、展開可能な成果物を作成する
+
+
+ レジストリへの登録
+ 作成した成果物をイメージレジストリなどに格納する
+
+
+ ステージング展開
+ 本番相当の環境に自動展開し、追加検証を行う
+
+
+ 本番展開
+ 承認を経て本番環境に展開する
+
+
+ 監視・フィードバック
+ 稼働状況を監視し、問題や改善点を開発側にフィードバックする
+
+
+
+
+ パイプライン全体の流れ
+
+
+
コミットから本番展開、監視、フィードバックまでのCI/CDパイプライン図です。
+
+
+
+
この章のポイント
+
+ CI(継続的インテグレーション)は「頻繁に統合し、自動テストで早期に問題を検出する」こと、CD(継続的デリバリー/デプロイ)は「いつでも展開できる状態を保ち、実際に自動展開まで行う」ことを指す。テスト失敗・承認却下時にフローが開発者へ戻る「フィードバックループ」がある点が重要。
+
+
+
+
+
+
+ 第5章 Pythonユニットテストの構築(4.5)
+
+ ユニットテストとは、プログラムの中の「最小単位(関数やメソッド)」が期待どおりに動くかを自動で検証するテストです。Pythonでは標準ライブラリのunittestモジュールがよく使われます。
+
+
+ テストの基本的な流れ(Arrange-Act-Assertパターン)
+
+
+
Arrange、Act、Assertによるユニットテストの基本フローです。
+
+
+ コード例
+
+
Python — unittest の基本形
+
import unittest
+
+def add(a, b):
+ """2つの数値を加算する簡単な関数"""
+ return a + b
+
+class TestAddFunction(unittest.TestCase):
+
+ def test_add_positive_numbers(self):
+ # Arrange
+ a, b = 2, 3
+ # Act
+ result = add(a, b)
+ # Assert
+ self.assertEqual(result, 5)
+
+ def test_add_negative_numbers(self):
+ result = add(-1, -1)
+ self.assertEqual(result, -2)
+
+if __name__ == "__main__":
+ unittest.main()
+
+
+
+
+ assertEqualのほかにも、assertTrue(真偽値の検証)、assertRaises(例外発生の検証)などがよく使われます。ユニットテストは「テスト駆動開発(TDD)」の考え方や、第4章のCI/CDパイプラインの「テストステージ」と直結しています。
+
+
+
+
この章のポイント
+
+ ユニットテストは「準備(Arrange)→実行(Act)→検証(Assert)」の3ステップで考えると書きやすい。CI/CDパイプラインでは、このユニットテストが自動テストステージの中核を担う。
+
+
+
+
+
+
+ 第6章 Dockerfileの読み方とDockerイメージの活用(4.6・4.7)
+
+ 4.6 Dockerfileの内容を解釈する
+
+ Dockerfileは、Dockerイメージをどのように構築するかを記述したテキストファイルです。代表的な命令は以下のとおりです。
+
+
+
+
+
+ 命令
+ 役割
+
+
+
+
+ FROM
+
+ ベースとなるイメージを指定する(例:FROM python:3.12-slim)
+
+
+
+ WORKDIR
+ コンテナ内の作業ディレクトリを設定する
+
+
+ COPY / ADD
+ ホスト側のファイルをイメージ内にコピーする
+
+
+ RUN
+
+ イメージ構築時にコマンドを実行する(パッケージのインストール等)
+
+
+
+ ENV
+ 環境変数を設定する
+
+
+ EXPOSE
+
+ コンテナが待ち受けるポート番号を明示する(実際の公開はdocker run -pで行う)
+
+
+
+ CMD
+ コンテナ起動時のデフォルトの実行コマンドを指定する
+
+
+ ENTRYPOINT
+
+ コンテナを実行ファイルのように振る舞わせる際のエントリポイントを指定する
+
+
+
+
+
+ サンプルDockerfile
+
+
Dockerfile
+
# ベースイメージを指定
+FROM python:3.12-slim
+
+# 作業ディレクトリを作成・移動
+WORKDIR /app
+
+# 依存関係定義ファイルを先にコピーしてキャッシュを効かせる
+COPY requirements.txt .
+RUN pip install --no-cache-dir -r requirements.txt
+
+# アプリ本体をコピー
+COPY . .
+
+# コンテナが待ち受けるポートを明示
+EXPOSE 8080
+
+# コンテナ起動時に実行するコマンド
+CMD ["python", "app.py"]
+
+
+
+ 4.7 ローカル開発環境でのDockerイメージの利用
+ よく使うDockerコマンドは以下のとおりです。
+
+
+
+
+ コマンド
+ 用途
+
+
+
+
+ docker build -t イメージ名:タグ .
+ カレントディレクトリのDockerfileからイメージをビルドする
+
+
+ docker images
+ ローカルに存在するイメージの一覧を表示する
+
+
+ docker run -d -p 8080:8080 イメージ名:タグ
+
+ イメージからコンテナをバックグラウンドで起動し、ポートを公開する
+
+
+
+ docker ps / docker ps -a
+ 稼働中(または全て)のコンテナ一覧を表示する
+
+
+ docker logs コンテナID
+ コンテナのログを表示する
+
+
+ docker exec -it コンテナID bash
+ 稼働中のコンテナ内でシェルを起動する
+
+
+ docker stop コンテナ名またはID / docker rm コンテナ名またはID
+ コンテナを停止・削除する
+
+
+ docker push イメージ名 / docker pull イメージ名
+ イメージをレジストリへ送信/取得する
+
+
+
+
+ イメージのライフサイクル
+
+
+
Dockerfileからイメージを作成し、コンテナとレジストリを操作するライフサイクル図です。
+
+
+
+
この章のポイント
+
+ Dockerfileは「上から順に1行ずつ実行される構築手順書」として読むと理解しやすい。COPY requirements.txtをCOPY . .より先に行うのは、依存関係が変わらない限りビルドキャッシュを再利用して高速化するための定石。
+
+
+
+
+
+
+
+ 第7章
+ アプリケーションセキュリティの基礎:シークレット保護・暗号化・データ取り扱い(4.8)
+
+
+ シークレット保護
+
+ 「シークレット」とは、パスワード、APIキー、証明書の秘密鍵など、漏洩すると重大な影響が出る機密情報を指します。基本原則は次のとおりです。
+
+
+
+ コードやDockerイメージにシークレットを直接書き込まない (ハードコードしない)
+
+
+ 環境変数、シークレット管理サービス(Vaultなど)、クラウドのシークレットマネージャーを利用する
+
+
+ Gitリポジトリに誤ってコミットしないよう.gitignoreや事前スキャンツールを活用する
+
+
+
+ 暗号化(保存時・転送時)
+
+
+
+ 種類
+ 説明
+ 代表例
+
+
+
+
+ 転送時の暗号化(Encryption in Transit)
+ ネットワークを流れるデータを暗号化し、盗聴・改ざんを防ぐ
+ TLS/HTTPS、SSH
+
+
+ 保存時の暗号化(Encryption at Rest)
+ ディスクやデータベースに保存されているデータを暗号化する
+ ディスク暗号化、データベースの列暗号化
+
+
+
+
+ データ取り扱いの考え方
+
+ 個人情報や機密データを扱う際は、「必要最小限のデータのみ収集・保持する」「アクセス権を最小限にする」「保持期間を定めて不要になったら削除する」といった原則が重要です。
+
+
+ セキュアなデータフローのイメージ
+
+
+
TLS、シークレット管理、保存時暗号化を組み合わせたデータフロー図です。
+
+
+
+
この章のポイント
+
+ 「シークレットはコードに書かない」「転送時と保存時の両方を暗号化する」の2点は特に頻出の観点。暗号化は「守る対象がどこにあるか(通信中か、保存中か)」で使う技術が異なる。
+
+
+
+
+
+
+
+ 第8章
+ ネットワーク境界のセキュリティ要素:ファイアウォール・DNS・ロードバランサー・リバースプロキシ(4.9)
+
+
+ アプリケーションを展開する際、ユーザーからのリクエストは複数のネットワーク要素を経由します。それぞれの役割を理解することが重要です。
+
+
+
+
+
+ 要素
+ 役割
+
+
+
+
+ DNS(Domain Name System)
+
+ ドメイン名(例:example.com)をIPアドレスに変換する名前解決を行う
+
+
+
+ ファイアウォール
+
+ 事前に定義したルールに基づき、許可された通信のみを通過させる
+
+
+
+ ロードバランサー
+
+ 複数のサーバーにリクエストを振り分け、負荷分散と可用性向上を実現する
+
+
+
+ リバースプロキシ
+
+ クライアントとサーバーの間に立ち、TLS終端・キャッシュ・経路制御・ヘッダー書き換えなどを行う
+
+
+
+
+
+ リクエストが辿る経路
+
+
+
DNSで名前解決した後、HTTPリクエストがFW、LB、リバースプロキシ、アプリへ進む構成例です。
+
+
+
+
この章のポイント
+
+ DNSの名前解決はHTTPリクエスト経路の前提処理であり、その後にユーザーからアプリ本体への通信が始まる。ファイアウォール、ロードバランサー、リバースプロキシの順序は代表的な構成例であり、実環境では要件に応じて配置や機能統合が変わる。試験では「アクセス制御」「負荷分散」「TLS終端・経路制御」という役割の違いで整理すると覚えやすい。
+
+
+
+
+
+
+ 第9章 OWASPトップの脅威(4.10)
+
+ OWASP(Open Worldwide Application Security
+ Project)は、Webアプリケーションセキュリティに関する非営利のコミュニティで、代表的な脅威をまとめた「OWASP
+ Top 10」を定期的に公開しています。
+
+
+ 試験ガイドでは代表例としてXSS(クロスサイトスクリプティング) 、SQLインジェクション 、CSRF(クロスサイトリクエストフォージェリ) が挙げられています。
+
+
+ 代表的な脅威の説明
+
+
+
+ 脅威
+ 概要
+ 典型的な対策
+
+
+
+
+ XSS(クロスサイトスクリプティング)
+
+ ユーザーの入力値がそのままページに出力され、悪意あるスクリプトがブラウザ上で実行されてしまう
+
+
+ 出力時のエスケープ処理、入力値の検証、Content Security
+ Policyの設定
+
+
+
+ SQLインジェクション
+
+ ユーザーの入力値がSQL文にそのまま埋め込まれ、意図しないSQLが実行されてしまう
+
+
+ プレースホルダ/パラメータ化クエリの利用、入力値の検証、最小権限のDBアカウント
+
+
+
+ CSRF(クロスサイトリクエストフォージェリ)
+
+ 認証済みユーザーのブラウザを利用して、本人の意図しないリクエストを別サイトから送信させられる
+
+
+ CSRFトークンの検証、SameSite属性付きCookie、重要操作での再認証
+
+
+
+
+
+
+ XSSとSQLインジェクションは「入力値や出力値の不十分な検証・処理」が主因となるのに対し、CSRFは「認証済みブラウザからのクロスサイト状態変更リクエストの信頼」に起因します。そのため、XSSには文脈に応じた出力エンコーディング、SQLインジェクションにはパラメータ化クエリ、CSRFにはCSRFトークン検証・Origin/Referer検証・SameSite Cookie属性による対策がそれぞれ不可欠です。
+
+
+
+
+
入力検証とCSRFトークン検証から、SQLi、XSS、CSRFのリスクを判定する図です。
+
+
+ 最新の公式リストとの関係
+
+ OWASP Top
+ 10は数年ごとに改訂されており、2025年版(2021年版の後継、8回目の改訂版)が現時点の最新版です。参考として、2025年版の10カテゴリを紹介します。
+
+
+
+
+
+ 順位
+ カテゴリ名(英語)
+
+
+
+
+ A01
+ Broken Access Control(アクセス制御の不備)
+
+
+ A02
+ Security Misconfiguration(セキュリティ設定の不備)
+
+
+ A03
+
+ Software Supply Chain
+ Failures(ソフトウェアサプライチェーンの問題)
+
+
+
+ A04
+ Cryptographic Failures(暗号化の不備)
+
+
+ A05
+ Injection(インジェクション)
+
+
+ A06
+ Insecure Design(安全でない設計)
+
+
+ A07
+ Authentication Failures(認証の不備)
+
+
+ A08
+
+ Software or Data Integrity
+ Failures(ソフトウェア/データ整合性の不備)
+
+
+
+ A09
+
+ Security Logging & Alerting
+ Failures(セキュリティログ・アラートの不備)
+
+
+
+ A10
+
+ Mishandling of Exceptional Conditions(例外的な状態の処理不備)
+
+
+
+
+
+
+ 試験ガイドが例示するSQLインジェクションとXSSは、現行の2025年版では主に「A05
+ Injection」に含まれる代表的な攻撃パターンです。CSRFは2017年版以降、単独の最上位カテゴリとしては掲載されなくなりましたが、現在も広く知られた古典的な攻撃パターンであり、アクセス制御や認証まわりの対策(トークン検証・Cookie属性の設定など)と合わせて理解しておく価値があります。試験対策としては、まず試験ガイドが明示するXSS・SQLインジェクション・CSRFの3つの仕組みと対策を確実に押さえるのがよいでしょう。
+
+
+
+
この章のポイント
+
+ XSSは信頼できないデータをHTMLへ安全でない形で出力することで発生するため、文脈に応じた出力エンコーディングを行う。SQLインジェクションは入力をSQL構文へ連結することで発生するため、パラメータ化クエリを使う。CSRFは認証済みブラウザに意図しない状態変更を実行させる攻撃であり、CSRFトークンまたはSameSite Cookieで防ぐ。OWASP Top 10は定期的に改訂されるため、順位や名称は最新の一次情報で確認する。
+
+
+
+
+
+
+ 第10章 Bashコマンドの活用(4.11)
+
+ Linux環境での自動化スクリプトや運用作業では、Bashコマンドの基本操作が欠かせません。
+
+
+ ファイル管理
+
+
+
+ コマンド
+ 用途
+
+
+
+
+ ls -l
+ ファイル・ディレクトリの一覧を詳細表示する
+
+
+ cp src dst
+ ファイルをコピーする
+
+
+ mv src dst
+ ファイルを移動・リネームする
+
+
+ rm file
+ ファイルを削除する(-rでディレクトリごと削除)
+
+
+ chmod 755 file
+ ファイルの権限を変更する
+
+
+ cat file
+ ファイルの内容を表示する
+
+
+ grep "keyword" file
+ ファイル内から文字列を検索する
+
+
+
+
+ ディレクトリ操作
+
+
+
+ コマンド
+ 用途
+
+
+
+
+ pwd
+ 現在のディレクトリを表示する
+
+
+ cd path
+ ディレクトリを移動する
+
+
+ mkdir dir
+ ディレクトリを作成する
+
+
+ rmdir dir
+ 空のディレクトリを削除する
+
+
+ find . -name "*.py"
+ 条件に合うファイルを再帰的に検索する
+
+
+
+
+ 環境変数
+
+
+
+ コマンド
+ 用途
+
+
+
+
+ echo $HOME
+ 環境変数の値を表示する
+
+
+ export VAR=value
+ 環境変数を設定する(現在のシェルとその子プロセスに反映)
+
+
+ env / printenv
+ 現在の環境変数一覧を表示する
+
+
+ unset VAR
+ 環境変数を削除する
+
+
+
+
+
+
この章のポイント
+
+ 「ファイル管理」「ディレクトリ操作」「環境変数」の3分類で整理すると覚えやすい。自動化スクリプト(第4章のCI/CDや第11章のDevOps運用)では、これらのコマンドが組み合わさって使われる。
+
+
+
+
+
+
+ 第11章 DevOpsの原則(4.12)
+
+ DevOpsとは、開発(Development)と運用(Operations)の壁をなくし、ソフトウェアを継続的に、かつ安全・迅速にリリースし続けるための文化・プラクティスです。
+
+
+ 代表的な原則は次のように整理できます。
+
+
+
+ 原則
+ 説明
+
+
+
+
+ 文化(Culture)
+
+ 開発チームと運用チームが責任を分断せず、協力してリリースに責任を持つ
+
+
+
+ 自動化(Automation)
+
+ ビルド・テスト・展開・監視といった繰り返し作業を自動化し、人的ミスと作業時間を減らす
+
+
+
+ 計測(Measurement / Lean)
+
+ リリース頻度、障害復旧時間、変更失敗率などの指標を計測し、継続的に改善する
+
+
+
+ 共有(Sharing)
+ ツール・知見・障害対応のノウハウをチーム間で共有する
+
+
+
+
+
+ これらは、第4章で扱ったCI/CDパイプラインを支える文化的・組織的な土台にあたります。DevOpsのプラクティスは、しばしば「計画→コーディング→ビルド→テスト→リリース→展開→運用→監視」という循環(ループ)として表現されます。
+
+
+
+
+
PlanからMonitorまでを循環し、監視結果を次の計画へ戻すDevOpsループです。
+
+
+
+
この章のポイント
+
+ DevOpsは単なるツールの導入ではなく、「文化・自動化・計測・共有」という考え方の集合体である。このループが途切れず回り続けることこそが、継続的デリバリー(CD)の本質。
+
+
+
+
+
+
+ 第12章 まとめ:ドメイン4.0 早見表
+ 試験直前の見直し用に、サブトピックごとの要点を1行にまとめました。
+
+
+
+
+ No.
+ サブトピック
+ 一言でいうと
+
+
+
+
+ 4.1
+ エッジコンピューティングの利点
+ 低遅延・帯域節約・オフライン耐性・データローカリティ
+
+
+ 4.2
+ 展開モデルの比較
+
+ プライベート/パブリック/ハイブリッド/エッジを、管理主体とコスト・拡張性で選ぶ
+
+
+
+ 4.3
+ VM・ベアメタル・コンテナ
+
+ 分離の強さと起動の速さはトレードオフ。コンテナは軽量・高速・高移植性
+
+
+
+ 4.4
+ CI/CDパイプライン
+
+ コミット→ビルド→テスト→パッケージ→展開→監視を自動化する一連の流れ
+
+
+
+ 4.5
+ Pythonユニットテスト
+ Arrange(準備)→Act(実行)→Assert(検証)の3ステップ
+
+
+ 4.6
+ Dockerfileの解釈
+
+ FROM/COPY/RUN/CMDなど、上から順に実行される構築手順書
+
+
+
+ 4.7
+ Dockerイメージの活用
+
+ build→images→run→ps→push/pullのライフサイクル
+
+
+
+ 4.8
+ アプリケーションセキュリティ
+
+ シークレットはコードに書かない/転送時と保存時の両方を暗号化する
+
+
+
+ 4.9
+ ネットワーク境界の要素
+
+ DNS→ファイアウォール→ロードバランサー→リバースプロキシの順で経由
+
+
+
+ 4.10
+ OWASPトップの脅威
+
+ XSS・SQLi・CSRFはいずれも「入力・リクエストを検証しない」ことが原因
+
+
+
+ 4.11
+ Bashコマンド
+ ファイル管理/ディレクトリ操作/環境変数の3分類
+
+
+ 4.12
+ DevOpsの原則
+ 文化・自動化・計測・共有、そして途切れないリリースループ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+